From f0224272fb94ba4e6829ee80bcd21998d4482892 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 25 Jan 2023 11:39:27 +0800 Subject: [PATCH 01/76] Sanitized yield macro usage. --- include/boost/requests/detail/define.hpp | 8 ++-- include/boost/requests/detail/undefine.hpp | 4 +- include/boost/requests/download.hpp | 18 ++++--- include/boost/requests/impl/connection.ipp | 48 +++++++++---------- .../boost/requests/impl/connection_pool.hpp | 2 - .../boost/requests/impl/connection_pool.ipp | 22 ++++----- include/boost/requests/impl/request.hpp | 8 ++-- include/boost/requests/impl/session.ipp | 18 ++++--- include/boost/requests/impl/stream.hpp | 8 ++-- include/boost/requests/impl/stream.ipp | 20 ++++---- include/boost/requests/json.hpp | 24 +++++----- test/detail/faux_coroutine.cpp | 9 ++-- test/detail/mutex.cpp | 7 ++- 13 files changed, 88 insertions(+), 108 deletions(-) diff --git a/include/boost/requests/detail/define.hpp b/include/boost/requests/detail/define.hpp index bf39416..a240f01 100644 --- a/include/boost/requests/detail/define.hpp +++ b/include/boost/requests/detail/define.hpp @@ -5,15 +5,15 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#define await(coro) \ +#define BOOST_REQUESTS_AWAIT(coro) \ coro = {}; \ while (!coro.is_complete()) \ - yield + BOOST_ASIO_CORO_YIELD -#define await_lock(Mutex, Lock) \ +#define BOOST_REQUESTS_AWAIT_LOCK(Mutex, Lock) \ if (!Mutex.try_lock()) \ { \ - yield Mutex.async_lock(std::move(self)); \ + BOOST_ASIO_CORO_YIELD Mutex.async_lock(std::move(self)); \ if (ec) \ break; \ } \ diff --git a/include/boost/requests/detail/undefine.hpp b/include/boost/requests/detail/undefine.hpp index ecb70c6..5dde55a 100644 --- a/include/boost/requests/detail/undefine.hpp +++ b/include/boost/requests/detail/undefine.hpp @@ -5,5 +5,5 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#undef await -#undef await_lock \ No newline at end of file +#undef BOOST_REQUESTS_AWAIT +#undef BOOST_REQUESTS_AWAIT_LOCK \ No newline at end of file diff --git a/include/boost/requests/download.hpp b/include/boost/requests/download.hpp index 9a829c6..eb26e5c 100644 --- a/include/boost/requests/download.hpp +++ b/include/boost/requests/download.hpp @@ -21,7 +21,6 @@ #include #endif -#include namespace boost { @@ -93,7 +92,7 @@ struct async_write_to_file_op : asio::coroutine std::size_t resume(requests::detail::faux_token_t self, system::error_code & ec, std::size_t n = 0u) { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { f.open(file.string().c_str(), asio::file_base::write_only | asio::file_base::create, ec); if (ec) @@ -102,7 +101,7 @@ struct async_write_to_file_op : asio::coroutine while (!str.done() && !ec) { // KDM: this could be in parallel to write using parallel_group. - yield { + BOOST_ASIO_CORO_YIELD { auto b = asio::buffer(buffer); str.async_read_some(b, std::move(self)); } @@ -111,7 +110,7 @@ struct async_write_to_file_op : asio::coroutine return written; ec_read = exchange(ec, {}); - yield asio::async_write(f, asio::buffer(buffer, n), std::move(self)); + BOOST_ASIO_CORO_YIELD asio::async_write(f, asio::buffer(buffer, n), std::move(self)); written += n; if (ec_read && !ec) @@ -183,7 +182,7 @@ struct async_write_to_file_op : asio::coroutine std::size_t resume(requests::detail::faux_token_t self, system::error_code & ec, std::size_t n = 0u) { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { f.open(file.string().c_str(), beast::file_mode::write_new, ec); if (ec) @@ -191,7 +190,7 @@ struct async_write_to_file_op : asio::coroutine while (!str.done() && !ec) { - yield str.async_read_some(asio::buffer(buffer), std::move(self)); + BOOST_ASIO_CORO_YIELD str.async_read_some(asio::buffer(buffer), std::move(self)); if (n == 0 && ec) return written; @@ -331,9 +330,9 @@ struct async_download_op : asio::coroutine system::error_code & ec, optional s = none) { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { - yield conn.async_ropen(http::verb::get, target, empty{}, std::move(req), std::move(self)); + BOOST_ASIO_CORO_YIELD conn.async_ropen(http::verb::get, target, empty{}, std::move(req), std::move(self)); if (ec) { rb.history = std::move(*s).history(); @@ -348,7 +347,7 @@ struct async_download_op : asio::coroutine if (!ec) { - yield async_write_to_file(*str_, rb.download_path, + BOOST_ASIO_CORO_YIELD async_write_to_file(*str_, rb.download_path, asio::deferred([](system::error_code ec, std::size_t){return asio::deferred.values(ec);})) (std::move(self)); } @@ -414,6 +413,5 @@ async_download(urls::url_view path, } } -#include #endif // BOOST_REQUESTS_DOWNLOAD_HPP diff --git a/include/boost/requests/impl/connection.ipp b/include/boost/requests/impl/connection.ipp index 993048b..12cdfc8 100644 --- a/include/boost/requests/impl/connection.ipp +++ b/include/boost/requests/impl/connection.ipp @@ -12,7 +12,6 @@ #include #include #include -#include namespace boost { @@ -320,11 +319,11 @@ auto connection::async_ropen_op::resume( requests::detail::faux_token_t self, system::error_code & ec, std::size_t res_) -> stream { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { if (ec_) { - yield asio::post(this_->get_executor(), std::move(self)); + BOOST_ASIO_CORO_YIELD asio::post(this_->get_executor(), std::move(self)); ec = ec_; } @@ -347,16 +346,16 @@ auto connection::async_ropen_op::resume( while (!ec) { - await_lock(this_->write_mtx_, lock); + BOOST_REQUESTS_AWAIT_LOCK(this_->write_mtx_, lock); // disconnect first if (!this_->is_open() && this_->keep_alive_set_.timeout < std::chrono::system_clock::now()) { - await_lock(this_->read_mtx_, alock); + BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, alock); // if the close goes wrong - so what, unless it's still open if (!ec && this_->use_ssl_) { - yield this_->next_layer_.async_shutdown(std::move(self)); + BOOST_ASIO_CORO_YIELD this_->next_layer_.async_shutdown(std::move(self)); } if (!ec) this_->next_layer_.next_layer().close(ec); @@ -368,12 +367,12 @@ auto connection::async_ropen_op::resume( retry: if (!alock) { - await_lock(this_->read_mtx_, alock); + BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, alock); } - yield this_->next_layer_.next_layer().async_connect(this_->endpoint_, std::move(self)); + BOOST_ASIO_CORO_YIELD this_->next_layer_.next_layer().async_connect(this_->endpoint_, std::move(self)); if (!ec && this_->use_ssl_) { - yield this_->next_layer_.async_handshake(asio::ssl::stream_base::client, std::move(self)); + BOOST_ASIO_CORO_YIELD this_->next_layer_.async_handshake(asio::ssl::stream_base::client, std::move(self)); } if (ec) break; @@ -382,11 +381,11 @@ auto connection::async_ropen_op::resume( alock.reset(); if (this_->use_ssl_) { - yield async_write_request(this_->next_layer_, method, path, headers, src, std::move(self)); + BOOST_ASIO_CORO_YIELD async_write_request(this_->next_layer_, method, path, headers, src, std::move(self)); } else { - yield async_write_request(this_->next_layer_.next_layer(), method, path, headers, src, std::move(self)); + BOOST_ASIO_CORO_YIELD async_write_request(this_->next_layer_.next_layer(), method, path, headers, src, std::move(self)); } if (ec == asio::error::broken_pipe || ec == asio::error::connection_reset) @@ -395,7 +394,7 @@ auto connection::async_ropen_op::resume( break; // release after acquire! - await_lock(this_->read_mtx_, lock); + BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, lock); // END OF write impl str.emplace(this_->get_executor(), this_); // , req.get_allocator().resource() @@ -404,11 +403,11 @@ auto connection::async_ropen_op::resume( if (this_->use_ssl_) { - yield beast::http::async_read_header(this_->next_layer_, this_->buffer_, *str->parser_, std::move(self)); + BOOST_ASIO_CORO_YIELD beast::http::async_read_header(this_->next_layer_, this_->buffer_, *str->parser_, std::move(self)); } else { - yield beast::http::async_read_header(this_->next_layer_.next_layer(), this_->buffer_, *str->parser_, std::move(self)); + BOOST_ASIO_CORO_YIELD beast::http::async_read_header(this_->next_layer_.next_layer(), this_->buffer_, *str->parser_, std::move(self)); } if (ec) @@ -446,7 +445,7 @@ auto connection::async_ropen_op::resume( if (method != http::verb::head) { - yield str->async_read(buf, std::move(self)); + BOOST_ASIO_CORO_YIELD str->async_read(buf, std::move(self)); } if (ec) break; @@ -577,14 +576,14 @@ void connection::do_async_close_(detail::faux_token_t void connection::async_connect_op::resume(requests::detail::faux_token_t self, system::error_code & ec) { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { - await_lock(this_->write_mtx_, write_lock); - await_lock(this_->read_mtx_, read_lock); - yield this_->next_layer_.next_layer().async_connect(this_->endpoint_ = ep, std::move(self)); + BOOST_REQUESTS_AWAIT_LOCK(this_->write_mtx_, write_lock); + BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, read_lock); + BOOST_ASIO_CORO_YIELD this_->next_layer_.next_layer().async_connect(this_->endpoint_ = ep, std::move(self)); if (!ec && this_->use_ssl_) { - yield this_->next_layer_.async_handshake(asio::ssl::stream_base::client, std::move(self)); + BOOST_ASIO_CORO_YIELD this_->next_layer_.async_handshake(asio::ssl::stream_base::client, std::move(self)); } } } @@ -592,13 +591,13 @@ void connection::async_connect_op::resume(requests::detail::faux_token_t self, system::error_code & ec) { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { - await_lock(this_->write_mtx_, write_lock); - await_lock(this_->read_mtx_, read_lock); + BOOST_REQUESTS_AWAIT_LOCK(this_->write_mtx_, write_lock); + BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, read_lock); if (!ec && this_->use_ssl_) { - yield this_->next_layer_.async_shutdown(std::move(self)); + BOOST_ASIO_CORO_YIELD this_->next_layer_.async_shutdown(std::move(self)); } if (this_->next_layer_.next_layer().is_open()) this_->next_layer_.next_layer().close(ec); @@ -623,6 +622,5 @@ void connection::do_close_(system::error_code & ec) } #include -#include #endif // BOOST_REQUESTS_REQUESTS_CONNECTION_IPP diff --git a/include/boost/requests/impl/connection_pool.hpp b/include/boost/requests/impl/connection_pool.hpp index 9658bc1..48f57a0 100644 --- a/include/boost/requests/impl/connection_pool.hpp +++ b/include/boost/requests/impl/connection_pool.hpp @@ -9,7 +9,6 @@ #define BOOST_REQUESTS_IMPL_CONNECTION_POOL_HPP #include -#include namespace boost { namespace requests { @@ -208,7 +207,6 @@ connection_pool::async_ropen(beast::http::verb method, } } -#include #if defined(BOOST_REQUESTS_HEADER_ONLY) #include diff --git a/include/boost/requests/impl/connection_pool.ipp b/include/boost/requests/impl/connection_pool.ipp index 017d6c2..7de0ac8 100644 --- a/include/boost/requests/impl/connection_pool.ipp +++ b/include/boost/requests/impl/connection_pool.ipp @@ -9,7 +9,6 @@ #define BOOST_REQUESTS_IMPL_CONNECTION_POOL_IPP #include -#include namespace boost { namespace requests { @@ -89,7 +88,7 @@ void connection_pool::lookup(urls::url_view sv, system::error_code & ec) void connection_pool::async_lookup_op::resume(requests::detail::faux_token_t self, system::error_code & ec, typename asio::ip::tcp::resolver::results_type eps) { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { if (sv.has_scheme()) scheme = sv.scheme(); @@ -100,7 +99,7 @@ void connection_pool::async_lookup_op::resume(requests::detail::faux_token_tmutex_.try_lock()) { - yield this_->mutex_.async_lock(std::move(self)); + BOOST_ASIO_CORO_YIELD this_->mutex_.async_lock(std::move(self)); } if (ec) return; @@ -115,7 +114,7 @@ void connection_pool::async_lookup_op::resume(requests::detail::faux_token_tasync_resolve( + BOOST_ASIO_CORO_YIELD resolver->async_resolve( asio::string_view(sv.encoded_host_name().data(), sv.encoded_host_name().size()), asio::string_view(service.data(), service.size()), std::move(self)); @@ -126,7 +125,7 @@ void connection_pool::async_lookup_op::resume(requests::detail::faux_token_tmutex_.try_lock()) { - yield this_->mutex_.async_lock(std::move(self)); + BOOST_ASIO_CORO_YIELD this_->mutex_.async_lock(std::move(self)); } if (ec) return; @@ -235,11 +234,11 @@ auto connection_pool::async_get_connection_op::resume( requests::detail::faux_token_t self, system::error_code & ec) -> std::shared_ptr { - reenter (this) + BOOST_ASIO_CORO_REENTER (this) { if (!this_->mutex_.try_lock()) { - yield this_->mutex_.async_lock(std::move(self)); + BOOST_ASIO_CORO_YIELD this_->mutex_.async_lock(std::move(self)); } if (ec) return nullptr; @@ -276,7 +275,7 @@ auto connection_pool::async_get_connection_op::resume( nconn = this_->use_ssl_ ? std::make_shared(get_executor(), this_->context_) : std::make_shared(get_executor()); nconn->set_host(this_->host_); - yield nconn->async_connect(ep, std::move(self)); // don't unlock here. + BOOST_ASIO_CORO_YIELD nconn->async_connect(ep, std::move(self)); // don't unlock here. if (ec) return nullptr; @@ -307,16 +306,16 @@ stream connection_pool::async_ropen_op::resume( system::error_code & ec, variant2::variant, stream> res) { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { - yield this_->async_get_connection(std::move(self)); + BOOST_ASIO_CORO_YIELD this_->async_get_connection(std::move(self)); conn = variant2::get<1>(res); if (!ec && conn == nullptr) ec = asio::error::not_found; if (ec) return stream{this_->get_executor(), nullptr}; - yield conn->async_ropen(method, path, headers, src, std::move(opt), jar, std::move(self)); + BOOST_ASIO_CORO_YIELD conn->async_ropen(method, path, headers, src, std::move(opt), jar, std::move(self)); return variant2::get<2>(std::move(res)); } return stream{get_executor(), nullptr}; @@ -325,6 +324,5 @@ stream connection_pool::async_ropen_op::resume( } } -#include #endif // BOOST_REQUESTS_IMPL_CONNECTION_POOL_IPP diff --git a/include/boost/requests/impl/request.hpp b/include/boost/requests/impl/request.hpp index 1a24397..1b5c357 100644 --- a/include/boost/requests/impl/request.hpp +++ b/include/boost/requests/impl/request.hpp @@ -11,7 +11,6 @@ #include #include #include -#include namespace boost { @@ -72,15 +71,15 @@ struct async_request_op : asio::coroutine system::error_code & ec, variant2::variant s) { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { - yield conn.async_ropen(method, target, + BOOST_ASIO_CORO_YIELD conn.async_ropen(method, target, std::forward(request_body), std::move(req), std::move(self)); str_.emplace(std::move(variant2::get<1>(s))); if (!ec) { - yield str_->async_read( rb.buffer, std::move(self)); + BOOST_ASIO_CORO_YIELD str_->async_read( rb.buffer, std::move(self)); } rb.headers = std::move(*str_).headers(); @@ -168,6 +167,5 @@ async_request(beast::http::verb method, } } -#include #endif // BOOST_REQUESTS_REQUEST_HPP diff --git a/include/boost/requests/impl/session.ipp b/include/boost/requests/impl/session.ipp index 97940b8..a4049ea 100644 --- a/include/boost/requests/impl/session.ipp +++ b/include/boost/requests/impl/session.ipp @@ -11,7 +11,6 @@ #include #include -#include namespace boost { namespace requests { @@ -202,9 +201,9 @@ std::shared_ptr session::async_get_pool_op::resume( requests::detail::faux_token_t self, error_code ec) { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { - await_lock(this_->mutex_, lock); + BOOST_REQUESTS_AWAIT_LOCK(this_->mutex_, lock); { auto itr = this_->pools_.find(url); @@ -212,7 +211,7 @@ std::shared_ptr session::async_get_pool_op::resume( return itr->second; } p = std::make_shared(this_->get_executor(), this_->sslctx_); - yield p->async_lookup(url, std::move(self)); + BOOST_ASIO_CORO_YIELD p->async_lookup(url, std::move(self)); if (!ec) { this_->pools_.emplace(url, p); @@ -226,7 +225,7 @@ auto session::async_ropen_op::resume(requests::detail::faux_token_t, stream> s) -> stream { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { headers.set(beast::http::field::host, url.encoded_host_and_port()); headers.set(beast::http::field::user_agent, "Requests-" BOOST_BEAST_VERSION_STRING); @@ -245,10 +244,10 @@ auto session::async_ropen_op::resume(requests::detail::faux_token_tasync_get_pool(url, std::move(self)); + BOOST_ASIO_CORO_YIELD this_->async_get_pool(url, std::move(self)); if (ec) return stream{get_executor(), nullptr}; - yield variant2::get<1>(s)->async_ropen(method, url.encoded_resource(), + BOOST_ASIO_CORO_YIELD variant2::get<1>(s)->async_ropen(method, url.encoded_resource(), headers, src, opts, &this_->jar_, std::move(self)); if (!ec || opts.max_redirects == variant2::get<2>(s).history().size()) @@ -317,10 +316,10 @@ auto session::async_ropen_op::resume(requests::detail::faux_token_t(s)).history().begin()), std::make_move_iterator(std::move(variant2::get<2>(s)).history().end())); - yield this_->async_get_pool(url, std::move(self)); + BOOST_ASIO_CORO_YIELD this_->async_get_pool(url, std::move(self)); if (ec) return stream{get_executor(), nullptr}; - yield variant2::get<1>(s)->async_ropen(method, url.encoded_resource(), headers, src, opts, &this_->jar_, std::move(self)); + BOOST_ASIO_CORO_YIELD variant2::get<1>(s)->async_ropen(method, url.encoded_resource(), headers, src, opts, &this_->jar_, std::move(self)); } variant2::get<2>(s).prepend_history(std::move(history)); return std::move(variant2::get<2>(s)); @@ -332,7 +331,6 @@ auto session::async_ropen_op::resume(requests::detail::faux_token_t #include #endif // BOOST_REQUESTS_IMPL_SESSION_IPP diff --git a/include/boost/requests/impl/stream.hpp b/include/boost/requests/impl/stream.hpp index db15a13..ed06791 100644 --- a/include/boost/requests/impl/stream.hpp +++ b/include/boost/requests/impl/stream.hpp @@ -14,7 +14,6 @@ #include #include -#include namespace boost { @@ -132,7 +131,7 @@ struct stream::async_read_op : asio::coroutine std::size_t resume(requests::detail::faux_token_t self, system::error_code & ec, std::size_t n = 0u) { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { if (!this_->parser_) BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_connected) @@ -147,7 +146,7 @@ struct stream::async_read_op : asio::coroutine while (!ec && !this_->parser_->is_done()) { - yield this_->async_read_some( + BOOST_ASIO_CORO_YIELD this_->async_read_some( buffer.prepare(this_->parser_->content_length_remaining().value_or(BOOST_REQUESTS_CHUNK_SIZE)), std::move(self)); buffer.commit(n); @@ -165,7 +164,7 @@ struct stream::async_read_op : asio::coroutine if (interpret_keep_alive_response(this_->impl_->keep_alive_set_, this_->parser_->get(), ec)) { std::swap(ec, ec_); - yield this_->impl_->do_async_close_(std::move(self)); + BOOST_ASIO_CORO_YIELD this_->impl_->do_async_close_(std::move(self)); std::swap(ec, ec_); } } @@ -268,7 +267,6 @@ stream::async_dump(CompletionToken && token) } } -#include #if defined(BOOST_REQUESTS_HEADER_ONLY) #include diff --git a/include/boost/requests/impl/stream.ipp b/include/boost/requests/impl/stream.ipp index 65f253a..6374a16 100644 --- a/include/boost/requests/impl/stream.ipp +++ b/include/boost/requests/impl/stream.ipp @@ -6,7 +6,6 @@ #define BOOST_REQUESTS_STREAM_IPP #include -#include namespace boost { @@ -56,7 +55,7 @@ std::size_t stream::async_read_some_op::resume(requests::detail::faux_token_tparser_) BOOST_REQUESTS_ASSIGN_EC(ec_, asio::error::not_connected) @@ -65,21 +64,21 @@ std::size_t stream::async_read_some_op::resume(requests::detail::faux_token_tget_executor(), std::move(self)); + BOOST_ASIO_CORO_YIELD asio::post(this_->get_executor(), std::move(self)); ec = ec_; return 0u; } if (buffer.size() == 0u) { - yield asio::post(this_->get_executor(), asio::append(std::move(self), ec_)); + BOOST_ASIO_CORO_YIELD asio::post(this_->get_executor(), asio::append(std::move(self), ec_)); return 0u; } this_->parser_->get().body().data = buffer.data(); this_->parser_->get().body().size = buffer.size(); - yield this_->impl_->do_async_read_some_(*this_->parser_, std::move(self)); + BOOST_ASIO_CORO_YIELD this_->impl_->do_async_read_some_(*this_->parser_, std::move(self)); if (!this_->parser_->is_done()) { this_->parser_->get().body().more = true; @@ -92,7 +91,7 @@ std::size_t stream::async_read_some_op::resume(requests::detail::faux_token_timpl_->keep_alive_set_, this_->parser_->get(), ec)) { ec_ = ec ; - yield this_->impl_->do_async_close_(std::move(self)); + BOOST_ASIO_CORO_YIELD this_->impl_->do_async_close_(std::move(self)); ec = ec_; } } @@ -105,11 +104,11 @@ std::size_t stream::async_read_some_op::resume(requests::detail::faux_token_t self, system::error_code ec, std::size_t n) { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { if (!this_->parser_ || !this_->parser_->is_done()) { - yield asio::post(this_->get_executor(), std::move(self)); + BOOST_ASIO_CORO_YIELD asio::post(this_->get_executor(), std::move(self)); ec = ec_; return; } @@ -119,13 +118,13 @@ void stream::async_dump_op::resume(requests::detail::faux_token_tparser_->get().body().data = buffer; this_->parser_->get().body().size = BOOST_REQUESTS_CHUNK_SIZE; - yield this_->impl_->do_async_read_some_(*this_->parser_, std::move(self)); + BOOST_ASIO_CORO_YIELD this_->impl_->do_async_read_some_(*this_->parser_, std::move(self)); } if (interpret_keep_alive_response(this_->impl_->keep_alive_set_, this_->parser_->get(), ec)) { ec_ = ec ; - yield this_->impl_->do_async_close_(std::move(self)); + BOOST_ASIO_CORO_YIELD this_->impl_->do_async_close_(std::move(self)); ec = ec_; } } @@ -135,6 +134,5 @@ void stream::async_dump_op::resume(requests::detail::faux_token_t #endif //BOOST_REQUESTS_STREAM_IPP diff --git a/include/boost/requests/json.hpp b/include/boost/requests/json.hpp index 2e2d0fa..eaafa4e 100644 --- a/include/boost/requests/json.hpp +++ b/include/boost/requests/json.hpp @@ -29,7 +29,6 @@ #include #include -#include namespace boost @@ -657,11 +656,11 @@ struct async_read_json_op : asio::coroutine value resume(requests::detail::faux_token_t self, system::error_code & ec, std::size_t n = 0u) { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { while (!sp.done() && !str.done()) { - yield str.async_read_some(asio::buffer(buffer), std::move(self)); + BOOST_ASIO_CORO_YIELD str.async_read_some(asio::buffer(buffer), std::move(self)); if (ec) return nullptr; sp.write_some(buffer, n, ec); @@ -696,16 +695,16 @@ struct async_read_optional_json_op : asio::coroutine optional resume(requests::detail::faux_token_t self, system::error_code & ec, std::size_t n = 0u) { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { - yield str.async_read_some(asio::buffer(buffer), std::move(self)); + BOOST_ASIO_CORO_YIELD str.async_read_some(asio::buffer(buffer), std::move(self)); if (n == 0 && str.done()) return boost::none; sp.write_some(buffer, n, ec); while (!sp.done() && !str.done()) { - yield str.async_read_some(asio::buffer(buffer), std::move(self)); + BOOST_ASIO_CORO_YIELD str.async_read_some(asio::buffer(buffer), std::move(self)); if (ec) return boost::none; sp.write_some(buffer, n, ec); @@ -797,9 +796,9 @@ struct async_request_json_op : asio::coroutine system::error_code & ec, variant2::variant s) { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { - yield conn.async_ropen(method, target, + BOOST_ASIO_CORO_YIELD conn.async_ropen(method, target, value_from(std::forward(request_body), std::is_same>{}), std::move(req), std::move(self)); @@ -808,7 +807,7 @@ struct async_request_json_op : asio::coroutine str_.emplace(std::move(variant2::get<1>(s))); rb.headers = std::move(*str_).headers(); rb.history = std::move(*str_).history(); - yield async_read_json(*str_, ptr, std::move(self)); + BOOST_ASIO_CORO_YIELD async_read_json(*str_, ptr, std::move(self)); } else { @@ -860,9 +859,9 @@ struct async_request_optional_json_op : asio::coroutine system::error_code & ec, variant2::variant> s) { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { - yield conn.async_ropen(method, target, + BOOST_ASIO_CORO_YIELD conn.async_ropen(method, target, ::boost::json::value_from(std::forward(request_body), ptr), std::move(req), std::move(self)); if (!ec) @@ -870,7 +869,7 @@ struct async_request_optional_json_op : asio::coroutine str_.emplace(std::move(variant2::get<1>(s))); rb.headers = std::move(*str_).headers(); rb.history = std::move(*str_).history(); - yield async_read_json(*str_, ptr, std::move(self)); + BOOST_ASIO_CORO_YIELD async_read_json(*str_, ptr, std::move(self)); } else { @@ -1154,7 +1153,6 @@ async_options(urls::url_view target, } } -#include #endif // BOOST_REQUESTS_JSON_HPP diff --git a/test/detail/faux_coroutine.cpp b/test/detail/faux_coroutine.cpp index 46b13f7..081981a 100644 --- a/test/detail/faux_coroutine.cpp +++ b/test/detail/faux_coroutine.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include "../doctest.h" @@ -47,7 +46,7 @@ struct my_coro : asio::coroutine void resume(requests::detail::faux_token_t token, system::error_code & ec, std::size_t n = {}) { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { if (ec_) { @@ -56,13 +55,13 @@ struct my_coro : asio::coroutine } while (source.is_open()) { - yield source.async_read_some(asio::buffer(buf), std::move(token)); + BOOST_ASIO_CORO_YIELD source.async_read_some(asio::buffer(buf), std::move(token)); if (ec && n == 0) break; - yield sink.async_write_some(asio::buffer(buf, n), std::move(token)); + BOOST_ASIO_CORO_YIELD sink.async_write_some(asio::buffer(buf, n), std::move(token)); if (ec) break; - yield { + BOOST_ASIO_CORO_YIELD { requests::detail::faux_token_t tt = std::move(token); asio::post(sink.get_executor(), std::move(tt)); }; diff --git a/test/detail/mutex.cpp b/test/detail/mutex.cpp index 692ad06..8cb20eb 100644 --- a/test/detail/mutex.cpp +++ b/test/detail/mutex.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include @@ -70,12 +69,12 @@ struct basic_main void resume(requests::detail::faux_token_t self, error_code & ec, lock_guard l = {}) { - reenter(this) + BOOST_ASIO_CORO_REENTER(this) { - yield mtx.async_lock(std::move(self)); + BOOST_ASIO_CORO_YIELD mtx.async_lock(std::move(self)); v.push_back(i); tim = std::make_unique(mtx.get_executor(), std::chrono::milliseconds(10)); - yield tim->async_wait(std::move(self)); + BOOST_ASIO_CORO_YIELD tim->async_wait(std::move(self)); v.push_back(i + 1); } } From 1d1cedb14ad65800b186b551450a98cf3baabe4d Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 25 Jan 2023 11:46:13 +0800 Subject: [PATCH 02/76] request_parameters is a better name. --- doc/requests/reference.adoc | 2 +- ..._settings.adoc => request_parameters.adoc} | 6 +++--- example/basic-auth-future.cpp | 6 +++--- example/basic-auth.cpp | 4 ++-- example/github-issues.hpp | 2 +- example/link.cpp | 10 ++++----- example/methods-future.cpp | 6 +++--- example/methods.cpp | 6 +++--- example/params.cpp | 4 ++-- example/wget.cpp | 4 ++-- example/xml.cpp | 4 ++-- include/boost/requests.hpp | 2 +- include/boost/requests/connection.hpp | 21 +++++++------------ include/boost/requests/connection_pool.hpp | 17 ++++++--------- include/boost/requests/impl/connection.hpp | 13 +++++------- .../boost/requests/impl/connection_pool.hpp | 5 ++--- .../boost/requests/impl/request_settings.ipp | 2 +- include/boost/requests/impl/session.ipp | 5 ++--- include/boost/requests/json.hpp | 4 ++-- ...st_settings.hpp => request_parameters.hpp} | 9 ++++---- include/boost/requests/session.hpp | 2 +- test/json.cpp | 4 ++-- test/request.cpp | 2 +- test/source.cpp | 2 +- 24 files changed, 63 insertions(+), 79 deletions(-) rename doc/requests/reference/{request_settings.adoc => request_parameters.adoc} (92%) rename include/boost/requests/{request_settings.hpp => request_parameters.hpp} (93%) diff --git a/doc/requests/reference.adoc b/doc/requests/reference.adoc index dd7dda5..039adac 100644 --- a/doc/requests/reference.adoc +++ b/doc/requests/reference.adoc @@ -16,7 +16,7 @@ include::reference/mime_types.adoc[] include::reference/public_suffix.adoc[] include::reference/redirect.adoc[] include::reference/request_options.adoc[] -include::reference/request_settings.adoc[] +include::reference/request_parameters.adoc[] include::reference/response.adoc[] include::reference/service.adoc[] include::reference/session.adoc[] diff --git a/doc/requests/reference/request_settings.adoc b/doc/requests/reference/request_parameters.adoc similarity index 92% rename from doc/requests/reference/request_settings.adoc rename to doc/requests/reference/request_parameters.adoc index 2e6327f..2ce15f6 100644 --- a/doc/requests/reference/request_settings.adoc +++ b/doc/requests/reference/request_parameters.adoc @@ -1,5 +1,5 @@ -## request_settings.hpp -[#reference::request_settings] +## request_parameters.hpp +[#reference::request_parameters] [source,cpp] @@ -27,7 +27,7 @@ auto headers(std::initializer_list fields, boost::container::pmr::memory_resource * res = boost::container::pmr::get_default_resource()); // The full request settings used in the connection. -struct request_settings +struct request_parameters { using allocator_type = boost::container::pmr::polymorphic_allocator; allocator_type get_allocator() const {return fields.get_allocator();} diff --git a/example/basic-auth-future.cpp b/example/basic-auth-future.cpp index bfc9dae..707349c 100644 --- a/example/basic-auth-future.cpp +++ b/example/basic-auth-future.cpp @@ -5,11 +5,11 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include +#include #include -#include +#include +#include #include -#include #include diff --git a/example/basic-auth.cpp b/example/basic-auth.cpp index 17014a7..e5f9707 100644 --- a/example/basic-auth.cpp +++ b/example/basic-auth.cpp @@ -5,9 +5,9 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include #include -#include +#include +#include #include #include diff --git a/example/github-issues.hpp b/example/github-issues.hpp index a7dbf30..5fe0b53 100644 --- a/example/github-issues.hpp +++ b/example/github-issues.hpp @@ -751,7 +751,7 @@ struct issue_client boost::container::pmr::unsynchronized_pool_resource memory_; boost::requests::cookie_jar jar_; - boost::requests::request_settings settings_{boost::requests::http::fields {&memory_}, + boost::requests::request_parameters settings_{boost::requests::http::fields {&memory_}, {true, boost::requests::redirect_mode::none, 0}, &jar_}; diff --git a/example/link.cpp b/example/link.cpp index 602a445..b834b7b 100644 --- a/example/link.cpp +++ b/example/link.cpp @@ -4,14 +4,14 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include -#include -#include +#include #include #include -#include -#include +#include +#include +#include #include +#include using namespace boost; diff --git a/example/methods-future.cpp b/example/methods-future.cpp index c08ebdc..aeb70ec 100644 --- a/example/methods-future.cpp +++ b/example/methods-future.cpp @@ -5,11 +5,11 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include +#include #include -#include +#include +#include #include -#include #include diff --git a/example/methods.cpp b/example/methods.cpp index adeaa9d..73fbeaa 100644 --- a/example/methods.cpp +++ b/example/methods.cpp @@ -5,11 +5,11 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include +#include #include -#include +#include +#include #include -#include #include using namespace boost; diff --git a/example/params.cpp b/example/params.cpp index c4f79a1..74db05d 100644 --- a/example/params.cpp +++ b/example/params.cpp @@ -12,9 +12,9 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include #include -#include +#include +#include #include #include diff --git a/example/wget.cpp b/example/wget.cpp index c395a6a..7eff001 100644 --- a/example/wget.cpp +++ b/example/wget.cpp @@ -13,9 +13,9 @@ // #include -#include #include -#include +#include +#include #include #include diff --git a/example/xml.cpp b/example/xml.cpp index af59762..61901f0 100644 --- a/example/xml.cpp +++ b/example/xml.cpp @@ -12,9 +12,9 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include #include -#include +#include +#include #include #include diff --git a/include/boost/requests.hpp b/include/boost/requests.hpp index 960689f..46479a1 100644 --- a/include/boost/requests.hpp +++ b/include/boost/requests.hpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/boost/requests/connection.hpp b/include/boost/requests/connection.hpp index 6d6b0b9..58cca93 100644 --- a/include/boost/requests/connection.hpp +++ b/include/boost/requests/connection.hpp @@ -10,13 +10,13 @@ #include #include #include -#include #include +#include #include #include #include #include -#include +#include #include #include #include @@ -186,20 +186,18 @@ struct connection core::string_view host() const {return host_;} constexpr static redirect_mode supported_redirect_mode() {return redirect_mode::endpoint;} - using request_type = request_settings; + using request_type = request_parameters; template auto ropen(beast::http::verb method, urls::url_view path, - RequestBody && body, - request_settings req, + RequestBody && body, request_parameters req, system::error_code & ec) -> stream; template auto ropen(beast::http::verb method, urls::url_view path, - RequestBody && body, - request_settings req) -> stream; + RequestBody && body, request_parameters req) -> stream; BOOST_REQUESTS_DECL auto ropen(beast::http::verb method, @@ -224,8 +222,7 @@ struct connection void (boost::system::error_code, stream)) async_ropen(beast::http::verb method, urls::url_view path, - RequestBody && body, - request_settings req, + RequestBody && body, request_parameters req, CompletionToken && completion_token); template @@ -314,8 +311,7 @@ struct connection::defaulted : connection BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, typename detail::defaulted_helper::type)) async_ropen(beast::http::verb method, urls::url_view path, - RequestBody && body, - request_settings req, + RequestBody && body, request_parameters req, CompletionToken && completion_token) { return connection::async_ropen(method, path, std::forward(body), std::move(req), @@ -339,8 +335,7 @@ struct connection::defaulted : connection template auto async_ropen(beast::http::verb method, urls::url_view path, - RequestBody && body, - request_settings req) + RequestBody && body, request_parameters req) { return this->async_ropen(method, path, std::forward(body), std::move(req), default_token()); } diff --git a/include/boost/requests/connection_pool.hpp b/include/boost/requests/connection_pool.hpp index 946f932..981fabc 100644 --- a/include/boost/requests/connection_pool.hpp +++ b/include/boost/requests/connection_pool.hpp @@ -112,7 +112,7 @@ struct connection_pool std::size_t limit() const {return limit_;} std::size_t active() const {return conns_.size();} - using request_type = request_settings; + using request_type = request_parameters; BOOST_REQUESTS_DECL std::shared_ptr get_connection(error_code & ec); std::shared_ptr get_connection() @@ -131,8 +131,7 @@ struct connection_pool template auto ropen(beast::http::verb method, urls::url_view path, - RequestBody && body, - request_settings req, + RequestBody && body, request_parameters req, system::error_code & ec) -> stream { auto conn = get_connection(ec); @@ -148,8 +147,7 @@ struct connection_pool template auto ropen(beast::http::verb method, urls::url_view path, - RequestBody && body, - request_settings req) -> stream + RequestBody && body, request_parameters req) -> stream { boost::system::error_code ec; auto res = ropen(method, path, std::forward(body), std::move(req), ec); @@ -197,8 +195,7 @@ struct connection_pool void (boost::system::error_code, stream)) async_ropen(beast::http::verb method, urls::url_view path, - RequestBody && body, - request_settings req, + RequestBody && body, request_parameters req, CompletionToken && completion_token); template @@ -267,8 +264,7 @@ struct connection_pool::defaulted : connection_pool BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, stream)) async_ropen(beast::http::verb method, urls::url_view path, - RequestBody && body, - request_settings req, + RequestBody && body, request_parameters req, CompletionToken && completion_token) { return connection_pool::async_ropen(method, path, std::forward(body), std::move(req), @@ -292,8 +288,7 @@ struct connection_pool::defaulted : connection_pool template auto async_ropen(beast::http::verb method, urls::url_view path, - RequestBody && body, - request_settings req) + RequestBody && body, request_parameters req) { return async_ropen(method, path, std::forward(body), std::move(req), default_token()); } diff --git a/include/boost/requests/impl/connection.hpp b/include/boost/requests/impl/connection.hpp index cd8638f..1c5cca2 100644 --- a/include/boost/requests/impl/connection.hpp +++ b/include/boost/requests/impl/connection.hpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include @@ -123,8 +123,7 @@ BOOST_REQUESTS_DECL bool check_endpoint( template auto connection::ropen(beast::http::verb method, urls::url_view path, - RequestBody && body, - request_settings req) -> stream + RequestBody && body, request_parameters req) -> stream { system::error_code ec; auto res = ropen(method, path, std::forward(body), std::move(req), ec); @@ -137,8 +136,7 @@ template auto connection::ropen( beast::http::verb method, urls::url_view path, - RequestBody && body, - request_settings req, + RequestBody && body, request_parameters req, system::error_code & ec) -> stream { const auto is_secure = use_ssl_; @@ -241,7 +239,7 @@ struct connection::async_ropen_op_body : async_ropen_op_body_base beast::http::verb method, urls::url_view path, RequestBody && body, - request_settings req) + request_parameters req) : async_ropen_op_body_base{std::forward(body), std::move(req.fields)}, async_ropen_op(this_, method, path, async_ropen_op_body_base::headers, this->source_impl, std::move(req.opts), req.jar) @@ -255,8 +253,7 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_c connection::async_ropen( beast::http::verb method, urls::url_view path, - RequestBody && body, - request_settings req, + RequestBody && body, request_parameters req, CompletionToken && completion_token) { using rp = async_ropen_op_body(body)))>>; diff --git a/include/boost/requests/impl/connection_pool.hpp b/include/boost/requests/impl/connection_pool.hpp index 48f57a0..278bf31 100644 --- a/include/boost/requests/impl/connection_pool.hpp +++ b/include/boost/requests/impl/connection_pool.hpp @@ -178,7 +178,7 @@ struct connection_pool::async_ropen_op_body : async_ropen_op_body_base{std::forward(body), std::move(req.fields)}, async_ropen_op{this_, method, path.encoded_resource(), async_ropen_op_body_base::headers, this->source_impl, @@ -193,8 +193,7 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, stream)) connection_pool::async_ropen(beast::http::verb method, urls::url_view path, - RequestBody && body, - request_settings req, + RequestBody && body, request_parameters req, CompletionToken && completion_token) { using rp = async_ropen_op_body(body)))>>; diff --git a/include/boost/requests/impl/request_settings.ipp b/include/boost/requests/impl/request_settings.ipp index a01ad26..0d2e258 100644 --- a/include/boost/requests/impl/request_settings.ipp +++ b/include/boost/requests/impl/request_settings.ipp @@ -11,7 +11,7 @@ #if defined(BOOST_REQUESTS_SOURCE) -#include +#include namespace boost { namespace requests { diff --git a/include/boost/requests/impl/session.ipp b/include/boost/requests/impl/session.ipp index a4049ea..2808c5f 100644 --- a/include/boost/requests/impl/session.ipp +++ b/include/boost/requests/impl/session.ipp @@ -136,9 +136,8 @@ auto session::ropen( return str; } -auto session::make_request_(http::fields fields) -> requests::request_settings -{ - return requests::request_settings{ +auto session::make_request_(http::fields fields) -> requests::request_parameters { + return requests::request_parameters{ std::move(fields), options_, &jar_ diff --git a/include/boost/requests/json.hpp b/include/boost/requests/json.hpp index eaafa4e..0ad69ca 100644 --- a/include/boost/requests/json.hpp +++ b/include/boost/requests/json.hpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include @@ -200,7 +200,7 @@ inline void set_accept_headers(http::fields & hd) hd.set(http::field::accept, "application/json"); } -inline void set_accept_headers(requests::request_settings & hd) +inline void set_accept_headers(requests::request_parameters & hd) { set_accept_headers(hd.fields); } diff --git a/include/boost/requests/request_settings.hpp b/include/boost/requests/request_parameters.hpp similarity index 93% rename from include/boost/requests/request_settings.hpp rename to include/boost/requests/request_parameters.hpp index 5c0bd59..df72423 100644 --- a/include/boost/requests/request_settings.hpp +++ b/include/boost/requests/request_parameters.hpp @@ -2,8 +2,8 @@ // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#ifndef BOOST_REQUESTS_REQUEST_SETTINGS_HPP -#define BOOST_REQUESTS_REQUEST_SETTINGS_HPP +#ifndef BOOST_REQUESTS_REQUEST_PARAMETERS_HPP +#define BOOST_REQUESTS_REQUEST_PARAMETERS_HPP #include #include @@ -69,8 +69,7 @@ inline auto headers(std::initializer_list fields, } -struct request_settings -{ +struct request_parameters { //Allocator using allocator_type = boost::container::pmr::polymorphic_allocator; allocator_type get_allocator() const {return fields.get_allocator();} @@ -83,4 +82,4 @@ struct request_settings } } -#endif // BOOST_REQUESTS_REQUEST_SETTINGS_HPP +#endif // BOOST_REQUESTS_REQUEST_PARAMETERS_HPP diff --git a/include/boost/requests/session.hpp b/include/boost/requests/session.hpp index b048580..bc99186 100644 --- a/include/boost/requests/session.hpp +++ b/include/boost/requests/session.hpp @@ -181,7 +181,7 @@ struct session template struct async_ropen_op_body_base; - BOOST_REQUESTS_DECL auto make_request_(http::fields fields) -> requests::request_settings; + BOOST_REQUESTS_DECL auto make_request_(http::fields fields) -> requests::request_parameters; BOOST_REQUESTS_DECL static urls::url normalize_(urls::url_view in); }; diff --git a/test/json.cpp b/test/json.cpp index fc85a6d..66abc75 100644 --- a/test/json.cpp +++ b/test/json.cpp @@ -172,7 +172,7 @@ void run_json_tests(error_code ec, { requests::json::async_get( hc, urls::url_view("/get"), - requests::request_settings{requests::headers({{"Test-Header", "it works"}}), {false}}, + requests::request_parameters{requests::headers({{"Test-Header", "it works"}}), {false}}, tracker( [url](error_code ec, requests::json::response<> hdr) { @@ -186,7 +186,7 @@ void run_json_tests(error_code ec, requests::json::async_get( hc, urls::url_view("/redirect-to?url=%2Fget"), - requests::request_settings{requests::headers({{"Test-Header", "it works"}}), {false}}, + requests::request_parameters{requests::headers({{"Test-Header", "it works"}}), {false}}, tracker( [url](error_code ec, requests::json::response<> hdr) { diff --git a/test/request.cpp b/test/request.cpp index 0e99a83..930409b 100644 --- a/test/request.cpp +++ b/test/request.cpp @@ -5,7 +5,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include +#include #include "doctest.h" diff --git a/test/source.cpp b/test/source.cpp index b8abef8..23f3c0a 100644 --- a/test/source.cpp +++ b/test/source.cpp @@ -4,8 +4,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include +#include #include -#include #include "doctest.h" #include "string_maker.hpp" From b9a042010c51c430e6be47a32ce4630d83b525e1 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 25 Jan 2023 12:56:12 +0800 Subject: [PATCH 03/76] beast's keep_live is used. --- include/boost/requests/connection.hpp | 9 --- include/boost/requests/impl/connection.ipp | 43 ++---------- .../boost/requests/impl/connection_pool.ipp | 6 +- include/boost/requests/impl/keep_alive.ipp | 67 ------------------- include/boost/requests/impl/stream.hpp | 8 +-- include/boost/requests/impl/stream.ipp | 7 +- include/boost/requests/keep_alive.hpp | 31 --------- 7 files changed, 13 insertions(+), 158 deletions(-) delete mode 100644 include/boost/requests/impl/keep_alive.ipp delete mode 100644 include/boost/requests/keep_alive.hpp diff --git a/include/boost/requests/connection.hpp b/include/boost/requests/connection.hpp index 58cca93..c55c31b 100644 --- a/include/boost/requests/connection.hpp +++ b/include/boost/requests/connection.hpp @@ -80,7 +80,6 @@ struct connection , host_(std::move(lhs.host_)) , buffer_(std::move(lhs.buffer_)) , ongoing_requests_(std::move(lhs.ongoing_requests_.load())) - , keep_alive_set_(std::move(lhs.keep_alive_set_)) , endpoint_(std::move(lhs.endpoint_)) {} @@ -93,7 +92,6 @@ struct connection host_ = std::move(lhs.host_); buffer_ = std::move(lhs.buffer_); ongoing_requests_ = std::move(lhs.ongoing_requests_.load()); - keep_alive_set_ = std::move(lhs.keep_alive_set_); endpoint_ = std::move(lhs.endpoint_); return *this; } @@ -160,12 +158,6 @@ struct connection // Endpoint endpoint_type endpoint() const {return endpoint_;} - // Timeout of the connection-alive - std::chrono::system_clock::time_point timeout() const - { - return keep_alive_set_.timeout; - } - std::size_t working_requests() const { return ongoing_requests_; } // Reserve memory for the internal buffer. @@ -247,7 +239,6 @@ struct connection std::string host_; beast::flat_buffer buffer_; std::atomic ongoing_requests_{0u}; - keep_alive keep_alive_set_; endpoint_type endpoint_; struct async_close_op; diff --git a/include/boost/requests/impl/connection.ipp b/include/boost/requests/impl/connection.ipp index 12cdfc8..017e936 100644 --- a/include/boost/requests/impl/connection.ipp +++ b/include/boost/requests/impl/connection.ipp @@ -142,32 +142,14 @@ auto connection::ropen(beast::http::verb method, lock_type wlock{write_mtx_, std::adopt_lock}; boost::optional alock; - // disconnect first - if (!is_open() && keep_alive_set_.timeout < std::chrono::system_clock::now()) { + if (!is_open()) + { + retry: read_mtx_.lock(ec); if (ec) return stream{get_executor(), nullptr}; alock.emplace(read_mtx_, std::adopt_lock); - // if the close goes wrong - so what, unless it's still open - if (use_ssl_) - next_layer_.shutdown(ec); - if (!ec) - next_layer_.next_layer().close(ec); - ec.clear(); - } - - if (!is_open()) - { - retry: - if (!alock) - { - read_mtx_.lock(ec); - if (ec) - return stream{get_executor(), nullptr}; - - alock.emplace(read_mtx_, std::adopt_lock); - } next_layer_.next_layer().connect(endpoint_, ec); if (use_ssl_ && !ec) @@ -348,27 +330,10 @@ auto connection::async_ropen_op::resume( { BOOST_REQUESTS_AWAIT_LOCK(this_->write_mtx_, lock); - // disconnect first - if (!this_->is_open() && this_->keep_alive_set_.timeout < std::chrono::system_clock::now()) - { - BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, alock); - // if the close goes wrong - so what, unless it's still open - if (!ec && this_->use_ssl_) - { - BOOST_ASIO_CORO_YIELD this_->next_layer_.async_shutdown(std::move(self)); - } - if (!ec) - this_->next_layer_.next_layer().close(ec); - ec.clear(); - } - if (!this_->is_open()) { retry: - if (!alock) - { - BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, alock); - } + BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, alock); BOOST_ASIO_CORO_YIELD this_->next_layer_.next_layer().async_connect(this_->endpoint_, std::move(self)); if (!ec && this_->use_ssl_) { diff --git a/include/boost/requests/impl/connection_pool.ipp b/include/boost/requests/impl/connection_pool.ipp index 7de0ac8..67293bb 100644 --- a/include/boost/requests/impl/connection_pool.ipp +++ b/include/boost/requests/impl/connection_pool.ipp @@ -172,7 +172,7 @@ auto connection_pool::get_connection(error_code & ec) -> std::shared_ptr> & conn) { - return conn.second->working_requests() == 0u; + return (conn.second->working_requests() == 0u) && conn.second->is_open(); }); if (itr != conns_.end()) @@ -218,7 +218,7 @@ auto connection_pool::get_connection(error_code & ec) -> std::shared_ptr> & rhs) { return (lhs.second->working_requests() + (lhs.second->is_open() ? 0 : 1)) - < (rhs.second->working_requests() + (rhs.second->is_open() ? 0 : 1)); + < (rhs.second->working_requests() + (rhs.second->is_open() ? 0 : 1)); }); if (itr == conns_.end()) { @@ -249,7 +249,7 @@ auto connection_pool::async_get_connection_op::resume( itr = std::find_if(this_->conns_.begin(), this_->conns_.end(), [](const std::pair> & conn) { - return conn.second->working_requests() == 0u; + return (conn.second->working_requests() == 0u) && conn.second->is_open();; }); if (itr != this_->conns_.end()) diff --git a/include/boost/requests/impl/keep_alive.ipp b/include/boost/requests/impl/keep_alive.ipp deleted file mode 100644 index d825584..0000000 --- a/include/boost/requests/impl/keep_alive.ipp +++ /dev/null @@ -1,67 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BOOST_REQUESTS_KEEP_ALIVE_IPP -#define BOOST_REQUESTS_KEEP_ALIVE_IPP - -#include -#include - -namespace boost -{ -namespace requests -{ - -bool interpret_keep_alive_response(keep_alive & ka, - http::fields & res, - system::error_code & ec) -{ - bool should_close = false; - - const auto now = std::chrono::system_clock::now(); - const auto conn_itr = res.find(beast::http::field::connection); - if (ec) - { - ka.timeout = std::chrono::system_clock::time_point::min(); - ka.max = 0ull; - should_close = true; - } - - if (conn_itr == res.end()) - return should_close; - - if (urls::grammar::ci_is_equal(conn_itr->value(), "close")) - should_close = true; - else if (!urls::grammar::ci_is_equal(conn_itr->value(), "keep-alive")) - BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::invalid_argument); - - if (!should_close) - { - const auto kl_itr = res.find(beast::http::field::keep_alive); - if (kl_itr == res.end()) - ka = keep_alive{}; // set to max - else - { - - auto rr = parse_keep_alive_field(kl_itr->value(), now); - if (rr.has_error()) - ec = rr.error(); - else - ka = *rr; - - if (ka.timeout < now) - should_close = true; - } - } - - return should_close; -} - -} -} - -#endif // BOOST_REQUESTS_KEEP_ALIVE_IPP diff --git a/include/boost/requests/impl/stream.hpp b/include/boost/requests/impl/stream.hpp index ed06791..d4396e2 100644 --- a/include/boost/requests/impl/stream.hpp +++ b/include/boost/requests/impl/stream.hpp @@ -54,8 +54,7 @@ std::size_t stream::read_some(const MutableBuffer & buffer, system::error_code & else { parser_->get().body().more = false; - bool should_close = interpret_keep_alive_response(impl_->keep_alive_set_, parser_->get(), ec); - if (should_close) + if (!parser_->get().keep_alive()) { boost::system::error_code ec_; impl_->do_close_(ec_); @@ -94,8 +93,7 @@ std::size_t stream::read(DynamicBuffer & buffer, system::error_code & ec) else { parser_->get().body().more = false; - bool should_close = interpret_keep_alive_response(impl_->keep_alive_set_, parser_->get(), ec); - if (should_close) + if (!parser_->get().keep_alive()) { boost::system::error_code ec_; impl_->do_close_(ec_); @@ -161,7 +159,7 @@ struct stream::async_read_op : asio::coroutine else { this_->parser_->get().body().more = false; - if (interpret_keep_alive_response(this_->impl_->keep_alive_set_, this_->parser_->get(), ec)) + if (this_->parser_->get().keep_alive()) { std::swap(ec, ec_); BOOST_ASIO_CORO_YIELD this_->impl_->do_async_close_(std::move(self)); diff --git a/include/boost/requests/impl/stream.ipp b/include/boost/requests/impl/stream.ipp index 6374a16..7d344d2 100644 --- a/include/boost/requests/impl/stream.ipp +++ b/include/boost/requests/impl/stream.ipp @@ -34,8 +34,7 @@ void stream::dump(system::error_code & ec) parser_->get().body().more = false; } - bool should_close = interpret_keep_alive_response(impl_->keep_alive_set_, parser_->get(), ec); - if (should_close) + if (!parser_->get().keep_alive()) { boost::system::error_code ec_; impl_->do_close_(ec_); @@ -88,7 +87,7 @@ std::size_t stream::async_read_some_op::resume(requests::detail::faux_token_tparser_->get().body().more = false; - if (interpret_keep_alive_response(this_->impl_->keep_alive_set_, this_->parser_->get(), ec)) + if (!this_->parser_->get().keep_alive()) { ec_ = ec ; BOOST_ASIO_CORO_YIELD this_->impl_->do_async_close_(std::move(self)); @@ -121,7 +120,7 @@ void stream::async_dump_op::resume(requests::detail::faux_token_timpl_->do_async_read_some_(*this_->parser_, std::move(self)); } - if (interpret_keep_alive_response(this_->impl_->keep_alive_set_, this_->parser_->get(), ec)) + if (!this_->parser_->get().keep_alive()) { ec_ = ec ; BOOST_ASIO_CORO_YIELD this_->impl_->do_async_close_(std::move(self)); diff --git a/include/boost/requests/keep_alive.hpp b/include/boost/requests/keep_alive.hpp deleted file mode 100644 index 2dceda0..0000000 --- a/include/boost/requests/keep_alive.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BOOST_REQUESTS_KEEP_ALIVE_HPP -#define BOOST_REQUESTS_KEEP_ALIVE_HPP - -#include -#include -#include - -namespace boost -{ -namespace requests -{ - -BOOST_REQUESTS_DECL bool interpret_keep_alive_response(keep_alive & ka, - http::fields & res, - system::error_code & ec); - -} -} - -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - -#endif // BOOST_REQUESTS_KEEP_ALIVE_HPP From 2fbcb501d1e1d52f2009f550a7519889aefa6170 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Fri, 27 Jan 2023 22:16:19 +0800 Subject: [PATCH 04/76] CI fixes. --- CMakeLists.txt | 3 +++ example/CMakeLists.txt | 6 +++--- include/boost/requests.hpp | 1 - include/boost/requests/detail/impl/mutex.ipp | 1 + include/boost/requests/grammar/fixed_token_rule.hpp | 2 +- include/boost/requests/impl/public_suffix.ipp | 1 - include/boost/requests/impl/session.hpp | 1 - include/boost/requests/impl/session.ipp | 2 -- include/boost/requests/impl/stream.hpp | 1 - include/boost/requests/src.hpp | 1 - test/CMakeLists.txt | 7 +------ test/cookie_grammar.cpp | 2 -- test/main_test.cpp | 2 -- 13 files changed, 9 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cfc8140..f356c8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,9 @@ if(BOOST_REQUESTS_INSTALL AND NOT BOOST_SUPERPROJECT_VERSION) ) endif() +if (OPENSSL_FOUND) + include_directories (${OPENSSL_INCLUDE_DIR}) +endif() if(BOOST_REQUESTS_BUILD_TESTS) add_subdirectory(test) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index cef3911..bbc4007 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -16,8 +16,8 @@ foreach(SRC ${ALL_EXAMPLES}) target_link_libraries(boost_requests_example_${NAME} PUBLIC OpenSSL::Crypto OpenSSL::SSL - Boost::requests Boost::json - Boost::filesystem Boost::system - Boost::url pugixml::static) + Boost::requests + Boost::filesystem + pugixml::static) target_compile_definitions(boost_requests_example_${NAME} PUBLIC BOOST_REQUESTS_SEPARATE_COMPILATION=1 BOOST_FILESYSTEM_NO_DEPRECATED=1) endforeach() diff --git a/include/boost/requests.hpp b/include/boost/requests.hpp index 46479a1..8708a0d 100644 --- a/include/boost/requests.hpp +++ b/include/boost/requests.hpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/include/boost/requests/detail/impl/mutex.ipp b/include/boost/requests/detail/impl/mutex.ipp index b3bec25..174ecc4 100644 --- a/include/boost/requests/detail/impl/mutex.ipp +++ b/include/boost/requests/detail/impl/mutex.ipp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include diff --git a/include/boost/requests/grammar/fixed_token_rule.hpp b/include/boost/requests/grammar/fixed_token_rule.hpp index 7b97b98..d6ba9c0 100644 --- a/include/boost/requests/grammar/fixed_token_rule.hpp +++ b/include/boost/requests/grammar/fixed_token_rule.hpp @@ -113,7 +113,7 @@ struct fixed_token_rule_2_t system::result { const auto it0 = it; - if(std::distance(it, end) < MinSize) + if (std::distance(it, end) < static_cast(MinSize)) BOOST_REQUESTS_RETURN_EC( urls::grammar::error::need_more); diff --git a/include/boost/requests/impl/public_suffix.ipp b/include/boost/requests/impl/public_suffix.ipp index 7bf481a..62d6a5d 100644 --- a/include/boost/requests/impl/public_suffix.ipp +++ b/include/boost/requests/impl/public_suffix.ipp @@ -18,7 +18,6 @@ namespace requests { const public_suffix_list & default_public_suffix_list() { - system::error_code ec; const static public_suffix_list def_list = { #include "public_suffix.inc" }; diff --git a/include/boost/requests/impl/session.hpp b/include/boost/requests/impl/session.hpp index 2ccd216..bf0e065 100644 --- a/include/boost/requests/impl/session.hpp +++ b/include/boost/requests/impl/session.hpp @@ -61,7 +61,6 @@ auto session::ropen( if (!is_secure && options_.enforce_tls) { - static constexpr auto loc((BOOST_CURRENT_LOCATION)); BOOST_REQUESTS_ASSIGN_EC(ec, error::insecure); return stream{get_executor(), nullptr}; } diff --git a/include/boost/requests/impl/session.ipp b/include/boost/requests/impl/session.ipp index 2808c5f..3a214e5 100644 --- a/include/boost/requests/impl/session.ipp +++ b/include/boost/requests/impl/session.ipp @@ -45,7 +45,6 @@ auto session::ropen( if (!is_secure && opts.enforce_tls) { - static constexpr auto loc((BOOST_CURRENT_LOCATION)); BOOST_REQUESTS_ASSIGN_EC(ec, error::insecure); return stream{get_executor(), nullptr}; } @@ -231,7 +230,6 @@ auto session::async_ropen_op::resume(requests::detail::faux_token_toptions_.enforce_tls) { - static constexpr auto loc((BOOST_CURRENT_LOCATION)); BOOST_REQUESTS_ASSIGN_EC(ec, error::insecure); return stream{get_executor(), nullptr}; } diff --git a/include/boost/requests/impl/stream.hpp b/include/boost/requests/impl/stream.hpp index d4396e2..927e995 100644 --- a/include/boost/requests/impl/stream.hpp +++ b/include/boost/requests/impl/stream.hpp @@ -11,7 +11,6 @@ #include #include #include -#include #include diff --git a/include/boost/requests/src.hpp b/include/boost/requests/src.hpp index 536398e..9d1d1a2 100644 --- a/include/boost/requests/src.hpp +++ b/include/boost/requests/src.hpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1c9797e..a07432e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,12 +3,7 @@ file(GLOB_RECURSE ALL_TEST_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CU add_executable(tests ${ALL_TEST_FILES}) target_link_libraries(tests PUBLIC - Threads::Threads - OpenSSL::Crypto OpenSSL::SSL - Boost::requests Boost::json - Boost::headers Boost::container - Boost::atomic Boost::url - Boost::filesystem rt) + Boost::requests Boost::atomic Boost::filesystem rt) target_compile_definitions(tests PUBLIC BOOST_REQUESTS_SEPARATE_COMPILATION=1 diff --git a/test/cookie_grammar.cpp b/test/cookie_grammar.cpp index df9e2c9..a170039 100644 --- a/test/cookie_grammar.cpp +++ b/test/cookie_grammar.cpp @@ -110,8 +110,6 @@ TEST_CASE("domain") TEST_CASE("set-cookie") { - constexpr auto & rule = requests::grammar::domain; - auto v1 = requests::parse_set_cookie_field("theme=light"); CHECK(v1); CHECK(v1->name == "theme"); diff --git a/test/main_test.cpp b/test/main_test.cpp index 121117e..d3d2a70 100644 --- a/test/main_test.cpp +++ b/test/main_test.cpp @@ -5,5 +5,3 @@ #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" - -#include \ No newline at end of file From 49168d358756cc0c9bb249cacb4536c307a9f04e Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Fri, 27 Jan 2023 23:42:55 +0800 Subject: [PATCH 05/76] Trying to fix noexcept. --- include/boost/requests/response.hpp | 14 ++++++++++---- test/cookie.cpp | 10 +++++----- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/include/boost/requests/response.hpp b/include/boost/requests/response.hpp index fb36474..403f1cd 100644 --- a/include/boost/requests/response.hpp +++ b/include/boost/requests/response.hpp @@ -27,6 +27,7 @@ #include #include #include +#include namespace boost { @@ -58,10 +59,15 @@ struct response_base ~response_base() = default; response_base(const response_base & ) = default; - response_base(response_base && ) noexcept = default; + response_base(response_base && ) + noexcept(std::is_move_constructible::value && + std::is_move_constructible::value) = default; response_base& operator=(const response_base & ) = default; - response_base& operator=(response_base && ) noexcept = default; + response_base& operator=(response_base && ) noexcept( + std::is_move_assignable::value && + std::is_move_assignable::value + ) = default; bool ok () const { @@ -136,10 +142,10 @@ struct response : response_base response(http::response_header header, history_type history, buffer_type buffer) : response_base(std::move(header), std::move(history)), buffer(std::move(buffer)) {} response(const response & ) = default; - response(response && ) noexcept = default; + response(response && ) noexcept(std::is_move_constructible::value && std::is_move_constructible::value) = default; response& operator=(const response & ) = default; - response& operator=(response && ) noexcept = default; + response& operator=(response && ) noexcept(std::is_move_assignable::value && std::is_move_assignable::value) = default; template> diff --git a/test/cookie.cpp b/test/cookie.cpp index bdd4a6e..1ac7d37 100644 --- a/test/cookie.cpp +++ b/test/cookie.cpp @@ -48,12 +48,12 @@ void http_request_cookie_connection(bool https) hc.connect(ep); requests::cookie_jar jar; - auto res = requests::json::get(hc, urls::url_view{"/cookies"}, {.opts={false}, .jar=&jar}); + auto res = requests::json::get(hc, urls::url_view{"/cookies"}, {{}, /*.opts=*/{false}, /*.jar=*/&jar}); CHECK(res.value.at("cookies").as_object().empty()); CHECK(jar.content.empty()); - res = requests::json::get(hc, urls::url_view{"/cookies/set?cookie-1=foo"}, {.opts={false}, .jar=&jar}); + res = requests::json::get(hc, urls::url_view{"/cookies/set?cookie-1=foo"}, {{}, /*.opts=*/{false}, /*.jar=*/&jar}); CHECK(res.value.at("cookies") == json::object{{"cookie-1", "foo"}}); REQUIRE(!jar.content.empty()); @@ -63,7 +63,7 @@ void http_request_cookie_connection(bool https) CHECK(citr->secure_only_flag == false); CHECK(citr->path == "/"); - res = requests::json::get(hc, urls::url_view{"/cookies/set/cookie-2/bar"}, {.opts={false}, .jar=&jar}); + res = requests::json::get(hc, urls::url_view{"/cookies/set/cookie-2/bar"}, {{}, /*.opts=*/{false}, /*.jar=*/&jar}); CHECK(res.value.at("cookies") == json::object{{"cookie-1", "foo"}, {"cookie-2", "bar"}}); REQUIRE(jar.content.size() == 2u); @@ -89,7 +89,7 @@ void http_request_cookie_connection(bool https) CHECK(citr->secure_only_flag == false); CHECK(citr->path == "/"); } - res = requests::json::get(hc, urls::url_view{"/cookies/delete?cookie-1"}, {.opts={false}, .jar=&jar}); + res = requests::json::get(hc, urls::url_view{"/cookies/delete?cookie-1"}, {{}, /*.opts=*/{false}, /*.jar=*/&jar}); CHECK(!jar.content.empty()); REQUIRE(jar.content.size() == 1u); @@ -99,7 +99,7 @@ void http_request_cookie_connection(bool https) CHECK(citr->secure_only_flag == false); CHECK(citr->path == "/"); - res = requests::json::get(hc, urls::url_view{"/cookies/delete?cookie-2"}, {.opts={false}, .jar=&jar}); + res = requests::json::get(hc, urls::url_view{"/cookies/delete?cookie-2"}, {{}, /*.opts=*/{false}, /*.jar=*/&jar}); CHECK(jar.content.empty()); } From fe707f95994588e2710b73163fc9b4d08782275c Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Sat, 28 Jan 2023 00:32:48 +0800 Subject: [PATCH 06/76] Added openssl install for osx. --- CMakeLists.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index f356c8f..a9224f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,23 @@ if (NOT BOOST_REQUESTS_BUILD_INLINE) include_directories(include) endif() + +#------------------------------------------------------------------------------- +# +# OpenSSL +# +#------------------------------------------------------------------------------- + + +if (APPLE AND NOT DEFINED ENV{OPENSSL_ROOT_DIR}) + find_program(HOMEBREW brew) + if (NOT HOMEBREW STREQUAL "HOMEBREW-NOTFOUND") + execute_process(COMMAND brew --prefix openssl + OUTPUT_VARIABLE OPENSSL_ROOT_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE) + endif() +endif() + find_package(OpenSSL) if (NOT MSVC) link_libraries(${OPENSSL_LIBRARIES}) From a2ccf2cd4e5a3f3134194184d95ba8fcb12361d5 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Sat, 28 Jan 2023 00:54:36 +0800 Subject: [PATCH 07/76] more ci fixes. --- Jamfile | 45 +++++++++++++++++++++++ include/boost/requests/response.hpp | 23 +++++++----- include/boost/requests/rfc/impl/dates.ipp | 1 - 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/Jamfile b/Jamfile index f4454f9..313d4a2 100644 --- a/Jamfile +++ b/Jamfile @@ -15,6 +15,51 @@ build-project test ; boost.use-project ; +# Microsoft Windows section. Refer to FAQ "Windows and OpenSSL" +if [ os.name ] = NT +{ + local OPENSSL_ROOT_DEFAULT = "C:/OpenSSL" ; + local OPENSSL_ROOT_ENV = [ os.environ OPENSSL_ROOT ] ; + local OPENSSL_ROOT = "" ; + if $(OPENSSL_ROOT_ENV) + { + OPENSSL_ROOT = $(OPENSSL_ROOT_ENV) ; + } + else + { + OPENSSL_ROOT = $(OPENSSL_ROOT_DEFAULT) ; + } + project + : requirements + $(OPENSSL_ROOT)/include + debug:$(OPENSSL_ROOT)/lib + windowsdebug:$(OPENSSL_ROOT)/debug/lib + release:$(OPENSSL_ROOT)/lib + ; + + if [ path.exists $(OPENSSL_ROOT)/lib/libssl.lib ] + { + echo "OpenSSL > 1.1.0. Including libssl" ; + lib ssl : : windows libssl ; + } + if [ path.exists $(OPENSSL_ROOT)/lib/libcrypto.lib ] + { + echo "OpenSSL > 1.1.0. Including libcrypto" ; + lib crypto : : windows libcrypto ; + } + if [ path.exists $(OPENSSL_ROOT)/lib/ssleay32.lib ] + { + echo "OpenSSL < 1.1.0. Including ssleay32" ; + lib ssl : : windows ssleay32 ; + } + if [ path.exists $(OPENSSL_ROOT)/lib/libeay32.lib ] + { + echo "OpenSSL < 1.1.0. Including libeay32" ; + lib crypto : : windows libeay32 ; + } +} + + feature.feature boost.requests.separate-compilation : on off : propagated composite ; feature.compose on : BOOST_REQUESTS_SEPARATE_COMPILATION ; diff --git a/include/boost/requests/response.hpp b/include/boost/requests/response.hpp index 403f1cd..f3121b4 100644 --- a/include/boost/requests/response.hpp +++ b/include/boost/requests/response.hpp @@ -59,15 +59,15 @@ struct response_base ~response_base() = default; response_base(const response_base & ) = default; - response_base(response_base && ) - noexcept(std::is_move_constructible::value && - std::is_move_constructible::value) = default; + response_base(response_base && lhs) noexcept : headers(std::move(lhs.headers)), history(std::move(lhs.history)) {} response_base& operator=(const response_base & ) = default; - response_base& operator=(response_base && ) noexcept( - std::is_move_assignable::value && - std::is_move_assignable::value - ) = default; + response_base& operator=(response_base && lhs) noexcept + { + headers = std::move(lhs.headers); + history = std::move(lhs.history); + return *this; + } bool ok () const { @@ -142,10 +142,15 @@ struct response : response_base response(http::response_header header, history_type history, buffer_type buffer) : response_base(std::move(header), std::move(history)), buffer(std::move(buffer)) {} response(const response & ) = default; - response(response && ) noexcept(std::is_move_constructible::value && std::is_move_constructible::value) = default; + response(response && lhs) noexcept : response_base(std::move(lhs)), buffer(std::move(lhs.buffer)) {} response& operator=(const response & ) = default; - response& operator=(response && ) noexcept(std::is_move_assignable::value && std::is_move_assignable::value) = default; + response& operator=(response && lhs) noexcept + { + response_base::operator=(std::move(lhs)); + buffer = std::move(lhs.buffer); + return *this; + } template> diff --git a/include/boost/requests/rfc/impl/dates.ipp b/include/boost/requests/rfc/impl/dates.ipp index 3d5514f..a32dc8d 100644 --- a/include/boost/requests/rfc/impl/dates.ipp +++ b/include/boost/requests/rfc/impl/dates.ipp @@ -75,7 +75,6 @@ template inline auto interpret_result(const T raw) -> system::result { - std::chrono::system_clock::time_point ts{}; const std::size_t wd = std::get<0>(raw).index(); const auto d1 = std::get<1>(raw); const auto t1 = std::get<2>(raw); From c312eda68ecc8a8225535415a7e5bcac5701d128 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Sat, 28 Jan 2023 01:11:22 +0800 Subject: [PATCH 08/76] more ci fixes. --- Jamfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jamfile b/Jamfile index 313d4a2..f0df444 100644 --- a/Jamfile +++ b/Jamfile @@ -8,7 +8,8 @@ # import feature ; -import boost ; +import boost ; +import os ; build-project example ; build-project test ; From 488366d0f76937be3cbb1db23bc91639eb525e89 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Sat, 28 Jan 2023 13:16:17 +0800 Subject: [PATCH 09/76] jam import path --- Jamfile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Jamfile b/Jamfile index f0df444..7f3ed34 100644 --- a/Jamfile +++ b/Jamfile @@ -10,12 +10,17 @@ import feature ; import boost ; import os ; +import path ; build-project example ; build-project test ; boost.use-project ; +lib ssl ; +lib crypto ; +lib crypt32 ; + # Microsoft Windows section. Refer to FAQ "Windows and OpenSSL" if [ os.name ] = NT { From 420c960b1ad103d98577fc66b4316921e3ed9843 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Sat, 28 Jan 2023 20:33:05 +0800 Subject: [PATCH 10/76] copied openssl config from beast. --- .drone/drone.bat | 8 ++++++++ .drone/drone.sh | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/.drone/drone.bat b/.drone/drone.bat index 0eb2236..6ec2d99 100755 --- a/.drone/drone.bat +++ b/.drone/drone.bat @@ -9,6 +9,14 @@ git clone https://github.com/boostorg/boost-ci.git boost-ci-cloned --depth 1 cp -prf boost-ci-cloned/ci . rm -rf boost-ci-cloned + +SET DRONE_BUILD_DIR=%CD: =% +choco install --no-progress -y openssl --x64 --version 1.1.1.1000 +mklink /D "C:\OpenSSL" "C:\Program Files\OpenSSL-Win64" +SET OPENSSL_ROOT=C:\OpenSSL +SET BOOST_BRANCH=develop +IF "%DRONE_BRANCH%" == "master" SET BOOST_BRANCH=master + REM source ci/travis/install.sh REM The contents of install.sh below: diff --git a/.drone/drone.sh b/.drone/drone.sh index ed3bfae..853f20c 100755 --- a/.drone/drone.sh +++ b/.drone/drone.sh @@ -24,6 +24,20 @@ common_install () { if [ "$TRAVIS_OS_NAME" == "osx" ]; then unset -f cd + echo "macos - set up homebrew openssl" + export OPENSSL_ROOT=/usr/local/opt/openssl + + cat > ~/user-config.jam </usr/local/opt/openssl/include + debug:/usr/local/opt/openssl/lib + windowsdebug:/usr/local/opt/openssl/debug/lib + release:/usr/local/opt/openssl/lib + ; +EOF fi export SELF=`basename $REPO_NAME` From 03e25ca501dda9cbf0c07c7ab3e8cb91ba7173c8 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Mon, 30 Jan 2023 02:18:10 +0800 Subject: [PATCH 11/76] openssl on osx & windows build issues. --- .drone/drone.sh | 5 ++++- example/github-issues.hpp | 2 +- include/boost/requests/connection_pool.hpp | 1 - include/boost/requests/download.hpp | 2 +- include/boost/requests/impl/connection_pool.ipp | 12 ++++++------ test/connection.cpp | 4 ++-- test/json.cpp | 4 ++-- test/link.cpp | 1 - 8 files changed, 16 insertions(+), 15 deletions(-) diff --git a/.drone/drone.sh b/.drone/drone.sh index 853f20c..08e3020 100755 --- a/.drone/drone.sh +++ b/.drone/drone.sh @@ -91,9 +91,12 @@ python tools/boostdep/depinst/depinst.py ../tools/quickbook ./bootstrap.sh ./b2 headers +cp libs/beast/tools/user-config.jam ~/user-config.jam +echo "using $TOOLSET : : $COMPILER : $CXX_FLAGS ;" >> ~/user-config.jam + echo '==================================> SCRIPT' -echo "using doxygen ; using boostbook ; using saxonhe ;" > tools/build/src/user-config.jam +echo "using doxygen ; using boostbook ; using saxonhe ;" >> tools/build/src/user-config.jam ./b2 -j3 libs/$SELF/doc//boostrelease elif [ "$DRONE_JOB_BUILDTYPE" == "codecov" ]; then diff --git a/example/github-issues.hpp b/example/github-issues.hpp index 5fe0b53..dc199be 100644 --- a/example/github-issues.hpp +++ b/example/github-issues.hpp @@ -502,7 +502,7 @@ struct issue_client issue_client( boost::asio::io_context & ctx, const std::string & auth_token, - const boost::asio::string_view & host_name = "api.github.com") : conn_(ctx.get_executor(), sslctx_) + BOOST_ASIO_STRING_VIEW_PARAM host_name = "api.github.com") : conn_(ctx.get_executor(), sslctx_) { conn_.set_host({host_name.begin(), host_name.end()}); boost::asio::ip::tcp::resolver res{ctx}; diff --git a/include/boost/requests/connection_pool.hpp b/include/boost/requests/connection_pool.hpp index 981fabc..97eaae7 100644 --- a/include/boost/requests/connection_pool.hpp +++ b/include/boost/requests/connection_pool.hpp @@ -218,7 +218,6 @@ struct connection_pool std::string host_; std::vector endpoints_; std::size_t limit_; - std::size_t connecting_{0u}; boost::unordered_multimap, diff --git a/include/boost/requests/download.hpp b/include/boost/requests/download.hpp index eb26e5c..e8403a7 100644 --- a/include/boost/requests/download.hpp +++ b/include/boost/requests/download.hpp @@ -8,11 +8,11 @@ #ifndef BOOST_REQUESTS_DOWNLOAD_HPP #define BOOST_REQUESTS_DOWNLOAD_HPP -#include #include #include #include #include +#include #include #if defined(BOOST_ASIO_HAS_FILE) diff --git a/include/boost/requests/impl/connection_pool.ipp b/include/boost/requests/impl/connection_pool.ipp index 67293bb..040db66 100644 --- a/include/boost/requests/impl/connection_pool.ipp +++ b/include/boost/requests/impl/connection_pool.ipp @@ -33,7 +33,7 @@ void connection_pool::lookup(urls::url_view sv, system::error_code & ec) use_ssl_ = false; host_ = "localhost"; endpoints_ = {asio::local::stream_protocol::endpoint( - asio::string_view( + std::string( sv.encoded_target().data(), sv.encoded_target().size() ))}; @@ -43,8 +43,8 @@ void connection_pool::lookup(urls::url_view sv, system::error_code & ec) asio::ip::tcp::resolver resolver{get_executor()}; const auto service = sv.has_port() ? sv.port() : scheme; auto eps = resolver.resolve( - asio::string_view(sv.encoded_host_name().data(), sv.encoded_host_name().size()), - asio::string_view(service.data(), service.size()), ec); + std::string(sv.encoded_host_name().data(), sv.encoded_host_name().size()), + std::string(service.data(), service.size()), ec); if (!ec && eps.empty()) BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found) @@ -108,15 +108,15 @@ void connection_pool::async_lookup_op::resume(requests::detail::faux_token_tuse_ssl_ = false; this_->host_ = "localhost"; this_->endpoints_ = {asio::local::stream_protocol::endpoint( - asio::string_view(sv.encoded_target().data(), sv.encoded_target().size()))}; + std::string(sv.encoded_target().data(), sv.encoded_target().size()))}; } else if (scheme == "http" || scheme == "https") { resolver.emplace(get_executor()); service = sv.has_port() ? sv.port() : scheme; BOOST_ASIO_CORO_YIELD resolver->async_resolve( - asio::string_view(sv.encoded_host_name().data(), sv.encoded_host_name().size()), - asio::string_view(service.data(), service.size()), std::move(self)); + std::string(sv.encoded_host_name().data(), sv.encoded_host_name().size()), + std::string(service.data(), service.size()), std::move(self)); if (!ec && eps.empty()) BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found) diff --git a/test/connection.cpp b/test/connection.cpp index 6a88955..cf329fd 100644 --- a/test/connection.cpp +++ b/test/connection.cpp @@ -562,7 +562,7 @@ TEST_CASE("async-connection-request") CHECK(!conn.uses_ssl()); conn.set_host(url.encoded_host()); - rslvr.async_resolve(asio::string_view(url.encoded_host().data(), url.encoded_host().size()), + rslvr.async_resolve(std::string(url.encoded_host().data(), url.encoded_host().size()), "80", do_the_thing) (asio::append(&run_tests, std::ref(conn), urls::url_view(url))); ctx.run(); @@ -575,7 +575,7 @@ TEST_CASE("async-connection-request") url.set_scheme("https"); CHECK(conn.uses_ssl()); conn.set_host(url.encoded_host()); - rslvr.async_resolve(asio::string_view(url.encoded_host().data(), url.encoded_host().size()), + rslvr.async_resolve(std::string(url.encoded_host().data(), url.encoded_host().size()), "443", do_the_thing) (asio::append(&run_tests, std::ref(conn), urls::url_view(url))); ctx.run(); diff --git a/test/json.cpp b/test/json.cpp index 66abc75..1338cf2 100644 --- a/test/json.cpp +++ b/test/json.cpp @@ -295,7 +295,7 @@ TEST_CASE("async-json-request") CHECK(!conn.uses_ssl()); conn.set_host(url.encoded_host()); - rslvr.async_resolve(asio::string_view(url.encoded_host().data(), url.encoded_host().size()), + rslvr.async_resolve(std::string(url.encoded_host().data(), url.encoded_host().size()), "80", do_the_thing) (asio::append(&run_json_tests, std::ref(conn), urls::url_view(url))); ctx.run(); @@ -308,7 +308,7 @@ TEST_CASE("async-json-request") url.set_scheme("https"); CHECK(conn.uses_ssl()); conn.set_host(url.encoded_host()); - rslvr.async_resolve(asio::string_view(url.encoded_host().data(), url.encoded_host().size()), + rslvr.async_resolve(std::string(url.encoded_host().data(), url.encoded_host().size()), "443", do_the_thing) (asio::append(&run_json_tests, std::ref(conn), urls::url_view(url))); ctx.run(); diff --git a/test/link.cpp b/test/link.cpp index 94f021b..a3f93b2 100644 --- a/test/link.cpp +++ b/test/link.cpp @@ -78,7 +78,6 @@ TEST_CASE("link") auto val = *lst->begin(); CHECK(val.attributes.begin() != val.attributes.end()); - auto ext = val.extensions(); std::vector vec; vec.assign(val.attributes.begin(), val.attributes.end()); CHECK(vec.size() == 1u); From d6059a449874aeac512912ba1869e15773f66d49 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Mon, 30 Jan 2023 16:00:38 +0800 Subject: [PATCH 12/76] added ec to capture operation_aborted. --- .../boost/requests/detail/faux_coroutine.hpp | 5 +- include/boost/requests/download.hpp | 2 +- .../boost/requests/impl/connection_pool.ipp | 2 +- include/boost/requests/json.hpp | 4 +- test/connection_pool.cpp | 1 + test/detail/mutex.cpp | 49 +++++++++++++++++++ test/string_maker.hpp | 12 +++-- 7 files changed, 64 insertions(+), 11 deletions(-) diff --git a/include/boost/requests/detail/faux_coroutine.hpp b/include/boost/requests/detail/faux_coroutine.hpp index d43ab59..70295ea 100644 --- a/include/boost/requests/detail/faux_coroutine.hpp +++ b/include/boost/requests/detail/faux_coroutine.hpp @@ -104,10 +104,7 @@ struct faux_token_t }; faux_token_t(const faux_token_t & ) = delete; - faux_token_t( faux_token_t && lhs) : impl_(std::move(lhs.impl_)) - { - BOOST_ASSERT(impl_ != nullptr); - } + faux_token_t( faux_token_t && lhs) = default; explicit faux_token_t(std::shared_ptr impl) : impl_(std::move(impl)) {} private: diff --git a/include/boost/requests/download.hpp b/include/boost/requests/download.hpp index e8403a7..c4bc260 100644 --- a/include/boost/requests/download.hpp +++ b/include/boost/requests/download.hpp @@ -188,7 +188,7 @@ struct async_write_to_file_op : asio::coroutine if (ec) return 0u; - while (!str.done() && !ec) + while (!ec && !str.done()) { BOOST_ASIO_CORO_YIELD str.async_read_some(asio::buffer(buffer), std::move(self)); diff --git a/include/boost/requests/impl/connection_pool.ipp b/include/boost/requests/impl/connection_pool.ipp index 040db66..89f095f 100644 --- a/include/boost/requests/impl/connection_pool.ipp +++ b/include/boost/requests/impl/connection_pool.ipp @@ -119,7 +119,7 @@ void connection_pool::async_lookup_op::resume(requests::detail::faux_token_t; + struct impl final : token_type::base + { + std::vector ecs; + + using allocator_type = container::pmr::polymorphic_allocator; + allocator_type get_allocator() override + { + return container::pmr::polymorphic_allocator{container::pmr::get_default_resource()}; + } + + void resume(boost::requests::detail::faux_token_t tk, error_code ec) override + { + ecs.push_back(ec); + } + }; + impl ip{}; + std::shared_ptr ptr{&ip, [](impl * ) {}}; + asio::cancellation_signal sig; + ip.slot = sig.slot(); + { + + + mutex mtx{ctx}; + mtx.lock(); + mtx.async_lock(token_type{ptr}); + mtx.async_lock(token_type{ptr}); + ctx.run_for(std::chrono::milliseconds(10)); + CHECK(ip.ecs.empty()); + + sig.emit(asio::cancellation_type::all); + ctx.restart(); + ctx.run_for(std::chrono::milliseconds(10)); + + REQUIRE(ip.ecs.size() == 1u); + CHECK(ip.ecs.front() == asio::error::operation_aborted); + } + + ctx.restart(); + ctx.run_for(std::chrono::milliseconds(10)); + CHECK(ip.ecs.size() == 2u); +} + + + TEST_SUITE_END(); \ No newline at end of file diff --git a/test/string_maker.hpp b/test/string_maker.hpp index ca8aaf1..beb9e58 100644 --- a/test/string_maker.hpp +++ b/test/string_maker.hpp @@ -164,6 +164,7 @@ struct tracker_t Handler handler; boost::source_location loc; bool called = false; + bool moved_from = false; tracker_t( Handler && handler, const boost::source_location & loc = BOOST_CURRENT_LOCATION) @@ -178,14 +179,17 @@ struct tracker_t tracker_t(tracker_t && lhs) : handler(std::move(lhs.handler)), loc(lhs.loc), called(lhs.called) { - lhs.called = true; + lhs.moved_from = true; } ~tracker_t() { - doctest::detail::ResultBuilder rb(doctest::assertType::DT_CHECK, loc.file_name(), loc.line(), loc.function_name()); - rb.setResult(doctest::detail::Result{called, "called"}); - DOCTEST_ASSERT_LOG_AND_REACT(rb); + if (!moved_from) + { + doctest::detail::ResultBuilder rb(doctest::assertType::DT_CHECK, loc.file_name(), loc.line(), loc.function_name()); + rb.setResult(doctest::detail::Result{called, "called"}); + DOCTEST_ASSERT_LOG_AND_REACT(rb); + } } }; From f05f8f726cacbee06e2003e93b4afa4e7e3f082f Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Mon, 30 Jan 2023 16:06:24 +0800 Subject: [PATCH 13/76] mutex dtor fix. --- include/boost/requests/detail/impl/mutex.ipp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/requests/detail/impl/mutex.ipp b/include/boost/requests/detail/impl/mutex.ipp index 174ecc4..d03b31c 100644 --- a/include/boost/requests/detail/impl/mutex.ipp +++ b/include/boost/requests/detail/impl/mutex.ipp @@ -127,9 +127,9 @@ mutex::~mutex() system::error_code ec; BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::operation_aborted); - for (auto & h : waiters_) + auto ww = std::move(waiters_); + for (auto & h : ww) { - h.get_cancellation_slot().clear(); asio::dispatch(exec_, asio::append(std::move(h), ec)); } From 7d1cb97bb5b15d984d6db714ebd7f1d90277eb31 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 31 Jan 2023 02:44:44 +0800 Subject: [PATCH 14/76] connection_impl restructure & bug fixes. --- include/boost/requests.hpp | 1 - include/boost/requests/connection.hpp | 477 +++++++++--------- include/boost/requests/connection_pool.hpp | 25 +- .../boost/requests/detail/connection_impl.hpp | 228 +++++++++ .../boost/requests/detail/faux_coroutine.hpp | 54 +- .../impl/connection_impl.hpp} | 65 ++- .../impl/connection_impl.ipp} | 48 +- include/boost/requests/detail/impl/mutex.ipp | 5 +- .../boost/requests/detail/state_machine.hpp | 77 --- include/boost/requests/download.hpp | 6 +- include/boost/requests/fields/impl/link.ipp | 2 +- .../boost/requests/fields/impl/set_cookie.ipp | 10 +- include/boost/requests/grammar/token_rule.hpp | 2 +- .../boost/requests/impl/connection_pool.hpp | 14 +- .../boost/requests/impl/connection_pool.ipp | 68 +-- include/boost/requests/impl/public_suffix.ipp | 2 +- include/boost/requests/impl/stream.ipp | 34 +- include/boost/requests/src.hpp | 2 +- include/boost/requests/stream.hpp | 21 +- test/connection.cpp | 20 +- test/connection_pool.cpp | 8 +- test/cookie.cpp | 10 +- test/method.cpp | 8 +- test/session.cpp | 8 +- test/string_maker.hpp | 7 +- 25 files changed, 655 insertions(+), 547 deletions(-) create mode 100644 include/boost/requests/detail/connection_impl.hpp rename include/boost/requests/{impl/connection.hpp => detail/impl/connection_impl.hpp} (81%) rename include/boost/requests/{impl/connection.ipp => detail/impl/connection_impl.ipp} (91%) delete mode 100644 include/boost/requests/detail/state_machine.hpp diff --git a/include/boost/requests.hpp b/include/boost/requests.hpp index 8708a0d..7b81292 100644 --- a/include/boost/requests.hpp +++ b/include/boost/requests.hpp @@ -7,7 +7,6 @@ #define BOOST_REQUESTS_HPP #include -#include #include #include #include diff --git a/include/boost/requests/connection.hpp b/include/boost/requests/connection.hpp index c55c31b..c3b8608 100644 --- a/include/boost/requests/connection.hpp +++ b/include/boost/requests/connection.hpp @@ -1,25 +1,17 @@ -// Copyright (c) 2021 Klemens D. Morgenstern +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + #ifndef BOOST_REQUESTS_CONNECTION_HPP #define BOOST_REQUESTS_CONNECTION_HPP -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include +#include + namespace boost { namespace requests { @@ -27,238 +19,219 @@ struct stream; struct connection { - /// The type of the next layer. - typedef asio::ssl::stream next_layer_type; - - /// The type of the executor associated with the object. - typedef typename next_layer_type::executor_type executor_type; - - /// The type of the executor associated with the object. - typedef typename next_layer_type::lowest_layer_type lowest_layer_type; - - /// This type with a defaulted completion token. - template - struct defaulted; - - /// Rebinds the socket type to another executor. - template - struct rebind_executor - { - /// The socket type when rebound to the specified executor. - using other = connection; - }; - - /// Get the executor - executor_type get_executor() noexcept - { - return next_layer_.get_executor(); - } - /// Get the underlying stream - const next_layer_type &next_layer() const noexcept - { - return next_layer_; - } - - /// Get the underlying stream - next_layer_type &next_layer() noexcept - { - return next_layer_; - } - - /// The protocol-type of the lowest layer. - using protocol_type = asio::generic::stream_protocol; - - /// The endpoint of the lowest lowest layer. - using endpoint_type = typename protocol_type::endpoint; - - - connection(connection && lhs) - : next_layer_(std::move(lhs.next_layer_)) - , use_ssl_(lhs.use_ssl_) - , read_mtx_(std::move(lhs.read_mtx_)) - , write_mtx_(std::move(lhs.write_mtx_)) - , host_(std::move(lhs.host_)) - , buffer_(std::move(lhs.buffer_)) - , ongoing_requests_(std::move(lhs.ongoing_requests_.load())) - , endpoint_(std::move(lhs.endpoint_)) - {} - - connection & operator=(connection && lhs) - { - next_layer_ = std::move(lhs.next_layer_); - use_ssl_ = lhs.use_ssl_; - read_mtx_ = std::move(lhs.read_mtx_); - write_mtx_ = std::move(lhs.write_mtx_); - host_ = std::move(lhs.host_); - buffer_ = std::move(lhs.buffer_); - ongoing_requests_ = std::move(lhs.ongoing_requests_.load()); - endpoint_ = std::move(lhs.endpoint_); - return *this; - } - - /// Construct a stream. - template - explicit connection(ExecutorOrContext && exec_or_ctx, asio::ssl::context & ctx) - : next_layer_(std::forward(exec_or_ctx), ctx), use_ssl_{true} {} - - template - explicit connection(ExecutionContext &context, - typename asio::constraint< - asio::is_convertible::value - >::type = 0) - : next_layer_( - context, - asio::use_service(context).get()), - use_ssl_{false} {} - - explicit connection(asio::any_io_executor exec) - : next_layer_( - exec, - asio::use_service( - asio::query(exec, asio::execution::context) - ).get()), use_ssl_{false} {} - - void connect(endpoint_type ep) - { - boost::system::error_code ec; - connect(ep, ec); - if (ec) - urls::detail::throw_system_error(ec); - } - - BOOST_REQUESTS_DECL void connect(endpoint_type ep, - system::error_code & ec); - - template - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code)) - async_connect(endpoint_type ep, - CompletionToken && completion_token); - - void close() - { - boost::system::error_code ec; - close(ec); - if (ec) - urls::detail::throw_system_error(ec); - } - - BOOST_REQUESTS_DECL void close(system::error_code & ec); - - template - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code)) - async_close(CompletionToken && completion_token); - - bool is_open() const - { - return beast::get_lowest_layer(next_layer_).is_open(); - } - - // Endpoint - endpoint_type endpoint() const {return endpoint_;} - - std::size_t working_requests() const { return ongoing_requests_; } - - // Reserve memory for the internal buffer. - void reserve(std::size_t size) - { - buffer_.reserve(size); - } - - void set_host(core::string_view sv) - { - boost::system::error_code ec; - set_host(sv, ec); - if (ec) - urls::detail::throw_system_error(ec); - } - - BOOST_REQUESTS_DECL void set_host(core::string_view sv, system::error_code & ec); - core::string_view host() const {return host_;} - constexpr static redirect_mode supported_redirect_mode() {return redirect_mode::endpoint;} - - using request_type = request_parameters; - - template - auto ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, request_parameters req, - system::error_code & ec) -> stream; - - template - auto ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, request_parameters req) -> stream; - - BOOST_REQUESTS_DECL - auto ropen(beast::http::verb method, - urls::pct_string_view path, - http::fields & headers, - source & src, - request_options opt, - cookie_jar * jar, - system::error_code & ec) -> stream; - - BOOST_REQUESTS_DECL - auto ropen(beast::http::verb method, - urls::pct_string_view path, - http::fields & headers, - source & src, - request_options opt, - cookie_jar * jar) -> stream; - - template - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, stream)) - async_ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, request_parameters req, - CompletionToken && completion_token); - - template - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, - stream)) - async_ropen(beast::http::verb method, - urls::pct_string_view path, - http::fields & headers, - source & src, - request_options opt, - cookie_jar * jar, - CompletionToken && completion_token); - bool uses_ssl() const {return use_ssl_;} - private: - - next_layer_type next_layer_; - bool use_ssl_{false}; - detail::mutex read_mtx_{next_layer_.get_executor()}, - write_mtx_{next_layer_.get_executor()}; - - std::string host_; - beast::flat_buffer buffer_; - std::atomic ongoing_requests_{0u}; - endpoint_type endpoint_; - - struct async_close_op; - struct async_connect_op; - - struct async_ropen_op; - - template - struct async_ropen_op_body; - - template - struct async_ropen_op_body_base; - - BOOST_REQUESTS_DECL std::size_t do_read_some_(beast::http::basic_parser & parser); - BOOST_REQUESTS_DECL std::size_t do_read_some_(beast::http::basic_parser & parser, system::error_code & ec) ; - BOOST_REQUESTS_DECL void do_async_read_some_(beast::http::basic_parser & parser, detail::faux_token_t) ; - BOOST_REQUESTS_DECL void do_async_close_(detail::faux_token_t); - BOOST_REQUESTS_DECL void do_close_(system::error_code & ec); - - friend struct stream; + /// The type of the next layer. + typedef asio::ssl::stream next_layer_type; + + /// The type of the executor associated with the object. + typedef typename next_layer_type::executor_type executor_type; + + /// The type of the executor associated with the object. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + /// This type with a defaulted completion token. + template + struct defaulted; + + /// Rebinds the socket type to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + using other = connection; + }; + + /// Get the executor + executor_type get_executor() noexcept + { + return impl_->get_executor(); + } + /// Get the underlying stream + const next_layer_type &next_layer() const noexcept + { + return impl_->next_layer(); + } + + /// Get the underlying stream + next_layer_type &next_layer() noexcept + { + return impl_->next_layer(); + } + + /// The protocol-type of the lowest layer. + using protocol_type = asio::generic::stream_protocol; + + /// The endpoint of the lowest lowest layer. + using endpoint_type = typename protocol_type::endpoint; + + + explicit connection(std::shared_ptr impl) : impl_(std::move(impl)) {} + + connection() = default; + connection(const connection & lhs) = default; + connection & operator=(const connection & lhs) = default; + + connection(connection && lhs) = default; + connection & operator=(connection && lhs) = default; + + /// Construct a stream. + template + explicit connection(ExecutorOrContext && exec_or_ctx, asio::ssl::context & ctx) + : impl_(std::make_shared(std::forward(exec_or_ctx), ctx)) {} + + template + explicit connection(ExecutionContext &context, + typename asio::constraint< + asio::is_convertible::value + >::type = 0) + : impl_(std::make_shared(context)) {} + + explicit connection(asio::any_io_executor exec) : impl_(std::make_shared(exec)) {} + + void connect(endpoint_type ep) + { + boost::system::error_code ec; + connect(ep, ec); + if (ec) + urls::detail::throw_system_error(ec); + } + + BOOST_REQUESTS_DECL void connect(endpoint_type ep, system::error_code & ec) + { + return impl_->connect(ep, ec); + } + + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + void (boost::system::error_code)) + async_connect(endpoint_type ep, + CompletionToken && completion_token) + + { + return impl_->async_connect(ep, std::forward(completion_token)); + } + + void close() + { + boost::system::error_code ec; + close(ec); + if (ec) + urls::detail::throw_system_error(ec); + } + + BOOST_REQUESTS_DECL void close(system::error_code & ec) + { + return impl_->close(ec); + } + + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + void (boost::system::error_coe)) + async_close(CompletionToken && completion_token) + { + return impl_->async_close(std::forward(completion_token)); + } + + bool is_open() const { return impl_ && impl_->is_open(); } + endpoint_type endpoint() const { return impl_->endpoint(); } + + + std::size_t working_requests() const { return impl_->working_requests(); } + void reserve(std::size_t size) { impl_->reserve(size); } + + void set_host(core::string_view sv) + { + boost::system::error_code ec; + set_host(sv, ec); + if (ec) + urls::detail::throw_system_error(ec); + } + + BOOST_REQUESTS_DECL void set_host(core::string_view sv, system::error_code & ec) + { + impl_->set_host(sv, ec); + } + core::string_view host() const {return impl_->host();} + constexpr static redirect_mode supported_redirect_mode() {return redirect_mode::endpoint;} + + using request_type = request_parameters; + + template + auto ropen(beast::http::verb method, + urls::url_view path, + RequestBody && body, + request_parameters req, + system::error_code & ec) -> stream + { + return impl_->ropen(method, path, std::forward(body), std::move(req), ec); + } + + template + auto ropen(beast::http::verb method, + urls::url_view path, + RequestBody && body, + request_parameters req) -> stream + { + return impl_->ropen(method, path, std::forward(body), std::move(req)); + } + + BOOST_REQUESTS_DECL + auto ropen(beast::http::verb method, + urls::pct_string_view path, + http::fields & headers, + source & src, + request_options opt, + cookie_jar * jar, + system::error_code & ec) -> stream + { + return impl_->ropen(method, path, headers, src, std::move(opt), jar, ec); + } + + BOOST_REQUESTS_DECL + auto ropen(beast::http::verb method, + urls::pct_string_view path, + http::fields & headers, + source & src, + request_options opt, + cookie_jar * jar) -> stream + { + return impl_->ropen(method, path, headers, src, std::move(opt), jar); + } + + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + void (boost::system::error_code, stream)) + async_ropen(beast::http::verb method, + urls::url_view path, + RequestBody && body, + request_parameters req, + CompletionToken && completion_token) + { + return impl_->async_ropen(method, path, std::forward(body), + std::move(req), std::forward(completion_token)); + } + + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + void (boost::system::error_code, + stream)) + async_ropen(beast::http::verb method, + urls::pct_string_view path, + http::fields & headers, + source & src, + request_options opt, + cookie_jar * jar, + CompletionToken && completion_token) + { + return impl_->async_ropen(method, path, headers, src, std::move(opt), jar, std::forward(completion_token)); + } + bool uses_ssl() const {return impl_->uses_ssl();} + + operator bool() const {return impl_ != nullptr;} +private: + std::shared_ptr impl_; + + friend struct stream; }; @@ -302,7 +275,8 @@ struct connection::defaulted : connection BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, typename detail::defaulted_helper::type)) async_ropen(beast::http::verb method, urls::url_view path, - RequestBody && body, request_parameters req, + RequestBody && body, + request_parameters req, CompletionToken && completion_token) { return connection::async_ropen(method, path, std::forward(body), std::move(req), @@ -326,7 +300,8 @@ struct connection::defaulted : connection template auto async_ropen(beast::http::verb method, urls::url_view path, - RequestBody && body, request_parameters req) + RequestBody && body, + request_parameters req) { return this->async_ropen(method, path, std::forward(body), std::move(req), default_token()); } @@ -345,6 +320,4 @@ struct connection::defaulted : connection } -#include - -#endif //BOOST_REQUESTS_CONNECTION_HPP +#endif // BOOST_REQUESTS_CONNECTION_HPP diff --git a/include/boost/requests/connection_pool.hpp b/include/boost/requests/connection_pool.hpp index 97eaae7..dc890a7 100644 --- a/include/boost/requests/connection_pool.hpp +++ b/include/boost/requests/connection_pool.hpp @@ -2,8 +2,9 @@ // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#ifndef BOOST_REQUESTS_POOL_HPP -#define BOOST_REQUESTS_POOL_HPP + +#ifndef BOOST_REQUESTS_CONNECTION_POOL_HPP +#define BOOST_REQUESTS_CONNECTION_POOL_HPP #include @@ -114,8 +115,8 @@ struct connection_pool using request_type = request_parameters; - BOOST_REQUESTS_DECL std::shared_ptr get_connection(error_code & ec); - std::shared_ptr get_connection() + BOOST_REQUESTS_DECL connection get_connection(error_code & ec); + connection get_connection() { boost::system::error_code ec; auto res = get_connection(ec); @@ -135,13 +136,13 @@ struct connection_pool system::error_code & ec) -> stream { auto conn = get_connection(ec); - if (!ec && conn == nullptr) + if (!ec && !conn ) BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); if (ec) return stream{get_executor(), nullptr}; - BOOST_ASSERT(conn != nullptr); - return conn->ropen(method, path, std::forward(body), std::move(req), ec); + BOOST_ASSERT(conn); + return conn.ropen(method, path, std::forward(body), std::move(req), ec); } template @@ -165,13 +166,13 @@ struct connection_pool system::error_code & ec) -> stream { auto conn = get_connection(ec); - if (!ec && conn == nullptr) + if (!ec && !conn) BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); if (ec) return stream{get_executor(), nullptr}; - BOOST_ASSERT(conn != nullptr); - return conn->ropen(method, path, headers, src, opt, jar, ec); + BOOST_ASSERT(conn); + return conn.ropen(method, path, headers, src, opt, jar, ec); } auto ropen(beast::http::verb method, @@ -220,7 +221,7 @@ struct connection_pool std::size_t limit_; boost::unordered_multimap, + std::shared_ptr, detail::endpoint_hash> conns_; struct async_lookup_op; @@ -308,4 +309,4 @@ struct connection_pool::defaulted : connection_pool #include -#endif //BOOST_REQUESTS_POOL_HPP +#endif //BOOST_REQUESTS_CONNECTION_POOL_HPP diff --git a/include/boost/requests/detail/connection_impl.hpp b/include/boost/requests/detail/connection_impl.hpp new file mode 100644 index 0000000..a3407f9 --- /dev/null +++ b/include/boost/requests/detail/connection_impl.hpp @@ -0,0 +1,228 @@ +// Copyright (c) 2021 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#ifndef BOOST_REQUESTS_DETAIL_CONNECTION_IMPL_HPP +#define BOOST_REQUESTS_DETAIL_CONNECTION_IMPL_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace requests { + +struct stream; + +namespace detail { + +struct connection_impl : std::enable_shared_from_this +{ + typedef asio::ssl::stream next_layer_type; + typedef typename next_layer_type::executor_type executor_type; + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + executor_type get_executor() noexcept + { + return next_layer_.get_executor(); + } + const next_layer_type &next_layer() const noexcept + { + return next_layer_; + } + + next_layer_type &next_layer() noexcept + { + return next_layer_; + } + + using protocol_type = asio::generic::stream_protocol; + using endpoint_type = typename protocol_type::endpoint; + + connection_impl(connection_impl && lhs) = delete; + connection_impl & operator=(connection_impl && lhs) = delete; + template + explicit connection_impl(ExecutorOrContext && exec_or_ctx, asio::ssl::context & ctx) + : next_layer_(std::forward(exec_or_ctx), ctx), use_ssl_{true} {} + + template + explicit connection_impl(ExecutionContext &context, + typename asio::constraint< + asio::is_convertible::value + >::type = 0) + : next_layer_( + context, + asio::use_service(context).get()), + use_ssl_{false} {} + + explicit connection_impl(asio::any_io_executor exec) + : next_layer_( + exec, + asio::use_service( + asio::query(exec, asio::execution::context) + ).get()), use_ssl_{false} {} + + void connect(endpoint_type ep) + { + boost::system::error_code ec; + connect(ep, ec); + if (ec) + urls::detail::throw_system_error(ec); + } + + BOOST_REQUESTS_DECL void connect(endpoint_type ep, + system::error_code & ec); + + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + void (boost::system::error_code)) + async_connect(endpoint_type ep, + CompletionToken && completion_token); + + void close() + { + boost::system::error_code ec; + close(ec); + if (ec) + urls::detail::throw_system_error(ec); + } + + BOOST_REQUESTS_DECL void close(system::error_code & ec); + + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + void (boost::system::error_code)) + async_close(CompletionToken && completion_token); + + bool is_open() const + { + return beast::get_lowest_layer(next_layer_).is_open(); + } + + // Endpoint + endpoint_type endpoint() const {return endpoint_;} + + std::size_t working_requests() const { return ongoing_requests_; } + + // Reserve memory for the internal buffer. + void reserve(std::size_t size) + { + buffer_.reserve(size); + } + + void set_host(core::string_view sv) + { + boost::system::error_code ec; + set_host(sv, ec); + if (ec) + urls::detail::throw_system_error(ec); + } + + BOOST_REQUESTS_DECL void set_host(core::string_view sv, system::error_code & ec); + core::string_view host() const {return host_;} + constexpr static redirect_mode supported_redirect_mode() {return redirect_mode::endpoint;} + + using request_type = request_parameters; + + template + auto ropen(beast::http::verb method, + urls::url_view path, + RequestBody && body, request_parameters req, + system::error_code & ec) -> stream; + + template + auto ropen(beast::http::verb method, + urls::url_view path, + RequestBody && body, request_parameters req) -> stream; + + BOOST_REQUESTS_DECL + auto ropen(beast::http::verb method, + urls::pct_string_view path, + http::fields & headers, + source & src, + request_options opt, + cookie_jar * jar, + system::error_code & ec) -> stream; + + BOOST_REQUESTS_DECL + auto ropen(beast::http::verb method, + urls::pct_string_view path, + http::fields & headers, + source & src, + request_options opt, + cookie_jar * jar) -> stream; + + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + void (boost::system::error_code, stream)) + async_ropen(beast::http::verb method, + urls::url_view path, + RequestBody && body, request_parameters req, + CompletionToken && completion_token); + + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + void (boost::system::error_code, + stream)) + async_ropen(beast::http::verb method, + urls::pct_string_view path, + http::fields & headers, + source & src, + request_options opt, + cookie_jar * jar, + CompletionToken && completion_token); + bool uses_ssl() const {return use_ssl_;} + + private: + + next_layer_type next_layer_; + bool use_ssl_{false}; + detail::mutex read_mtx_{next_layer_.get_executor()}, + write_mtx_{next_layer_.get_executor()}; + + std::string host_; + beast::flat_buffer buffer_; + std::atomic ongoing_requests_{0u}; + endpoint_type endpoint_; + + struct async_close_op; + struct async_connect_op; + + struct async_ropen_op; + + template + struct async_ropen_op_body; + + template + struct async_ropen_op_body_base; + + BOOST_REQUESTS_DECL std::size_t do_read_some_(beast::http::basic_parser & parser); + BOOST_REQUESTS_DECL std::size_t do_read_some_(beast::http::basic_parser & parser, system::error_code & ec) ; + BOOST_REQUESTS_DECL void do_async_read_some_(beast::http::basic_parser & parser, detail::faux_token_t) ; + BOOST_REQUESTS_DECL void do_async_close_(detail::faux_token_t); + BOOST_REQUESTS_DECL void do_close_(system::error_code & ec); + + friend struct boost::requests::stream; +}; + + +} +} +} + +#include + +#endif //BOOST_REQUESTS_DETAIL_CONNECTION_IMPL_HPP diff --git a/include/boost/requests/detail/faux_coroutine.hpp b/include/boost/requests/detail/faux_coroutine.hpp index 70295ea..421d8f4 100644 --- a/include/boost/requests/detail/faux_coroutine.hpp +++ b/include/boost/requests/detail/faux_coroutine.hpp @@ -57,10 +57,7 @@ struct faux_token_t }; faux_token_t(const faux_token_t & ) = delete; - faux_token_t( faux_token_t && lhs) : impl_(std::move(lhs.impl_)) - { - BOOST_ASSERT(impl_ != nullptr); - } + faux_token_t( faux_token_t && lhs) : impl_(lhs.impl_) { } explicit faux_token_t(std::shared_ptr impl) : impl_(std::move(impl)) {} private: @@ -104,7 +101,7 @@ struct faux_token_t }; faux_token_t(const faux_token_t & ) = delete; - faux_token_t( faux_token_t && lhs) = default; + faux_token_t( faux_token_t && lhs) : impl_(lhs.impl_) { } explicit faux_token_t(std::shared_ptr impl) : impl_(std::move(impl)) {} private: @@ -121,10 +118,18 @@ template struct faux_token_t { using cancellation_slot_type = asio::cancellation_slot; - cancellation_slot_type get_cancellation_slot() const {BOOST_ASSERT(impl_ != nullptr); return impl_->slot;} + cancellation_slot_type get_cancellation_slot() const + { + BOOST_ASSERT(impl_ != nullptr); + return impl_->slot; + } using allocator_type = container::pmr::polymorphic_allocator; - allocator_type get_allocator() const {BOOST_ASSERT(impl_ != nullptr); return impl_->get_allocator();} + allocator_type get_allocator() const + { + BOOST_ASSERT(impl_ != nullptr); + return impl_->get_allocator(); + } void operator()(T1 t1 = {}, T2 t2 = {}) { @@ -145,24 +150,17 @@ struct faux_token_t } }; - faux_token_t(const faux_token_t & ) = delete; - faux_token_t( faux_token_t && lhs) : impl_(std::move(lhs.impl_)) - { - BOOST_ASSERT(impl_ != nullptr); - } - + faux_token_t( faux_token_t && lhs) : impl_(lhs.impl_) { } + operator faux_token_t () && { - return faux_token_t{impl_}; + return faux_token_t{std::move(impl_)}; } operator faux_token_t () && { - return faux_token_t{impl_}; + return faux_token_t{std::move(impl_)}; } - - std::size_t use_count() const {return impl_.use_count();} - explicit faux_token_t(std::shared_ptr impl) : impl_(std::move(impl)) {} private: @@ -198,7 +196,7 @@ struct faux_runner if (buf.use_count() == 0u) return ; auto h = std::move(handler); - //BOOST_ASSERT(buf.use_count() == 1); + BOOST_ASSERT(buf.use_count() >= 1); auto exec = asio::get_associated_executor(h, impl.get_executor()); buf = nullptr; asio::dispatch(exec, asio::append(std::move(h), ec)); @@ -214,8 +212,8 @@ struct faux_runner if (buf.use_count() == 0u) return ; auto h = std::move(handler); + BOOST_ASSERT(buf.use_count() >= 1); auto tmp = std::move(res); - //BOOST_ASSERT(buf.use_count() == 1); auto exec = asio::get_associated_executor(h, impl.get_executor()); buf = nullptr; asio::dispatch(exec, asio::append(std::move(h), ec, std::move(tmp))); @@ -231,16 +229,17 @@ struct faux_runner void initiate_impl(std::true_type, token_type tk, system::error_code ec, Args ... args) { + BOOST_ASSERT(tk.impl_.get() == this); auto buf = tk.impl_; impl.resume(std::move(tk), ec, std::move(args)...); if (impl.is_complete()) { if (buf.use_count() == 0u) return ; + auto h = std::move(handler); - // BOOST_ASSERT(buf.use_count() == 1); auto exec = asio::get_associated_executor(h, impl.get_executor()); - buf = nullptr; + buf.reset(); asio::post(exec, asio::append(std::move(h), ec)); } } @@ -258,8 +257,7 @@ struct faux_runner auto h = std::move(handler); auto tmp = std::move(res); auto exec = asio::get_associated_executor(h, impl.get_executor()); - buf = nullptr; - asio::post(exec, asio::append(std::move(h), ec, std::move(tmp))); + asio::post(exec, asio::append(std::move(h), ec, std::move(tmp))); } } @@ -311,7 +309,7 @@ struct faux_runner : impl(std::forward(args)...) , handler(std::forward(h)) { - this->token_type::base::slot = asio::get_associated_cancellation_slot(handler); + this->token_type::base::slot = asio::get_associated_cancellation_slot(handler); } }; @@ -320,9 +318,9 @@ struct faux_runner { auto alloc = asio::get_associated_allocator(h, asio::recycling_allocator()); using impl_t = impl_>; - token_type tt{std::allocate_shared(alloc, std::forward(h), std::forward(args)...)}; - auto * impl = static_cast(tt.impl_.get()); - impl->initiate(std::move(tt)); + auto ptr = std::allocate_shared(alloc, std::forward(h), std::forward(args)...); + auto * impl = ptr.get(); + impl->initiate(token_type{std::move(ptr)}); } }; diff --git a/include/boost/requests/impl/connection.hpp b/include/boost/requests/detail/impl/connection_impl.hpp similarity index 81% rename from include/boost/requests/impl/connection.hpp rename to include/boost/requests/detail/impl/connection_impl.hpp index 1c5cca2..fe10bdd 100644 --- a/include/boost/requests/impl/connection.hpp +++ b/include/boost/requests/detail/impl/connection_impl.hpp @@ -8,7 +8,7 @@ #ifndef BOOST_REQUESTS_IMPL_CONNECTION_HPP #define BOOST_REQUESTS_IMPL_CONNECTION_HPP -#include +#include #include #include #include @@ -31,18 +31,19 @@ namespace boost { namespace requests { +namespace detail { -struct connection::async_connect_op : asio::coroutine +struct connection_impl::async_connect_op : asio::coroutine { using executor_type = asio::any_io_executor; executor_type get_executor() {return this_->get_executor(); } - connection * this_; + connection_impl * this_; endpoint_type ep; asio::coroutine inner_coro; - async_connect_op(connection * this_, endpoint_type ep) : this_(this_), ep(ep) {} + async_connect_op(connection_impl * this_, endpoint_type ep) : this_(this_), ep(ep) {} using lock_type = detail::lock_guard; @@ -58,16 +59,16 @@ struct connection::async_connect_op : asio::coroutine template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (system::error_code)) -connection::async_connect(endpoint_type ep, CompletionToken && completion_token) +connection_impl::async_connect(endpoint_type ep, CompletionToken && completion_token) { return detail::faux_run( std::forward(completion_token), this, ep); } -struct connection::async_close_op : asio::coroutine +struct connection_impl::async_close_op : asio::coroutine { - connection * this_; + connection_impl * this_; detail::tracker t{this_->ongoing_requests_}; using executor_type = asio::any_io_executor; @@ -77,7 +78,7 @@ struct connection::async_close_op : asio::coroutine lock_type read_lock, write_lock; - async_close_op(connection * this_) : this_(this_) {} + async_close_op(connection_impl * this_) : this_(this_) {} using completion_signature_type = void(system::error_code); using step_signature_type = void(system::error_code); @@ -89,13 +90,11 @@ struct connection::async_close_op : asio::coroutine template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code)) -connection::async_close(CompletionToken && completion_token) +connection_impl::async_close(CompletionToken && completion_token) { return detail::faux_run( std::forward(completion_token), this); } -namespace detail -{ BOOST_REQUESTS_DECL bool check_endpoint( urls::url_view path, @@ -118,10 +117,9 @@ BOOST_REQUESTS_DECL bool check_endpoint( bool has_ssl, system::error_code & ec); -} template -auto connection::ropen(beast::http::verb method, +auto connection_impl::ropen(beast::http::verb method, urls::url_view path, RequestBody && body, request_parameters req) -> stream { @@ -133,7 +131,7 @@ auto connection::ropen(beast::http::verb method, } template -auto connection::ropen( +auto connection_impl::ropen( beast::http::verb method, urls::url_view path, RequestBody && body, request_parameters req, @@ -157,7 +155,7 @@ auto connection::ropen( } -struct connection::async_ropen_op +struct connection_impl::async_ropen_op : boost::asio::coroutine { using executor_type = asio::any_io_executor; @@ -165,7 +163,7 @@ struct connection::async_ropen_op using lock_type = detail::lock_guard; - connection * this_; + std::shared_ptr this_; optional str; detail::tracker t{this_->ongoing_requests_}; @@ -184,19 +182,18 @@ struct connection::async_ropen_op response_base::history_type history; system::error_code ec_; - async_ropen_op(connection * this_, + async_ropen_op(std::shared_ptr this_, beast::http::verb method, urls::pct_string_view path, http::fields & headers, source & src, request_options opts, cookie_jar * jar) - : this_(this_), method(method), path(path), headers(headers), src(src), opts(std::move(opts)), jar(jar) + : this_(std::move(this_)), method(method), path(path), headers(headers), src(src), opts(std::move(opts)), jar(jar) { } - - async_ropen_op(connection * this_, + async_ropen_op(std::shared_ptr this_, beast::http::verb method, urls::url_view path, http::fields & headers, @@ -218,7 +215,7 @@ struct connection::async_ropen_op }; template -struct connection::async_ropen_op_body_base +struct connection_impl::async_ropen_op_body_base { RequestSource source_impl; http::fields headers; @@ -231,17 +228,16 @@ struct connection::async_ropen_op_body_base }; template -struct connection::async_ropen_op_body : async_ropen_op_body_base, async_ropen_op +struct connection_impl::async_ropen_op_body : async_ropen_op_body_base, async_ropen_op { template - async_ropen_op_body( - connection * this_, - beast::http::verb method, - urls::url_view path, - RequestBody && body, + async_ropen_op_body(std::shared_ptr this_, + beast::http::verb method, + urls::url_view path, + RequestBody && body, request_parameters req) : async_ropen_op_body_base{std::forward(body), std::move(req.fields)}, - async_ropen_op(this_, method, path, async_ropen_op_body_base::headers, + async_ropen_op(std::move(this_), method, path, async_ropen_op_body_base::headers, this->source_impl, std::move(req.opts), req.jar) {} }; @@ -250,7 +246,7 @@ struct connection::async_ropen_op_body : async_ropen_op_body_base template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, stream)) -connection::async_ropen( +connection_impl::async_ropen( beast::http::verb method, urls::url_view path, RequestBody && body, request_parameters req, @@ -259,7 +255,7 @@ connection::async_ropen( using rp = async_ropen_op_body(body)))>>; return detail::faux_run( std::forward(completion_token), - this, method, path, std::forward(body), + shared_from_this(), method, path, std::forward(body), std::move(req)); } @@ -267,7 +263,7 @@ template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, stream)) -connection::async_ropen(beast::http::verb method, +connection_impl::async_ropen(beast::http::verb method, urls::pct_string_view path, http::fields & headers, source & src, @@ -276,7 +272,10 @@ connection::async_ropen(beast::http::verb method, CompletionToken && completion_token) { return detail::faux_run(std::forward(completion_token), - this, method, path, std::ref(headers), std::ref(src), std::move(opt), jar); + shared_from_this(), method, path, + std::ref(headers), std::ref(src), std::move(opt), jar); +} + } } @@ -284,7 +283,7 @@ connection::async_ropen(beast::http::verb method, #if defined(BOOST_REQUESTS_HEADER_ONLY) -#include +#include #endif #endif // BOOST_REQUESTS_IMPL_CONNECTION_HPP diff --git a/include/boost/requests/impl/connection.ipp b/include/boost/requests/detail/impl/connection_impl.ipp similarity index 91% rename from include/boost/requests/impl/connection.ipp rename to include/boost/requests/detail/impl/connection_impl.ipp index 017e936..da77e65 100644 --- a/include/boost/requests/impl/connection.ipp +++ b/include/boost/requests/detail/impl/connection_impl.ipp @@ -8,10 +8,10 @@ #ifndef BOOST_REQUESTS_IMPL_CONNECTION_IPP #define BOOST_REQUESTS_IMPL_CONNECTION_IPP -#include -#include -#include +#include #include +#include +#include namespace boost { @@ -84,10 +84,8 @@ bool check_endpoint( } } -} - -auto connection::ropen(beast::http::verb method, +auto connection_impl::ropen(beast::http::verb method, urls::pct_string_view path, http::fields & headers, source & src, @@ -102,7 +100,7 @@ auto connection::ropen(beast::http::verb method, } -auto connection::ropen(beast::http::verb method, +auto connection_impl::ropen(beast::http::verb method, urls::pct_string_view path, http::fields & headers, source & src, @@ -182,7 +180,7 @@ auto connection::ropen(beast::http::verb method, if (ec) return stream{get_executor(), nullptr}; - stream str{get_executor(), this}; + stream str{get_executor(), shared_from_this()}; str.parser_ = detail::make_pmr>(headers.get_allocator().resource(), http::response_header{http::fields(headers.get_allocator())}); @@ -239,8 +237,7 @@ auto connection::ropen(beast::http::verb method, auto loc_itr = res.find(http::field::location); if (loc_itr == res.end()) { - static constexpr auto loc((BOOST_CURRENT_LOCATION)); - ec.assign(error::invalid_redirect, &loc); + BOOST_REQUESTS_ASSIGN_EC(ec, error::invalid_redirect); break ; } @@ -255,15 +252,13 @@ auto connection::ropen(beast::http::verb method, host_ == url->encoded_host() && !same_endpoint_on_host(*url, endpoint())) { - static constexpr auto sloc((BOOST_CURRENT_LOCATION)); - ec.assign(error::forbidden_redirect, &sloc); + BOOST_REQUESTS_ASSIGN_EC(ec, error::forbidden_redirect); break ; } if (--opt.max_redirects == 0) { - static constexpr auto sloc((BOOST_CURRENT_LOCATION)); - ec.assign(error::too_many_redirects, &sloc); + BOOST_REQUESTS_ASSIGN_EC(ec, error::too_many_redirects); break ; } @@ -284,20 +279,20 @@ auto connection::ropen(beast::http::verb method, read_lock = {}; } - stream str{get_executor(), this}; + stream str{get_executor(), shared_from_this()}; str.history_ = std::move(history); return str; } -void connection::set_host(core::string_view sv, system::error_code & ec) +void connection_impl::set_host(core::string_view sv, system::error_code & ec) { next_layer_.set_verify_callback(asio::ssl::host_name_verification(host_ = sv), ec); } BOOST_REQUESTS_DECL -auto connection::async_ropen_op::resume( +auto connection_impl::async_ropen_op::resume( requests::detail::faux_token_t self, system::error_code & ec, std::size_t res_) -> stream { @@ -474,7 +469,7 @@ auto connection::async_ropen_op::resume( } -void connection::connect(endpoint_type ep, system::error_code & ec) +void connection_impl::connect(endpoint_type ep, system::error_code & ec) { auto wlock = detail::lock(write_mtx_, ec); if (ec) @@ -491,7 +486,7 @@ void connection::connect(endpoint_type ep, system::error_code & ec) } -void connection::close(system::error_code & ec) +void connection_impl::close(system::error_code & ec) { auto wlock = detail::lock(write_mtx_, ec); if (ec) @@ -509,7 +504,7 @@ void connection::close(system::error_code & ec) } -std::size_t connection::do_read_some_(beast::http::basic_parser & parser) +std::size_t connection_impl::do_read_some_(beast::http::basic_parser & parser) { if (use_ssl_) return beast::http::read_some(next_layer_, buffer_, parser); @@ -517,7 +512,7 @@ std::size_t connection::do_read_some_(beast::http::basic_parser & parser) return beast::http::read_some(next_layer_.next_layer(), buffer_, parser); } -std::size_t connection::do_read_some_(beast::http::basic_parser & parser, system::error_code & ec) +std::size_t connection_impl::do_read_some_(beast::http::basic_parser & parser, system::error_code & ec) { if (use_ssl_) return beast::http::read_some(next_layer_, buffer_, parser, ec); @@ -525,7 +520,7 @@ std::size_t connection::do_read_some_(beast::http::basic_parser & parser, return beast::http::read_some(next_layer_.next_layer(), buffer_, parser, ec); } -void connection::do_async_read_some_(beast::http::basic_parser & parser, detail::faux_token_t tk) +void connection_impl::do_async_read_some_(beast::http::basic_parser & parser, detail::faux_token_t tk) { if (use_ssl_) beast::http::async_read_some(next_layer_, buffer_, parser, std::move(tk)); @@ -533,12 +528,12 @@ void connection::do_async_read_some_(beast::http::basic_parser & parser, beast::http::async_read_some(next_layer_.next_layer(), buffer_, parser, std::move(tk)); } -void connection::do_async_close_(detail::faux_token_t tk) +void connection_impl::do_async_close_(detail::faux_token_t tk) { async_close(std::move(tk)); } -void connection::async_connect_op::resume(requests::detail::faux_token_t self, +void connection_impl::async_connect_op::resume(requests::detail::faux_token_t self, system::error_code & ec) { BOOST_ASIO_CORO_REENTER(this) @@ -553,7 +548,7 @@ void connection::async_connect_op::resume(requests::detail::faux_token_t self, +void connection_impl::async_close_op::resume(requests::detail::faux_token_t self, system::error_code & ec) { BOOST_ASIO_CORO_REENTER(this) @@ -570,7 +565,7 @@ void connection::async_close_op::resume(requests::detail::faux_token_t; container::pmr::memory_resource * resource; + asio::any_io_executor executor; allocator_type get_allocator() override { return container::pmr::polymorphic_allocator{resource}; @@ -75,7 +76,7 @@ void mutex::lock(system::error_code & ec) var.notify_all(); } - impl(container::pmr::memory_resource * res ) : resource(res) {} + impl(container::pmr::memory_resource * res, asio::any_io_executor executor) : resource(res), executor(executor) {} system::error_code ec; bool done = false; std::condition_variable var; @@ -89,7 +90,7 @@ void mutex::lock(system::error_code & ec) char buf[4096]; container::pmr::monotonic_buffer_resource res{buf, sizeof(buf)}; - impl ip{&res}; + impl ip{&res, get_executor()}; std::unique_lock lock(mtx_); std::shared_ptr ptr{&ip, [](impl * ) {}}; diff --git a/include/boost/requests/detail/state_machine.hpp b/include/boost/requests/detail/state_machine.hpp deleted file mode 100644 index 9fa1e0f..0000000 --- a/include/boost/requests/detail/state_machine.hpp +++ /dev/null @@ -1,77 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BOOST_REQUESTS_STATE_MACHINE_HPP -#define BOOST_REQUESTS_STATE_MACHINE_HPP - -#include -#include -#include - -#define BOOST_REQUESTS_CHECK_ERROR_1(Expr) Expr(ec); \ - if (!ec.has_location()) \ - { \ - constexpr auto loc = BOOST_CURRENT_LOCATION; \ - ec = system::error_code(ec, &loc); \ - } \ - if (ec) \ - return - -#define BOOST_REQUESTS_CHECK_ERROR_N(Expr, ...) Expr(__VA_ARGS__, ec); \ - if (!ec.has_location()) \ - { \ - constexpr auto loc = BOOST_CURRENT_LOCATION; \ - ec = system::error_code(ec, &loc); \ - } \ - if (ec) \ - return - -#define BOOST_REQUESTS_CHECK_ERROR(...) \ - BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 1), BOOST_REQUESTS_CHECK_ERROR_1, BOOST_REQUESTS_CHECK_ERROR_N)(__VA_ARGS__) - -#define BOOST_REQUESTS_STATE(Name) if (false) Name: -/* -#define BOOST_REQUESTS_AINIT template void operator()(Self && self) - -#define BOOST_REQUESTS_ASTATE_1(Name) \ - struct Name##_tag {}; \ - template void operator()(Self && self, Name##_tag) - - -#define BOOST_REQUESTS_ASTATE_N(Name, ...) \ - struct Name##_tag {}; \ - template void operator()(Self && self, Name##_tag, __VA_ARGS__) - -#define BOOST_REQUESTS_ASTATE(...) \ - BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 1), BOOST_REQUESTS_ASTATE_1, BOOST_REQUESTS_ASTATE_N)(__VA_ARGS__) - -#define BOOST_REQUESTS_ANEXT_1(State) \ - boost::asio::prepend(std::move(self), State##_tag{}) - - -#define BOOST_REQUESTS_ANEXT_N(State, ...) \ - boost::asio::prepend(boost::asio::append(std::move(self), __VA_ARGS__), State##_tag{}) - - -#define BOOST_REQUESTS_ANEXT(...) \ - BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 1), BOOST_REQUESTS_ANEXT_1, BOOST_REQUESTS_ANEXT_N)(__VA_ARGS__) - -#define BOOST_REQUESTS_AGOTO_1(State) \ - return (*this)(std::move(self), State##_tag{}) - - -#define BOOST_REQUESTS_AGOTO_N(State, ...) \ - return (*this)(std::move(self), State##_tag{}, __VA_ARGS__) - -#define BOOST_REQUESTS_AGOTO(...) \ - BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 1), BOOST_REQUESTS_AGOTO_1, BOOST_REQUESTS_AGOTO_N)(__VA_ARGS__) - -#define BOOST_REQUESTS_ACOMPLETE(...) \ - return self.complete(__VA_ARGS__)*/ - - -#endif // BOOST_REQUESTS_STATE_MACHINE_HPP diff --git a/include/boost/requests/download.hpp b/include/boost/requests/download.hpp index c4bc260..84db4af 100644 --- a/include/boost/requests/download.hpp +++ b/include/boost/requests/download.hpp @@ -261,9 +261,9 @@ inline auto download(Connection & conn, if (ec) return download_response{std::move(ro).headers(), std::move(ro).history(), {}}; - if (filesystem::exists(download_path, ec) && filesystem::is_directory(download_path) && !target.empty()) + if (filesystem::exists(download_path, ec) && filesystem::is_directory(download_path, ec) && !target.empty()) download_path /= target.segments().back(); // so we can download to a folder - + ec.clear(); if (!ec) write_to_file(ro, download_path, ec); return download_response{std::move(ro).headers(), std::move(ro).history(), std::move(download_path)}; @@ -344,7 +344,7 @@ struct async_download_op : asio::coroutine rb.download_path = download_path / target.segments().back(); // so we can download to a folder else rb.download_path = std::move(download_path); - + ec.clear(); if (!ec) { BOOST_ASIO_CORO_YIELD async_write_to_file(*str_, rb.download_path, diff --git a/include/boost/requests/fields/impl/link.ipp b/include/boost/requests/fields/impl/link.ipp index 03207c6..3be636c 100644 --- a/include/boost/requests/fields/impl/link.ipp +++ b/include/boost/requests/fields/impl/link.ipp @@ -8,7 +8,7 @@ #ifndef BOOST_REQUESTS_FIELDS_IMPL_LINK_IPP #define BOOST_REQUESTS_FIELDS_IMPL_LINK_IPP -#include "boost/requests/grammar/alternate_rule.hpp" +#include #include #include #include diff --git a/include/boost/requests/fields/impl/set_cookie.ipp b/include/boost/requests/fields/impl/set_cookie.ipp index 129c7dc..7564133 100644 --- a/include/boost/requests/fields/impl/set_cookie.ipp +++ b/include/boost/requests/fields/impl/set_cookie.ipp @@ -5,11 +5,11 @@ #ifndef BOOST_REQUESTS_SET_COOKIE_IPP #define BOOST_REQUESTS_SET_COOKIE_IPP -#include "boost/requests/fields/set_cookie.hpp" -#include "boost/requests/grammar/any_char_except_ctl_semicolon.hpp" -#include "boost/requests/grammar/cookie_octet.hpp" -#include "boost/requests/grammar/cookie_token.hpp" -#include "boost/requests/grammar/domain.hpp" +#include +#include +#include +#include +#include #include namespace boost { diff --git a/include/boost/requests/grammar/token_rule.hpp b/include/boost/requests/grammar/token_rule.hpp index 08bc80c..05f3892 100644 --- a/include/boost/requests/grammar/token_rule.hpp +++ b/include/boost/requests/grammar/token_rule.hpp @@ -8,7 +8,7 @@ #ifndef BOOST_REQUESTS_GRAMMAR_TOKEN_RULE_HPP #define BOOST_REQUESTS_GRAMMAR_TOKEN_RULE_HPP -#include "boost/requests/detail/config.hpp" +#include #include namespace boost { diff --git a/include/boost/requests/impl/connection_pool.hpp b/include/boost/requests/impl/connection_pool.hpp index 278bf31..c92f048 100644 --- a/include/boost/requests/impl/connection_pool.hpp +++ b/include/boost/requests/impl/connection_pool.hpp @@ -63,20 +63,20 @@ struct connection_pool::async_get_connection_op : asio::coroutine using lock_type = detail::lock_guard; using conn_t = boost::unordered_multimap, + std::shared_ptr, detail::endpoint_hash>; typename conn_t::iterator itr; - std::shared_ptr nconn = nullptr; + std::shared_ptr nconn = nullptr; lock_type lock; endpoint_type ep; - using completion_signature_type = void(system::error_code, std::shared_ptr); + using completion_signature_type = void(system::error_code, connection); using step_signature_type = void(system::error_code); BOOST_REQUESTS_DECL auto resume(requests::detail::faux_token_t self, - system::error_code & ec) -> std::shared_ptr; + system::error_code & ec) -> connection; }; template)) CompletionToken> @@ -103,7 +103,7 @@ struct connection_pool::async_ropen_op : asio::coroutine request_options opt; cookie_jar * jar; - std::shared_ptr conn; + connection conn; async_ropen_op(connection_pool * this_, beast::http::verb method, @@ -130,12 +130,12 @@ struct connection_pool::async_ropen_op : asio::coroutine } using completion_signature_type = void(system::error_code, stream); - using step_signature_type = void(system::error_code, variant2::variant, stream>); + using step_signature_type = void(system::error_code, variant2::variant); BOOST_REQUESTS_DECL auto resume(requests::detail::faux_token_t self, system::error_code & ec, - variant2::variant, stream> res = variant2::monostate()) -> stream; + variant2::variant res = variant2::monostate()) -> stream; }; diff --git a/include/boost/requests/impl/connection_pool.ipp b/include/boost/requests/impl/connection_pool.ipp index 89f095f..0b4c38c 100644 --- a/include/boost/requests/impl/connection_pool.ipp +++ b/include/boost/requests/impl/connection_pool.ipp @@ -161,22 +161,22 @@ void connection_pool::async_lookup_op::resume(requests::detail::faux_token_t std::shared_ptr +auto connection_pool::get_connection(error_code & ec) -> connection { auto lock = detail::lock(mutex_, ec); if (ec) - return nullptr; + return connection(); // find an idle connection auto itr = std::find_if(conns_.begin(), conns_.end(), - [](const std::pair> & conn) + [](const std::pair> & conn) { return (conn.second->working_requests() == 0u) && conn.second->is_open(); }); if (itr != conns_.end()) - return itr->second; + return connection(itr->second); // check if we can make more connections. -> open another connection. // the race here is that we might open one too many @@ -185,7 +185,7 @@ auto connection_pool::get_connection(error_code & ec) -> std::shared_ptr std::shared_ptr nconn = - use_ssl_ ? std::make_shared(get_executor(), context_) - : std::make_shared(get_executor()); + std::shared_ptr nconn = + use_ssl_ ? std::make_shared(get_executor(), context_) + : std::make_shared(get_executor()); nconn->set_host(host_); nconn->connect(ep, ec); if (ec) - return nullptr; + return connection(); if (ec) - return nullptr; + return connection(); conns_.emplace(ep, nconn); - return nconn; + return connection(nconn); } // find the one with the lowest usage itr = std::min_element(conns_.begin(), conns_.end(), - [](const std::pair> & lhs, - const std::pair> & rhs) + [](const std::pair> & lhs, + const std::pair> & rhs) { return (lhs.second->working_requests() + (lhs.second->is_open() ? 0 : 1)) < (rhs.second->working_requests() + (rhs.second->is_open() ? 0 : 1)); @@ -223,16 +223,16 @@ auto connection_pool::get_connection(error_code & ec) -> std::shared_ptrsecond; + return connection(itr->second); } auto connection_pool::async_get_connection_op::resume( requests::detail::faux_token_t self, - system::error_code & ec) -> std::shared_ptr + system::error_code & ec) -> connection { BOOST_ASIO_CORO_REENTER (this) { @@ -241,19 +241,19 @@ auto connection_pool::async_get_connection_op::resume( BOOST_ASIO_CORO_YIELD this_->mutex_.async_lock(std::move(self)); } if (ec) - return nullptr; + return connection(); lock = {this_->mutex_, std::adopt_lock}; // find an idle connection itr = std::find_if(this_->conns_.begin(), this_->conns_.end(), - [](const std::pair> & conn) + [](const std::pair> & conn) { return (conn.second->working_requests() == 0u) && conn.second->is_open();; }); if (itr != this_->conns_.end()) - return itr->second; + return connection(itr->second); // check if we can make more connections. -> open another connection. // the race here is that we might open one too many @@ -262,7 +262,7 @@ auto connection_pool::async_get_connection_op::resume( if (this_->endpoints_.empty()) { BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); - return nullptr; + return connection(); } //sort the endpoints by connections that use it @@ -272,20 +272,20 @@ auto connection_pool::async_get_connection_op::resume( return this_->conns_.count(a) < this_->conns_.count(b); }); ep = this_->endpoints_.front(); - nconn = this_->use_ssl_ ? std::make_shared(get_executor(), this_->context_) - : std::make_shared(get_executor()); + nconn = this_->use_ssl_ ? std::make_shared(get_executor(), this_->context_) + : std::make_shared(get_executor()); nconn->set_host(this_->host_); BOOST_ASIO_CORO_YIELD nconn->async_connect(ep, std::move(self)); // don't unlock here. if (ec) - return nullptr; + return connection(); this_->conns_.emplace(ep, nconn); - return std::move(nconn); + return connection(std::move(nconn)); } // find the one with the lowest usage itr = std::min_element(this_->conns_.begin(), this_->conns_.end(), - [](const std::pair> & lhs, - const std::pair> & rhs) + [](const std::pair> & lhs, + const std::pair> & rhs) { return (lhs.second->working_requests() + (lhs.second->is_open() ? 0 : 1)) < (rhs.second->working_requests() + (rhs.second->is_open() ? 0 : 1)); @@ -293,29 +293,29 @@ auto connection_pool::async_get_connection_op::resume( if (itr == this_->conns_.end()) { BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); - return nullptr; + return connection(); } else - return itr->second; + return connection(itr->second); } - return nullptr; + return connection(); } stream connection_pool::async_ropen_op::resume( requests::detail::faux_token_t self, system::error_code & ec, - variant2::variant, stream> res) + variant2::variant res) { BOOST_ASIO_CORO_REENTER(this) { BOOST_ASIO_CORO_YIELD this_->async_get_connection(std::move(self)); - conn = variant2::get<1>(res); - if (!ec && conn == nullptr) - ec = asio::error::not_found; + conn = std::move(variant2::get<1>(res)); + if (!ec && !conn) + BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); if (ec) return stream{this_->get_executor(), nullptr}; - BOOST_ASIO_CORO_YIELD conn->async_ropen(method, path, headers, src, std::move(opt), jar, std::move(self)); + BOOST_ASIO_CORO_YIELD conn.async_ropen(method, path, headers, src, std::move(opt), jar, std::move(self)); return variant2::get<2>(std::move(res)); } return stream{get_executor(), nullptr}; diff --git a/include/boost/requests/impl/public_suffix.ipp b/include/boost/requests/impl/public_suffix.ipp index 62d6a5d..1b23a67 100644 --- a/include/boost/requests/impl/public_suffix.ipp +++ b/include/boost/requests/impl/public_suffix.ipp @@ -5,7 +5,7 @@ #ifndef BOOST_REQUESTS_COOKIES_IMPL_PUBLIC_SUFFIX_IPP #define BOOST_REQUESTS_COOKIES_IMPL_PUBLIC_SUFFIX_IPP -#include "boost/requests/public_suffix.hpp" +#include #include #include #include diff --git a/include/boost/requests/impl/stream.ipp b/include/boost/requests/impl/stream.ipp index 7d344d2..aeb1930 100644 --- a/include/boost/requests/impl/stream.ipp +++ b/include/boost/requests/impl/stream.ipp @@ -18,13 +18,13 @@ void stream::dump(system::error_code & ec) return; char data[65535]; - while (parser_->get().body().more) + while (!ec && parser_->get().body().more) { parser_->get().body().data = data; parser_->get().body().size = sizeof(data); impl_->do_read_some_(*parser_, ec); - if (!parser_->is_done()) + if (!ec && !parser_->is_done()) { parser_->get().body().more = true; if (ec == beast::http::error::need_buffer) @@ -34,7 +34,7 @@ void stream::dump(system::error_code & ec) parser_->get().body().more = false; } - if (!parser_->get().keep_alive()) + if (ec || !parser_->get().keep_alive()) { boost::system::error_code ec_; impl_->do_close_(ec_); @@ -57,28 +57,20 @@ std::size_t stream::async_read_some_op::resume(requests::detail::faux_token_tparser_) - BOOST_REQUESTS_ASSIGN_EC(ec_, asio::error::not_connected) + BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_connected) else if (!this_->parser_->get().body().more) - BOOST_REQUESTS_ASSIGN_EC(ec_, asio::error::eof) + BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::eof) + else if (buffer.size() == 0u) + BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::no_buffer_space) - if (ec_) - { - BOOST_ASIO_CORO_YIELD asio::post(this_->get_executor(), std::move(self)); - ec = ec_; - return 0u; - } - - if (buffer.size() == 0u) - { - BOOST_ASIO_CORO_YIELD asio::post(this_->get_executor(), asio::append(std::move(self), ec_)); - return 0u; - } + if (ec) + return std::size_t(-1); this_->parser_->get().body().data = buffer.data(); this_->parser_->get().body().size = buffer.size(); BOOST_ASIO_CORO_YIELD this_->impl_->do_async_read_some_(*this_->parser_, std::move(self)); - if (!this_->parser_->is_done()) + if (!ec && !this_->parser_->is_done()) { this_->parser_->get().body().more = true; if (ec == beast::http::error::need_buffer) @@ -87,7 +79,7 @@ std::size_t stream::async_read_some_op::resume(requests::detail::faux_token_tparser_->get().body().more = false; - if (!this_->parser_->get().keep_alive()) + if (ec || !this_->parser_->get().keep_alive()) { ec_ = ec ; BOOST_ASIO_CORO_YIELD this_->impl_->do_async_close_(std::move(self)); @@ -112,7 +104,7 @@ void stream::async_dump_op::resume(requests::detail::faux_token_tparser_->is_done()) + while (!ec && !this_->parser_->is_done()) { this_->parser_->get().body().data = buffer; this_->parser_->get().body().size = BOOST_REQUESTS_CHUNK_SIZE; @@ -120,7 +112,7 @@ void stream::async_dump_op::resume(requests::detail::faux_token_timpl_->do_async_read_some_(*this_->parser_, std::move(self)); } - if (!this_->parser_->get().keep_alive()) + if (ec || !this_->parser_->get().keep_alive()) { ec_ = ec ; BOOST_ASIO_CORO_YIELD this_->impl_->do_async_close_(std::move(self)); diff --git a/include/boost/requests/src.hpp b/include/boost/requests/src.hpp index 9d1d1a2..1c674fb 100644 --- a/include/boost/requests/src.hpp +++ b/include/boost/requests/src.hpp @@ -15,12 +15,12 @@ #define BOOST_REQUESTS_SOURCE #endif +#include #include #include #include #include #include -#include #include #include #include diff --git a/include/boost/requests/stream.hpp b/include/boost/requests/stream.hpp index 5368ba7..f5cfc48 100644 --- a/include/boost/requests/stream.hpp +++ b/include/boost/requests/stream.hpp @@ -6,11 +6,11 @@ // // this is on purpose -#include - #ifndef BOOST_REQUESTS_STREAM_HPP + #define BOOST_REQUESTS_STREAM_HPP +#include #include #include #include @@ -46,7 +46,7 @@ struct stream struct rebind_executor { /// The socket type when rebound to the specified executor. - using other = connection; + using other = defaulted; }; @@ -146,12 +146,11 @@ struct stream bool done() const {return !parser_ || parser_->is_done();} explicit stream(executor_type executor, std::nullptr_t ) : executor_{executor}, impl_(nullptr) {} - explicit stream(executor_type executor, - connection * impl, - detail::tracker t = {}) + explicit stream(executor_type executor, std::shared_ptr impl, detail::tracker t = {}) : executor_{executor}, - impl_(impl), - t_(std::move(t)) {} + impl_(std::move(impl)), + t_(std::move(t)) + {} http::response_header &&headers() && { @@ -174,7 +173,7 @@ struct stream } private: executor_type executor_; - connection* impl_; + std::shared_ptr impl_; detail::lock_guard lock_; std::unique_ptr, @@ -187,7 +186,7 @@ struct stream struct async_dump_op; struct async_read_some_op; - friend struct connection; + friend struct detail::connection_impl; }; template @@ -223,7 +222,7 @@ struct stream::defaulted : stream } } -#include +#include #include diff --git a/test/connection.cpp b/test/connection.cpp index cf329fd..9bd359b 100644 --- a/test/connection.cpp +++ b/test/connection.cpp @@ -3,14 +3,14 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#include #include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include #include "doctest.h" #include "string_maker.hpp" @@ -169,13 +169,13 @@ void http_request_connection(bool https) filesystem::remove(target); CHECK(!filesystem::exists(target)); - CHECK( filesystem::exists(tmp)); + CHECK_MESSAGE( filesystem::exists(tmp), tmp); auto res = download(hc, urls::url_view("/image"), {{}, {false}}, target); CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK(filesystem::exists(target)); + CHECK_MESSAGE(filesystem::exists(target), target); fs_error_code ec_; filesystem::remove(target, ec_); } @@ -196,7 +196,7 @@ void http_request_connection(bool https) CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK(filesystem::exists(target)); + CHECK_MESSAGE(filesystem::exists(target), target); fs_error_code ec; filesystem::remove(target, ec); } @@ -392,7 +392,7 @@ void run_tests(error_code ec, CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK(filesystem::exists(target)); + CHECK_MESSAGE(filesystem::exists(target), target); fs_error_code ec_; filesystem::remove(target, ec_); } @@ -417,7 +417,7 @@ void run_tests(error_code ec, CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK(filesystem::exists(target)); + CHECK_MESSAGE(filesystem::exists(target), target); fs_error_code ec_; filesystem::remove(target, ec_); } diff --git a/test/connection_pool.cpp b/test/connection_pool.cpp index abb56b1..37245eb 100644 --- a/test/connection_pool.cpp +++ b/test/connection_pool.cpp @@ -145,7 +145,7 @@ void http_request_connection_pool(bool https) CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK(filesystem::exists(target)); + CHECK_MESSAGE(filesystem::exists(target), target); fs_error_code ec; filesystem::remove(target, ec); } @@ -166,7 +166,7 @@ void http_request_connection_pool(bool https) CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK(filesystem::exists(target)); + CHECK_MESSAGE(filesystem::exists(target), target); fs_error_code ec; filesystem::remove(target, ec); } @@ -362,7 +362,7 @@ void run_tests(error_code ec, CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK(filesystem::exists(target)); + CHECK_MESSAGE(filesystem::exists(target), target); fs_error_code ec_; filesystem::remove(target, ec_); } @@ -387,7 +387,7 @@ void run_tests(error_code ec, CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK(filesystem::exists(target)); + CHECK_MESSAGE(filesystem::exists(target), target); fs_error_code ec_; filesystem::remove(target, ec_); } diff --git a/test/cookie.cpp b/test/cookie.cpp index 1ac7d37..16ba752 100644 --- a/test/cookie.cpp +++ b/test/cookie.cpp @@ -3,14 +3,14 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#include #include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include #include "doctest.h" #include "string_maker.hpp" diff --git a/test/method.cpp b/test/method.cpp index 37e0f77..d011089 100644 --- a/test/method.cpp +++ b/test/method.cpp @@ -150,7 +150,7 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK(filesystem::exists(target)); + CHECK_MESSAGE(filesystem::exists(target), target); fs_error_code ec; filesystem::remove(target, ec); } @@ -172,7 +172,7 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK(filesystem::exists(target)); + CHECK_MESSAGE(filesystem::exists(target), target); fs_error_code ec; filesystem::remove(target, ec); } @@ -374,7 +374,7 @@ void async_http_pool_request(asio::any_io_executor exec, CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK(filesystem::exists(target)); + CHECK_MESSAGE(filesystem::exists(target), target); filesystem::remove(target, ec); } })); @@ -399,7 +399,7 @@ void async_http_pool_request(asio::any_io_executor exec, CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK(filesystem::exists(target)); + CHECK_MESSAGE(filesystem::exists(target), target); filesystem::remove(target, ec); } })); diff --git a/test/session.cpp b/test/session.cpp index d30c683..5f78283 100644 --- a/test/session.cpp +++ b/test/session.cpp @@ -150,7 +150,7 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK(filesystem::exists(target)); + CHECK_MESSAGE(filesystem::exists(target), target); fs_error_code ec; filesystem::remove(target, ec); } @@ -172,7 +172,7 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK(filesystem::exists(target)); + CHECK_MESSAGE(filesystem::exists(target), target); fs_error_code ec; filesystem::remove(target, ec); } @@ -371,7 +371,7 @@ void async_http_pool_request(requests::session & sess, CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK(filesystem::exists(target)); + CHECK_MESSAGE(filesystem::exists(target), target); fs_error_code ec_; filesystem::remove(target, ec_); } @@ -396,7 +396,7 @@ void async_http_pool_request(requests::session & sess, CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK(filesystem::exists(target)); + CHECK_MESSAGE(filesystem::exists(target), target); fs_error_code ec_; filesystem::remove(target, ec_); } diff --git a/test/string_maker.hpp b/test/string_maker.hpp index beb9e58..26e42a1 100644 --- a/test/string_maker.hpp +++ b/test/string_maker.hpp @@ -48,9 +48,10 @@ struct StringMaker static String convert(boost::system::error_code ec) { if (ec.has_location()) - return toString(ec.location().to_string() + ": " + ec.message()); + return toString( + ec.location().to_string() + " [" + std::to_string(ec.value()) + "/" + ec.category().name() + "]: " + ec.message()); else - return toString(ec.message()); + return toString("[" + std::to_string(ec.value()) + "/" + ec.category().name() + "]: " + ec.message()); } }; @@ -151,8 +152,6 @@ struct StringMaker inline void check_ec(boost::system::error_code ec, boost::source_location loc = BOOST_CURRENT_LOCATION) { - if (ec.has_location()) - loc = ec.location(); doctest::detail::ResultBuilder rb(doctest::assertType::DT_REQUIRE, loc.file_name(), loc.line(), loc.function_name()); rb.setResult(doctest::detail::Result{!ec, doctest::StringMaker< boost::system::error_code>::convert(ec)}); DOCTEST_ASSERT_LOG_AND_REACT(rb); From d4e459962be777cf2d7c99cc9e746e50ae940f4c Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 31 Jan 2023 12:31:36 +0800 Subject: [PATCH 15/76] Ci fixes. --- .drone.star | 3 +-- example/github-issues.hpp | 4 ++-- include/boost/requests/sources/file.hpp | 4 ++-- test/method.cpp | 1 + 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.drone.star b/.drone.star index 8f56573..d7a0086 100644 --- a/.drone.star +++ b/.drone.star @@ -28,8 +28,7 @@ def main(ctx): linux_cxx("clang 3.8", "clang++-3.8", packages="clang-3.8", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu1604:1", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-3.8', 'B2_CXXSTD': '14', 'DRONE_JOB_UUID': '7b52009b64'}, globalenv=globalenv), osx_cxx("clang", "g++", packages="", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'B2_TOOLSET': 'clang', 'B2_CXXSTD': '14,17', 'DRONE_JOB_UUID': '91032ad7bb'}, globalenv=globalenv), linux_cxx("coverity", "g++", packages="", buildtype="coverity", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'COMMENT': 'Coverity Scan', 'B2_TOOLSET': 'clang', 'DRONE_JOB_UUID': '472b07b9fc'}, globalenv=globalenv), - windows_cxx("msvc-14.1", "", image="cppalliance/dronevs2017", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "VARIANT": "release", "TOOLSET": "msvc-14.1", "CXXSTD": "14", "DEFINE" : "BOOST_BEAST_USE_STD_STRING_VIEW", "ADDRESS_MODEL": "64"}), - windows_cxx("msvc-14.3", "", image="cppalliance/dronevs2022:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "VARIANT": "release", "TOOLSET": "msvc-14.3", "CXXSTD": "14", "DEFINE" : "BOOST_BEAST_USE_STD_STRING_VIEW", "ADDRESS_MODEL": "64"}), + windows_cxx("msvc-14.3", "", image="cppalliance/dronevs2022:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "VARIANT": "release", "TOOLSET": "msvc-14.3", "CXXSTD": "17", "DEFINE" : "BOOST_BEAST_USE_STD_STRING_VIEW", "ADDRESS_MODEL": "64"}), ] # from https://github.com/boostorg/boost-ci diff --git a/example/github-issues.hpp b/example/github-issues.hpp index dc199be..d6c7df7 100644 --- a/example/github-issues.hpp +++ b/example/github-issues.hpp @@ -502,9 +502,9 @@ struct issue_client issue_client( boost::asio::io_context & ctx, const std::string & auth_token, - BOOST_ASIO_STRING_VIEW_PARAM host_name = "api.github.com") : conn_(ctx.get_executor(), sslctx_) + const std::string & host_name = "api.github.com") : conn_(ctx.get_executor(), sslctx_) { - conn_.set_host({host_name.begin(), host_name.end()}); + conn_.set_host(host_name); boost::asio::ip::tcp::resolver res{ctx}; conn_.connect(boost::asio::ip::tcp::endpoint(*(res.resolve(host_name, "https").begin()))); settings_.fields = boost::requests::headers({ diff --git a/include/boost/requests/sources/file.hpp b/include/boost/requests/sources/file.hpp index 3a3ce85..c9279c4 100644 --- a/include/boost/requests/sources/file.hpp +++ b/include/boost/requests/sources/file.hpp @@ -74,11 +74,11 @@ struct file_source : source }; BOOST_REQUESTS_DECL file_source tag_invoke(const make_source_tag&, const boost::filesystem::path & path); -BOOST_REQUESTS_DECL file_source tag_invoke(const make_source_tag&, boost::filesystem::path &&) = delete; +file_source tag_invoke(const make_source_tag&, boost::filesystem::path &&) = delete; #if defined(__cpp_lib_filesystem) BOOST_REQUESTS_DECL file_source tag_invoke(const make_source_tag&, const std::filesystem::path & path); -BOOST_REQUESTS_DECL file_source tag_invoke(const make_source_tag&, std::filesystem::path &&) = delete; +file_source tag_invoke(const make_source_tag&, std::filesystem::path &&) = delete; #endif } diff --git a/test/method.cpp b/test/method.cpp index d011089..9f43fbe 100644 --- a/test/method.cpp +++ b/test/method.cpp @@ -363,6 +363,7 @@ void async_http_pool_request(asio::any_io_executor exec, if (filesystem::exists(target)) filesystem::remove(target); + CHECK_MESSAGE(filesystem::exists(pt), pt); CHECK(!filesystem::exists(target)); requests::async_download(u("/image"), {}, target, tracker(exec, From 3fb026eb2288c255e677acaac61c0a23c658a4d5 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 31 Jan 2023 13:39:24 +0800 Subject: [PATCH 16/76] Ci fixes. --- include/boost/requests/form.hpp | 1 - test/quoted_string.cpp | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/boost/requests/form.hpp b/include/boost/requests/form.hpp index c2b605e..8443e8e 100644 --- a/include/boost/requests/form.hpp +++ b/include/boost/requests/form.hpp @@ -26,7 +26,6 @@ struct form form(form && ) = default; form(const form & ) = default; - form(form & rhs) : storage(rhs.storage) {} template form(Container && ct) diff --git a/test/quoted_string.cpp b/test/quoted_string.cpp index f108ee4..7bd55b2 100644 --- a/test/quoted_string.cpp +++ b/test/quoted_string.cpp @@ -22,13 +22,13 @@ TEST_CASE("quoted_string") CHECK(ug::parse(R"("foobar")", br::rfc::quoted_string) == "\"foobar\""); CHECK(ug::parse(R"(foobar)", br::rfc::quoted_string) == ug::error::mismatch); - - CHECK(ug::parse(R"("foo\"bar")", br::rfc::quoted_string) == "\"foo\\\"bar\""); +#if !defined(_MSC_VER) + CHECK(ug::parse(R"("foo\\\"bar\")", br::rfc::quoted_string) == "\"foo\\\"bar\""); CHECK( br::rfc::unquote_string(ug::parse(R"("foo\"bar")", br::rfc::quoted_string).value()) == "foo\"bar"); - +#endif } TEST_SUITE_END(); From 3672120ddba1e3aff41aa8e7f877e1f55aedef88 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 1 Feb 2023 17:26:54 +0800 Subject: [PATCH 17/76] Ci fixes. --- .drone/drone.sh | 2 +- include/boost/requests/form.hpp | 2 +- include/boost/requests/json.hpp | 9 +++++---- include/boost/requests/source.hpp | 16 ++++++++-------- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/.drone/drone.sh b/.drone/drone.sh index 08e3020..a8e4e43 100755 --- a/.drone/drone.sh +++ b/.drone/drone.sh @@ -91,7 +91,7 @@ python tools/boostdep/depinst/depinst.py ../tools/quickbook ./bootstrap.sh ./b2 headers -cp libs/beast/tools/user-config.jam ~/user-config.jam +cp libs/requests/tools/user-config.jam ~/user-config.jam echo "using $TOOLSET : : $COMPILER : $CXX_FLAGS ;" >> ~/user-config.jam echo '==================================> SCRIPT' diff --git a/include/boost/requests/form.hpp b/include/boost/requests/form.hpp index 8443e8e..f417c38 100644 --- a/include/boost/requests/form.hpp +++ b/include/boost/requests/form.hpp @@ -28,7 +28,7 @@ struct form form(const form & ) = default; template - form(Container && ct) + form(Container && ct, decltype(std::begin(ct)) * = nullptr) { storage.params().assign(std::begin(ct), std::end(ct)); } diff --git a/include/boost/requests/json.hpp b/include/boost/requests/json.hpp index 630b2b7..1471850 100644 --- a/include/boost/requests/json.hpp +++ b/include/boost/requests/json.hpp @@ -8,10 +8,7 @@ #ifndef BOOST_REQUESTS_JSON_HPP #define BOOST_REQUESTS_JSON_HPP -#include -#include -#include -#include + #include #include #include @@ -19,6 +16,10 @@ #include #include #include +#include +#include +#include +#include #include #include diff --git a/include/boost/requests/source.hpp b/include/boost/requests/source.hpp index d924109..30bdceb 100644 --- a/include/boost/requests/source.hpp +++ b/include/boost/requests/source.hpp @@ -27,14 +27,6 @@ struct source virtual core::string_view default_content_type() {return "";} }; -struct make_source_tag {}; - -template -auto make_source(Source && source) -> decltype(tag_invoke(make_source_tag{}, std::declval())) -{ - return tag_invoke(make_source_tag{}, std::forward(source)); -} - template auto tag_invoke(const make_source_tag&, Source && s) -> std::enable_if_t< @@ -44,6 +36,14 @@ auto tag_invoke(const make_source_tag&, Source && s) return std::forward(s); } +struct make_source_tag {}; + +template +auto make_source(Source && source) -> decltype(tag_invoke(make_source_tag{}, std::declval())) +{ + return tag_invoke(make_source_tag{}, std::forward(source)); +} + template std::size_t write_request( Stream & stream, From 1aef49d19591ecdcd4399e2abb1dfd66abfdfc90 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 1 Feb 2023 17:30:24 +0800 Subject: [PATCH 18/76] Ci fixes. --- include/boost/requests/source.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/boost/requests/source.hpp b/include/boost/requests/source.hpp index 30bdceb..3413349 100644 --- a/include/boost/requests/source.hpp +++ b/include/boost/requests/source.hpp @@ -27,6 +27,9 @@ struct source virtual core::string_view default_content_type() {return "";} }; +struct make_source_tag {}; + + template auto tag_invoke(const make_source_tag&, Source && s) -> std::enable_if_t< @@ -36,7 +39,6 @@ auto tag_invoke(const make_source_tag&, Source && s) return std::forward(s); } -struct make_source_tag {}; template auto make_source(Source && source) -> decltype(tag_invoke(make_source_tag{}, std::declval())) From 2faad75d3c760802d4e59d8cb3f929164959018b Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 1 Feb 2023 18:10:29 +0800 Subject: [PATCH 19/76] Trying to add ssl for osx --- test/Jamfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Jamfile b/test/Jamfile index f354456..7ed305c 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -19,5 +19,6 @@ requirements ../../.. alias asio : ../../beast//lib-asio ; alias asio-ssl : ../../beast//lib-asio-ssl ; alias beast : ../../beast//lib-beast ; +alias ssl : ../../beast//ssl ; -run [ glob-tree *.cpp ] /boost//json /boost//url asio asio-ssl beast ; \ No newline at end of file +run [ glob-tree *.cpp ] /boost//json /boost//url asio asio-ssl beast ssl ; \ No newline at end of file From 76c33cec260ea46f49d75298e213a8897e270587 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 1 Feb 2023 18:13:55 +0800 Subject: [PATCH 20/76] added explicit ssl to build script --- example/Jamfile | 4 ++-- include/boost/requests/detail/impl/connection_impl.hpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/example/Jamfile b/example/Jamfile index 31aebdd..bce1df8 100644 --- a/example/Jamfile +++ b/example/Jamfile @@ -21,12 +21,12 @@ requirements ../../.. alias asio : ../../beast//lib-asio ; alias asio-ssl : ../../beast//lib-asio-ssl ; alias beast : ../../beast//lib-beast ; - +alias ssl : ../../beast//ssl ; import regex ; for local src in [ glob *.cpp : xml.cpp ] { local name = [ regex.match "([^.]*)\\..*" : $(src) ] ; - exe $(name) : $(src) /boost//json /boost//url asio beast asio-ssl ; + exe $(name) : $(src) /boost//json /boost//url asio asio-ssl beast ssl ; } diff --git a/include/boost/requests/detail/impl/connection_impl.hpp b/include/boost/requests/detail/impl/connection_impl.hpp index fe10bdd..dba8492 100644 --- a/include/boost/requests/detail/impl/connection_impl.hpp +++ b/include/boost/requests/detail/impl/connection_impl.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include From 222b84b5680e6e8ff6807af11cf320c4609f1d42 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Fri, 3 Feb 2023 02:25:52 +0800 Subject: [PATCH 21/76] fixed quoted_string parser rule. --- include/boost/requests/rfc/impl/quoted_string.ipp | 6 +++--- test/link.cpp | 2 +- test/quoted_string.cpp | 15 +++++---------- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/include/boost/requests/rfc/impl/quoted_string.ipp b/include/boost/requests/rfc/impl/quoted_string.ipp index 04f14bc..21b9441 100644 --- a/include/boost/requests/rfc/impl/quoted_string.ipp +++ b/include/boost/requests/rfc/impl/quoted_string.ipp @@ -26,11 +26,11 @@ quoted_string_t::parse( namespace ug = boost::urls::grammar; if (it == end) - return urls::grammar::error::need_more; + BOOST_REQUESTS_RETURN_EC(urls::grammar::error::need_more); const auto it0 = it; if (*it != '"') - return urls::grammar::error::mismatch; + BOOST_REQUESTS_RETURN_EC(urls::grammar::error::mismatch); it++; while (it != end && *it != '"') { @@ -41,7 +41,7 @@ quoted_string_t::parse( } if (*it != '"') - return urls::grammar::error::mismatch; + BOOST_REQUESTS_RETURN_EC(urls::grammar::error::mismatch); return value_type(it0, ++it); } diff --git a/test/link.cpp b/test/link.cpp index a3f93b2..697f6bf 100644 --- a/test/link.cpp +++ b/test/link.cpp @@ -12,7 +12,7 @@ #include "string_maker.hpp" -TEST_SUITE_BEGIN("rfc"); +TEST_SUITE_BEGIN("rfc.link"); TEST_CASE("link") { diff --git a/test/quoted_string.cpp b/test/quoted_string.cpp index 7bd55b2..a15537a 100644 --- a/test/quoted_string.cpp +++ b/test/quoted_string.cpp @@ -13,22 +13,17 @@ #include "string_maker.hpp" -TEST_SUITE_BEGIN("rfc"); +TEST_SUITE_BEGIN("rfc.quoted_string"); TEST_CASE("quoted_string") { namespace br = boost::requests; namespace ug = boost::urls::grammar; - CHECK(ug::parse(R"("foobar")", br::rfc::quoted_string) == "\"foobar\""); - CHECK(ug::parse(R"(foobar)", br::rfc::quoted_string) == ug::error::mismatch); -#if !defined(_MSC_VER) - CHECK(ug::parse(R"("foo\\\"bar\")", br::rfc::quoted_string) == "\"foo\\\"bar\""); - - CHECK( - br::rfc::unquote_string(ug::parse(R"("foo\"bar")", br::rfc::quoted_string).value()) - == "foo\"bar"); -#endif + CHECK(ug::parse("\"foobar\"", br::rfc::quoted_string) == "\"foobar\""); + CHECK(ug::parse("foobar", br::rfc::quoted_string) == ug::error::mismatch); + CHECK(ug::parse("\"foo\\\"bar\"", br::rfc::quoted_string) == "\"foo\\\"bar\""); + CHECK(br::rfc::unquote_string(ug::parse("\"foo\\\"bar\"", br::rfc::quoted_string).value())== "foo\"bar"); } TEST_SUITE_END(); From b897727c579559d48921b6553ee53cd11c40903e Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Fri, 3 Feb 2023 02:58:46 +0800 Subject: [PATCH 22/76] Added links to build scripts. --- build/Jamfile | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/build/Jamfile b/build/Jamfile index d86d629..e26b2a5 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -25,9 +25,58 @@ alias requests_sources explicit requests_deps requests_sources ; +import os ; +lib ssl ; +lib crypto ; +lib crypt32 ; + +# Microsoft Windows section. Refer to FAQ "Windows and OpenSSL" +if [ os.name ] = NT +{ + local OPENSSL_ROOT_DEFAULT = "C:/OpenSSL" ; + local OPENSSL_ROOT_ENV = [ os.environ OPENSSL_ROOT ] ; + local OPENSSL_ROOT = "" ; + if $(OPENSSL_ROOT_ENV) + { + OPENSSL_ROOT = $(OPENSSL_ROOT_ENV) ; + } + else + { + OPENSSL_ROOT = $(OPENSSL_ROOT_DEFAULT) ; + } + project + : requirements + $(OPENSSL_ROOT)/include + debug:$(OPENSSL_ROOT)/lib + windowsdebug:$(OPENSSL_ROOT)/debug/lib + release:$(OPENSSL_ROOT)/lib + ; + + if [ path.exists $(OPENSSL_ROOT)/lib/libssl.lib ] + { + echo "OpenSSL > 1.1.0. Including libssl" ; + lib ssl : : windows libssl ; + } + if [ path.exists $(OPENSSL_ROOT)/lib/libcrypto.lib ] + { + echo "OpenSSL > 1.1.0. Including libcrypto" ; + lib crypto : : windows libcrypto ; + } + if [ path.exists $(OPENSSL_ROOT)/lib/ssleay32.lib ] + { + echo "OpenSSL < 1.1.0. Including ssleay32" ; + lib ssl : : windows ssleay32 ; + } + if [ path.exists $(OPENSSL_ROOT)/lib/libeay32.lib ] + { + echo "OpenSSL < 1.1.0. Including libeay32" ; + lib crypto : : windows libeay32 ; + } +} + lib boost_requests - : requests_sources + : requests_sources ssl /boost//url /boost//json : usage-requirements /boost//container/off From da187916c6b124a26e4b4930b68f3650edcf5151 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Fri, 3 Feb 2023 02:58:46 +0800 Subject: [PATCH 23/76] Added links to build scripts. --- build/Jamfile | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/build/Jamfile b/build/Jamfile index e26b2a5..f1f0b5f 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -75,6 +75,55 @@ if [ os.name ] = NT } +lib ssl ; +lib crypto ; +lib crypt32 ; + +# Microsoft Windows section. Refer to FAQ "Windows and OpenSSL" +if [ os.name ] = NT +{ + local OPENSSL_ROOT_DEFAULT = "C:/OpenSSL" ; + local OPENSSL_ROOT_ENV = [ os.environ OPENSSL_ROOT ] ; + local OPENSSL_ROOT = "" ; + if $(OPENSSL_ROOT_ENV) + { + OPENSSL_ROOT = $(OPENSSL_ROOT_ENV) ; + } + else + { + OPENSSL_ROOT = $(OPENSSL_ROOT_DEFAULT) ; + } + project + : requirements + $(OPENSSL_ROOT)/include + debug:$(OPENSSL_ROOT)/lib + windowsdebug:$(OPENSSL_ROOT)/debug/lib + release:$(OPENSSL_ROOT)/lib + ; + + if [ path.exists $(OPENSSL_ROOT)/lib/libssl.lib ] + { + echo "OpenSSL > 1.1.0. Including libssl" ; + lib ssl : : windows libssl ; + } + if [ path.exists $(OPENSSL_ROOT)/lib/libcrypto.lib ] + { + echo "OpenSSL > 1.1.0. Including libcrypto" ; + lib crypto : : windows libcrypto ; + } + if [ path.exists $(OPENSSL_ROOT)/lib/ssleay32.lib ] + { + echo "OpenSSL < 1.1.0. Including ssleay32" ; + lib ssl : : windows ssleay32 ; + } + if [ path.exists $(OPENSSL_ROOT)/lib/libeay32.lib ] + { + echo "OpenSSL < 1.1.0. Including libeay32" ; + lib crypto : : windows libeay32 ; + } +} + + lib boost_requests : requests_sources ssl /boost//url /boost//json : usage-requirements From ab815791035fe139db7444596c9b614dfb439ecd Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Fri, 3 Feb 2023 10:18:50 +0800 Subject: [PATCH 24/76] Added `import path ;` --- build/Jamfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/Jamfile b/build/Jamfile index f1f0b5f..4d29575 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -26,6 +26,8 @@ alias requests_sources explicit requests_deps requests_sources ; import os ; +import path ; + lib ssl ; lib crypto ; lib crypt32 ; From 61971a1906c298bc89c1e79305ef687d7a2ae5b8 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Fri, 3 Feb 2023 12:51:40 +0800 Subject: [PATCH 25/76] Another fix attempt. --- Jamfile | 49 ------------------------------------------------- build/Jamfile | 2 +- test/Jamfile | 2 ++ 3 files changed, 3 insertions(+), 50 deletions(-) diff --git a/Jamfile b/Jamfile index 7f3ed34..4a9b8d3 100644 --- a/Jamfile +++ b/Jamfile @@ -17,55 +17,6 @@ build-project test ; boost.use-project ; -lib ssl ; -lib crypto ; -lib crypt32 ; - -# Microsoft Windows section. Refer to FAQ "Windows and OpenSSL" -if [ os.name ] = NT -{ - local OPENSSL_ROOT_DEFAULT = "C:/OpenSSL" ; - local OPENSSL_ROOT_ENV = [ os.environ OPENSSL_ROOT ] ; - local OPENSSL_ROOT = "" ; - if $(OPENSSL_ROOT_ENV) - { - OPENSSL_ROOT = $(OPENSSL_ROOT_ENV) ; - } - else - { - OPENSSL_ROOT = $(OPENSSL_ROOT_DEFAULT) ; - } - project - : requirements - $(OPENSSL_ROOT)/include - debug:$(OPENSSL_ROOT)/lib - windowsdebug:$(OPENSSL_ROOT)/debug/lib - release:$(OPENSSL_ROOT)/lib - ; - - if [ path.exists $(OPENSSL_ROOT)/lib/libssl.lib ] - { - echo "OpenSSL > 1.1.0. Including libssl" ; - lib ssl : : windows libssl ; - } - if [ path.exists $(OPENSSL_ROOT)/lib/libcrypto.lib ] - { - echo "OpenSSL > 1.1.0. Including libcrypto" ; - lib crypto : : windows libcrypto ; - } - if [ path.exists $(OPENSSL_ROOT)/lib/ssleay32.lib ] - { - echo "OpenSSL < 1.1.0. Including ssleay32" ; - lib ssl : : windows ssleay32 ; - } - if [ path.exists $(OPENSSL_ROOT)/lib/libeay32.lib ] - { - echo "OpenSSL < 1.1.0. Including libeay32" ; - lib crypto : : windows libeay32 ; - } -} - - feature.feature boost.requests.separate-compilation : on off : propagated composite ; feature.compose on : BOOST_REQUESTS_SEPARATE_COMPILATION ; diff --git a/build/Jamfile b/build/Jamfile index 4d29575..55158fc 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -127,7 +127,7 @@ if [ os.name ] = NT lib boost_requests - : requests_sources ssl /boost//url /boost//json + : requests_sources crypto ssl /boost//url /boost//json : usage-requirements /boost//container/off diff --git a/test/Jamfile b/test/Jamfile index 7ed305c..18a54a3 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -15,6 +15,8 @@ requirements ../../.. off:/boost//filesystem ; +import testing ; + alias asio : ../../beast//lib-asio ; alias asio-ssl : ../../beast//lib-asio-ssl ; From 2fc5a6e18ac38b988e200b5bd8204f3253dc0a14 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Fri, 3 Feb 2023 14:33:16 +0800 Subject: [PATCH 26/76] update build/Jamefile --- build/Jamfile | 56 ++++++--------------------------------------------- 1 file changed, 6 insertions(+), 50 deletions(-) diff --git a/build/Jamfile b/build/Jamfile index 55158fc..b61ffe1 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -27,6 +27,7 @@ explicit requests_deps requests_sources ; import os ; import path ; +import ac ; lib ssl ; lib crypto ; @@ -76,58 +77,13 @@ if [ os.name ] = NT } } - -lib ssl ; -lib crypto ; -lib crypt32 ; - -# Microsoft Windows section. Refer to FAQ "Windows and OpenSSL" -if [ os.name ] = NT -{ - local OPENSSL_ROOT_DEFAULT = "C:/OpenSSL" ; - local OPENSSL_ROOT_ENV = [ os.environ OPENSSL_ROOT ] ; - local OPENSSL_ROOT = "" ; - if $(OPENSSL_ROOT_ENV) - { - OPENSSL_ROOT = $(OPENSSL_ROOT_ENV) ; - } - else - { - OPENSSL_ROOT = $(OPENSSL_ROOT_DEFAULT) ; - } - project - : requirements - $(OPENSSL_ROOT)/include - debug:$(OPENSSL_ROOT)/lib - windowsdebug:$(OPENSSL_ROOT)/debug/lib - release:$(OPENSSL_ROOT)/lib - ; - - if [ path.exists $(OPENSSL_ROOT)/lib/libssl.lib ] - { - echo "OpenSSL > 1.1.0. Including libssl" ; - lib ssl : : windows libssl ; - } - if [ path.exists $(OPENSSL_ROOT)/lib/libcrypto.lib ] - { - echo "OpenSSL > 1.1.0. Including libcrypto" ; - lib crypto : : windows libcrypto ; - } - if [ path.exists $(OPENSSL_ROOT)/lib/ssleay32.lib ] - { - echo "OpenSSL < 1.1.0. Including ssleay32" ; - lib ssl : : windows ssleay32 ; - } - if [ path.exists $(OPENSSL_ROOT)/lib/libeay32.lib ] - { - echo "OpenSSL < 1.1.0. Including libeay32" ; - lib crypto : : windows libeay32 ; - } -} - - lib boost_requests : requests_sources crypto ssl /boost//url /boost//json + : requirements + + [ ac.check-library ssl : ssl/shared : no ] + [ ac.check-library crypto : crypto/shared : no ] + : usage-requirements /boost//container/off From 13015901f76f95ad7b64432dc50231d5bb527edb Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Fri, 3 Feb 2023 17:25:16 +0800 Subject: [PATCH 27/76] Changed http to httpbin.org bc of certs --- .drone.star | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.drone.star b/.drone.star index d7a0086..5e87c99 100644 --- a/.drone.star +++ b/.drone.star @@ -14,21 +14,21 @@ windowsglobalimage="cppalliance/dronevs2019" def main(ctx): return [ - linux_cxx("docs", "", packages="docbook docbook-xml docbook-xsl xsltproc libsaxonhe-java default-jre-headless flex libfl-dev bison unzip rsync mlocate", image="cppalliance/droneubuntu1804:1", buildtype="docs", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "COMMENT": "docs"}, globalenv=globalenv), - linux_cxx("asan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'COMMENT': 'asan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '14', 'B2_ASAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_EXTRA_PRIVILEGED': 'True', 'DRONE_JOB_UUID': '356a192b79'}, globalenv=globalenv, privileged=True), - linux_cxx("ubsan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'COMMENT': 'ubsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '14', 'B2_UBSAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'B2_LINKFLAGS': '-fuse-ld=gold', 'DRONE_JOB_UUID': '77de68daec'}, globalenv=globalenv), - linux_cxx("gcc 11 arm64", "g++-11", packages="g++-11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:multiarch", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'B2_TOOLSET': 'gcc-11', 'B2_CXXSTD': '14', 'DRONE_JOB_UUID': '17ba079169m'}, arch="arm64", globalenv=globalenv), + linux_cxx("docs", "", packages="docbook docbook-xml docbook-xsl xsltproc libsaxonhe-java default-jre-headless flex libfl-dev bison unzip rsync mlocate", image="cppalliance/droneubuntu1804:1", buildtype="docs", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", "COMMENT": "docs"}, globalenv=globalenv), + linux_cxx("asan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", 'COMMENT': 'asan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '14', 'B2_ASAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_EXTRA_PRIVILEGED': 'True', 'DRONE_JOB_UUID': '356a192b79'}, globalenv=globalenv, privileged=True), + linux_cxx("ubsan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", 'COMMENT': 'ubsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '14', 'B2_UBSAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'B2_LINKFLAGS': '-fuse-ld=gold', 'DRONE_JOB_UUID': '77de68daec'}, globalenv=globalenv), + linux_cxx("gcc 11 arm64", "g++-11", packages="g++-11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:multiarch", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", 'B2_TOOLSET': 'gcc-11', 'B2_CXXSTD': '14', 'DRONE_JOB_UUID': '17ba079169m'}, arch="arm64", globalenv=globalenv), linux_cxx("GCC 10, Debug + Coverage", "g++-10", packages="g++-10 libssl-dev libffi-dev binutils-gold gdb mlocate", - image="cppalliance/droneubuntu2004:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "GCOV": "gcov-10", "LCOV_VERSION": "1.15", "VARIANT": "process_coverage", "TOOLSET": "gcc", "COMPILER": "g++-10", "CXXSTD": "14", "DRONE_BEFORE_INSTALL" : "process_coverage", "CODECOV_TOKEN": {"from_secret": "codecov_token"}}, globalenv=globalenv, privileged=True), + image="cppalliance/droneubuntu2004:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", "GCOV": "gcov-10", "LCOV_VERSION": "1.15", "VARIANT": "process_coverage", "TOOLSET": "gcc", "COMPILER": "g++-10", "CXXSTD": "14", "DRONE_BEFORE_INSTALL" : "process_coverage", "CODECOV_TOKEN": {"from_secret": "codecov_token"}}, globalenv=globalenv, privileged=True), # A set of jobs based on the earlier .travis.yml configuration: - linux_cxx("Default clang++ with libc++", "clang++-libc++", packages="libc++-dev mlocate", image="cppalliance/droneubuntu1604:1", buildtype="buildtype", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "B2_TOOLSET": "clang-7", "B2_CXXSTD": "14", "VARIANT": "debug", "TOOLSET": "clang", "COMPILER": "clang++-libc++", "CXXSTD": "14", "CXX_FLAGS": "-stdlib=libc++ -stdlib=libc++", "TRAVISCLANG" : "yes" }, globalenv=globalenv), - linux_cxx("Default g++", "g++", packages="mlocate", image="cppalliance/droneubuntu1604:1", buildtype="buildtype", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "VARIANT": "release", "TOOLSET": "gcc", "COMPILER": "g++", "CXXSTD": "14" }, globalenv=globalenv), - linux_cxx("Clang 3.8, UBasan", "clang++-3.8", packages="clang-3.8 libssl-dev mlocate", llvm_os="precise", llvm_ver="3.8", image="cppalliance/droneubuntu1604:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "VARIANT": "process_ubasan", "TOOLSET": "clang", "COMPILER": "clang++-3.8", "CXXSTD": "14", "UBSAN_OPTIONS": 'print_stacktrace=1', "DRONE_BEFORE_INSTALL": "UBasan" }, globalenv=globalenv), - linux_cxx("gcc 6", "g++-6", packages="g++-6", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'B2_TOOLSET': 'gcc-6', 'B2_CXXSTD': '14', 'DRONE_JOB_UUID': '902ba3cda1'}, globalenv=globalenv), - linux_cxx("clang 3.8", "clang++-3.8", packages="clang-3.8", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu1604:1", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-3.8', 'B2_CXXSTD': '14', 'DRONE_JOB_UUID': '7b52009b64'}, globalenv=globalenv), - osx_cxx("clang", "g++", packages="", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'B2_TOOLSET': 'clang', 'B2_CXXSTD': '14,17', 'DRONE_JOB_UUID': '91032ad7bb'}, globalenv=globalenv), - linux_cxx("coverity", "g++", packages="", buildtype="coverity", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'COMMENT': 'Coverity Scan', 'B2_TOOLSET': 'clang', 'DRONE_JOB_UUID': '472b07b9fc'}, globalenv=globalenv), - windows_cxx("msvc-14.3", "", image="cppalliance/dronevs2022:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "VARIANT": "release", "TOOLSET": "msvc-14.3", "CXXSTD": "17", "DEFINE" : "BOOST_BEAST_USE_STD_STRING_VIEW", "ADDRESS_MODEL": "64"}), + linux_cxx("Default clang++ with libc++", "clang++-libc++", packages="libc++-dev mlocate", image="cppalliance/droneubuntu1604:1", buildtype="buildtype", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", "B2_TOOLSET": "clang-7", "B2_CXXSTD": "14", "VARIANT": "debug", "TOOLSET": "clang", "COMPILER": "clang++-libc++", "CXXSTD": "14", "CXX_FLAGS": "-stdlib=libc++ -stdlib=libc++", "TRAVISCLANG" : "yes" }, globalenv=globalenv), + linux_cxx("Default g++", "g++", packages="mlocate", image="cppalliance/droneubuntu1604:1", buildtype="buildtype", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", "VARIANT": "release", "TOOLSET": "gcc", "COMPILER": "g++", "CXXSTD": "14" }, globalenv=globalenv), + linux_cxx("Clang 3.8, UBasan", "clang++-3.8", packages="clang-3.8 libssl-dev mlocate", llvm_os="precise", llvm_ver="3.8", image="cppalliance/droneubuntu1604:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", "VARIANT": "process_ubasan", "TOOLSET": "clang", "COMPILER": "clang++-3.8", "CXXSTD": "14", "UBSAN_OPTIONS": 'print_stacktrace=1', "DRONE_BEFORE_INSTALL": "UBasan" }, globalenv=globalenv), + linux_cxx("gcc 6", "g++-6", packages="g++-6", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", 'B2_TOOLSET': 'gcc-6', 'B2_CXXSTD': '14', 'DRONE_JOB_UUID': '902ba3cda1'}, globalenv=globalenv), + linux_cxx("clang 3.8", "clang++-3.8", packages="clang-3.8", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu1604:1", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", 'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-3.8', 'B2_CXXSTD': '14', 'DRONE_JOB_UUID': '7b52009b64'}, globalenv=globalenv), + osx_cxx("clang", "g++", packages="", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", 'B2_TOOLSET': 'clang', 'B2_CXXSTD': '14,17', 'DRONE_JOB_UUID': '91032ad7bb'}, globalenv=globalenv), + linux_cxx("coverity", "g++", packages="", buildtype="coverity", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", 'COMMENT': 'Coverity Scan', 'B2_TOOLSET': 'clang', 'DRONE_JOB_UUID': '472b07b9fc'}, globalenv=globalenv), + windows_cxx("msvc-14.3", "", image="cppalliance/dronevs2022:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", "VARIANT": "release", "TOOLSET": "msvc-14.3", "CXXSTD": "17", "DEFINE" : "BOOST_BEAST_USE_STD_STRING_VIEW", "ADDRESS_MODEL": "64"}), ] # from https://github.com/boostorg/boost-ci From 22b6e44e402886a8222b1f8bade38e626c95778d Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Fri, 3 Feb 2023 18:26:50 +0800 Subject: [PATCH 28/76] Another CI update --- .drone.star | 2 +- build/Jamfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.star b/.drone.star index 5e87c99..e9eb503 100644 --- a/.drone.star +++ b/.drone.star @@ -28,7 +28,7 @@ def main(ctx): linux_cxx("clang 3.8", "clang++-3.8", packages="clang-3.8", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu1604:1", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", 'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-3.8', 'B2_CXXSTD': '14', 'DRONE_JOB_UUID': '7b52009b64'}, globalenv=globalenv), osx_cxx("clang", "g++", packages="", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", 'B2_TOOLSET': 'clang', 'B2_CXXSTD': '14,17', 'DRONE_JOB_UUID': '91032ad7bb'}, globalenv=globalenv), linux_cxx("coverity", "g++", packages="", buildtype="coverity", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", 'COMMENT': 'Coverity Scan', 'B2_TOOLSET': 'clang', 'DRONE_JOB_UUID': '472b07b9fc'}, globalenv=globalenv), - windows_cxx("msvc-14.3", "", image="cppalliance/dronevs2022:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", "VARIANT": "release", "TOOLSET": "msvc-14.3", "CXXSTD": "17", "DEFINE" : "BOOST_BEAST_USE_STD_STRING_VIEW", "ADDRESS_MODEL": "64"}), + windows_cxx("msvc-14.3", "", image="cppalliance/dronevs2022:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", "VARIANT": "release", "TOOLSET": "msvc-14.3", "CXXSTD": "17", "ADDRESS_MODEL": "64"}), ] # from https://github.com/boostorg/boost-ci diff --git a/build/Jamfile b/build/Jamfile index b61ffe1..9aa251b 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -83,7 +83,7 @@ lib boost_requests [ ac.check-library ssl : ssl/shared : no ] [ ac.check-library crypto : crypto/shared : no ] - + off:/boost//filesystem : usage-requirements /boost//container/off From d1f5ea8c2e3e4174ddcedc1b6f888d6f957bc879 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Sun, 5 Feb 2023 02:39:18 +0800 Subject: [PATCH 29/76] msvc fixes --- include/boost/requests/connection_pool.hpp | 4 ++-- include/boost/requests/detail/connection_impl.hpp | 3 +-- include/boost/requests/detail/impl/connection_impl.hpp | 5 ++--- include/boost/requests/impl/connection_pool.hpp | 10 +++++----- include/boost/requests/impl/connection_pool.ipp | 2 +- include/boost/requests/impl/stream.hpp | 1 - include/boost/requests/sources/file.hpp | 3 ++- 7 files changed, 13 insertions(+), 15 deletions(-) diff --git a/include/boost/requests/connection_pool.hpp b/include/boost/requests/connection_pool.hpp index dc890a7..f0069a3 100644 --- a/include/boost/requests/connection_pool.hpp +++ b/include/boost/requests/connection_pool.hpp @@ -125,8 +125,8 @@ struct connection_pool return res; } - template)) CompletionToken> - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (system::error_code, std::shared_ptr)) + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, connection)) async_get_connection(CompletionToken && completion_token); template diff --git a/include/boost/requests/detail/connection_impl.hpp b/include/boost/requests/detail/connection_impl.hpp index a3407f9..927bb27 100644 --- a/include/boost/requests/detail/connection_impl.hpp +++ b/include/boost/requests/detail/connection_impl.hpp @@ -86,8 +86,7 @@ struct connection_impl : std::enable_shared_from_this system::error_code & ec); template - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code)) + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code)) async_connect(endpoint_type ep, CompletionToken && completion_token); diff --git a/include/boost/requests/detail/impl/connection_impl.hpp b/include/boost/requests/detail/impl/connection_impl.hpp index dba8492..a3bd7ad 100644 --- a/include/boost/requests/detail/impl/connection_impl.hpp +++ b/include/boost/requests/detail/impl/connection_impl.hpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -58,8 +57,8 @@ struct connection_impl::async_connect_op : asio::coroutine system::error_code & ec); }; -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (system::error_code)) +template +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code)) connection_impl::async_connect(endpoint_type ep, CompletionToken && completion_token) { diff --git a/include/boost/requests/impl/connection_pool.hpp b/include/boost/requests/impl/connection_pool.hpp index c92f048..4e124de 100644 --- a/include/boost/requests/impl/connection_pool.hpp +++ b/include/boost/requests/impl/connection_pool.hpp @@ -79,8 +79,8 @@ struct connection_pool::async_get_connection_op : asio::coroutine system::error_code & ec) -> connection; }; -template)) CompletionToken> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (system::error_code, std::shared_ptr>)) +template +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, connection)) connection_pool::async_get_connection(CompletionToken && completion_token) { // async_get_connection_op @@ -129,12 +129,12 @@ struct connection_pool::async_ropen_op : asio::coroutine { } - using completion_signature_type = void(system::error_code, stream); - using step_signature_type = void(system::error_code, variant2::variant); + using completion_signature_type = void(boost::system::error_code, stream); + using step_signature_type = void(boost::system::error_code, variant2::variant); BOOST_REQUESTS_DECL auto resume(requests::detail::faux_token_t self, - system::error_code & ec, + boost::system::error_code & ec, variant2::variant res = variant2::monostate()) -> stream; }; diff --git a/include/boost/requests/impl/connection_pool.ipp b/include/boost/requests/impl/connection_pool.ipp index 0b4c38c..4aba57c 100644 --- a/include/boost/requests/impl/connection_pool.ipp +++ b/include/boost/requests/impl/connection_pool.ipp @@ -303,7 +303,7 @@ auto connection_pool::async_get_connection_op::resume( stream connection_pool::async_ropen_op::resume( requests::detail::faux_token_t self, - system::error_code & ec, + boost::system::error_code & ec, variant2::variant res) { BOOST_ASIO_CORO_REENTER(this) diff --git a/include/boost/requests/impl/stream.hpp b/include/boost/requests/impl/stream.hpp index 927e995..2d91f1b 100644 --- a/include/boost/requests/impl/stream.hpp +++ b/include/boost/requests/impl/stream.hpp @@ -10,7 +10,6 @@ #include #include -#include #include diff --git a/include/boost/requests/sources/file.hpp b/include/boost/requests/sources/file.hpp index c9279c4..2cbc33f 100644 --- a/include/boost/requests/sources/file.hpp +++ b/include/boost/requests/sources/file.hpp @@ -5,6 +5,7 @@ #ifndef BOOST_REQUESTS_SOURCES_FILE_HPP #define BOOST_REQUESTS_SOURCES_FILE_HPP +#include #include #include #include @@ -26,7 +27,7 @@ namespace requests struct file_source : source { - filesystem::path path; + requests::filesystem::path path; beast::file file; system::error_code ec; From 9422f15f12f16094aab49e7a664fae335ff3b031 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Mon, 6 Feb 2023 21:28:26 +0800 Subject: [PATCH 30/76] Remove use_ssl stuff from connection & pool. --- include/boost/requests/connection.hpp | 1 + include/boost/requests/connection_pool.hpp | 11 ++++------- include/boost/requests/detail/connection_impl.hpp | 9 ++++----- include/boost/requests/impl/connection_pool.hpp | 2 +- include/boost/requests/impl/connection_pool.ipp | 11 +++++------ include/boost/requests/ssl.hpp | 0 6 files changed, 15 insertions(+), 19 deletions(-) create mode 100644 include/boost/requests/ssl.hpp diff --git a/include/boost/requests/connection.hpp b/include/boost/requests/connection.hpp index c3b8608..e2bd89b 100644 --- a/include/boost/requests/connection.hpp +++ b/include/boost/requests/connection.hpp @@ -226,6 +226,7 @@ struct connection return impl_->async_ropen(method, path, headers, src, std::move(opt), jar, std::forward(completion_token)); } bool uses_ssl() const {return impl_->uses_ssl();} + void use_ssl(bool use_ssl = true) {impl_->use_ssl(use_ssl);} operator bool() const {return impl_ != nullptr;} private: diff --git a/include/boost/requests/connection_pool.hpp b/include/boost/requests/connection_pool.hpp index f0069a3..f3591e8 100644 --- a/include/boost/requests/connection_pool.hpp +++ b/include/boost/requests/connection_pool.hpp @@ -62,8 +62,7 @@ struct connection_pool */ explicit connection_pool(asio::any_io_executor exec, std::size_t limit = BOOST_REQUESTS_DEFAULT_POOL_SIZE) - : use_ssl_(false), - context_( + : context_( asio::use_service( asio::query(exec, asio::execution::context) ).get()), mutex_(exec), limit_(limit) {} @@ -74,8 +73,7 @@ struct connection_pool asio::is_convertible::value, std::size_t >::type limit = BOOST_REQUESTS_DEFAULT_POOL_SIZE) - : use_ssl_(false), - context_( + : context_( asio::use_service(context).get()), mutex_(context), limit_(limit) {} @@ -89,7 +87,7 @@ struct connection_pool explicit connection_pool(Exec && exec, asio::ssl::context & ctx, std::size_t limit = BOOST_REQUESTS_DEFAULT_POOL_SIZE) - : use_ssl_(true), context_(ctx), mutex_(std::forward(exec)), limit_(limit) {} + : context_(ctx), mutex_(std::forward(exec)), limit_(limit) {} /// Move constructor connection_pool(connection_pool && ) = default; @@ -211,9 +209,8 @@ struct connection_pool CompletionToken && completion_token); bool uses_ssl() const {return use_ssl_;} - private: - bool use_ssl_{false}; + bool use_ssl_{true}; asio::ssl::context & context_; detail::mutex mutex_; std::string host_; diff --git a/include/boost/requests/detail/connection_impl.hpp b/include/boost/requests/detail/connection_impl.hpp index 927bb27..81fee21 100644 --- a/include/boost/requests/detail/connection_impl.hpp +++ b/include/boost/requests/detail/connection_impl.hpp @@ -64,15 +64,14 @@ struct connection_impl : std::enable_shared_from_this >::type = 0) : next_layer_( context, - asio::use_service(context).get()), - use_ssl_{false} {} + asio::use_service(context).get()) {} explicit connection_impl(asio::any_io_executor exec) : next_layer_( exec, asio::use_service( asio::query(exec, asio::execution::context) - ).get()), use_ssl_{false} {} + ).get()) {} void connect(endpoint_type ep) { @@ -184,11 +183,11 @@ struct connection_impl : std::enable_shared_from_this cookie_jar * jar, CompletionToken && completion_token); bool uses_ssl() const {return use_ssl_;} - + void use_ssl(bool use_ssl = true) {use_ssl_ = true;} private: next_layer_type next_layer_; - bool use_ssl_{false}; + bool use_ssl_{true}; detail::mutex read_mtx_{next_layer_.get_executor()}, write_mtx_{next_layer_.get_executor()}; diff --git a/include/boost/requests/impl/connection_pool.hpp b/include/boost/requests/impl/connection_pool.hpp index 4e124de..506832a 100644 --- a/include/boost/requests/impl/connection_pool.hpp +++ b/include/boost/requests/impl/connection_pool.hpp @@ -23,7 +23,7 @@ struct connection_pool::async_lookup_op : asio::coroutine const urls::url_view sv; optional resolver; - urls::string_view scheme = this_->use_ssl_ ? "https" : "http"; + urls::string_view scheme = "https"; urls::string_view service; using mutex_type = detail::mutex; diff --git a/include/boost/requests/impl/connection_pool.ipp b/include/boost/requests/impl/connection_pool.ipp index 4aba57c..dc68859 100644 --- a/include/boost/requests/impl/connection_pool.ipp +++ b/include/boost/requests/impl/connection_pool.ipp @@ -16,7 +16,7 @@ namespace requests { void connection_pool::lookup(urls::url_view sv, system::error_code & ec) { - urls::string_view scheme = use_ssl_ ? "https" : "http"; + urls::string_view scheme = "https"; if (sv.has_scheme()) scheme = sv.scheme(); @@ -196,9 +196,8 @@ auto connection_pool::get_connection(error_code & ec) -> connection }); const auto ep = endpoints_.front(); - std::shared_ptr nconn = - use_ssl_ ? std::make_shared(get_executor(), context_) - : std::make_shared(get_executor()); + std::shared_ptr nconn = std::make_shared(get_executor(), context_); + nconn->use_ssl(use_ssl_); nconn->set_host(host_); nconn->connect(ep, ec); if (ec) @@ -272,8 +271,8 @@ auto connection_pool::async_get_connection_op::resume( return this_->conns_.count(a) < this_->conns_.count(b); }); ep = this_->endpoints_.front(); - nconn = this_->use_ssl_ ? std::make_shared(get_executor(), this_->context_) - : std::make_shared(get_executor()); + nconn = std::make_shared(get_executor(), this_->context_); + nconn->use_ssl(this_->use_ssl_); nconn->set_host(this_->host_); BOOST_ASIO_CORO_YIELD nconn->async_connect(ep, std::move(self)); // don't unlock here. if (ec) diff --git a/include/boost/requests/ssl.hpp b/include/boost/requests/ssl.hpp new file mode 100644 index 0000000..e69de29 From 7e3169bce8c9de0bf6bd72eb4c49523b35b192f2 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 7 Feb 2023 13:16:33 +0800 Subject: [PATCH 31/76] Made ssl service public. --- include/boost/requests.hpp | 1 + .../boost/requests/detail/connection_impl.hpp | 2 +- include/boost/requests/detail/ssl.hpp | 2 +- include/boost/requests/service.hpp | 6 ++++- include/boost/requests/ssl.hpp | 27 +++++++++++++++++++ 5 files changed, 35 insertions(+), 3 deletions(-) diff --git a/include/boost/requests.hpp b/include/boost/requests.hpp index 7b81292..d7d1481 100644 --- a/include/boost/requests.hpp +++ b/include/boost/requests.hpp @@ -26,5 +26,6 @@ #include #include #include +#include #endif //BOOST_REQUESTS_HPP diff --git a/include/boost/requests/detail/connection_impl.hpp b/include/boost/requests/detail/connection_impl.hpp index 81fee21..79f375b 100644 --- a/include/boost/requests/detail/connection_impl.hpp +++ b/include/boost/requests/detail/connection_impl.hpp @@ -183,7 +183,7 @@ struct connection_impl : std::enable_shared_from_this cookie_jar * jar, CompletionToken && completion_token); bool uses_ssl() const {return use_ssl_;} - void use_ssl(bool use_ssl = true) {use_ssl_ = true;} + void use_ssl(bool use_ssl = true) {use_ssl_ = use_ssl;} private: next_layer_type next_layer_; diff --git a/include/boost/requests/detail/ssl.hpp b/include/boost/requests/detail/ssl.hpp index 3e6bf38..691d018 100644 --- a/include/boost/requests/detail/ssl.hpp +++ b/include/boost/requests/detail/ssl.hpp @@ -34,7 +34,7 @@ struct ssl_context_service : asio::detail::execution_context_service_base { @@ -44,10 +46,12 @@ struct session_service : asio::detail::execution_context_service_base session_; }; +} + inline auto default_session(asio::any_io_executor exec = asio::system_executor()) -> session & { - auto & so = asio::use_service(exec.context()).session_; + auto & so = asio::use_service(exec.context()).session_; if (!so) so.emplace(exec); return *so; diff --git a/include/boost/requests/ssl.hpp b/include/boost/requests/ssl.hpp index e69de29..aa5b82c 100644 --- a/include/boost/requests/ssl.hpp +++ b/include/boost/requests/ssl.hpp @@ -0,0 +1,27 @@ +// Copyright (c) 2021 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_REQUESTS_SSL_HPP +#define BOOST_REQUESTS_SSL_HPP + +#include + +namespace boost +{ +namespace requests +{ + + +inline auto default_ssl_context(asio::any_io_executor exec = asio::system_executor()) -> asio::ssl::context & +{ + return asio::use_service(exec.context()).get(); +} + + +} +} + +#endif // BOOST_REQUESTS_SSL_HPP + From a384f372e0a6b18e51b770b4f56de3ebeb7a6fe3 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 7 Feb 2023 13:16:56 +0800 Subject: [PATCH 32/76] [Jamfile] updated define --- Jamfile | 4 ++-- build/Jamfile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Jamfile b/Jamfile index 4a9b8d3..9d97675 100644 --- a/Jamfile +++ b/Jamfile @@ -18,7 +18,7 @@ build-project test ; boost.use-project ; feature.feature boost.requests.separate-compilation : on off : propagated composite ; -feature.compose on : BOOST_REQUESTS_SEPARATE_COMPILATION ; +feature.compose on : BOOST_REQUESTS_SEPARATE_COMPILATION=1 ; feature.feature boost.requests.use-std-fs : off on : propagated composite ; -feature.compose on : BOOST_REQUESTS_USE_STD_FS ; +feature.compose on : BOOST_REQUESTS_USE_STD_FS=1 ; diff --git a/build/Jamfile b/build/Jamfile index 9aa251b..39fbc1e 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -4,7 +4,7 @@ project boost/requests BOOST_FILESYSTEM_NO_DEPRECATED=1 shared:BOOST_REQUESTS_DYN_LINK=1 static:BOOST_REQUESTS_STATIC_LINK=1 - BOOST_REQUESTS_SOURCE + BOOST_REQUESTS_SOURCE=1 BOOST_FILESYSTEM_NO_DEPRECATED=1 : usage-requirements shared:BOOST_REQUESTS_DYN_LINK=1 From ac4ea6e167a5deb8b36bf7417865cfd81ba50938 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 7 Feb 2023 13:23:45 +0800 Subject: [PATCH 33/76] [test] Extended error info. --- test/connection.cpp | 225 +++++++++++++-------------- test/connection_pool.cpp | 325 +++++++++++++++++++++------------------ test/cookie.cpp | 2 + test/json.cpp | 44 ++++-- test/method.cpp | 238 ++++++++++++++-------------- test/session.cpp | 254 +++++++++++++++--------------- test/string_maker.hpp | 4 + 7 files changed, 586 insertions(+), 506 deletions(-) diff --git a/test/connection.cpp b/test/connection.cpp index 9bd359b..b3b9cca 100644 --- a/test/connection.cpp +++ b/test/connection.cpp @@ -56,28 +56,30 @@ void http_request_connection(bool https) auto hc = https ? requests::connection(ctx.get_executor(), sslctx) : requests::connection(ctx.get_executor()); hc.set_host(url); + hc.use_ssl(https); asio::ip::tcp::resolver rslvr{ctx}; asio::ip::tcp::endpoint ep = *rslvr.resolve(url, https ? "https" : "http").begin(); - + hc.use_ssl(https); hc.connect(ep); - // SUBCASE("headers") + // headers { auto hdr = request(hc, requests::http::verb::get, urls::url_view("/headers"), requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}); auto hd = as_json(hdr).at("headers"); - + CHECK_HTTP_RESULT(hdr.headers); CHECK(hd.at("Host") == json::value(url)); CHECK(hd.at("Test-Header") == json::value("it works")); } - // SUBCASE("stream") + // stream { auto str = hc.ropen(requests::http::verb::get, urls::url_view("/get"), requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}); + CHECK_HTTP_RESULT(str.headers()); json::stream_parser sp; char buf[32]; @@ -92,17 +94,17 @@ void http_request_connection(bool https) } auto hd = sp.release().at("headers"); - CHECK(hd.at("Host") == json::value(url)); CHECK(hd.at("Test-Header") == json::value("it works")); } - // SUBCASE("stream-all") + // stream-all { auto str = hc.ropen(requests::http::verb::get, urls::url_view("/get"), requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}); + CHECK_HTTP_RESULT(str.headers()); std::string buf; auto bb = asio::dynamic_buffer(buf); @@ -119,28 +121,31 @@ void http_request_connection(bool https) } - // SUBCASE("stream-dump") + // stream-dump { auto str = hc.ropen(requests::http::verb::get, urls::url_view("/get"), requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}); + CHECK_HTTP_RESULT(str.headers()); str.dump(); } - // SUBCASE("get") + // get { auto hdr = get(hc, urls::url_view("/get"), {requests::headers({{"Test-Header", "it works"}}), {false}}); auto hd = as_json(hdr).at("headers"); + CHECK_HTTP_RESULT(hdr.headers); CHECK(hd.at("Host") == json::value(url)); CHECK(hd.at("Test-Header") == json::value("it works")); } - // SUBCASE("get-redirect") + // get-redirect { auto hdr = get(hc, urls::url_view("/redirect-to?url=%2Fget"), {requests::headers({{"Test-Header", "it works"}}), {false}}); + CHECK_HTTP_RESULT(hdr.headers); CHECK(hdr.history.size() == 1u); CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); @@ -150,16 +155,17 @@ void http_request_connection(bool https) CHECK(hd.at("Test-Header") == json::value("it works")); } - // SUBCASE("too-many-redirects") + // too-many-redirects { error_code ec; auto res = get(hc, urls::url_view("/redirect/10"), {{}, {false, requests::redirect_mode::private_domain, 5}}, ec); + CHECK_HTTP_RESULT(res.headers); CHECK(res.history.size() == 5); CHECK(res.headers.begin() == res.headers.end()); CHECK(ec == requests::error::too_many_redirects); } - // SUBCASE("download") + // download { error_code ec; const auto tmp = filesystem::temp_directory_path(ec); @@ -171,6 +177,7 @@ void http_request_connection(bool https) CHECK(!filesystem::exists(target)); CHECK_MESSAGE( filesystem::exists(tmp), tmp); auto res = download(hc, urls::url_view("/image"), {{}, {false}}, target); + CHECK_HTTP_RESULT(res.headers); CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); @@ -181,7 +188,7 @@ void http_request_connection(bool https) } - // SUBCASE("download-redirect") + // download-redirect { const auto target = filesystem::temp_directory_path() / "requests-test.png"; if (filesystem::exists(target)) @@ -189,7 +196,7 @@ void http_request_connection(bool https) CHECK(!filesystem::exists(target)); auto res = download(hc, urls::url_view("/redirect-to?url=%2Fimage"), {{}, {false}}, target); - + CHECK_HTTP_RESULT(res.headers); CHECK(res.history.size() == 1u); CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); @@ -202,15 +209,16 @@ void http_request_connection(bool https) } - // SUBCASE("download-too-many-redirects") + // download-too-many-redirects { error_code ec; const auto target = filesystem::temp_directory_path() / "requests-test.html"; if (filesystem::exists(target)) filesystem::remove(target); auto res = download(hc, urls::url_view("/redirect/10"), {{}, {false, requests::redirect_mode::private_domain, 3}}, target, ec); - CHECK(res.history.size() == 3); + CHECK_HTTP_RESULT(res.headers); + CHECK(res.history.size() == 3); CHECK(res.headers.begin() == res.headers.end()); CHECK(ec == requests::error::too_many_redirects); @@ -218,19 +226,21 @@ void http_request_connection(bool https) } - // SUBCASE("delete") + // delete { auto hdr = delete_(hc, urls::url_view("/delete"), json::value{{"test-key", "test-value"}}, {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(to_status_class(hdr.headers.result()) == requests::http::status_class::successful); CHECK(js.at("headers").at("Content-Type") == "application/json"); } - // SUBCASE("patch-json") + // patch-json { json::value msg {{"test-key", "test-value"}}; auto hdr = patch(hc, urls::url_view("/patch"), msg, {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); @@ -238,11 +248,12 @@ void http_request_connection(bool https) CHECK(js.at("json") == msg); } - // SUBCASE("patch-form") + // patch-form { auto hdr = patch(hc, urls::url_view("/patch"), requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); @@ -250,10 +261,11 @@ void http_request_connection(bool https) CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); } - // SUBCASE("put-json") + // put-json { json::value msg {{"test-key", "test-value"}}; auto hdr = put(hc, urls::url_view("/put"), msg, {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); @@ -261,11 +273,12 @@ void http_request_connection(bool https) CHECK(js.at("json") == msg); } - // SUBCASE("put-form") + // put-form { auto hdr = put(hc, urls::url_view("/put"), requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); @@ -273,10 +286,11 @@ void http_request_connection(bool https) CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); } - // SUBCASE("post-json") + // post-json { json::value msg {{"test-key", "test-value"}}; auto hdr = post(hc, urls::url_view("/post"), msg, {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); @@ -284,11 +298,12 @@ void http_request_connection(bool https) CHECK(js.at("json") == msg); } - // SUBCASE("post-form") + // post-form { auto hdr = post(hc, urls::url_view("/post"), requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); @@ -312,6 +327,8 @@ void run_tests(error_code ec, namespace filesystem = requests::filesystem; check_ec(ec); + + // headers async_request( conn, http::verb::get, urls::url_view("/headers"), @@ -319,64 +336,60 @@ void run_tests(error_code ec, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("headers") - { - - check_ec(ec); - auto hd = as_json(hdr).at("headers"); - - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } + check_ec(ec); + auto hd = as_json(hdr).at("headers"); + CHECK_HTTP_RESULT(hdr.headers); + CHECK(hd.at("Host") == json::value(url.host_name())); + CHECK(hd.at("Test-Header") == json::value("it works")); })); + // headers async_get(conn, urls::url_view("/get"), {requests::headers({{"Test-Header", "it works"}}), {false}}, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("headers") - { - check_ec(ec); - auto hd = as_json(hdr).at("headers"); + check_ec(ec); + auto hd = as_json(hdr).at("headers"); + CHECK_HTTP_RESULT(hdr.headers); + CHECK(hd.at("Host") == json::value(url.host_name())); + CHECK(hd.at("Test-Header") == json::value("it works")); - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } })); + // get-redirect async_get(conn, urls::url_view("/redirect-to?url=%2Fget"), {requests::headers({{"Test-Header", "it works"}}), {false}}, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("get-redirect") { CHECK(hdr.history.size() == 1u); CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); + CHECK_HTTP_RESULT(hdr.headers); auto hd = as_json(hdr).at("headers"); - CHECK(hd.at("Host") == json::value(url.host_name())); CHECK(hd.at("Test-Header") == json::value("it works")); } })); + // too-many-redirects async_get(conn, urls::url_view("/redirect/10"), {{}, {false, requests::redirect_mode::private_domain, 5}}, tracker( [url](error_code ec, requests::response res) { - // SUBCASE("too-many-redirects") - { - CHECK(res.history.size() == 5); - CHECK(res.headers.begin() == res.headers.end()); - CHECK(ec == requests::error::too_many_redirects); - } + CHECK_HTTP_RESULT(res.headers); + + CHECK(res.history.size() == 5); + CHECK(res.headers.begin() == res.headers.end()); + CHECK(ec == requests::error::too_many_redirects); + })); - { + { // download auto pt = filesystem::temp_directory_path(); const auto target = pt / "requests-test.png"; if (filesystem::exists(target)) @@ -387,15 +400,13 @@ void run_tests(error_code ec, tracker( [url, target](error_code ec, requests::response_base res) { - // SUBCASE("download") - { - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec_; - filesystem::remove(target, ec_); - } + CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); + CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); + CHECK_HTTP_RESULT(res.headers); + + CHECK_MESSAGE(filesystem::exists(target), target); + fs_error_code ec_; + filesystem::remove(target, ec_); })); } @@ -405,126 +416,115 @@ void run_tests(error_code ec, filesystem::remove(target); CHECK(!filesystem::exists(target)); + // download-redirect async_download(conn, urls::url_view("/redirect-to?url=%2Fimage"), {{}, {false}}, target, tracker( [url, target](error_code ec, requests::response_base res) { - // SUBCASE("download-redirect") - { - CHECK(res.history.size() == 1u); - CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); + CHECK(res.history.size() == 1u); + CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); + CHECK_HTTP_RESULT(res.headers); - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); + CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); + CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec_; - filesystem::remove(target, ec_); - } + CHECK_MESSAGE(filesystem::exists(target), target); + fs_error_code ec_; + filesystem::remove(target, ec_); })); } + // delete async_delete(conn, urls::url_view("/delete"), json::value{{"test-key", "test-value"}}, {{}, {false}}, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("delete") - { - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - } + CHECK_HTTP_RESULT(hdr.headers); + auto js = as_json(hdr); + CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); + CHECK(js.at("headers").at("Content-Type") == "application/json"); })); + // patch-json async_patch(conn, urls::url_view("/patch"), json::value{{"test-key", "test-value"}}, {{}, {false}}, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("patch-json") - { + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); CHECK(js.at("headers").at("Content-Type") == "application/json"); - } })); - - + // patch-form async_patch(conn, urls::url_view("/patch"), requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, {{}, {false}}, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("patch-form") - { - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } + CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + CHECK(hdr.headers.result() == requests::http::status::ok); + CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); + CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); + })); - + // put-json async_put(conn, urls::url_view("/put"), json::value{{"test-key", "test-value"}}, {{}, {false}}, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("put-json") - { - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - } + CHECK_HTTP_RESULT(hdr.headers); + auto js = as_json(hdr); + CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); + CHECK(js.at("headers").at("Content-Type") == "application/json"); })); - - + // put-form async_put(conn, urls::url_view("/put"), requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, {{}, {false}}, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("put-form") - { - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } + CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + CHECK(hdr.headers.result() == requests::http::status::ok); + CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); + CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); + })); - + // post-json async_post(conn, urls::url_view("/post"), json::value{{"test-key", "test-value"}}, {{}, {false}}, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("post-json") - { + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); CHECK(js.at("headers").at("Content-Type") == "application/json"); - } })); - + // post-form async_post(conn, urls::url_view("/post"), requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, {{}, {false}}, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("post-form") - { - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } + CHECK_HTTP_RESULT(hdr.headers); + auto js = as_json(hdr); + CHECK(hdr.headers.result() == requests::http::status::ok); + CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); + CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); })); } @@ -559,6 +559,7 @@ TEST_CASE("async-connection-request") conn = requests::connection(ctx); asio::ip::tcp::resolver rslvr{ctx}; url.set_scheme("http"); + conn.use_ssl(false); CHECK(!conn.uses_ssl()); conn.set_host(url.encoded_host()); diff --git a/test/connection_pool.cpp b/test/connection_pool.cpp index 37245eb..e7ac82b 100644 --- a/test/connection_pool.cpp +++ b/test/connection_pool.cpp @@ -57,22 +57,25 @@ void http_request_connection_pool(bool https) hc.lookup(url); CHECK(https == hc.uses_ssl()); - // SUBCASE("headers") + + // headers { - auto hdr = request(hc, requests::http::verb::get, urls::url_view( urls::url_view("/headers")), - requests::empty{}, - {requests::headers({{"Test-Header", "it works"}}), {false}}); + auto hdr = request(hc, requests::http::verb::get, urls::url_view("/headers"), + requests::empty{}, + {requests::headers({{"Test-Header", "it works"}}), {false}}); auto hd = as_json(hdr).at("headers"); - - CHECK(hd.at("Host") == json::value(url.authority().buffer())); + CHECK_HTTP_RESULT(hdr.headers); + CHECK(hd.at("Host") == json::value(url.encoded_host())); CHECK(hd.at("Test-Header") == json::value("it works")); } - // SUBCASE("stream") + // stream { - auto str = hc.ropen(requests::http::verb::get, urls::url_view( urls::url_view("/get")), requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}); + auto str = hc.ropen(requests::http::verb::get, urls::url_view("/get"), + requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}); + CHECK_HTTP_RESULT(str.headers()); json::stream_parser sp; char buf[32]; @@ -87,79 +90,109 @@ void http_request_connection_pool(bool https) } auto hd = sp.release().at("headers"); + CHECK(hd.at("Host") == json::value(url.encoded_host())); + CHECK(hd.at("Test-Header") == json::value("it works")); + } + + + // stream-all + { + auto str = hc.ropen(requests::http::verb::get, urls::url_view("/get"), + requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}); + + CHECK_HTTP_RESULT(str.headers()); + std::string buf; + auto bb = asio::dynamic_buffer(buf); - CHECK(hd.at("Host") == json::value(url.authority().buffer())); + error_code ec; + CHECK(str.read(bb, ec) > 0u); + + CHECK(ec == error_code{}); + + auto val = json::parse(buf); + + auto hd = val.at("headers"); + CHECK(hd.at("Host") == json::value(url.encoded_host())); CHECK(hd.at("Test-Header") == json::value("it works")); } - // SUBCASE("stream-dump") + + // stream-dump { - auto str = hc.ropen(requests::http::verb::get, urls::url_view( urls::url_view("/get")), + auto str = hc.ropen(requests::http::verb::get, urls::url_view("/get"), requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}); + CHECK_HTTP_RESULT(str.headers()); str.dump(); } - - // SUBCASE("get") + // get { - auto hdr = get(hc, urls::url_view( urls::url_view("/get")), {requests::headers({{"Test-Header", "it works"}}), {false}}); + auto hdr = get(hc, urls::url_view("/get"), {requests::headers({{"Test-Header", "it works"}}), {false}}); auto hd = as_json(hdr).at("headers"); + CHECK_HTTP_RESULT(hdr.headers); - CHECK(hd.at("Host") == json::value(url.authority().buffer())); + CHECK(hd.at("Host") == json::value(url.encoded_host())); CHECK(hd.at("Test-Header") == json::value("it works")); } - // SUBCASE("get-redirect") + // get-redirect { - auto hdr = get(hc, urls::url_view("/redirect-to?url=%2Fget"), {requests::headers({{"Test-Header", "it works"}}), {false}}); + auto hdr = get(hc, urls::url_view("/redirect-to?url=%2Fget"), + {requests::headers({{"Test-Header", "it works"}}), {false}}); + CHECK_HTTP_RESULT(hdr.headers); CHECK(hdr.history.size() == 1u); CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); auto hd = as_json(hdr).at("headers"); - CHECK(hd.at("Host") == json::value(url.authority().buffer())); + CHECK(hd.at("Host") == json::value(url.encoded_host())); CHECK(hd.at("Test-Header") == json::value("it works")); } - // SUBCASE("too-many-redirects") + // too-many-redirects { error_code ec; - auto res = get(hc, urls::url_view("/redirect/10"), {{}, {false, requests::redirect_mode::private_domain, 5}}, ec); + auto res = get(hc, urls::url_view("/redirect/10"), {{}, {false, requests::redirect_mode::private_domain, 5}}, ec); + CHECK_HTTP_RESULT(res.headers); CHECK(res.history.size() == 5); CHECK(res.headers.begin() == res.headers.end()); CHECK(ec == requests::error::too_many_redirects); } - // SUBCASE("download") + // download { - const auto target = filesystem::temp_directory_path() / "requests-test.png"; - MESSAGE(target); + error_code ec; + const auto tmp = filesystem::temp_directory_path(ec); + check_ec(ec); + const auto target = tmp / "requests-test.png"; if (filesystem::exists(target)) filesystem::remove(target); CHECK(!filesystem::exists(target)); - auto res = download(hc, urls::url_view("/image"), {{}, {false}}, target); + CHECK_MESSAGE( filesystem::exists(tmp), tmp); + auto res = download(hc, urls::url_view("/image"), {{}, {false}}, target); + CHECK_HTTP_RESULT(res.headers); CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec; - filesystem::remove(target, ec); + fs_error_code ec_; + filesystem::remove(target, ec_); } - // SUBCASE("download-redirect") + // download-redirect { const auto target = filesystem::temp_directory_path() / "requests-test.png"; if (filesystem::exists(target)) filesystem::remove(target); CHECK(!filesystem::exists(target)); - auto res = download(hc, urls::url_view("/redirect-to?url=%2Fimage"), {{}, {false}}, target); - + auto res = download(hc, urls::url_view("/redirect-to?url=%2Fimage"), {{}, {false}}, target); + CHECK_HTTP_RESULT(res.headers); CHECK(res.history.size() == 1u); CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); @@ -172,34 +205,38 @@ void http_request_connection_pool(bool https) } - // SUBCASE("download-too-many-redirects") + // download-too-many-redirects { error_code ec; const auto target = filesystem::temp_directory_path() / "requests-test.html"; if (filesystem::exists(target)) filesystem::remove(target); auto res = download(hc, urls::url_view("/redirect/10"), {{}, {false, requests::redirect_mode::private_domain, 3}}, target, ec); - CHECK(res.history.size() == 3); + CHECK_HTTP_RESULT(res.headers); + CHECK(res.history.size() == 3); CHECK(res.headers.begin() == res.headers.end()); CHECK(ec == requests::error::too_many_redirects); CHECK(!filesystem::exists(target)); } - // SUBCASE("delete") + + // delete { - auto hdr = delete_(hc, urls::url_view("/delete"), json::value{{"test-key", "test-value"}}, {{}, {false}}); + auto hdr = delete_(hc, urls::url_view("/delete"), json::value{{"test-key", "test-value"}}, {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); + CHECK(to_status_class(hdr.headers.result()) == requests::http::status_class::successful); CHECK(js.at("headers").at("Content-Type") == "application/json"); } - // SUBCASE("patch-json") + // patch-json { json::value msg {{"test-key", "test-value"}}; - auto hdr = patch(hc, urls::url_view("/patch"), msg, {{}, {false}}); + auto hdr = patch(hc, urls::url_view("/patch"), msg, {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); @@ -207,11 +244,12 @@ void http_request_connection_pool(bool https) CHECK(js.at("json") == msg); } - // SUBCASE("patch-form") + // patch-form { - auto hdr = patch(hc, urls::url_view("/patch"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {{}, {false}}); + auto hdr = patch(hc, urls::url_view("/patch"), + requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, + {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); @@ -219,10 +257,11 @@ void http_request_connection_pool(bool https) CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); } - // SUBCASE("put-json") + // put-json { json::value msg {{"test-key", "test-value"}}; - auto hdr = put(hc, urls::url_view("/put"), msg, {{}, {false}}); + auto hdr = put(hc, urls::url_view("/put"), msg, {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); @@ -230,22 +269,24 @@ void http_request_connection_pool(bool https) CHECK(js.at("json") == msg); } - // SUBCASE("put-form") + // put-form { - auto hdr = put(hc, urls::url_view("/put"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {{}, {false}}); + auto hdr = put(hc, urls::url_view("/put"), + requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, + {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); } - - // SUBCASE("post-json") + + // post-json { json::value msg {{"test-key", "test-value"}}; auto hdr = post(hc, urls::url_view("/post"), msg, {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); @@ -253,11 +294,12 @@ void http_request_connection_pool(bool https) CHECK(js.at("json") == msg); } - // SUBCASE("post-form") + // post-form { auto hdr = post(hc, urls::url_view("/post"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {{}, {false}}); + requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, + {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); @@ -282,6 +324,8 @@ void run_tests(error_code ec, namespace filesystem = requests::filesystem; check_ec(ec); + + // headers async_request( conn, http::verb::get, urls::url_view("/headers"), @@ -289,64 +333,60 @@ void run_tests(error_code ec, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("headers") - { - - check_ec(ec); - auto hd = as_json(hdr).at("headers"); - - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } + check_ec(ec); + auto hd = as_json(hdr).at("headers"); + CHECK_HTTP_RESULT(hdr.headers); + CHECK(hd.at("Host") == json::value(url.host_name())); + CHECK(hd.at("Test-Header") == json::value("it works")); })); + // headers async_get(conn, urls::url_view("/get"), {requests::headers({{"Test-Header", "it works"}}), {false}}, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("headers") - { - check_ec(ec); - auto hd = as_json(hdr).at("headers"); + check_ec(ec); + auto hd = as_json(hdr).at("headers"); + CHECK_HTTP_RESULT(hdr.headers); + CHECK(hd.at("Host") == json::value(url.host_name())); + CHECK(hd.at("Test-Header") == json::value("it works")); - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } })); + // get-redirect async_get(conn, urls::url_view("/redirect-to?url=%2Fget"), {requests::headers({{"Test-Header", "it works"}}), {false}}, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("get-redirect") { CHECK(hdr.history.size() == 1u); CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); + CHECK_HTTP_RESULT(hdr.headers); auto hd = as_json(hdr).at("headers"); - CHECK(hd.at("Host") == json::value(url.host_name())); CHECK(hd.at("Test-Header") == json::value("it works")); } })); + // too-many-redirects async_get(conn, urls::url_view("/redirect/10"), {{}, {false, requests::redirect_mode::private_domain, 5}}, tracker( [url](error_code ec, requests::response res) { - // SUBCASE("too-many-redirects") - { - CHECK(res.history.size() == 5); - CHECK(res.headers.begin() == res.headers.end()); - CHECK(ec == requests::error::too_many_redirects); - } + CHECK_HTTP_RESULT(res.headers); + + CHECK(res.history.size() == 5); + CHECK(res.headers.begin() == res.headers.end()); + CHECK(ec == requests::error::too_many_redirects); + })); - { + { // download auto pt = filesystem::temp_directory_path(); const auto target = pt / "requests-test.png"; if (filesystem::exists(target)) @@ -357,15 +397,13 @@ void run_tests(error_code ec, tracker( [url, target](error_code ec, requests::response_base res) { - // SUBCASE("download") - { - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec_; - filesystem::remove(target, ec_); - } + CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); + CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); + CHECK_HTTP_RESULT(res.headers); + + CHECK_MESSAGE(filesystem::exists(target), target); + fs_error_code ec_; + filesystem::remove(target, ec_); })); } @@ -375,126 +413,115 @@ void run_tests(error_code ec, filesystem::remove(target); CHECK(!filesystem::exists(target)); + // download-redirect async_download(conn, urls::url_view("/redirect-to?url=%2Fimage"), {{}, {false}}, target, tracker( [url, target](error_code ec, requests::response_base res) { - // SUBCASE("download-redirect") - { - CHECK(res.history.size() == 1u); - CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); - - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec_; - filesystem::remove(target, ec_); - } + CHECK(res.history.size() == 1u); + CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); + CHECK_HTTP_RESULT(res.headers); + + CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); + CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); + + CHECK_MESSAGE(filesystem::exists(target), target); + fs_error_code ec_; + filesystem::remove(target, ec_); })); } + // delete async_delete(conn, urls::url_view("/delete"), json::value{{"test-key", "test-value"}}, {{}, {false}}, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("delete") - { - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - } + CHECK_HTTP_RESULT(hdr.headers); + auto js = as_json(hdr); + CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); + CHECK(js.at("headers").at("Content-Type") == "application/json"); })); + // patch-json async_patch(conn, urls::url_view("/patch"), json::value{{"test-key", "test-value"}}, {{}, {false}}, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("patch-json") - { - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - } + CHECK_HTTP_RESULT(hdr.headers); + auto js = as_json(hdr); + CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); + CHECK(js.at("headers").at("Content-Type") == "application/json"); })); - - + // patch-form async_patch(conn, urls::url_view("/patch"), requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, {{}, {false}}, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("patch-form") - { - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } - })); + CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + CHECK(hdr.headers.result() == requests::http::status::ok); + CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); + CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); + })); + // put-json async_put(conn, urls::url_view("/put"), json::value{{"test-key", "test-value"}}, {{}, {false}}, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("put-json") - { - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - } + CHECK_HTTP_RESULT(hdr.headers); + auto js = as_json(hdr); + CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); + CHECK(js.at("headers").at("Content-Type") == "application/json"); })); - - + // put-form async_put(conn, urls::url_view("/put"), requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, {{}, {false}}, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("put-form") - { - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } - })); + CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + CHECK(hdr.headers.result() == requests::http::status::ok); + CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); + CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); + })); + // post-json async_post(conn, urls::url_view("/post"), json::value{{"test-key", "test-value"}}, {{}, {false}}, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("post-json") - { - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - } + CHECK_HTTP_RESULT(hdr.headers); + auto js = as_json(hdr); + CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); + CHECK(js.at("headers").at("Content-Type") == "application/json"); })); - + // post-form async_post(conn, urls::url_view("/post"), requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, {{}, {false}}, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("post-form") - { - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } + CHECK_HTTP_RESULT(hdr.headers); + auto js = as_json(hdr); + CHECK(hdr.headers.result() == requests::http::status::ok); + CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); + CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); })); } diff --git a/test/cookie.cpp b/test/cookie.cpp index 16ba752..10285bd 100644 --- a/test/cookie.cpp +++ b/test/cookie.cpp @@ -42,6 +42,8 @@ void http_request_cookie_connection(bool https) sslctx.set_default_verify_paths(); auto hc = https ? requests::connection(ctx.get_executor(), sslctx) : requests::connection(ctx.get_executor()); hc.set_host(url); + hc.use_ssl(https); + asio::ip::tcp::resolver rslvr{ctx}; asio::ip::tcp::endpoint ep = *rslvr.resolve(url, https ? "https" : "http").begin(); diff --git a/test/json.cpp b/test/json.cpp index 1338cf2..7c6e9ef 100644 --- a/test/json.cpp +++ b/test/json.cpp @@ -15,6 +15,7 @@ #include "doctest.h" #include "string_maker.hpp" + namespace requests = boost::requests; namespace filesystem = requests::filesystem; namespace asio = boost::asio; @@ -46,31 +47,36 @@ void json_request_connection(bool https) sslctx.set_verify_mode(asio::ssl::verify_peer); sslctx.set_default_verify_paths(); - auto hc = https ? requests::connection(ctx.get_executor(), sslctx) : requests::connection(ctx.get_executor()); + requests::connection hc(ctx.get_executor(), sslctx); + hc.set_host(url); + hc.use_ssl(https); + CHECK(hc.uses_ssl() == https); + asio::ip::tcp::resolver rslvr{ctx}; asio::ip::tcp::endpoint ep = *rslvr.resolve(url, https ? "https" : "http").begin(); hc.connect(ep); - // SUBCASE("headers") + // headers { auto hdr = request(hc, requests::http::verb::get, urls::url_view("/headers"), requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto hd = as_json(hdr).at("headers"); CHECK(hd.at("Host") == json::value(url)); CHECK(hd.at("Test-Header") == json::value("it works")); } - // SUBCASE("stream") + // stream { auto str = hc.ropen(requests::http::verb::get, urls::url_view("/get"), requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}); - + CHECK_HTTP_RESULT(str.headers()); json::stream_parser sp; char buf[32]; @@ -90,11 +96,11 @@ void json_request_connection(bool https) CHECK(hd.at("Test-Header") == json::value("it works")); } - // SUBCASE("get-redirect") + // get-redirect { auto hdr = requests::json::get(hc, urls::url_view("/redirect-to?url=%2Fget"), {requests::headers({{"Test-Header", "it works"}}), {false}}); - + CHECK_HTTP_RESULT(hdr.headers); CHECK(hdr.history.size() == 1u); CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); @@ -104,18 +110,21 @@ void json_request_connection(bool https) CHECK(hd.at("Test-Header") == json::value("it works")); } - // SUBCASE("too-many-redirects") + // too-many-redirects { error_code ec; - auto res = requests::json::get(hc, urls::url_view("/redirect/10"), {{}, {false, requests::redirect_mode::private_domain, 5}}, ec); + auto res = requests::json::get(hc, urls::url_view("/redirect/10"), + {{}, {false, requests::redirect_mode::private_domain, 5}}, ec); + CHECK_HTTP_RESULT(res.headers); CHECK(res.history.size() == 5); CHECK(res.headers.begin() == res.headers.end()); CHECK(ec == requests::error::too_many_redirects); } - // SUBCASE("delete") + // delete { auto hdr = requests::json::delete_(hc, urls::url_view("/delete"), json::value{{"test-key", "test-value"}}, {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto & js = hdr.value; CHECK(beast::http::to_status_class(hdr.headers.result()) == beast::http::status_class::successful); @@ -123,10 +132,11 @@ void json_request_connection(bool https) CHECK(js->at("headers").at("Content-Type") == "application/json"); } - // SUBCASE("patch") + // patch { json::value msg {{"test-key", "test-value"}}; auto hdr = requests::json::patch(hc, urls::url_view("/patch"), msg, {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto & js = hdr.value; CHECK(hdr.headers.result() == beast::http::status::ok); @@ -134,10 +144,11 @@ void json_request_connection(bool https) CHECK(js.at("json") == msg); } - // SUBCASE("json") + // json { json::value msg {{"test-key", "test-value"}}; auto hdr = requests::json::put(hc, urls::url_view("/put"), msg, {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto & js = hdr.value; @@ -147,10 +158,11 @@ void json_request_connection(bool https) CHECK(js->at("json") == msg); } - // SUBCASE("post") + // post { json::value msg {{"test-key", "test-value"}}; auto hdr = requests::json::post(hc, urls::url_view("/post"), msg, {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); auto & js = hdr.value; CHECK(hdr.headers.result() == beast::http::status::ok); @@ -178,6 +190,7 @@ void run_json_tests(error_code ec, { check_ec(ec); auto & hd = hdr.value.at("headers"); + CHECK_HTTP_RESULT(hdr.headers); CHECK(hd.at("Host") == json::value(url.encoded_host())); CHECK(hd.at("Test-Header") == json::value("it works")); @@ -191,6 +204,7 @@ void run_json_tests(error_code ec, [url](error_code ec, requests::json::response<> hdr) { check_ec(ec); + CHECK_HTTP_RESULT(hdr.headers); CHECK(hdr.history.size() == 1u); CHECK(hdr.history.at(0u).at(beast::http::field::location) == "/get"); @@ -206,6 +220,7 @@ void run_json_tests(error_code ec, tracker( [url](error_code ec, requests::json::response<> res) { + CHECK_HTTP_RESULT(res.headers); CHECK(res.history.size() == 5); CHECK(res.headers.begin() == res.headers.end()); CHECK(ec == requests::error::too_many_redirects); @@ -218,6 +233,7 @@ void run_json_tests(error_code ec, [url](error_code ec, requests::json::response<> hdr) { check_ec(ec); + CHECK_HTTP_RESULT(hdr.headers); auto & js = hdr.value; CHECK(beast::http::to_status_class(hdr.headers.result()) == beast::http::status_class::successful); CHECK(js.at("headers").at("Content-Type") == "application/json"); @@ -230,6 +246,7 @@ void run_json_tests(error_code ec, { check_ec(ec); auto & js = hdr.value; + CHECK_HTTP_RESULT(hdr.headers); CHECK(hdr.headers.result() == beast::http::status::ok); CHECK(js.at("headers").at("Content-Type") == "application/json"); CHECK(js.at("json") == json::value{{"test-key", "test-value"}}); @@ -242,6 +259,7 @@ void run_json_tests(error_code ec, { check_ec(ec); auto & js = hdr.value; + CHECK_HTTP_RESULT(hdr.headers); CHECK(hdr.headers.result() == beast::http::status::ok); REQUIRE(js); CHECK(js->at("headers").at("Content-Type") == "application/json"); @@ -255,6 +273,7 @@ void run_json_tests(error_code ec, { check_ec(ec); auto & js = hdr.value; + CHECK_HTTP_RESULT(hdr.headers); CHECK(hdr.headers.result() == beast::http::status::ok); CHECK(js.at("headers").at("Content-Type") == "application/json"); CHECK(js.at("json") == json::value{{"test-key", "test-value"}}); @@ -292,6 +311,7 @@ TEST_CASE("async-json-request") conn = requests::connection(ctx); asio::ip::tcp::resolver rslvr{ctx}; url.set_scheme("http"); + conn.use_ssl(false); CHECK(!conn.uses_ssl()); conn.set_host(url.encoded_host()); diff --git a/test/method.cpp b/test/method.cpp index 9f43fbe..8499e3a 100644 --- a/test/method.cpp +++ b/test/method.cpp @@ -91,13 +91,13 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) requests::default_options().enforce_tls = false; requests::default_options().max_redirects = 5; - // SUBCASE("headers") + // headers { auto hdr = requests::request(requests::http::verb::get, u("/headers"), requests::empty{}, requests::headers({{"Test-Header", "it works"}})); - + CHECK_HTTP_RESULT(hdr.headers); auto hd = as_json(hdr).at("headers"); CHECK(hd.at("Host") == json::value(httpbin())); @@ -105,20 +105,22 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) } - // SUBCASE("get") + // get { auto hdr = requests::get(u("/get"), requests::headers({{"Test-Header", "it works"}})); + CHECK_HTTP_RESULT(hdr.headers); auto hd = as_json(hdr).at("headers"); CHECK(hd.at("Host") == json::value(httpbin())); CHECK(hd.at("Test-Header") == json::value("it works")); } - // SUBCASE("get-redirect") + // get-redirect { auto hdr = requests::get(u("/redirect-to?url=%2Fget"), requests::headers({{"Test-Header", "it works"}})); + CHECK_HTTP_RESULT(hdr.headers); CHECK(hdr.history.size() == 1u); CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); @@ -128,17 +130,18 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) CHECK(hd.at("Test-Header") == json::value("it works")); } - // SUBCASE("too-many-redirects") + // too-many-redirects { boost::system::error_code ec; requests::default_session().options().max_redirects = 3; auto res = requests::get(u("/redirect/10"), {}, ec); + CHECK_HTTP_RESULT(res.headers); CHECK(res.history.size() == 3); CHECK(res.headers.begin() == res.headers.end()); CHECK(ec == requests::error::too_many_redirects); } - // SUBCASE("download") + // download { const auto target = filesystem::temp_directory_path() / "requests-test.png"; if (filesystem::exists(target)) @@ -146,7 +149,7 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) CHECK(!filesystem::exists(target)); auto res = requests::download(u("/image"), {}, target); - + CHECK_HTTP_RESULT(res.headers); CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); @@ -156,7 +159,7 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) } - // SUBCASE("download-redirect") + // download-redirect { const auto target = filesystem::temp_directory_path() / "requests-test.png"; @@ -165,7 +168,7 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) CHECK(!filesystem::exists(target)); auto res = requests::download(u("/redirect-to?url=%2Fimage"), {}, target); - + CHECK_HTTP_RESULT(res.headers); CHECK(res.history.size() == 1u); CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); @@ -178,7 +181,7 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) } - // SUBCASE("download-too-many-redirects") + // download-too-many-redirects { error_code ec; requests::default_options().max_redirects = 3; @@ -188,26 +191,28 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) filesystem::remove(target); auto res = requests::download(u("/redirect/10"), {}, target, ec); CHECK(res.history.size() == 3); - + CHECK_HTTP_RESULT(res.headers); CHECK(res.headers.begin() == res.headers.end()); CHECK(ec == requests::error::too_many_redirects); CHECK(!filesystem::exists(target)); } - // SUBCASE("delete") + // delete { auto hdr = requests::delete_(u("/delete"), json::value{{"test-key", "test-value"}}, {}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); CHECK(js.at("headers").at("Content-Type") == "application/json"); } - // SUBCASE("patch-json") + // patch-json { json::value msg {{"test-key", "test-value"}}; auto hdr = requests::patch(u("/patch"), msg, {}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); @@ -215,11 +220,12 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) CHECK(js.at("json") == msg); } - // SUBCASE("patch-form") + // patch-form { auto hdr = requests::patch(u("/patch"), requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, {}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); @@ -227,10 +233,11 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); } - // SUBCASE("put-json") + // put-json { json::value msg {{"test-key", "test-value"}}; auto hdr = requests::put(u("/put"), msg, {}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); @@ -238,11 +245,12 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) CHECK(js.at("json") == msg); } - // SUBCASE("put-form") + // put-form { auto hdr = requests::put(u("/put"), requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, {}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); @@ -250,10 +258,11 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); } - // SUBCASE("post-json") + // post-json { json::value msg {{"test-key", "test-value"}}; auto hdr = requests::post(u("/post"), msg, {}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); @@ -261,11 +270,12 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) CHECK(js.at("json") == msg); } - // SUBCASE("post-form") + // post-form { auto hdr = requests::post(u("/post"), requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, {}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); @@ -301,15 +311,14 @@ void async_http_pool_request(asio::any_io_executor exec, tracker(exec, [url](error_code ec, requests::response hdr) { - // SUBCASE("headers") - { + // headers - check_ec(ec); - auto hd = as_json(hdr).at("headers"); + check_ec(ec); + auto hd = as_json(hdr).at("headers"); + CHECK_HTTP_RESULT(hdr.headers); - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } + CHECK(hd.at("Host") == json::value(url.host_name())); + CHECK(hd.at("Test-Header") == json::value("it works")); })); requests::async_get(u("/get"), @@ -317,14 +326,13 @@ void async_http_pool_request(asio::any_io_executor exec, tracker(exec, [url](error_code ec, requests::response hdr) { - // SUBCASE("headers") - { - check_ec(ec); - auto hd = as_json(hdr).at("headers"); + // headers + check_ec(ec); + auto hd = as_json(hdr).at("headers"); + CHECK_HTTP_RESULT(hdr.headers); - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } + CHECK(hd.at("Host") == json::value(url.host_name())); + CHECK(hd.at("Test-Header") == json::value("it works")); })); requests::async_get(u("/redirect-to", "url=/get"), @@ -332,29 +340,29 @@ void async_http_pool_request(asio::any_io_executor exec, tracker(exec, [url](error_code ec, requests::response hdr) { - // SUBCASE("get-redirect") - { - check_ec(ec); - CHECK(hdr.history.size() == 1u); - CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); + // get-redirect + check_ec(ec); + CHECK(hdr.history.size() == 1u); + CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); + CHECK_HTTP_RESULT(hdr.headers); - auto hd = as_json(hdr).at("headers"); + auto hd = as_json(hdr).at("headers"); - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } + CHECK(hd.at("Host") == json::value(url.host_name())); + CHECK(hd.at("Test-Header") == json::value("it works")); })); requests::async_get(u("/redirect/10"), {}, tracker(exec, [url](error_code ec, requests::response res) { - // SUBCASE("too-many-redirects") - { - CHECK(res.history.size() == 3); - CHECK(res.headers.begin() == res.headers.end()); - CHECK(ec == requests::error::too_many_redirects); - } + // too-many-redirects + CHECK_HTTP_RESULT(res.headers); + + CHECK(res.history.size() == 3); + CHECK(res.headers.begin() == res.headers.end()); + CHECK(ec == requests::error::too_many_redirects); + })); { @@ -369,15 +377,15 @@ void async_http_pool_request(asio::any_io_executor exec, tracker(exec, [url, target](error_code ec, requests::response_base res) { - // SUBCASE("download") - { - check_ec(ec); - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - filesystem::remove(target, ec); - } + // download + CHECK_HTTP_RESULT(res.headers); + + check_ec(ec); + CHECK_MESSAGE(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u, res.headers); + CHECK_MESSAGE(res.headers.at(requests::http::field::content_type) == "image/png", res.headers); + + CHECK_MESSAGE(filesystem::exists(target), target); + filesystem::remove(target, ec); })); } @@ -391,18 +399,19 @@ void async_http_pool_request(asio::any_io_executor exec, tracker(exec, [url, target](error_code ec, requests::response_base res) { - // SUBCASE("download-redirect") - { - check_ec(ec); - CHECK(res.history.size() == 1u); - CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); - - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - filesystem::remove(target, ec); - } + // download-redirect + CHECK_HTTP_RESULT(res.headers); + + check_ec(ec); + CHECK(res.history.size() == 1u); + CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); + + CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); + CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); + + CHECK_MESSAGE(filesystem::exists(target), target); + filesystem::remove(target, ec); + })); } @@ -411,13 +420,12 @@ void async_http_pool_request(asio::any_io_executor exec, tracker(exec, [url](error_code ec, requests::response hdr) { - // SUBCASE("delete") - { + // delete check_ec(ec); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); CHECK(js.at("headers").at("Content-Type") == "application/json"); - } })); @@ -425,13 +433,14 @@ void async_http_pool_request(asio::any_io_executor exec, tracker(exec, [url](error_code ec, requests::response hdr) { - // SUBCASE("patch-json") - { + // patch-json + + CHECK_HTTP_RESULT(hdr.headers); check_ec(ec); auto js = as_json(hdr); CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); CHECK(js.at("headers").at("Content-Type") == "application/json"); - } + })); @@ -442,14 +451,14 @@ void async_http_pool_request(asio::any_io_executor exec, tracker(exec, [url](error_code ec, requests::response hdr) { - // SUBCASE("patch-form") - { - check_ec(ec); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } + // patch-form + check_ec(ec); + auto js = as_json(hdr); + CHECK(hdr.headers.result() == requests::http::status::ok); + CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); + CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); + CHECK_HTTP_RESULT(hdr.headers); + })); @@ -457,13 +466,14 @@ void async_http_pool_request(asio::any_io_executor exec, tracker(exec, [url](error_code ec, requests::response hdr) { - // SUBCASE("put-json") - { - check_ec(ec); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - } + // put-json + CHECK_HTTP_RESULT(hdr.headers); + + check_ec(ec); + auto js = as_json(hdr); + CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); + CHECK(js.at("headers").at("Content-Type") == "application/json"); + })); @@ -474,14 +484,15 @@ void async_http_pool_request(asio::any_io_executor exec, tracker(exec, [url](error_code ec, requests::response hdr) { - // SUBCASE("put-form") - { - check_ec(ec); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } + // put-form + CHECK_HTTP_RESULT(hdr.headers); + + check_ec(ec); + auto js = as_json(hdr); + CHECK(hdr.headers.result() == requests::http::status::ok); + CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); + CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); + })); @@ -489,13 +500,14 @@ void async_http_pool_request(asio::any_io_executor exec, tracker(exec, [url](error_code ec, requests::response hdr) { - // SUBCASE("post-json") - { - check_ec(ec); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - } + // post-json + CHECK_HTTP_RESULT(hdr.headers); + + check_ec(ec); + auto js = as_json(hdr); + CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); + CHECK(js.at("headers").at("Content-Type") == "application/json"); + })); @@ -506,14 +518,16 @@ void async_http_pool_request(asio::any_io_executor exec, tracker(exec, [url](error_code ec, requests::response hdr) { - // SUBCASE("post-form") - { - check_ec(ec); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } + // post-form + + CHECK_HTTP_RESULT(hdr.headers); + + check_ec(ec); + auto js = as_json(hdr); + CHECK(hdr.headers.result() == requests::http::status::ok); + CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); + CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); + })); } diff --git a/test/session.cpp b/test/session.cpp index 5f78283..a7a3d91 100644 --- a/test/session.cpp +++ b/test/session.cpp @@ -92,13 +92,13 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) hc.options().enforce_tls = false; hc.options().max_redirects = 5; - // SUBCASE("headers") + // headers { auto hdr = request(hc, requests::http::verb::get, u("/headers"), requests::empty{}, requests::headers({{"Test-Header", "it works"}})); - + CHECK_HTTP_RESULT(hdr.headers); auto hd = as_json(hdr).at("headers"); CHECK(hd.at("Host") == json::value(httpbin())); @@ -106,39 +106,41 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) } - // SUBCASE("get") + // get { auto hdr = get(hc, u("/get"), requests::headers({{"Test-Header", "it works"}})); - + CHECK_HTTP_RESULT(hdr.headers); auto hd = as_json(hdr).at("headers"); CHECK(hd.at("Host") == json::value(httpbin())); CHECK(hd.at("Test-Header") == json::value("it works")); } - // SUBCASE("get-redirect") + // get-redirect { auto hdr = get(hc, u("/redirect-to?url=%2Fget"), requests::headers({{"Test-Header", "it works"}})); CHECK(hdr.history.size() == 1u); CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); + CHECK_HTTP_RESULT(hdr.headers); auto hd = as_json(hdr).at("headers"); CHECK(hd.at("Host") == json::value(httpbin())); CHECK(hd.at("Test-Header") == json::value("it works")); } - // SUBCASE("too-many-redirects") + // too-many-redirects { error_code ec; auto res = get(hc, u("/redirect/10"), {}, ec); + CHECK_HTTP_RESULT(res.headers); CHECK(res.history.size() == 5); CHECK(res.headers.begin() == res.headers.end()); CHECK(ec == requests::error::too_many_redirects); } - // SUBCASE("download") + // download { const auto target = filesystem::temp_directory_path() / "requests-test.png"; if (filesystem::exists(target)) @@ -147,6 +149,7 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) CHECK(!filesystem::exists(target)); auto res = download(hc, u("/image"), {}, target); + CHECK_HTTP_RESULT(res.headers); CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); @@ -156,7 +159,7 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) } - // SUBCASE("download-redirect") + // download-redirect { const auto target = filesystem::temp_directory_path() / "requests-test.png"; @@ -165,7 +168,7 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) CHECK(!filesystem::exists(target)); auto res = download(hc, u("/redirect-to?url=%2Fimage"), {}, target); - + CHECK_HTTP_RESULT(res.headers); CHECK(res.history.size() == 1u); CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); @@ -178,7 +181,7 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) } - // SUBCASE("download-too-many-redirects") + // download-too-many-redirects { error_code ec; hc.options().max_redirects = 3; @@ -187,85 +190,90 @@ TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) filesystem::remove(target); auto res = download(hc, u("/redirect/10"), {}, target, ec); CHECK(res.history.size() == 3); - + CHECK_HTTP_RESULT(res.headers); CHECK(res.headers.begin() == res.headers.end()); CHECK(ec == requests::error::too_many_redirects); CHECK(!filesystem::exists(target)); } - // SUBCASE("delete") + // delete { auto hdr = delete_(hc, u("/delete"), json::value{{"test-key", "test-value"}}, {}); - + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(to_status_class(hdr.headers.result()) == requests::http::status_class::successful); CHECK(js.at("headers").at("Content-Type") == "application/json"); } - // SUBCASE("patch-json") + // patch-json { json::value msg {{"test-key", "test-value"}}; auto hdr = patch(hc, u("/patch"), msg, {}); - + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); CHECK(js.at("headers").at("Content-Type") == "application/json"); CHECK(js.at("json") == msg); } - // SUBCASE("patch-form") + // patch-form { auto hdr = patch(hc, u("/patch"), requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, {}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); } - // SUBCASE("put-json") + // put-json { json::value msg {{"test-key", "test-value"}}; auto hdr = put(hc, u("/put"), msg, {}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); CHECK(js.at("headers").at("Content-Type") == "application/json"); CHECK(js.at("json") == msg); } - // SUBCASE("put-form") + // put-form { auto hdr = put(hc, u("/put"), requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, {}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); } - // SUBCASE("post-json") + // post-json { json::value msg {{"test-key", "test-value"}}; auto hdr = post(hc, u("/post"), msg, {}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); CHECK(js.at("headers").at("Content-Type") == "application/json"); CHECK(js.at("json") == msg); } - // SUBCASE("post-form") + // post-form { auto hdr = post(hc, u("/post"), requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, {}); + CHECK_HTTP_RESULT(hdr.headers); auto js = as_json(hdr); CHECK(hdr.headers.result() == requests::http::status::ok); CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); @@ -299,15 +307,13 @@ void async_http_pool_request(requests::session & sess, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("headers") - { - - check_ec(ec); - auto hd = as_json(hdr).at("headers"); + // headers + check_ec(ec); + auto hd = as_json(hdr).at("headers"); + CHECK_HTTP_RESULT(hdr.headers); - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } + CHECK(hd.at("Host") == json::value(url.host_name())); + CHECK(hd.at("Test-Header") == json::value("it works")); })); async_get(sess, @@ -316,14 +322,14 @@ void async_http_pool_request(requests::session & sess, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("headers") - { - check_ec(ec); - auto hd = as_json(hdr).at("headers"); + // headers + check_ec(ec); + CHECK_HTTP_RESULT(hdr.headers); + auto hd = as_json(hdr).at("headers"); + CHECK_HTTP_RESULT(hdr.headers); - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } + CHECK(hd.at("Host") == json::value(url.host_name())); + CHECK(hd.at("Test-Header") == json::value("it works")); })); async_get(sess, u("/redirect-to", "url=/get"), @@ -331,28 +337,28 @@ void async_http_pool_request(requests::session & sess, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("get-redirect") - { - CHECK(hdr.history.size() == 1u); - CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); + // get-redirect + CHECK_HTTP_RESULT(hdr.headers); + CHECK(hdr.history.size() == 1u); + CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); - auto hd = as_json(hdr).at("headers"); + auto hd = as_json(hdr).at("headers"); - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } + CHECK(hd.at("Host") == json::value(url.host_name())); + CHECK(hd.at("Test-Header") == json::value("it works")); })); async_get(sess, u("/redirect/10"), {}, tracker( [url](error_code ec, requests::response res) { - // SUBCASE("too-many-redirects") - { - CHECK(res.history.size() == 3); - CHECK(res.headers.begin() == res.headers.end()); - CHECK(ec == requests::error::too_many_redirects); - } + // too-many-redirects + CHECK_HTTP_RESULT(res.headers); + + CHECK(res.history.size() == 3); + CHECK(res.headers.begin() == res.headers.end()); + CHECK(ec == requests::error::too_many_redirects); + })); { @@ -366,15 +372,15 @@ void async_http_pool_request(requests::session & sess, tracker( [url, target](error_code ec, requests::response_base res) { - // SUBCASE("download") - { - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec_; - filesystem::remove(target, ec_); - } + // download + CHECK_HTTP_RESULT(res.headers); + CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); + CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); + + CHECK_MESSAGE(filesystem::exists(target), target); + fs_error_code ec_; + filesystem::remove(target, ec_); + })); } @@ -388,18 +394,19 @@ void async_http_pool_request(requests::session & sess, tracker( [url, target](error_code ec, requests::response_base res) { - // SUBCASE("download-redirect") - { - CHECK(res.history.size() == 1u); - CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); - - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec_; - filesystem::remove(target, ec_); - } + // download-redirect + CHECK_HTTP_RESULT(res.headers); + + CHECK(res.history.size() == 1u); + CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); + + CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); + CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); + + CHECK_MESSAGE(filesystem::exists(target), target); + fs_error_code ec_; + filesystem::remove(target, ec_); + })); } @@ -408,12 +415,13 @@ void async_http_pool_request(requests::session & sess, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("delete") - { - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - } + // delete + CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); + CHECK(js.at("headers").at("Content-Type") == "application/json"); + })); @@ -421,13 +429,14 @@ void async_http_pool_request(requests::session & sess, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("patch-json") - { - check_ec(ec); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - } + // patch-json + CHECK_HTTP_RESULT(hdr.headers); + + check_ec(ec); + auto js = as_json(hdr); + CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); + CHECK(js.at("headers").at("Content-Type") == "application/json"); + })); @@ -438,14 +447,15 @@ void async_http_pool_request(requests::session & sess, tracker( [url](error_code ec, requests::response hdr) { - // SUBCASE("patch-form") - { - check_ec(ec); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } + // patch-form + CHECK_HTTP_RESULT(hdr.headers); + + check_ec(ec); + auto js = as_json(hdr); + CHECK(hdr.headers.result() == requests::http::status::ok); + CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); + CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); + })); @@ -454,13 +464,14 @@ void async_http_pool_request(requests::session & sess, [url](error_code ec, requests::response hdr) { check_ec(ec); - // SUBCASE("put-json") - { - check_ec(ec); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - } + // put-json + CHECK_HTTP_RESULT(hdr.headers); + + check_ec(ec); + auto js = as_json(hdr); + CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); + CHECK(js.at("headers").at("Content-Type") == "application/json"); + })); @@ -472,14 +483,15 @@ void async_http_pool_request(requests::session & sess, [url](error_code ec, requests::response hdr) { check_ec(ec); - // SUBCASE("put-form") - { - check_ec(ec); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } + // put-form + CHECK_HTTP_RESULT(hdr.headers); + + check_ec(ec); + auto js = as_json(hdr); + CHECK(hdr.headers.result() == requests::http::status::ok); + CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); + CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); + })); @@ -488,13 +500,12 @@ void async_http_pool_request(requests::session & sess, [url](error_code ec, requests::response hdr) { check_ec(ec); - // SUBCASE("post-json") - { - check_ec(ec); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - } + // post-json + CHECK_HTTP_RESULT(hdr.headers); + auto js = as_json(hdr); + CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); + CHECK(js.at("headers").at("Content-Type") == "application/json"); + })); @@ -505,15 +516,16 @@ void async_http_pool_request(requests::session & sess, tracker( [url](error_code ec, requests::response hdr) { + // post-form + check_ec(ec); - // SUBCASE("post-form") - { - check_ec(ec); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } + CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + CHECK(hdr.headers.result() == requests::http::status::ok); + CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); + CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); + })); } diff --git a/test/string_maker.hpp b/test/string_maker.hpp index 26e42a1..16cb017 100644 --- a/test/string_maker.hpp +++ b/test/string_maker.hpp @@ -207,4 +207,8 @@ auto tracker(boost::asio::any_io_executor exec, Handler && handler, return boost::asio::bind_executor(exec, tracker_t>(std::forward(handler), loc)); } +#define CHECK_HTTP_RESULT(Response) \ + CHECK_MESSAGE(boost::requests::http::to_status_class(Response.result()) == requests::http::status_class::successful, Response) + + #endif \ No newline at end of file From 7fc363154f29f936b1eb4ad17b030e2edc962b3f Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 7 Feb 2023 16:01:53 +0800 Subject: [PATCH 34/76] include src.hpp from beast. --- Jamfile | 4 ++-- build/Jamfile | 8 ++++---- include/boost/requests/detail/impl/connection_impl.ipp | 8 ++++---- test/main_test.cpp | 2 ++ 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Jamfile b/Jamfile index 9d97675..4a9b8d3 100644 --- a/Jamfile +++ b/Jamfile @@ -18,7 +18,7 @@ build-project test ; boost.use-project ; feature.feature boost.requests.separate-compilation : on off : propagated composite ; -feature.compose on : BOOST_REQUESTS_SEPARATE_COMPILATION=1 ; +feature.compose on : BOOST_REQUESTS_SEPARATE_COMPILATION ; feature.feature boost.requests.use-std-fs : off on : propagated composite ; -feature.compose on : BOOST_REQUESTS_USE_STD_FS=1 ; +feature.compose on : BOOST_REQUESTS_USE_STD_FS ; diff --git a/build/Jamfile b/build/Jamfile index 39fbc1e..20330fe 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -79,10 +79,10 @@ if [ os.name ] = NT lib boost_requests : requests_sources crypto ssl /boost//url /boost//json - : requirements - - [ ac.check-library ssl : ssl/shared : no ] - [ ac.check-library crypto : crypto/shared : no ] + : requirements + static + [ ac.check-library ssl : ssl/shared : no ] + [ ac.check-library crypto : crypto/shared : no ] off:/boost//filesystem : usage-requirements /boost//container/off diff --git a/include/boost/requests/detail/impl/connection_impl.ipp b/include/boost/requests/detail/impl/connection_impl.ipp index da77e65..d2ce49f 100644 --- a/include/boost/requests/detail/impl/connection_impl.ipp +++ b/include/boost/requests/detail/impl/connection_impl.ipp @@ -211,11 +211,11 @@ auto connection_impl::ropen(beast::http::verb method, auto rc = str.parser_->get().base().result(); - if ((opt.redirect < redirect_mode::endpoint) + if ((opt.redirect == redirect_mode::none) || ((rc != http::status::moved_permanently) - && (rc != http::status::found) - && (rc != http::status::temporary_redirect) - && (rc != http::status::permanent_redirect))) + && (rc != http::status::found) + && (rc != http::status::temporary_redirect) + && (rc != http::status::permanent_redirect))) { // GO str.t_ = std::move(t); diff --git a/test/main_test.cpp b/test/main_test.cpp index d3d2a70..c00ae89 100644 --- a/test/main_test.cpp +++ b/test/main_test.cpp @@ -5,3 +5,5 @@ #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" + +#include \ No newline at end of file From 3db3debe8a9f29b11ab49724e3d160c3a840e01f Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 7 Feb 2023 16:56:31 +0800 Subject: [PATCH 35/76] trying to link in osx --- test/Jamfile | 2 +- test/main_test.cpp | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/test/Jamfile b/test/Jamfile index 18a54a3..8e8229c 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -23,4 +23,4 @@ alias asio-ssl : ../../beast//lib-asio-ssl ; alias beast : ../../beast//lib-beast ; alias ssl : ../../beast//ssl ; -run [ glob-tree *.cpp ] /boost//json /boost//url asio asio-ssl beast ssl ; \ No newline at end of file +run [ glob-tree *.cpp ] /boost//json /boost//url ssl ; \ No newline at end of file diff --git a/test/main_test.cpp b/test/main_test.cpp index c00ae89..ae56bbf 100644 --- a/test/main_test.cpp +++ b/test/main_test.cpp @@ -4,6 +4,4 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN -#include "doctest.h" - -#include \ No newline at end of file +#include "doctest.h" \ No newline at end of file From 9c3075c14623ac16a95014887152ea5782a13035 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 7 Feb 2023 17:53:12 +0800 Subject: [PATCH 36/76] readded beast dependency. --- test/Jamfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Jamfile b/test/Jamfile index 8e8229c..e99b836 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -23,4 +23,4 @@ alias asio-ssl : ../../beast//lib-asio-ssl ; alias beast : ../../beast//lib-beast ; alias ssl : ../../beast//ssl ; -run [ glob-tree *.cpp ] /boost//json /boost//url ssl ; \ No newline at end of file +run [ glob-tree *.cpp ] /boost//json /boost//url ssl asio asio-ssl beast ssl ; \ No newline at end of file From d041e09df9fa22596129199e68f1d323b4afb6ec Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 8 Feb 2023 10:44:25 +0800 Subject: [PATCH 37/76] switched to cppal httpbin --- .drone.star | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.drone.star b/.drone.star index e9eb503..1119bf6 100644 --- a/.drone.star +++ b/.drone.star @@ -14,21 +14,21 @@ windowsglobalimage="cppalliance/dronevs2019" def main(ctx): return [ - linux_cxx("docs", "", packages="docbook docbook-xml docbook-xsl xsltproc libsaxonhe-java default-jre-headless flex libfl-dev bison unzip rsync mlocate", image="cppalliance/droneubuntu1804:1", buildtype="docs", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", "COMMENT": "docs"}, globalenv=globalenv), - linux_cxx("asan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", 'COMMENT': 'asan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '14', 'B2_ASAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_EXTRA_PRIVILEGED': 'True', 'DRONE_JOB_UUID': '356a192b79'}, globalenv=globalenv, privileged=True), - linux_cxx("ubsan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", 'COMMENT': 'ubsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '14', 'B2_UBSAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'B2_LINKFLAGS': '-fuse-ld=gold', 'DRONE_JOB_UUID': '77de68daec'}, globalenv=globalenv), - linux_cxx("gcc 11 arm64", "g++-11", packages="g++-11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:multiarch", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", 'B2_TOOLSET': 'gcc-11', 'B2_CXXSTD': '14', 'DRONE_JOB_UUID': '17ba079169m'}, arch="arm64", globalenv=globalenv), + linux_cxx("docs", "", packages="docbook docbook-xml docbook-xsl xsltproc libsaxonhe-java default-jre-headless flex libfl-dev bison unzip rsync mlocate", image="cppalliance/droneubuntu1804:1", buildtype="docs", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "COMMENT": "docs"}, globalenv=globalenv), + linux_cxx("asan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'COMMENT': 'asan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '14', 'B2_ASAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_EXTRA_PRIVILEGED': 'True', 'DRONE_JOB_UUID': '356a192b79'}, globalenv=globalenv, privileged=True), + linux_cxx("ubsan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'COMMENT': 'ubsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '14', 'B2_UBSAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'B2_LINKFLAGS': '-fuse-ld=gold', 'DRONE_JOB_UUID': '77de68daec'}, globalenv=globalenv), + linux_cxx("gcc 11 arm64", "g++-11", packages="g++-11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:multiarch", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'B2_TOOLSET': 'gcc-11', 'B2_CXXSTD': '14', 'DRONE_JOB_UUID': '17ba079169m'}, arch="arm64", globalenv=globalenv), linux_cxx("GCC 10, Debug + Coverage", "g++-10", packages="g++-10 libssl-dev libffi-dev binutils-gold gdb mlocate", - image="cppalliance/droneubuntu2004:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", "GCOV": "gcov-10", "LCOV_VERSION": "1.15", "VARIANT": "process_coverage", "TOOLSET": "gcc", "COMPILER": "g++-10", "CXXSTD": "14", "DRONE_BEFORE_INSTALL" : "process_coverage", "CODECOV_TOKEN": {"from_secret": "codecov_token"}}, globalenv=globalenv, privileged=True), + image="cppalliance/droneubuntu2004:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "GCOV": "gcov-10", "LCOV_VERSION": "1.15", "VARIANT": "process_coverage", "TOOLSET": "gcc", "COMPILER": "g++-10", "CXXSTD": "14", "DRONE_BEFORE_INSTALL" : "process_coverage", "CODECOV_TOKEN": {"from_secret": "codecov_token"}}, globalenv=globalenv, privileged=True), # A set of jobs based on the earlier .travis.yml configuration: - linux_cxx("Default clang++ with libc++", "clang++-libc++", packages="libc++-dev mlocate", image="cppalliance/droneubuntu1604:1", buildtype="buildtype", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", "B2_TOOLSET": "clang-7", "B2_CXXSTD": "14", "VARIANT": "debug", "TOOLSET": "clang", "COMPILER": "clang++-libc++", "CXXSTD": "14", "CXX_FLAGS": "-stdlib=libc++ -stdlib=libc++", "TRAVISCLANG" : "yes" }, globalenv=globalenv), - linux_cxx("Default g++", "g++", packages="mlocate", image="cppalliance/droneubuntu1604:1", buildtype="buildtype", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", "VARIANT": "release", "TOOLSET": "gcc", "COMPILER": "g++", "CXXSTD": "14" }, globalenv=globalenv), - linux_cxx("Clang 3.8, UBasan", "clang++-3.8", packages="clang-3.8 libssl-dev mlocate", llvm_os="precise", llvm_ver="3.8", image="cppalliance/droneubuntu1604:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", "VARIANT": "process_ubasan", "TOOLSET": "clang", "COMPILER": "clang++-3.8", "CXXSTD": "14", "UBSAN_OPTIONS": 'print_stacktrace=1', "DRONE_BEFORE_INSTALL": "UBasan" }, globalenv=globalenv), - linux_cxx("gcc 6", "g++-6", packages="g++-6", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", 'B2_TOOLSET': 'gcc-6', 'B2_CXXSTD': '14', 'DRONE_JOB_UUID': '902ba3cda1'}, globalenv=globalenv), - linux_cxx("clang 3.8", "clang++-3.8", packages="clang-3.8", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu1604:1", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", 'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-3.8', 'B2_CXXSTD': '14', 'DRONE_JOB_UUID': '7b52009b64'}, globalenv=globalenv), - osx_cxx("clang", "g++", packages="", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", 'B2_TOOLSET': 'clang', 'B2_CXXSTD': '14,17', 'DRONE_JOB_UUID': '91032ad7bb'}, globalenv=globalenv), - linux_cxx("coverity", "g++", packages="", buildtype="coverity", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", 'COMMENT': 'Coverity Scan', 'B2_TOOLSET': 'clang', 'DRONE_JOB_UUID': '472b07b9fc'}, globalenv=globalenv), - windows_cxx("msvc-14.3", "", image="cppalliance/dronevs2022:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.org", "VARIANT": "release", "TOOLSET": "msvc-14.3", "CXXSTD": "17", "ADDRESS_MODEL": "64"}), + linux_cxx("Default clang++ with libc++", "clang++-libc++", packages="libc++-dev mlocate", image="cppalliance/droneubuntu1604:1", buildtype="buildtype", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "B2_TOOLSET": "clang-7", "B2_CXXSTD": "14", "VARIANT": "debug", "TOOLSET": "clang", "COMPILER": "clang++-libc++", "CXXSTD": "14", "CXX_FLAGS": "-stdlib=libc++ -stdlib=libc++", "TRAVISCLANG" : "yes" }, globalenv=globalenv), + linux_cxx("Default g++", "g++", packages="mlocate", image="cppalliance/droneubuntu1604:1", buildtype="buildtype", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "VARIANT": "release", "TOOLSET": "gcc", "COMPILER": "g++", "CXXSTD": "14" }, globalenv=globalenv), + linux_cxx("Clang 3.8, UBasan", "clang++-3.8", packages="clang-3.8 libssl-dev mlocate", llvm_os="precise", llvm_ver="3.8", image="cppalliance/droneubuntu1604:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "VARIANT": "process_ubasan", "TOOLSET": "clang", "COMPILER": "clang++-3.8", "CXXSTD": "14", "UBSAN_OPTIONS": 'print_stacktrace=1', "DRONE_BEFORE_INSTALL": "UBasan" }, globalenv=globalenv), + linux_cxx("gcc 6", "g++-6", packages="g++-6", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'B2_TOOLSET': 'gcc-6', 'B2_CXXSTD': '14', 'DRONE_JOB_UUID': '902ba3cda1'}, globalenv=globalenv), + linux_cxx("clang 3.8", "clang++-3.8", packages="clang-3.8", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu1604:1", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-3.8', 'B2_CXXSTD': '14', 'DRONE_JOB_UUID': '7b52009b64'}, globalenv=globalenv), + osx_cxx("clang", "g++", packages="", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'B2_TOOLSET': 'clang', 'B2_CXXSTD': '14,17', 'DRONE_JOB_UUID': '91032ad7bb'}, globalenv=globalenv), + linux_cxx("coverity", "g++", packages="", buildtype="coverity", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'COMMENT': 'Coverity Scan', 'B2_TOOLSET': 'clang', 'DRONE_JOB_UUID': '472b07b9fc'}, globalenv=globalenv), + windows_cxx("msvc-14.3", "", image="cppalliance/dronevs2022:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "VARIANT": "release", "TOOLSET": "msvc-14.3", "CXXSTD": "17", "ADDRESS_MODEL": "64"}), ] # from https://github.com/boostorg/boost-ci From 25e283fe3eaaff1c21e5c83df2b7e71c238e5d4f Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 8 Feb 2023 12:22:11 +0800 Subject: [PATCH 38/76] removed /boost//filesystem dep from reqests lib --- build/Jamfile | 1 - test/Jamfile | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/build/Jamfile b/build/Jamfile index 20330fe..436a41a 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -83,7 +83,6 @@ lib boost_requests static [ ac.check-library ssl : ssl/shared : no ] [ ac.check-library crypto : crypto/shared : no ] - off:/boost//filesystem : usage-requirements /boost//container/off diff --git a/test/Jamfile b/test/Jamfile index e99b836..9be5880 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -11,8 +11,7 @@ project : requirements ../../.. 17:on 20:on - on:/boost//requests - off:/boost//filesystem + on:/boost//requests ; import testing ; @@ -23,4 +22,5 @@ alias asio-ssl : ../../beast//lib-asio-ssl ; alias beast : ../../beast//lib-beast ; alias ssl : ../../beast//ssl ; -run [ glob-tree *.cpp ] /boost//json /boost//url ssl asio asio-ssl beast ssl ; \ No newline at end of file +run [ glob-tree *.cpp ] /boost//json /boost//url ssl asio asio-ssl beast ssl + : off:/boost//filesystem ; \ No newline at end of file From 15900fd89b7a52a2a61ccfae27e875ddd3d699cc Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 8 Feb 2023 18:11:09 +0800 Subject: [PATCH 39/76] [cmake] avoiding collision --- test/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a07432e..ff75083 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,10 +1,10 @@ file(GLOB_RECURSE ALL_TEST_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) -add_executable(tests ${ALL_TEST_FILES}) -target_link_libraries(tests PUBLIC +add_executable(boost_requests_tests ${ALL_TEST_FILES}) +target_link_libraries(boost_requests_tests PUBLIC Boost::requests Boost::atomic Boost::filesystem rt) -target_compile_definitions(tests PUBLIC +target_compile_definitions(boost_requests_tests PUBLIC BOOST_REQUESTS_SEPARATE_COMPILATION=1 BOOST_FILESYSTEM_NO_DEPRECATED=1) \ No newline at end of file From e20d84799806803eddf4f4a593e2299544b92fee Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 8 Feb 2023 18:33:57 +0800 Subject: [PATCH 40/76] Jam fixes --- Jamfile | 4 --- build/Jamfile | 77 ++------------------------------------------------- test/Jamfile | 4 +-- 3 files changed, 4 insertions(+), 81 deletions(-) diff --git a/Jamfile b/Jamfile index 4a9b8d3..a4b6a51 100644 --- a/Jamfile +++ b/Jamfile @@ -9,11 +9,7 @@ import feature ; import boost ; -import os ; -import path ; -build-project example ; -build-project test ; boost.use-project ; diff --git a/build/Jamfile b/build/Jamfile index 436a41a..764f0db 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -12,80 +12,7 @@ project boost/requests : source-location ../src ; -alias requests_deps - : usage-requirements - /boost//container/off - ; - -alias requests_sources - : src.cpp - : usage-requirements - /boost//container/off - ; - -explicit requests_deps requests_sources ; - -import os ; -import path ; -import ac ; - -lib ssl ; -lib crypto ; -lib crypt32 ; - -# Microsoft Windows section. Refer to FAQ "Windows and OpenSSL" -if [ os.name ] = NT -{ - local OPENSSL_ROOT_DEFAULT = "C:/OpenSSL" ; - local OPENSSL_ROOT_ENV = [ os.environ OPENSSL_ROOT ] ; - local OPENSSL_ROOT = "" ; - if $(OPENSSL_ROOT_ENV) - { - OPENSSL_ROOT = $(OPENSSL_ROOT_ENV) ; - } - else - { - OPENSSL_ROOT = $(OPENSSL_ROOT_DEFAULT) ; - } - project - : requirements - $(OPENSSL_ROOT)/include - debug:$(OPENSSL_ROOT)/lib - windowsdebug:$(OPENSSL_ROOT)/debug/lib - release:$(OPENSSL_ROOT)/lib - ; - - if [ path.exists $(OPENSSL_ROOT)/lib/libssl.lib ] - { - echo "OpenSSL > 1.1.0. Including libssl" ; - lib ssl : : windows libssl ; - } - if [ path.exists $(OPENSSL_ROOT)/lib/libcrypto.lib ] - { - echo "OpenSSL > 1.1.0. Including libcrypto" ; - lib crypto : : windows libcrypto ; - } - if [ path.exists $(OPENSSL_ROOT)/lib/ssleay32.lib ] - { - echo "OpenSSL < 1.1.0. Including ssleay32" ; - lib ssl : : windows ssleay32 ; - } - if [ path.exists $(OPENSSL_ROOT)/lib/libeay32.lib ] - { - echo "OpenSSL < 1.1.0. Including libeay32" ; - lib crypto : : windows libeay32 ; - } -} - -lib boost_requests - : requests_sources crypto ssl /boost//url /boost//json - : requirements - static - [ ac.check-library ssl : ssl/shared : no ] - [ ac.check-library crypto : crypto/shared : no ] - : usage-requirements - /boost//container/off - - ; +alias requests_sources : src.cpp ; +lib boost_requests : requests_sources ; boost-install boost_requests ; \ No newline at end of file diff --git a/test/Jamfile b/test/Jamfile index 9be5880..508ad72 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -12,6 +12,7 @@ requirements ../../.. 17:on 20:on on:/boost//requests + off:/boost//filesystem ; import testing ; @@ -22,5 +23,4 @@ alias asio-ssl : ../../beast//lib-asio-ssl ; alias beast : ../../beast//lib-beast ; alias ssl : ../../beast//ssl ; -run [ glob-tree *.cpp ] /boost//json /boost//url ssl asio asio-ssl beast ssl - : off:/boost//filesystem ; \ No newline at end of file +run [ glob-tree *.cpp ] /boost//json /boost//url ssl asio asio-ssl beast ssl ; \ No newline at end of file From cdfa3d6813f50265ef30e82458520d00fcdb402d Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Fri, 10 Feb 2023 12:29:14 +0800 Subject: [PATCH 41/76] removed defined= from windows build --- .drone/drone.bat | 4 ++-- include/boost/requests/detail/impl/connection_impl.ipp | 2 +- include/boost/requests/json.hpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.drone/drone.bat b/.drone/drone.bat index 6ec2d99..cac2987 100755 --- a/.drone/drone.bat +++ b/.drone/drone.bat @@ -34,8 +34,8 @@ set B2_TARGETS=libs/!SELF!/test cd !BOOST_ROOT! call bootstrap.bat b2 headers -b2 --debug-configuration variant=%VARIANT% cxxstd=%CXXSTD% define=%DEFINE% address-model=%ADDRESS_MODEL% toolset=%TOOLSET% --verbose-test libs/!SELF!/test -j3 -b2 --debug-configuration variant=%VARIANT% cxxstd=%CXXSTD% define=%DEFINE% address-model=%ADDRESS_MODEL% toolset=%TOOLSET% --verbose-test libs/!SELF!/example -j3 +b2 --debug-configuration variant=%VARIANT% cxxstd=%CXXSTD% address-model=%ADDRESS_MODEL% toolset=%TOOLSET% --verbose-test libs/!SELF!/test -j3 +b2 --debug-configuration variant=%VARIANT% cxxstd=%CXXSTD% address-model=%ADDRESS_MODEL% toolset=%TOOLSET% --verbose-test libs/!SELF!/example -j3 ) else if "%DRONE_JOB_BUILDTYPE%" == "standalone-windows" ( diff --git a/include/boost/requests/detail/impl/connection_impl.ipp b/include/boost/requests/detail/impl/connection_impl.ipp index d2ce49f..c190890 100644 --- a/include/boost/requests/detail/impl/connection_impl.ipp +++ b/include/boost/requests/detail/impl/connection_impl.ipp @@ -459,7 +459,7 @@ auto connection_impl::async_ropen_op::resume( } - stream str_{this_->get_executor(), this_}; + stream str_{this_->get_executor(), nullptr}; str_.history_ = std::move(history); return str_; diff --git a/include/boost/requests/json.hpp b/include/boost/requests/json.hpp index 1471850..b50aa83 100644 --- a/include/boost/requests/json.hpp +++ b/include/boost/requests/json.hpp @@ -659,7 +659,7 @@ struct async_read_json_op : asio::coroutine { BOOST_ASIO_CORO_REENTER(this) { - while (!sp.done() && !str.done()) + while (!sp.done() && !str.done() && !ec) { BOOST_ASIO_CORO_YIELD str.async_read_some(asio::buffer(buffer), std::move(self)); if (ec) From a927ed56431e32afa2f22c589032f043f65307ad Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Fri, 10 Feb 2023 13:17:35 +0800 Subject: [PATCH 42/76] fixed cmake for inline. --- CMakeLists.txt | 27 ++++++++++++++++----------- test/CMakeLists.txt | 3 +-- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a9224f3..38a930b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,10 +21,6 @@ endif() option(BOOST_REQUESTS_BUILD_INLINE "Configure as if part of the boost source tree" ${BOOST_REQUESTS_SHOULD_BE_INLINE}) - -if(BOOST_REQUESTS_IS_ROOT) - include(CTest) -endif() if(NOT BOOST_SUPERPROJECT_VERSION) option(BOOST_REQUESTS_INSTALL "Install boost::requests files" ON) option(BOOST_REQUESTS_BUILD_TESTS "Build boost::requests tests" ${BUILD_TESTING}) @@ -38,17 +34,17 @@ if(BOOST_REQUESTS_IS_ROOT AND BOOST_REQUESTS_BUILD_INLINE) # # Building inside Boost tree, but as a separate project e.g. on Travis or # other CI, or when producing Visual Studio Solution and Projects. - - set(BOOST_INCLUDE_LIBRARIES REQUESTS) - set(BOOST_EXCLUDE_LIBRARIES REQUESTS) + set(BOOST_INCLUDE_LIBRARIES requests) + set(BOOST_EXCLUDE_LIBRARIES requests) set(CMAKE_FOLDER _deps) add_subdirectory(../.. _deps/boost EXCLUDE_FROM_ALL) unset(CMAKE_FOLDER) endif() +find_package(Threads REQUIRED) + if (NOT BOOST_REQUESTS_BUILD_INLINE) - find_package(Threads REQUIRED) find_package(Boost REQUIRED atomic container filesystem system json url) include_directories(include) endif() @@ -77,9 +73,18 @@ endif() add_library(boost_requests src/src.cpp) target_include_directories(boost_requests PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") -target_link_libraries(boost_requests PUBLIC - Boost::container Boost::url Boost::system Boost::json - OpenSSL::SSL OpenSSL::Crypto Threads::Threads) +target_link_libraries( + boost_requests + PUBLIC + Boost::container + Boost::filesystem + Boost::json + Boost::system + Boost::url + OpenSSL::SSL + OpenSSL::Crypto + Threads::Threads) + target_compile_definitions(boost_requests PUBLIC BOOST_REQUESTS_SEPARATE_COMPILATION=1 BOOST_FILESYSTEM_NO_DEPRECATED=1) add_library(Boost::requests ALIAS boost_requests) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ff75083..675e6f0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,8 +2,7 @@ file(GLOB_RECURSE ALL_TEST_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) add_executable(boost_requests_tests ${ALL_TEST_FILES}) -target_link_libraries(boost_requests_tests PUBLIC - Boost::requests Boost::atomic Boost::filesystem rt) +target_link_libraries(boost_requests_tests PUBLIC Boost::requests Boost::filesystem rt) target_compile_definitions(boost_requests_tests PUBLIC BOOST_REQUESTS_SEPARATE_COMPILATION=1 From 7ae79125b6ce5f98802d5800d4dd6885684c09dc Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Fri, 10 Feb 2023 19:00:03 +0800 Subject: [PATCH 43/76] Switched source to source_ptr. --- CMakeLists.txt | 1 + example/Jamfile | 2 +- example/xml.cpp | 7 +- include/boost/requests/connection_pool.hpp | 2 - .../boost/requests/detail/connection_impl.hpp | 2 - .../boost/requests/detail/faux_coroutine.hpp | 62 ++++++++++------- .../requests/detail/impl/connection_impl.hpp | 26 +++---- include/boost/requests/form.hpp | 4 +- .../boost/requests/impl/connection_pool.hpp | 22 +++--- include/boost/requests/impl/session.hpp | 28 ++++---- include/boost/requests/session.hpp | 2 - include/boost/requests/source.hpp | 68 ++++++++++++++++--- include/boost/requests/sources/buffer.hpp | 10 +-- include/boost/requests/sources/empty.hpp | 4 +- include/boost/requests/sources/file.hpp | 7 -- include/boost/requests/sources/form.hpp | 20 ++---- .../boost/requests/sources/impl/buffer.ipp | 30 ++++++++ include/boost/requests/sources/impl/empty.ipp | 6 +- include/boost/requests/sources/impl/file.ipp | 10 +-- include/boost/requests/sources/impl/form.ipp | 28 ++++++++ include/boost/requests/sources/impl/json.ipp | 25 +++++++ include/boost/requests/sources/json.hpp | 15 +--- include/boost/requests/sources/string.hpp | 7 +- .../boost/requests/sources/string_view.hpp | 34 +++++++--- include/boost/requests/src.hpp | 2 + test/source.cpp | 7 +- 26 files changed, 285 insertions(+), 146 deletions(-) create mode 100644 include/boost/requests/sources/impl/buffer.ipp create mode 100644 include/boost/requests/sources/impl/form.ipp diff --git a/CMakeLists.txt b/CMakeLists.txt index 38a930b..6af4a3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,6 +76,7 @@ target_include_directories(boost_requests PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/in target_link_libraries( boost_requests PUBLIC + Boost::beast Boost::container Boost::filesystem Boost::json diff --git a/example/Jamfile b/example/Jamfile index bce1df8..9991fa1 100644 --- a/example/Jamfile +++ b/example/Jamfile @@ -28,5 +28,5 @@ import regex ; for local src in [ glob *.cpp : xml.cpp ] { local name = [ regex.match "([^.]*)\\..*" : $(src) ] ; - exe $(name) : $(src) /boost//json /boost//url asio asio-ssl beast ssl ; + exe $(name) : $(src) /boost//json /boost//url /boost//program_options asio asio-ssl beast ssl ; } diff --git a/example/xml.cpp b/example/xml.cpp index 61901f0..10c4c6e 100644 --- a/example/xml.cpp +++ b/example/xml.cpp @@ -62,9 +62,12 @@ struct xml_ref_source final : requests::source namespace pugi { -xml_ref_source tag_invoke(const requests::make_source_tag&, const pugi::xml_document &doc) +requests::source_ptr tag_invoke(const requests::make_source_tag&, const pugi::xml_document &doc, + container::pmr::memory_resource * res) { - return xml_ref_source{std::move(doc)}; + return std::allocate_shared( + container::pmr::polymorphic_allocator(res), + std::move(doc)); } diff --git a/include/boost/requests/connection_pool.hpp b/include/boost/requests/connection_pool.hpp index f3591e8..b8640a1 100644 --- a/include/boost/requests/connection_pool.hpp +++ b/include/boost/requests/connection_pool.hpp @@ -224,10 +224,8 @@ struct connection_pool struct async_lookup_op; struct async_get_connection_op; - template struct async_ropen_op_body; - template struct async_ropen_op_body_base; struct async_ropen_op; diff --git a/include/boost/requests/detail/connection_impl.hpp b/include/boost/requests/detail/connection_impl.hpp index 79f375b..98f3efd 100644 --- a/include/boost/requests/detail/connection_impl.hpp +++ b/include/boost/requests/detail/connection_impl.hpp @@ -201,10 +201,8 @@ struct connection_impl : std::enable_shared_from_this struct async_ropen_op; - template struct async_ropen_op_body; - template struct async_ropen_op_body_base; BOOST_REQUESTS_DECL std::size_t do_read_some_(beast::http::basic_parser & parser); diff --git a/include/boost/requests/detail/faux_coroutine.hpp b/include/boost/requests/detail/faux_coroutine.hpp index 421d8f4..48aca02 100644 --- a/include/boost/requests/detail/faux_coroutine.hpp +++ b/include/boost/requests/detail/faux_coroutine.hpp @@ -171,7 +171,8 @@ struct faux_token_t template friend struct faux_runner; }; - +struct with_allocator_t {}; +constexpr static with_allocator_t with_allocator{}; template struct faux_runner @@ -261,17 +262,27 @@ struct faux_runner } } + Handler handler; + container::pmr::resource_adaptor_imp< + typename std::allocator_traits>::template rebind_alloc + > alloc_re{asio::get_associated_allocator(handler)}; Implementation impl; - Handler handler; + template + typename token_type::allocator_type get_allocator_impl(T ) + { + + } - typename token_type::allocator_type get_allocator_impl(std::allocator) + template + typename token_type::allocator_type get_allocator_impl(std::allocator) { return {}; } - typename token_type::allocator_type get_allocator_impl(container::pmr::polymorphic_allocator alloc) + template + typename token_type::allocator_type get_allocator_impl(container::pmr::polymorphic_allocator alloc) { return alloc; } @@ -280,34 +291,21 @@ struct faux_runner { return get_allocator_impl(asio::get_associated_allocator(handler)); } - container::pmr::resource_adaptor_imp< - typename std::allocator_traits>::template rebind_alloc - > alloc_res{ - asio::get_associated_allocator(handler) - }; - template - container::pmr::memory_resource * get_resource(container::pmr::polymorphic_allocator alloc) - { - return alloc.resource(); - } - template - container::pmr::memory_resource * get_resource(Alloc alloc) - { - return &alloc_res; - } - template - container::pmr::memory_resource * get_resource(std::allocator other_alloc) + template + impl_(Handler_ && h, Args_ && ... args) + : handler(std::forward(h)) + , impl(std::forward(args)...) { - return boost::container::pmr::get_default_resource(); + this->token_type::base::slot = asio::get_associated_cancellation_slot(handler); } template - impl_(Handler_ && h, Args_ && ... args) - : impl(std::forward(args)...) - , handler(std::forward(h)) + impl_(Handler_ && h, const with_allocator_t &, Args_ && ... args) + : handler(std::forward(h)) + , impl(get_allocator(), std::forward(args)...) { this->token_type::base::slot = asio::get_associated_cancellation_slot(handler); } @@ -322,6 +320,8 @@ struct faux_runner auto * impl = ptr.get(); impl->initiate(token_type{std::move(ptr)}); } + + }; template{}, token, std::forward(args)...); } +template +auto faux_run_with_allocator(Token && token, Args && ... args) +{ + static_assert(std::is_constructible, + Args&&...>::value, + "Can't construct implementation from those args"); + return asio::async_initiate( + faux_runner{}, token, with_allocator, std::forward(args)...); +} } diff --git a/include/boost/requests/detail/impl/connection_impl.hpp b/include/boost/requests/detail/impl/connection_impl.hpp index a3bd7ad..291821c 100644 --- a/include/boost/requests/detail/impl/connection_impl.hpp +++ b/include/boost/requests/detail/impl/connection_impl.hpp @@ -151,7 +151,7 @@ auto connection_impl::ropen( } auto src = requests::make_source(std::forward(body)); - return ropen(method, path.encoded_target(), req.fields, src, std::move(req.opts), req.jar, ec); + return ropen(method, path.encoded_target(), req.fields, *src, std::move(req.opts), req.jar, ec); } @@ -214,31 +214,32 @@ struct connection_impl::async_ropen_op system::error_code & ec, std::size_t res_ = 0u) -> stream; }; -template struct connection_impl::async_ropen_op_body_base { - RequestSource source_impl; + source_ptr source_impl; http::fields headers; template - async_ropen_op_body_base(RequestBody && body, http::fields headers) - : source_impl(requests::make_source(std::forward(body))), headers(std::move(headers)) + async_ropen_op_body_base( + container::pmr::polymorphic_allocator alloc, + RequestBody && body, http::fields headers) + : source_impl(requests::make_source(std::forward(body), alloc.resource())), headers(std::move(headers)) { } }; -template -struct connection_impl::async_ropen_op_body : async_ropen_op_body_base, async_ropen_op +struct connection_impl::async_ropen_op_body : async_ropen_op_body_base, async_ropen_op { template - async_ropen_op_body(std::shared_ptr this_, + async_ropen_op_body(container::pmr::polymorphic_allocator alloc, + std::shared_ptr this_, beast::http::verb method, urls::url_view path, RequestBody && body, request_parameters req) - : async_ropen_op_body_base{std::forward(body), std::move(req.fields)}, - async_ropen_op(std::move(this_), method, path, async_ropen_op_body_base::headers, - this->source_impl, std::move(req.opts), req.jar) + : async_ropen_op_body_base{alloc, std::forward(body), std::move(req.fields)}, + async_ropen_op(std::move(this_), method, path, async_ropen_op_body_base::headers, + *this->source_impl, std::move(req.opts), req.jar) {} }; @@ -252,8 +253,7 @@ connection_impl::async_ropen( RequestBody && body, request_parameters req, CompletionToken && completion_token) { - using rp = async_ropen_op_body(body)))>>; - return detail::faux_run( + return detail::faux_run_with_allocator( std::forward(completion_token), shared_from_this(), method, path, std::forward(body), std::move(req)); diff --git a/include/boost/requests/form.hpp b/include/boost/requests/form.hpp index f417c38..8c0eea3 100644 --- a/include/boost/requests/form.hpp +++ b/include/boost/requests/form.hpp @@ -19,7 +19,7 @@ struct form { urls::url storage; - form(std::initializer_list params) + explicit form(std::initializer_list params) { storage.params().assign(std::move(params)); } @@ -28,7 +28,7 @@ struct form form(const form & ) = default; template - form(Container && ct, decltype(std::begin(ct)) * = nullptr) + explicit form(Container && ct, decltype(std::begin(ct)) * = nullptr) { storage.params().assign(std::begin(ct), std::end(ct)); } diff --git a/include/boost/requests/impl/connection_pool.hpp b/include/boost/requests/impl/connection_pool.hpp index 506832a..0e54721 100644 --- a/include/boost/requests/impl/connection_pool.hpp +++ b/include/boost/requests/impl/connection_pool.hpp @@ -155,33 +155,34 @@ connection_pool::async_ropen(beast::http::verb method, this, method, path, std::ref(headers), std::ref(src), std::move(opt), jar); } -template struct connection_pool::async_ropen_op_body_base { - RequestSource source_impl; + source_ptr source_impl; http::fields headers; template - async_ropen_op_body_base(RequestBody && body, http::fields headers) - : source_impl(requests::make_source(std::forward(body))), headers(std::move(headers)) + async_ropen_op_body_base( + container::pmr::polymorphic_allocator alloc, + RequestBody && body, http::fields headers) + : source_impl(requests::make_source(std::forward(body), alloc.resource())), headers(std::move(headers)) { } }; -template -struct connection_pool::async_ropen_op_body : async_ropen_op_body_base, async_ropen_op +struct connection_pool::async_ropen_op_body : async_ropen_op_body_base, async_ropen_op { template async_ropen_op_body( + container::pmr::polymorphic_allocator alloc, connection_pool * this_, beast::http::verb method, urls::url_view path, RequestBody && body, request_parameters req) - : async_ropen_op_body_base{std::forward(body), std::move(req.fields)}, - async_ropen_op{this_, method, path.encoded_resource(), async_ropen_op_body_base::headers, - this->source_impl, + : async_ropen_op_body_base{alloc, std::forward(body), std::move(req.fields)}, + async_ropen_op{this_, method, path.encoded_resource(), async_ropen_op_body_base::headers, + *this->source_impl, std::move(req.opts), req.jar} {} }; @@ -196,8 +197,7 @@ connection_pool::async_ropen(beast::http::verb method, RequestBody && body, request_parameters req, CompletionToken && completion_token) { - using rp = async_ropen_op_body(body)))>>; - return detail::faux_run( + return detail::faux_run_with_allocator( std::forward(completion_token), this, method, path, std::forward(body), std::move(req)); diff --git a/include/boost/requests/impl/session.hpp b/include/boost/requests/impl/session.hpp index bf0e065..b584809 100644 --- a/include/boost/requests/impl/session.hpp +++ b/include/boost/requests/impl/session.hpp @@ -66,7 +66,7 @@ auto session::ropen( } auto src = make_source(body); - return ropen(method, url, fields, src, ec); + return ropen(method, url, fields, *src, ec); } struct session::async_ropen_op : asio::coroutine @@ -111,32 +111,34 @@ struct session::async_ropen_op : asio::coroutine }; -template struct session::async_ropen_op_body_base { - RequestSource source_impl; + source_ptr source_impl; http::fields headers; template - async_ropen_op_body_base(RequestBody && body, http::fields headers) - : source_impl(requests::make_source(std::forward(body))), headers(std::move(headers)) + async_ropen_op_body_base( + container::pmr::polymorphic_allocator alloc, + RequestBody && body, http::fields headers) + : source_impl(requests::make_source(std::forward(body), alloc.resource())), + headers(std::move(headers)) { } }; -template -struct session::async_ropen_op_body : async_ropen_op_body_base, async_ropen_op +struct session::async_ropen_op_body : async_ropen_op_body_base, async_ropen_op { template async_ropen_op_body( + container::pmr::polymorphic_allocator alloc, session * this_, beast::http::verb method, urls::url_view path, RequestBody && body, http::fields req) - : async_ropen_op_body_base{std::forward(body), std::move(req)}, - async_ropen_op(this_, method, path, async_ropen_op_body_base::headers, - this->source_impl) + : async_ropen_op_body_base{alloc, std::forward(body), std::move(req)}, + async_ropen_op(this_, method, path, async_ropen_op_body_base::headers, + *this->source_impl) {} }; @@ -150,9 +152,9 @@ session::async_ropen(beast::http::verb method, http::fields req, CompletionToken && completion_token) { - using op_t = async_ropen_op_body(body)))>>; - return detail::faux_run(std::forward(completion_token), - this, method, path, std::forward(body), std::move(req)); + return detail::faux_run_with_allocator( + std::forward(completion_token), + this, method, path, std::forward(body), std::move(req)); } template diff --git a/include/boost/requests/session.hpp b/include/boost/requests/session.hpp index bc99186..81d06d0 100644 --- a/include/boost/requests/session.hpp +++ b/include/boost/requests/session.hpp @@ -175,10 +175,8 @@ struct session struct async_ropen_op; - template struct async_ropen_op_body; - template struct async_ropen_op_body_base; BOOST_REQUESTS_DECL auto make_request_(http::fields fields) -> requests::request_parameters; diff --git a/include/boost/requests/source.hpp b/include/boost/requests/source.hpp index 3413349..b57f4a8 100644 --- a/include/boost/requests/source.hpp +++ b/include/boost/requests/source.hpp @@ -12,6 +12,11 @@ #include #include #include +#include + +#if defined(__cpp_lib_filesystem) +#include +#endif namespace boost { @@ -29,23 +34,35 @@ struct source struct make_source_tag {}; +using source_ptr = std::shared_ptr; -template -auto tag_invoke(const make_source_tag&, Source && s) - -> std::enable_if_t< - std::is_base_of>::value, - Source> +inline auto tag_invoke(const make_source_tag&, source_ptr s) +{ + return s; +} + +inline auto tag_invoke(const make_source_tag&, source_ptr s, + container::pmr::memory_resource *) { - return std::forward(s); + return s; } template -auto make_source(Source && source) -> decltype(tag_invoke(make_source_tag{}, std::declval())) +auto make_source(Source && source, container::pmr::memory_resource * = container::pmr::get_default_resource()) + -> decltype(tag_invoke(make_source_tag{}, std::declval())) { return tag_invoke(make_source_tag{}, std::forward(source)); } +template +auto make_source(Source && source, container::pmr::memory_resource * res = container::pmr::get_default_resource()) + -> decltype(tag_invoke(make_source_tag{}, std::declval(), res)) +{ + return tag_invoke(make_source_tag{}, std::forward(source), res); +} + + template std::size_t write_request( Stream & stream, @@ -84,14 +101,45 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(system::error_code, std source &src, CompletionToken && token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(typename Stream::executor_type)); +using empty = beast::http::empty_body::value_type; + +BOOST_REQUESTS_DECL +source_ptr tag_invoke(make_source_tag, asio::const_buffer cb, container::pmr::memory_resource * res); +BOOST_REQUESTS_DECL +source_ptr tag_invoke(const make_source_tag&, const empty &); +BOOST_REQUESTS_DECL +source_ptr tag_invoke(const make_source_tag&, const none_t &); + +BOOST_REQUESTS_DECL +source_ptr tag_invoke(const make_source_tag&, const boost::filesystem::path & path, container::pmr::memory_resource * res); +source_ptr tag_invoke(const make_source_tag&, boost::filesystem::path &&, container::pmr::memory_resource * res) = delete; + +#if defined(__cpp_lib_filesystem) +BOOST_REQUESTS_DECL +source_ptr tag_invoke(const make_source_tag&, const std::filesystem::path & path, container::pmr::memory_resource * res); +source_ptr tag_invoke(const make_source_tag&, std::filesystem::path &&, container::pmr::memory_resource * res) = delete; +#endif + +BOOST_REQUESTS_DECL +source_ptr tag_invoke(make_source_tag, struct form form_, container::pmr::memory_resource * res); + +BOOST_REQUESTS_DECL +source_ptr tag_invoke(make_source_tag, boost::json::value value, container::pmr::memory_resource * res); +BOOST_REQUESTS_DECL +source_ptr tag_invoke(make_source_tag, boost::json::object obj, container::pmr::memory_resource * res); +BOOST_REQUESTS_DECL +source_ptr tag_invoke(make_source_tag, boost::json::array arr, container::pmr::memory_resource * res); +BOOST_REQUESTS_DECL +source_ptr tag_invoke(make_source_tag, const boost::json::string & arr, + container::pmr::memory_resource * res); +source_ptr tag_invoke(make_source_tag, boost::json::string &&str, container::pmr::memory_resource * res) = delete; + + } } #include -#include -#include -#include #include #include diff --git a/include/boost/requests/sources/buffer.hpp b/include/boost/requests/sources/buffer.hpp index 8f33b56..4a7535f 100644 --- a/include/boost/requests/sources/buffer.hpp +++ b/include/boost/requests/sources/buffer.hpp @@ -38,12 +38,14 @@ struct buffer_source : source core::string_view default_content_type() override {return "application/octet-stream";} }; -inline buffer_source tag_invoke(make_source_tag, asio::const_buffer cb) -{ - return buffer_source(cb); -} +BOOST_REQUESTS_DECL +source_ptr tag_invoke(make_source_tag, asio::const_buffer cb, container::pmr::memory_resource * res); } } +#if defined(BOOST_REQUESTS_HEADER_ONLY) +#include +#endif + #endif //BOOST_REQUESTS_SOURCES_BUFFER_HPP diff --git a/include/boost/requests/sources/empty.hpp b/include/boost/requests/sources/empty.hpp index 3fe3c62..61c61de 100644 --- a/include/boost/requests/sources/empty.hpp +++ b/include/boost/requests/sources/empty.hpp @@ -41,8 +41,8 @@ struct empty_source : source } }; -BOOST_REQUESTS_DECL empty_source &tag_invoke(const make_source_tag&, const empty &); -BOOST_REQUESTS_DECL empty_source &tag_invoke(const make_source_tag&, const none_t &); +BOOST_REQUESTS_DECL source_ptr tag_invoke(const make_source_tag&, const empty &); +BOOST_REQUESTS_DECL source_ptr tag_invoke(const make_source_tag&, const none_t &); } } diff --git a/include/boost/requests/sources/file.hpp b/include/boost/requests/sources/file.hpp index 2cbc33f..b8286a7 100644 --- a/include/boost/requests/sources/file.hpp +++ b/include/boost/requests/sources/file.hpp @@ -74,13 +74,6 @@ struct file_source : source } }; -BOOST_REQUESTS_DECL file_source tag_invoke(const make_source_tag&, const boost::filesystem::path & path); -file_source tag_invoke(const make_source_tag&, boost::filesystem::path &&) = delete; - -#if defined(__cpp_lib_filesystem) -BOOST_REQUESTS_DECL file_source tag_invoke(const make_source_tag&, const std::filesystem::path & path); -file_source tag_invoke(const make_source_tag&, std::filesystem::path &&) = delete; -#endif } } diff --git a/include/boost/requests/sources/form.hpp b/include/boost/requests/sources/form.hpp index d33543f..c22194f 100644 --- a/include/boost/requests/sources/form.hpp +++ b/include/boost/requests/sources/form.hpp @@ -6,11 +6,9 @@ #define BOOST_REQUESTS_SOURCES_FORM_HPP #include +#include #include -#if defined(__cpp_lib_filesystem) -#include -#endif namespace boost @@ -58,20 +56,14 @@ struct form_source : source core::string_view default_content_type() override { return "application/x-www-form-urlencoded"; } }; -inline form_source tag_invoke(const make_source_tag&, urls::params_encoded_view pev) -{ - return form_source(pev); +BOOST_REQUESTS_DECL +source_ptr tag_invoke(make_source_tag, form form_, container::pmr::memory_resource * res); } - -template -inline auto tag_invoke(const make_source_tag&, Form && f) - -> std::enable_if_t, form>::value, form_source> -{ - return form_source(std::forward
(f)); } -} -} +#if defined(BOOST_REQUESTS_HEADER_ONLY) +#include +#endif #endif //BOOST_REQUESTS_SOURCES_FILE_HPP diff --git a/include/boost/requests/sources/impl/buffer.ipp b/include/boost/requests/sources/impl/buffer.ipp new file mode 100644 index 0000000..f75ebe2 --- /dev/null +++ b/include/boost/requests/sources/impl/buffer.ipp @@ -0,0 +1,30 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_REQUESTS_SOURCES_IMPL_BUFFER_IPP +#define BOOST_REQUESTS_SOURCES_IMPL_BUFFER_IPP + +#include + +namespace boost +{ +namespace requests +{ +namespace sources +{ + +source_ptr tag_invoke(make_source_tag, asio::const_buffer cb, container::pmr::memory_resource * res) +{ + return std::allocate_shared(container::pmr::polymorphic_allocator(res), cb); +} + + +} +} +} + +#endif // BOOST_REQUESTS_SOURCES_IMPL_BUFFER_IPP diff --git a/include/boost/requests/sources/impl/empty.ipp b/include/boost/requests/sources/impl/empty.ipp index 9732d0c..30a6e7c 100644 --- a/include/boost/requests/sources/impl/empty.ipp +++ b/include/boost/requests/sources/impl/empty.ipp @@ -15,13 +15,13 @@ namespace boost namespace requests { -empty_source& tag_invoke(const make_source_tag&, const empty &) +source_ptr tag_invoke(const make_source_tag&, const empty &) { - static empty_source empty_; + static source_ptr empty_{std::make_shared()}; return empty_; } -empty_source& tag_invoke(const make_source_tag& tag, const none_t &) +source_ptr tag_invoke(const make_source_tag& tag, const none_t &) { return tag_invoke(tag, empty()); } diff --git a/include/boost/requests/sources/impl/file.ipp b/include/boost/requests/sources/impl/file.ipp index 999299f..a8d7461 100644 --- a/include/boost/requests/sources/impl/file.ipp +++ b/include/boost/requests/sources/impl/file.ipp @@ -21,9 +21,10 @@ file_source::file_source(const boost::filesystem::path & file) : path(file.c_str this->file.open(file.string().c_str(), beast::file_mode::read, ec); } -file_source tag_invoke(const make_source_tag &tag, const boost::filesystem::path & path) +source_ptr tag_invoke(const make_source_tag &tag, const boost::filesystem::path & path, + container::pmr::memory_resource * res) { - return file_source(path); + return std::allocate_shared(container::pmr::polymorphic_allocator(res), path); } #if defined(__cpp_lib_filesystem) @@ -33,9 +34,10 @@ file_source::file_source(const std::filesystem::path & file) : path(file.c_str() this->file.open(file.string().c_str(), beast::file_mode::read, ec); } -file_source tag_invoke(const make_source_tag &tag, const std::filesystem::path & path) +source_ptr tag_invoke(const make_source_tag &tag, const std::filesystem::path & path, + container::pmr::memory_resource * res) { - return file_source(path); + return std::allocate_shared(container::pmr::polymorphic_allocator(res), path); } diff --git a/include/boost/requests/sources/impl/form.ipp b/include/boost/requests/sources/impl/form.ipp new file mode 100644 index 0000000..9defdfe --- /dev/null +++ b/include/boost/requests/sources/impl/form.ipp @@ -0,0 +1,28 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_REQUESTS_SOURCE_IMPL_FORM_IPP +#define BOOST_REQUESTS_SOURCE_IMPL_FORM_IPP + +#include + +namespace boost +{ +namespace requests +{ + +source_ptr tag_invoke(make_source_tag, form form_, container::pmr::memory_resource * res) +{ + return std::allocate_shared( + container::pmr::polymorphic_allocator(res), + std::move(form_)); +} + +} +} + +#endif // BOOST_REQUESTS_SOURCE_IMPL_FORM_IPP diff --git a/include/boost/requests/sources/impl/json.ipp b/include/boost/requests/sources/impl/json.ipp index fcdeccb..74d9f1a 100644 --- a/include/boost/requests/sources/impl/json.ipp +++ b/include/boost/requests/sources/impl/json.ipp @@ -36,6 +36,31 @@ std::pair json_source::read_some(void * data_, std::size_t si json_source::~json_source() = default; +BOOST_REQUESTS_DECL +source_ptr tag_invoke(make_source_tag, boost::json::value value, container::pmr::memory_resource * res) +{ + return std::allocate_shared(container::pmr::polymorphic_allocator(res), std::move(value)); + +} + +BOOST_REQUESTS_DECL +source_ptr tag_invoke(make_source_tag, boost::json::object obj, container::pmr::memory_resource * res) +{ + return std::allocate_shared(container::pmr::polymorphic_allocator(res), std::move(obj)); +} +BOOST_REQUESTS_DECL +source_ptr tag_invoke(make_source_tag, boost::json::array arr, container::pmr::memory_resource * res) +{ + return std::allocate_shared(container::pmr::polymorphic_allocator(res), std::move(arr)); +} + +BOOST_REQUESTS_DECL +source_ptr tag_invoke(make_source_tag, const boost::json::string & arr, container::pmr::memory_resource * res) +{ + return std::allocate_shared(container::pmr::polymorphic_allocator(res), std::move(arr)); +} + + } } diff --git a/include/boost/requests/sources/json.hpp b/include/boost/requests/sources/json.hpp index 79b28b9..e6c738a 100644 --- a/include/boost/requests/sources/json.hpp +++ b/include/boost/requests/sources/json.hpp @@ -6,7 +6,8 @@ #define BOOST_REQUESTS_SOURCES_JSON_HPP #include -#include +#include +#include namespace boost { @@ -29,18 +30,6 @@ struct json_source : source core::string_view default_content_type() override {return "application/json";} }; -template -auto tag_invoke(const make_source_tag&, Json && f) - -> std::enable_if_t< - std::is_same, json::value>::value || - std::is_same, json::array>::value || - std::is_same, json::object>::value || - std::is_same, json::string>::value, - json_source> -{ - return {std::forward(f)}; -} - } } diff --git a/include/boost/requests/sources/string.hpp b/include/boost/requests/sources/string.hpp index 1ee6b81..cc85f0e 100644 --- a/include/boost/requests/sources/string.hpp +++ b/include/boost/requests/sources/string.hpp @@ -59,9 +59,12 @@ struct basic_string_source final : source }; template -inline basic_string_source tag_invoke(make_source_tag, std::basic_string data) +inline basic_string_source tag_invoke( + make_source_tag, std::basic_string data, + container::pmr::memory_resource * res) { - return basic_string_source(std::move(data)); + return std::allocate_shared>( + container::pmr::polymorphic_allocator(res), std::move(data)); } } diff --git a/include/boost/requests/sources/string_view.hpp b/include/boost/requests/sources/string_view.hpp index 05e4b8d..8c3feea 100644 --- a/include/boost/requests/sources/string_view.hpp +++ b/include/boost/requests/sources/string_view.hpp @@ -61,38 +61,50 @@ struct basic_string_view_source final : source }; template -basic_string_view_source tag_invoke(make_source_tag, const char (&data)[N]) +basic_string_view_source tag_invoke(make_source_tag, const char (&data)[N], + container::pmr::memory_resource * res) { - return basic_string_view_source(data); + return std::allocate_shared>( + container::pmr::polymorphic_allocator(res), data); } template -basic_string_view_source tag_invoke(make_source_tag, const core::basic_string_view & data) +basic_string_view_source tag_invoke(make_source_tag, const core::basic_string_view & data, + container::pmr::memory_resource * res) { - return basic_string_view_source(std::move(data)); + return std::allocate_shared>( + container::pmr::polymorphic_allocator(res), std::move(data)); } template -basic_string_view_source tag_invoke(make_source_tag, core::basic_string_view && data) = delete; +basic_string_view_source tag_invoke(make_source_tag, core::basic_string_view && data, + container::pmr::memory_resource * res) = delete; template -basic_string_view_source tag_invoke(make_source_tag, const boost::basic_string_view & data) +basic_string_view_source tag_invoke(make_source_tag, const boost::basic_string_view & data, + container::pmr::memory_resource * res) { - return basic_string_view_source(std::move(data)); + return std::allocate_shared>( + container::pmr::polymorphic_allocator(res), std::move(data)); } template -basic_string_view_source tag_invoke(make_source_tag, boost::basic_string_view && data) = delete; +basic_string_view_source tag_invoke(make_source_tag, boost::basic_string_view && data, + container::pmr::memory_resource * res) = delete; #if defined(__cpp_lib_string_view) template -basic_string_view_source tag_invoke(make_source_tag, const std::basic_string_view & data) +basic_string_view_source tag_invoke(make_source_tag, const std::basic_string_view & data, + container::pmr::memory_resource * res) { - return basic_string_view_source(core::basic_string_view(data.data(), data.size())); + return std::allocate_shared>( + container::pmr::polymorphic_allocator(res), + core::basic_string_view(data.data(), data.size())); } template -basic_string_view_source tag_invoke(make_source_tag, std::basic_string_view && data) = delete; +basic_string_view_source tag_invoke(make_source_tag, std::basic_string_view && data, + container::pmr::memory_resource * res) = delete; #endif } diff --git a/include/boost/requests/src.hpp b/include/boost/requests/src.hpp index 1c674fb..461d7c4 100644 --- a/include/boost/requests/src.hpp +++ b/include/boost/requests/src.hpp @@ -35,8 +35,10 @@ #include #include #include +#include #include #include +#include #include #endif //BOOST_REQUESTS_SRC_IPP diff --git a/test/source.cpp b/test/source.cpp index 23f3c0a..470ec00 100644 --- a/test/source.cpp +++ b/test/source.cpp @@ -31,18 +31,19 @@ TEST_CASE("sync") std::thread thr{ [&]{ system::error_code ec; - auto sp = tag_invoke(requests::make_source_tag{}, json::value{"foobaria"}); + auto sp = tag_invoke(requests::make_source_tag{}, json::value{"foobaria"}, + container::pmr::get_default_resource()); requests::http::fields hd; write_request(wp, requests::http::verb::post, "/test", hd, - sp, + *sp, ec); CHECK(ec == system::error_code{}); hd.clear(); auto ep = requests::make_source(requests::empty()); write_request(wp, requests::http::verb::get, "/test2", hd, - ep, + *ep, ec); CHECK(ec == system::error_code{}); }}; From 640d75e49a961619009505a7fe7539aef3ed7541 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Fri, 10 Feb 2023 19:00:15 +0800 Subject: [PATCH 44/76] Copied beast build script. --- .drone/drone.bat | 43 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/.drone/drone.bat b/.drone/drone.bat index cac2987..91aa1a3 100755 --- a/.drone/drone.bat +++ b/.drone/drone.bat @@ -1,14 +1,10 @@ + @ECHO ON setlocal enabledelayedexpansion if "%DRONE_JOB_BUILDTYPE%" == "boost" ( -echo '==================================> INSTALL' - -git clone https://github.com/boostorg/boost-ci.git boost-ci-cloned --depth 1 -cp -prf boost-ci-cloned/ci . -rm -rf boost-ci-cloned - +echo "============> INSTALL" SET DRONE_BUILD_DIR=%CD: =% choco install --no-progress -y openssl --x64 --version 1.1.1.1000 @@ -16,29 +12,26 @@ mklink /D "C:\OpenSSL" "C:\Program Files\OpenSSL-Win64" SET OPENSSL_ROOT=C:\OpenSSL SET BOOST_BRANCH=develop IF "%DRONE_BRANCH%" == "master" SET BOOST_BRANCH=master +cp tools\user-config.jam %USERPROFILE%\user-config.jam +cd .. +SET GET_BOOST=!DRONE_BUILD_DIR!\tools\get-boost.sh +bash -c "$GET_BOOST $DRONE_BRANCH $DRONE_BUILD_DIR" +cd boost-root +call bootstrap.bat +b2 headers -REM source ci/travis/install.sh -REM The contents of install.sh below: - -for /F %%i in ("%DRONE_REPO%") do @set SELF=%%~nxi -SET BOOST_CI_TARGET_BRANCH=%DRONE_COMMIT_BRANCH% -SET BOOST_CI_SRC_FOLDER=%cd% - -call ci\common_install.bat - -echo '==================================> COMPILE' +echo "============> SCRIPT" -REM set B2_TARGETS=libs/!SELF!/test libs/!SELF!/example -set B2_TARGETS=libs/!SELF!/test +IF DEFINED DEFINE SET B2_DEFINE="define=%DEFINE%" -cd !BOOST_ROOT! -call bootstrap.bat -b2 headers -b2 --debug-configuration variant=%VARIANT% cxxstd=%CXXSTD% address-model=%ADDRESS_MODEL% toolset=%TOOLSET% --verbose-test libs/!SELF!/test -j3 -b2 --debug-configuration variant=%VARIANT% cxxstd=%CXXSTD% address-model=%ADDRESS_MODEL% toolset=%TOOLSET% --verbose-test libs/!SELF!/example -j3 +echo "Running tests" +b2 --debug-configuration variant=%VARIANT% cxxstd=%CXXSTD% %B2_DEFINE% address-model=%ADDRESS_MODEL% toolset=%TOOLSET% --verbose-test libs/requests/test -j3 +if !errorlevel! neq 0 exit /b !errorlevel! -) else if "%DRONE_JOB_BUILDTYPE%" == "standalone-windows" ( +echo "Running libs/requests/example" +b2 --debug-configuration variant=%VARIANT% cxxstd=%CXXSTD% %B2_DEFINE% address-model=%ADDRESS_MODEL% toolset=%TOOLSET% libs/requests/example -j3 +if !errorlevel! neq 0 exit /b !errorlevel! -REM not used +echo "============> COMPLETED" ) \ No newline at end of file From 7897b56373ff52d612695e4bbac1b21d500bc798 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Sat, 11 Feb 2023 12:32:44 +0800 Subject: [PATCH 45/76] Added multipart/form-data support. --- include/boost/requests/form.hpp | 37 +++- include/boost/requests/impl/source.hpp | 2 - include/boost/requests/source.hpp | 2 + include/boost/requests/sources/file.hpp | 3 +- include/boost/requests/sources/form.hpp | 35 +++- include/boost/requests/sources/impl/form.ipp | 161 ++++++++++++++++++ include/boost/requests/sources/string.hpp | 2 +- .../boost/requests/sources/string_view.hpp | 27 ++- test/connection.cpp | 15 ++ test/form.cpp | 72 ++++++++ 10 files changed, 333 insertions(+), 23 deletions(-) create mode 100644 test/form.cpp diff --git a/include/boost/requests/form.hpp b/include/boost/requests/form.hpp index 8c0eea3..9b3316c 100644 --- a/include/boost/requests/form.hpp +++ b/include/boost/requests/form.hpp @@ -10,6 +10,7 @@ #include #include +#include namespace boost { namespace requests { @@ -35,10 +36,42 @@ struct form }; +struct multi_part_form +{ + struct form_data + { + core::string_view name; + source_ptr source; + + form_data(core::string_view name, source_ptr source) : name(name), source(std::move(source)) {} + + template + form_data(core::string_view name, + Source && source, + container::pmr::memory_resource * resource = container::pmr::get_default_resource(), + decltype(make_source(std::declval())) * = nullptr) + : name(name), source(make_source(std::forward(source), resource)) + { + } + }; + + std::vector storage; + + multi_part_form(multi_part_form && ) = default; + multi_part_form(const multi_part_form & ) = default; + + explicit multi_part_form(std::initializer_list params) : storage(std::move(params)) + { + } + + template + explicit multi_part_form(Container && ct, decltype(std::begin(ct)) * = nullptr) + : storage(std::begin(ct), std::end(ct)) + { + } +}; } } -#include - #endif // BOOST_REQUESTS_FORM_HPP diff --git a/include/boost/requests/impl/source.hpp b/include/boost/requests/impl/source.hpp index 77bc919..cee9d88 100644 --- a/include/boost/requests/impl/source.hpp +++ b/include/boost/requests/impl/source.hpp @@ -16,8 +16,6 @@ namespace boost { namespace requests { - - namespace detail { diff --git a/include/boost/requests/source.hpp b/include/boost/requests/source.hpp index b57f4a8..5df6be3 100644 --- a/include/boost/requests/source.hpp +++ b/include/boost/requests/source.hpp @@ -122,6 +122,8 @@ source_ptr tag_invoke(const make_source_tag&, std::filesystem::path &&, BOOST_REQUESTS_DECL source_ptr tag_invoke(make_source_tag, struct form form_, container::pmr::memory_resource * res); +BOOST_REQUESTS_DECL +source_ptr tag_invoke(make_source_tag, struct multi_part_form mpf, container::pmr::memory_resource * res); BOOST_REQUESTS_DECL source_ptr tag_invoke(make_source_tag, boost::json::value value, container::pmr::memory_resource * res); diff --git a/include/boost/requests/sources/file.hpp b/include/boost/requests/sources/file.hpp index b8286a7..bf2de76 100644 --- a/include/boost/requests/sources/file.hpp +++ b/include/boost/requests/sources/file.hpp @@ -58,9 +58,8 @@ struct file_source : source ec = this->ec; return {0u, true}; } - auto n = file.read(data, size, ec); - return {n, file.pos(ec) == file.size(ec)}; + return {n, file.pos(ec) != file.size(ec)}; } core::string_view default_content_type() override { diff --git a/include/boost/requests/sources/form.hpp b/include/boost/requests/sources/form.hpp index c22194f..19f3622 100644 --- a/include/boost/requests/sources/form.hpp +++ b/include/boost/requests/sources/form.hpp @@ -9,12 +9,17 @@ #include #include - - namespace boost { namespace requests { +namespace detail +{ + +BOOST_REQUESTS_DECL +std::array make_boundary_value(); + +} struct form_source : source { @@ -56,9 +61,35 @@ struct form_source : source core::string_view default_content_type() override { return "application/x-www-form-urlencoded"; } }; +struct multi_part_form_source : source +{ + std::array boundary_and_type{detail::make_boundary_value()}; + multi_part_form mpf; + typename std::vector::const_iterator current = mpf.storage.cbegin(); + + boost::optional coro_state; + std::size_t remaining = 0; + + multi_part_form_source(multi_part_form && mpf) : mpf(std::move(mpf)) {} + multi_part_form_source(const multi_part_form & mpf) : mpf(mpf) {} + + BOOST_REQUESTS_DECL + optional size() const override; + BOOST_REQUESTS_DECL + void reset() override; + BOOST_REQUESTS_DECL + std::pair read_some(void * data, std::size_t size, system::error_code & ec) override; + BOOST_REQUESTS_DECL + core::string_view default_content_type() override; +}; + BOOST_REQUESTS_DECL source_ptr tag_invoke(make_source_tag, form form_, container::pmr::memory_resource * res); +BOOST_REQUESTS_DECL +source_ptr tag_invoke(make_source_tag, multi_part_form mpf, container::pmr::memory_resource * res); + + } } diff --git a/include/boost/requests/sources/impl/form.ipp b/include/boost/requests/sources/impl/form.ipp index 9defdfe..e782d81 100644 --- a/include/boost/requests/sources/impl/form.ipp +++ b/include/boost/requests/sources/impl/form.ipp @@ -9,11 +9,32 @@ #define BOOST_REQUESTS_SOURCE_IMPL_FORM_IPP #include +#include +#include namespace boost { namespace requests { +namespace detail +{ + +std::array make_boundary_value() +{ + std::array res{}; + static std::random_device rd; + + const char prefix[31] = "multipart/form-data; boundary="; + + constexpr static char values[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + std::uniform_int_distribution dist(0, sizeof(values) - 2); + + auto itr = std::copy(std::begin(prefix), std::prev(std::end(prefix)), res.begin()); + std::generate(itr, res.end(), [&]{return values[dist(rd)];}); + return res; +} + +} source_ptr tag_invoke(make_source_tag, form form_, container::pmr::memory_resource * res) { @@ -22,6 +43,146 @@ source_ptr tag_invoke(make_source_tag, form form_, container::pmr::memory_resour std::move(form_)); } +source_ptr tag_invoke(make_source_tag, multi_part_form mpf, container::pmr::memory_resource * res) +{ + return std::allocate_shared( + container::pmr::polymorphic_allocator(res), + std::move(mpf)); +} + +optional multi_part_form_source::size() const +{ + std::size_t res = 0u; + + for (auto & mm : mpf.storage) + { + res += 2; // -- + res += 32; // boundary + res += sizeof("\r\nContent-Disposition: form-data; name=\"") - 1; + res += mm.name.size(); + + auto fsrc = dynamic_cast(mm.source.get()); + if (fsrc != nullptr) + { + res += sizeof("\"; filename=\"") - 1; + res += fsrc->path.filename().string().size(); + } + res += sizeof("\"\r\nContent-Type: ") - 1; + res += mm.source->default_content_type().size(); + res += 4; // \r\n\r\n; + + auto sz = mm.source->size(); + if (!sz.has_value()) + return sz; + + res += *sz; + res += 2; // \r\n; + } + res += 2; // -- + res += 32; // boundary + res += 2; // -- + return res; +} + + +core::string_view multi_part_form_source::default_content_type() +{ + return core::string_view{boundary_and_type.data(), boundary_and_type.size()}; +} + +void multi_part_form_source::reset() +{ + coro_state.reset(); + + while (current != mpf.storage.begin()) + { + if (current != mpf.storage.end()) + current->source->reset(); + current--; + } +} + +std::pair multi_part_form_source::read_some(void * data, std::size_t size, system::error_code & ec) +{ + if (!coro_state) + coro_state.emplace(); + std::size_t written = 0u; + + auto puts = + [&](core::string_view s) + { + auto cc = static_cast(data); + auto len = (std::min)(s.size(), size); + std::memcpy(data, s.data(), len); + data = cc+len; + size -= len; + written += len; + return len; + }; + + core::string_view msg; + +#define BOOST_REQUESTS_CORO_PUTS(Msg) \ + remaining = core::string_view(Msg).size(); \ + while (remaining > 0u) \ + { \ + msg = Msg; \ + remaining -= puts(msg.substr(msg.size() - remaining)); \ + if (size == 0) { BOOST_ASIO_CORO_YIELD ; } \ + } + + const core::string_view boundary{&*boundary_and_type.end() - 32, 32}; + + file_source * fsrc = nullptr; + if (current != mpf.storage.cend()) + fsrc = dynamic_cast(current->source.get()); + + std::pair tmp = {0u, true}; + BOOST_ASIO_CORO_REENTER(&*coro_state) + { + while (current != mpf.storage.cend()) + { + if (size == 0) { BOOST_ASIO_CORO_YIELD; } + BOOST_REQUESTS_CORO_PUTS("--"); + BOOST_REQUESTS_CORO_PUTS(boundary) + BOOST_REQUESTS_CORO_PUTS("\r\nContent-Disposition: form-data; name=\"") + BOOST_REQUESTS_CORO_PUTS(current->name) + + fsrc = dynamic_cast(current->source.get()); + if (fsrc != nullptr) + { + BOOST_REQUESTS_CORO_PUTS("\"; filename=\"") + BOOST_REQUESTS_CORO_PUTS(fsrc->path.filename().string()) + } + BOOST_REQUESTS_CORO_PUTS("\"\r\nContent-Type: ") + BOOST_REQUESTS_CORO_PUTS(current->source->default_content_type()) + BOOST_REQUESTS_CORO_PUTS("\r\n\r\n") + + tmp.second = true; + while (tmp.second) + { + tmp = current->source->read_some(data, size, ec); + size -= tmp.first; + written += tmp.first; + data = static_cast(data) + tmp.first; + if (ec) + return {written, true}; + if (size == 0) {BOOST_ASIO_CORO_YIELD; } + } + BOOST_REQUESTS_CORO_PUTS("\r\n") + current ++; + } + + BOOST_REQUESTS_CORO_PUTS("--") + BOOST_REQUESTS_CORO_PUTS(boundary) + BOOST_REQUESTS_CORO_PUTS("--") + } + + return {written, !coro_state->is_complete()}; +} + +#undef BOOST_REQUESTS_CORO_PUTS + } } diff --git a/include/boost/requests/sources/string.hpp b/include/boost/requests/sources/string.hpp index cc85f0e..bfebdd5 100644 --- a/include/boost/requests/sources/string.hpp +++ b/include/boost/requests/sources/string.hpp @@ -59,7 +59,7 @@ struct basic_string_source final : source }; template -inline basic_string_source tag_invoke( +inline source_ptr tag_invoke( make_source_tag, std::basic_string data, container::pmr::memory_resource * res) { diff --git a/include/boost/requests/sources/string_view.hpp b/include/boost/requests/sources/string_view.hpp index 8c3feea..b71e905 100644 --- a/include/boost/requests/sources/string_view.hpp +++ b/include/boost/requests/sources/string_view.hpp @@ -61,41 +61,40 @@ struct basic_string_view_source final : source }; template -basic_string_view_source tag_invoke(make_source_tag, const char (&data)[N], - container::pmr::memory_resource * res) +source_ptr tag_invoke(make_source_tag, const char (&data)[N], container::pmr::memory_resource * res) { return std::allocate_shared>( container::pmr::polymorphic_allocator(res), data); } template -basic_string_view_source tag_invoke(make_source_tag, const core::basic_string_view & data, - container::pmr::memory_resource * res) +source_ptr tag_invoke(make_source_tag, const core::basic_string_view & data, + container::pmr::memory_resource * res) { return std::allocate_shared>( container::pmr::polymorphic_allocator(res), std::move(data)); } template -basic_string_view_source tag_invoke(make_source_tag, core::basic_string_view && data, - container::pmr::memory_resource * res) = delete; +source_ptr tag_invoke(make_source_tag, core::basic_string_view && data, + container::pmr::memory_resource * res) = delete; template -basic_string_view_source tag_invoke(make_source_tag, const boost::basic_string_view & data, - container::pmr::memory_resource * res) +source_ptr tag_invoke(make_source_tag, const boost::basic_string_view & data, + container::pmr::memory_resource * res) { return std::allocate_shared>( container::pmr::polymorphic_allocator(res), std::move(data)); } template -basic_string_view_source tag_invoke(make_source_tag, boost::basic_string_view && data, - container::pmr::memory_resource * res) = delete; +source_ptr tag_invoke(make_source_tag, boost::basic_string_view && data, + container::pmr::memory_resource * res) = delete; #if defined(__cpp_lib_string_view) template -basic_string_view_source tag_invoke(make_source_tag, const std::basic_string_view & data, - container::pmr::memory_resource * res) +source_ptr tag_invoke(make_source_tag, const std::basic_string_view & data, + container::pmr::memory_resource * res) { return std::allocate_shared>( container::pmr::polymorphic_allocator(res), @@ -103,8 +102,8 @@ basic_string_view_source tag_invoke(make_source_tag, const std::basic_stri } template -basic_string_view_source tag_invoke(make_source_tag, std::basic_string_view && data, - container::pmr::memory_resource * res) = delete; +source_ptr tag_invoke(make_source_tag, std::basic_string_view && data, + container::pmr::memory_resource * res) = delete; #endif } diff --git a/test/connection.cpp b/test/connection.cpp index b3b9cca..cb8554a 100644 --- a/test/connection.cpp +++ b/test/connection.cpp @@ -310,6 +310,21 @@ void http_request_connection(bool https) CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); } + + // post-multipart-form + { + boost::system::error_code ec; + auto hdr = post(hc, urls::url_view("/post"), + requests::multi_part_form{{"foo", "data 1"}, {"bar", "data 2"}, {"foobar", "data 3"}}, + {{}, {false}}, ec); + check_ec(ec); + CHECK_HTTP_RESULT(hdr.headers); + auto js = as_json(hdr); + CHECK_MESSAGE(hdr.headers.result() == requests::http::status::ok, hdr.headers); + CHECK_MESSAGE(js.at("headers").at("Content-Type").as_string().starts_with("multipart/form-data"), + js.at("headers").at("Content-Type")); + CHECK(js.at("form") == json::value{{"foo", "data 1"}, {"bar", "data 2"}, {"foobar" , "data 3"}}); + } } TEST_CASE("sync-connection-request") diff --git a/test/form.cpp b/test/form.cpp new file mode 100644 index 0000000..0cedb55 --- /dev/null +++ b/test/form.cpp @@ -0,0 +1,72 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include + +#include +#include +#include + +#include "doctest.h" +#include "string_maker.hpp" + +using namespace boost; + +// language=http +static auto cmp = +"--01234567890123456789012345678901\r\n" +"Content-Disposition: form-data; name=\"text-field\"\r\n" +"Content-Type: text/plain; charset=utf-8\r\n" +"\r\n" +"Test\r\n" +"--01234567890123456789012345678901\r\n" +"Content-Disposition: form-data; name=\"box\"\r\n" +"Content-Type: text/plain; charset=utf-8\r\n" +"\r\n" +"on\r\n" +"--01234567890123456789012345678901\r\n" +"Content-Disposition: form-data; name=\"my-file\"; filename=\"form-test.txt\"\r\n" +"Content-Type: text/plain\r\n" +"\r\n" +"test-string2\r\n" +"--01234567890123456789012345678901--"; + +TEST_SUITE_BEGIN("form"); +TEST_CASE("multi-part") +{ + filesystem::path pt = filesystem::temp_directory_path() / "form-test.txt"; + std::ofstream(pt.string()) << "test-string2"; + + constexpr char boundary[33] = "01234567890123456789012345678901"; + + requests::multi_part_form mpf{{"text-field", "Test"}, {"box", "on"}, {"my-file", pt}}; + requests::multi_part_form_source bd{mpf}; + std::copy(std::begin(boundary), std::end(boundary) - 1, bd.boundary_and_type.begin() + 32); + + REQUIRE(bd.current == bd.mpf.storage.cbegin()); + auto sz = bd.size(); + + std::string data; + + for (;;) + { + char buf[4096]; + boost::system::error_code ec; + auto rr = bd.read_some(&buf[0], sizeof(buf), ec); + REQUIRE(ec == boost::system::error_code{}); + data.append(buf, rr.first); + if (!rr.second) + break; + } + + CHECK(sz == data.size()); + CHECK(data.size() == string_view(cmp).size()); + CHECK(data == string_view(cmp)); +} + +TEST_SUITE_END(); \ No newline at end of file From be932b88939c4baa08b6fba4e32b4a37854b8822 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Sat, 11 Feb 2023 12:42:57 +0800 Subject: [PATCH 46/76] Added tool scripts for windows. --- tools/get-boost.sh | 93 +++++++++++++++++++++++++++++++++++++++++++ tools/user-config.jam | 13 ++++++ 2 files changed, 106 insertions(+) create mode 100755 tools/get-boost.sh create mode 100644 tools/user-config.jam diff --git a/tools/get-boost.sh b/tools/get-boost.sh new file mode 100755 index 0000000..3f2d2c0 --- /dev/null +++ b/tools/get-boost.sh @@ -0,0 +1,93 @@ +#! /bin/sh + +set -e + +build_dir=$2 + +branch="master" + +if [ "$1" != "master" -a "$1" != "refs/heads/master" ]; then + branch="develop" +fi + +echo "BUILD_DIR: $build_dir" +echo "BRANCH: $branch" + +git clone -b $branch --depth 1 https://github.com/boostorg/boost.git boost-root +cd boost-root + +# Use a reasonably large depth to prevent intermittent update failures due to +# commits being on a submodule's master before the superproject is updated. +git submodule update --init --depth 20 --jobs 4 \ + libs/array \ + libs/headers \ + tools/build \ + tools/boost_install \ + tools/boostdep \ + libs/align \ + libs/asio \ + libs/assert \ + libs/config \ + libs/core \ + libs/describe \ + libs/endian \ + libs/filesystem \ + libs/intrusive \ + libs/locale \ + libs/optional \ + libs/smart_ptr \ + libs/static_assert \ + libs/static_string \ + libs/system \ + libs/throw_exception \ + libs/type_traits \ + libs/utility \ + libs/winapi \ + libs/algorithm \ + libs/array \ + libs/atomic \ + libs/bind \ + libs/chrono \ + libs/concept_check \ + libs/container \ + libs/container_hash \ + libs/context \ + libs/conversion \ + libs/coroutine \ + libs/date_time \ + libs/detail \ + libs/exception \ + libs/function \ + libs/function_types \ + libs/functional \ + libs/fusion \ + libs/integer \ + libs/io \ + libs/iterator \ + libs/lambda \ + libs/lexical_cast \ + libs/logic \ + libs/math \ + libs/move \ + libs/mp11 \ + libs/mpl \ + libs/numeric/conversion \ + libs/pool \ + libs/predef \ + libs/preprocessor \ + libs/random \ + libs/range \ + libs/ratio \ + libs/rational \ + libs/thread \ + libs/tuple \ + libs/type_index \ + libs/typeof \ + libs/unordered \ + libs/variant2 \ + libs/json + +echo Submodule update complete + +rm -rf libs/beast +cp -r $build_dir libs/beast diff --git a/tools/user-config.jam b/tools/user-config.jam new file mode 100644 index 0000000..215e5ad --- /dev/null +++ b/tools/user-config.jam @@ -0,0 +1,13 @@ +# Used on CI + +import os ; + +local OPENSSL_ROOT = [ os.environ OPENSSL_ROOT ] ; + +project + : requirements + $(OPENSSL_ROOT)/include + debug:$(OPENSSL_ROOT)/lib + windowsdebug:$(OPENSSL_ROOT)/debug/lib + release:$(OPENSSL_ROOT)/lib + ; From 5d6b4575c9a17b7273967db57d07d68fe6d5c1af Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Sat, 11 Feb 2023 13:01:10 +0800 Subject: [PATCH 47/76] more build fixes. --- include/boost/requests/sources/form.hpp | 13 ++++++++++--- include/boost/requests/sources/impl/form.ipp | 6 ++++++ test/form.cpp | 2 +- tools/get-boost.sh | 6 ++++-- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/include/boost/requests/sources/form.hpp b/include/boost/requests/sources/form.hpp index 19f3622..278d7f0 100644 --- a/include/boost/requests/sources/form.hpp +++ b/include/boost/requests/sources/form.hpp @@ -61,7 +61,7 @@ struct form_source : source core::string_view default_content_type() override { return "application/x-www-form-urlencoded"; } }; -struct multi_part_form_source : source +struct multi_part_form_source final : source { std::array boundary_and_type{detail::make_boundary_value()}; multi_part_form mpf; @@ -70,8 +70,15 @@ struct multi_part_form_source : source boost::optional coro_state; std::size_t remaining = 0; - multi_part_form_source(multi_part_form && mpf) : mpf(std::move(mpf)) {} - multi_part_form_source(const multi_part_form & mpf) : mpf(mpf) {} + BOOST_REQUESTS_DECL + ~multi_part_form_source(); + BOOST_REQUESTS_DECL + multi_part_form_source(const multi_part_form_source & rhs); + + BOOST_REQUESTS_DECL + multi_part_form_source(multi_part_form && mpf); + BOOST_REQUESTS_DECL + multi_part_form_source(const multi_part_form & mpf); BOOST_REQUESTS_DECL optional size() const override; diff --git a/include/boost/requests/sources/impl/form.ipp b/include/boost/requests/sources/impl/form.ipp index e782d81..4526e80 100644 --- a/include/boost/requests/sources/impl/form.ipp +++ b/include/boost/requests/sources/impl/form.ipp @@ -183,6 +183,12 @@ std::pair multi_part_form_source::read_some(void * data, std: #undef BOOST_REQUESTS_CORO_PUTS +multi_part_form_source::~multi_part_form_source() = default; +multi_part_form_source::multi_part_form_source(const multi_part_form_source & rhs)= default; + +multi_part_form_source::multi_part_form_source(multi_part_form && mpf) : mpf(std::move(mpf)) {} +multi_part_form_source::multi_part_form_source(const multi_part_form & mpf) : mpf(mpf) {} + } } diff --git a/test/form.cpp b/test/form.cpp index 0cedb55..8191f7f 100644 --- a/test/form.cpp +++ b/test/form.cpp @@ -46,7 +46,7 @@ TEST_CASE("multi-part") requests::multi_part_form mpf{{"text-field", "Test"}, {"box", "on"}, {"my-file", pt}}; requests::multi_part_form_source bd{mpf}; - std::copy(std::begin(boundary), std::end(boundary) - 1, bd.boundary_and_type.begin() + 32); + std::copy(std::begin(boundary), std::end(boundary) - 1, bd.boundary_and_type.begin() + 30); REQUIRE(bd.current == bd.mpf.storage.cbegin()); auto sz = bd.size(); diff --git a/tools/get-boost.sh b/tools/get-boost.sh index 3f2d2c0..011ae23 100755 --- a/tools/get-boost.sh +++ b/tools/get-boost.sh @@ -27,6 +27,7 @@ git submodule update --init --depth 20 --jobs 4 \ libs/align \ libs/asio \ libs/assert \ + libs/beast \ libs/config \ libs/core \ libs/describe \ @@ -84,10 +85,11 @@ git submodule update --init --depth 20 --jobs 4 \ libs/type_index \ libs/typeof \ libs/unordered \ + libs/url \ libs/variant2 \ libs/json echo Submodule update complete -rm -rf libs/beast -cp -r $build_dir libs/beast +rm -rf libs/requests +cp -r $build_dir libs/requests From cee0c116ad239d4984aa34abbe30c773611ae9f0 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Sun, 12 Feb 2023 13:11:42 +0800 Subject: [PATCH 48/76] removed unnecessary include. --- example/github.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/example/github.cpp b/example/github.cpp index 2c5d30a..aabbb7b 100644 --- a/example/github.cpp +++ b/example/github.cpp @@ -7,8 +7,6 @@ #include "github-issues.hpp" -#include - #include // printing functions From e03fdca7fc8cae3843a0d339c21f5ab46e388267 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 14 Feb 2023 16:59:06 +0800 Subject: [PATCH 49/76] faux_coroutine with_alloc hotfix. --- include/boost/requests/detail/faux_coroutine.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/boost/requests/detail/faux_coroutine.hpp b/include/boost/requests/detail/faux_coroutine.hpp index 48aca02..9957fd9 100644 --- a/include/boost/requests/detail/faux_coroutine.hpp +++ b/include/boost/requests/detail/faux_coroutine.hpp @@ -309,6 +309,14 @@ struct faux_runner { this->token_type::base::slot = asio::get_associated_cancellation_slot(handler); } + + template + impl_(Handler_ && h, with_allocator_t &&, Args_ && ... args) + : handler(std::forward(h)) + , impl(get_allocator(), std::forward(args)...) + { + this->token_type::base::slot = asio::get_associated_cancellation_slot(handler); + } }; template From 461222948f2419a79c79890c24b29c778f820895 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 14 Feb 2023 17:15:05 +0800 Subject: [PATCH 50/76] Removed dual filesystem library usage. --- include/boost/requests/source.hpp | 12 ++--------- include/boost/requests/sources/impl/file.ipp | 21 ++------------------ 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/include/boost/requests/source.hpp b/include/boost/requests/source.hpp index 5df6be3..27594ee 100644 --- a/include/boost/requests/source.hpp +++ b/include/boost/requests/source.hpp @@ -14,9 +14,6 @@ #include #include -#if defined(__cpp_lib_filesystem) -#include -#endif namespace boost { @@ -111,14 +108,9 @@ BOOST_REQUESTS_DECL source_ptr tag_invoke(const make_source_tag&, const none_t &); BOOST_REQUESTS_DECL -source_ptr tag_invoke(const make_source_tag&, const boost::filesystem::path & path, container::pmr::memory_resource * res); -source_ptr tag_invoke(const make_source_tag&, boost::filesystem::path &&, container::pmr::memory_resource * res) = delete; +source_ptr tag_invoke(const make_source_tag&, const filesystem::path & path, container::pmr::memory_resource * res); +source_ptr tag_invoke(const make_source_tag&, filesystem::path &&, container::pmr::memory_resource * res) = delete; -#if defined(__cpp_lib_filesystem) -BOOST_REQUESTS_DECL -source_ptr tag_invoke(const make_source_tag&, const std::filesystem::path & path, container::pmr::memory_resource * res); -source_ptr tag_invoke(const make_source_tag&, std::filesystem::path &&, container::pmr::memory_resource * res) = delete; -#endif BOOST_REQUESTS_DECL source_ptr tag_invoke(make_source_tag, struct form form_, container::pmr::memory_resource * res); diff --git a/include/boost/requests/sources/impl/file.ipp b/include/boost/requests/sources/impl/file.ipp index a8d7461..a2ad43a 100644 --- a/include/boost/requests/sources/impl/file.ipp +++ b/include/boost/requests/sources/impl/file.ipp @@ -16,34 +16,17 @@ namespace boost namespace requests { -file_source::file_source(const boost::filesystem::path & file) : path(file.c_str()) +file_source::file_source(const filesystem::path & file) : path(file.c_str()) { this->file.open(file.string().c_str(), beast::file_mode::read, ec); } -source_ptr tag_invoke(const make_source_tag &tag, const boost::filesystem::path & path, +source_ptr tag_invoke(const make_source_tag &tag, const filesystem::path & path, container::pmr::memory_resource * res) { return std::allocate_shared(container::pmr::polymorphic_allocator(res), path); } -#if defined(__cpp_lib_filesystem) - -file_source::file_source(const std::filesystem::path & file) : path(file.c_str()) -{ - this->file.open(file.string().c_str(), beast::file_mode::read, ec); -} - -source_ptr tag_invoke(const make_source_tag &tag, const std::filesystem::path & path, - container::pmr::memory_resource * res) -{ - return std::allocate_shared(container::pmr::polymorphic_allocator(res), path); -} - - -#endif - - } } From 491412db4106e458a04a1534f36882654cc4f2fb Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 14 Feb 2023 19:57:34 +0800 Subject: [PATCH 51/76] Fix for stream::async_read_some. --- include/boost/requests/impl/stream.ipp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/requests/impl/stream.ipp b/include/boost/requests/impl/stream.ipp index aeb1930..9f82043 100644 --- a/include/boost/requests/impl/stream.ipp +++ b/include/boost/requests/impl/stream.ipp @@ -70,7 +70,7 @@ std::size_t stream::async_read_some_op::resume(requests::detail::faux_token_tparser_->get().body().size = buffer.size(); BOOST_ASIO_CORO_YIELD this_->impl_->do_async_read_some_(*this_->parser_, std::move(self)); - if (!ec && !this_->parser_->is_done()) + if (!this_->parser_->is_done()) { this_->parser_->get().body().more = true; if (ec == beast::http::error::need_buffer) @@ -79,7 +79,7 @@ std::size_t stream::async_read_some_op::resume(requests::detail::faux_token_tparser_->get().body().more = false; - if (ec || !this_->parser_->get().keep_alive()) + if (!this_->parser_->get().keep_alive()) { ec_ = ec ; BOOST_ASIO_CORO_YIELD this_->impl_->do_async_close_(std::move(self)); From bde57db860fbcd0411d1f08540b7dd8d35cb3c3e Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 14 Feb 2023 20:34:41 +0800 Subject: [PATCH 52/76] set parser.body_limit to none. --- include/boost/requests/detail/impl/connection_impl.ipp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/requests/detail/impl/connection_impl.ipp b/include/boost/requests/detail/impl/connection_impl.ipp index c190890..3fedaaf 100644 --- a/include/boost/requests/detail/impl/connection_impl.ipp +++ b/include/boost/requests/detail/impl/connection_impl.ipp @@ -183,7 +183,7 @@ auto connection_impl::ropen(beast::http::verb method, stream str{get_executor(), shared_from_this()}; str.parser_ = detail::make_pmr>(headers.get_allocator().resource(), http::response_header{http::fields(headers.get_allocator())}); - + str.parser_->body_limit(boost::none); if (use_ssl_) beast::http::read_header(next_layer_, buffer_, *str.parser_, ec); else @@ -360,7 +360,7 @@ auto connection_impl::async_ropen_op::resume( str.emplace(this_->get_executor(), this_); // , req.get_allocator().resource() str->parser_ = detail::make_pmr>(headers.get_allocator().resource(), http::response_header{http::fields(headers.get_allocator())}); - + str->parser_->body_limit(boost::none); if (this_->use_ssl_) { BOOST_ASIO_CORO_YIELD beast::http::async_read_header(this_->next_layer_, this_->buffer_, *str->parser_, std::move(self)); From 3fb46ad63f0b5c291778b478f124869c6e095534 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 21 Feb 2023 15:00:25 +0800 Subject: [PATCH 53/76] Switched to jam's openssl module. --- build/Jamfile | 2 +- test/Jamfile | 2 +- tools/user-config.jam | 10 +--------- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/build/Jamfile b/build/Jamfile index 764f0db..ae39c03 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -14,5 +14,5 @@ project boost/requests alias requests_sources : src.cpp ; -lib boost_requests : requests_sources ; +lib boost_requests : requests_sources /openssl//ssl ; boost-install boost_requests ; \ No newline at end of file diff --git a/test/Jamfile b/test/Jamfile index 508ad72..b8f4b50 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -23,4 +23,4 @@ alias asio-ssl : ../../beast//lib-asio-ssl ; alias beast : ../../beast//lib-beast ; alias ssl : ../../beast//ssl ; -run [ glob-tree *.cpp ] /boost//json /boost//url ssl asio asio-ssl beast ssl ; \ No newline at end of file +run [ glob-tree *.cpp ] /boost//json /boost//url ssl asio asio-ssl beast /openssl//ssl ; \ No newline at end of file diff --git a/tools/user-config.jam b/tools/user-config.jam index 215e5ad..da57124 100644 --- a/tools/user-config.jam +++ b/tools/user-config.jam @@ -2,12 +2,4 @@ import os ; -local OPENSSL_ROOT = [ os.environ OPENSSL_ROOT ] ; - -project - : requirements - $(OPENSSL_ROOT)/include - debug:$(OPENSSL_ROOT)/lib - windowsdebug:$(OPENSSL_ROOT)/debug/lib - release:$(OPENSSL_ROOT)/lib - ; +using openssl ; From f6534ae438d0cb48772a989c161829b724102850 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Sun, 9 Apr 2023 10:35:08 +0800 Subject: [PATCH 54/76] Drone star from sam --- .drone.star | 228 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 200 insertions(+), 28 deletions(-) diff --git a/.drone.star b/.drone.star index 1119bf6..a1ab606 100644 --- a/.drone.star +++ b/.drone.star @@ -1,35 +1,207 @@ -# Use, modification, and distribution are -# subject to the Boost Software License, Version 1.0. (See accompanying -# file LICENSE.txt) # -# Copyright Rene Rivera 2020. - -# For Drone CI we use the Starlark scripting language to reduce duplication. -# As the yaml syntax for Drone CI is rather limited. +# Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com) # +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) # -globalenv={'B2_CI_VERSION': '1', 'B2_VARIANT': 'release'} -linuxglobalimage="cppalliance/droneubuntu1804:1" -windowsglobalimage="cppalliance/dronevs2019" + +deps = [ + 'libs/array', + 'libs/align', + 'libs/asio', + 'libs/assert', + 'libs/beast', + 'libs/chrono', + 'libs/config', + 'libs/core', + 'libs/date_time', + 'libs/headers', + 'libs/integer', + 'libs/mpl', + 'libs/numeric', + 'libs/predef', + 'libs/preprocessor', + 'libs/ratio', + 'libs/system', + 'libs/static_assert', + 'libs/smart_ptr', + 'libs/test', + 'libs/thread', + 'libs/throw_exception', + 'libs/utility', + 'libs/type_traits', + 'libs/winapi', + 'tools/build', + 'tools/boost_install', + 'tools/boostdep' + ] + + +def git_boost_steps(branch, image="alpine/git", env_win_style=False): + return [{ + "name": "boost ({})".format(branch), + "image": image, + "commands": [ + "git clone -b {} --depth 1 https://github.com/boostorg/boost.git boost".format(branch) + ] + }, + { + "name": "boost submodules", + "image": image, + "commands": [ + "cd boost", + "git submodule update --init --depth 20 --jobs 8 " + " ".join(deps) + ] + }, + { + "name": "clone", + "image": image, + "commands": [ + "cd boost/libs", + "git clone {}".format("$Env:DRONE_REMOTE_URL" if env_win_style else "$DRONE_REMOTE_URL"), + "cd sam", + "git checkout {}".format("$Env:DRONE_COMMIT" if env_win_style else "$DRONE_COMMIT") + ] + } + ] + + +def format_b2_args(**kwargs): + res = "" + for k in kwargs: + res += " {}={}".format(k.replace('_', '-'), kwargs[k]) + return res + + +def linux_build_steps(image, **kwargs): + args = format_b2_args(**kwargs) + return [ + { + "name": "bootstrap", + "image": image, + "commands": [ + "cd boost", + "./bootstrap.sh" + ] + }, + { + "name": "build", + "image": image, + "commands" : [ + "cd boost/libs/sam", + "../../b2 build -j8 " + args + ] + }, + { + "name": "test", + "image": image, + "commands" : [ + "cd boost/libs/sam", + "../../b2 test -j8 " + args + ] + }, + { + "name": "bench", + "image": image, + "commands" : [ + "cd boost/libs/sam", + "../../b2 bench -j8 " + args + ] + }] + + +def windows_build_steps(image, **kwargs): + args = format_b2_args(**kwargs) + return [ + { + "name": "bootstrap", + "image": image, + "commands": [ + "cd boost", + ".\\\\bootstrap.bat" + ] + }, + { + "name" : "build", + "image" : image, + "commands": [ + "cd boost/libs/sam", + "..\\\\..\\\\b2 build -j8 " + args + ] + }, + { + "name": "test", + "image": image, + "commands": [ + "cd boost/libs/sam", + "..\\\\..\\\\b2 test -j8 " + args + ] + }, + { + "name": "bench", + "image": image, + "commands": [ + "cd boost/libs/sam", + "..\\\\..\\\\b2 bench -j8 " + args + ] + }] + + +def linux( + name, + branch, + image, + **kwargs): + + return { + "kind": "pipeline", + "type": "docker", + "name": name, + "clone": {"disable": True}, + "platform": { + "os": "linux", + "arch": "amd64" + }, + "steps": git_boost_steps(branch) + linux_build_steps(image, **kwargs) + } + + +def windows( + name, + branch, + image, + **kwargs): + + return { + "kind": "pipeline", + "type": "docker", + "name": name, + "clone": {"disable": True}, + "platform": { + "os": "windows", + "arch": "amd64" + }, + "steps": git_boost_steps(branch, image, True) + windows_build_steps(image, **kwargs) + } + def main(ctx): - return [ - linux_cxx("docs", "", packages="docbook docbook-xml docbook-xsl xsltproc libsaxonhe-java default-jre-headless flex libfl-dev bison unzip rsync mlocate", image="cppalliance/droneubuntu1804:1", buildtype="docs", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "COMMENT": "docs"}, globalenv=globalenv), - linux_cxx("asan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'COMMENT': 'asan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '14', 'B2_ASAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_EXTRA_PRIVILEGED': 'True', 'DRONE_JOB_UUID': '356a192b79'}, globalenv=globalenv, privileged=True), - linux_cxx("ubsan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'COMMENT': 'ubsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '14', 'B2_UBSAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'B2_LINKFLAGS': '-fuse-ld=gold', 'DRONE_JOB_UUID': '77de68daec'}, globalenv=globalenv), - linux_cxx("gcc 11 arm64", "g++-11", packages="g++-11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:multiarch", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'B2_TOOLSET': 'gcc-11', 'B2_CXXSTD': '14', 'DRONE_JOB_UUID': '17ba079169m'}, arch="arm64", globalenv=globalenv), - linux_cxx("GCC 10, Debug + Coverage", "g++-10", packages="g++-10 libssl-dev libffi-dev binutils-gold gdb mlocate", - image="cppalliance/droneubuntu2004:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "GCOV": "gcov-10", "LCOV_VERSION": "1.15", "VARIANT": "process_coverage", "TOOLSET": "gcc", "COMPILER": "g++-10", "CXXSTD": "14", "DRONE_BEFORE_INSTALL" : "process_coverage", "CODECOV_TOKEN": {"from_secret": "codecov_token"}}, globalenv=globalenv, privileged=True), - # A set of jobs based on the earlier .travis.yml configuration: - linux_cxx("Default clang++ with libc++", "clang++-libc++", packages="libc++-dev mlocate", image="cppalliance/droneubuntu1604:1", buildtype="buildtype", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "B2_TOOLSET": "clang-7", "B2_CXXSTD": "14", "VARIANT": "debug", "TOOLSET": "clang", "COMPILER": "clang++-libc++", "CXXSTD": "14", "CXX_FLAGS": "-stdlib=libc++ -stdlib=libc++", "TRAVISCLANG" : "yes" }, globalenv=globalenv), - linux_cxx("Default g++", "g++", packages="mlocate", image="cppalliance/droneubuntu1604:1", buildtype="buildtype", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "VARIANT": "release", "TOOLSET": "gcc", "COMPILER": "g++", "CXXSTD": "14" }, globalenv=globalenv), - linux_cxx("Clang 3.8, UBasan", "clang++-3.8", packages="clang-3.8 libssl-dev mlocate", llvm_os="precise", llvm_ver="3.8", image="cppalliance/droneubuntu1604:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "VARIANT": "process_ubasan", "TOOLSET": "clang", "COMPILER": "clang++-3.8", "CXXSTD": "14", "UBSAN_OPTIONS": 'print_stacktrace=1', "DRONE_BEFORE_INSTALL": "UBasan" }, globalenv=globalenv), - linux_cxx("gcc 6", "g++-6", packages="g++-6", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'B2_TOOLSET': 'gcc-6', 'B2_CXXSTD': '14', 'DRONE_JOB_UUID': '902ba3cda1'}, globalenv=globalenv), - linux_cxx("clang 3.8", "clang++-3.8", packages="clang-3.8", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu1604:1", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-3.8', 'B2_CXXSTD': '14', 'DRONE_JOB_UUID': '7b52009b64'}, globalenv=globalenv), - osx_cxx("clang", "g++", packages="", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'B2_TOOLSET': 'clang', 'B2_CXXSTD': '14,17', 'DRONE_JOB_UUID': '91032ad7bb'}, globalenv=globalenv), - linux_cxx("coverity", "g++", packages="", buildtype="coverity", buildscript="drone", image=linuxglobalimage, environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", 'COMMENT': 'Coverity Scan', 'B2_TOOLSET': 'clang', 'DRONE_JOB_UUID': '472b07b9fc'}, globalenv=globalenv), - windows_cxx("msvc-14.3", "", image="cppalliance/dronevs2022:1", buildtype="boost", buildscript="drone", environment={ "BOOST_REQUEST_HTTPBIN": "httpbin.cpp.al", "VARIANT": "release", "TOOLSET": "msvc-14.3", "CXXSTD": "17", "ADDRESS_MODEL": "64"}), + branch = ctx.build.branch + if ctx.build.event == 'tag' or (branch != 'master' and branch != 'refs/heads/master'): + branch = 'develop' + + return [ + linux("gcc-12", branch, "docker.io/library/gcc:12", variant="release", cxxstd="11,14,17,20"), + linux("gcc-12 (asan)", branch, "docker.io/library/gcc:12", variant="release", cxxstd="11,20", debug_symbols="on", address_sanitizer="on"), + linux("gcc-10", branch, "docker.io/library/gcc:10", variant="release", cxxstd="11,14,17,20"), + linux("gcc-8", branch, "docker.io/library/gcc:8", variant="release", cxxstd="11,14,17"), + linux("gcc-6", branch, "docker.io/library/gcc:6", variant="release", cxxstd="11,14"), + linux("clang", branch, "docker.io/silkeh/clang", toolset='clang', variant="release", cxxstd="11,14,17,20"), + linux("clang (asan)", branch, "docker.io/silkeh/clang", toolset='clang', variant="release", cxxstd="11,20", debug_symbols="on", address_sanitizer="on"), + linux("clang (tsan)", branch, "docker.io/silkeh/clang", toolset='clang', variant="release", cxxstd="11,20", debug_symbols="on", thread_sanitizer="on"), + windows("msvc-14.2 (x64)", branch, "cppalliance/dronevs2019:1", variant="release", cxxstd="11,14,17,20", address_model="64"), + windows("msvc-14.2 (x32)", branch, "cppalliance/dronevs2019:1", variant="release", cxxstd="11,14,17,20", address_model="32"), + windows("msvc-14.3 (x64)", branch, "cppalliance/dronevs2022:1", variant="release", cxxstd="11,14,17,20", address_model="64"), + windows("msvc-14.3 (x32)", branch, "cppalliance/dronevs2022:1", variant="release", cxxstd="11,14,17,20", address_model="32") ] -# from https://github.com/boostorg/boost-ci -load("@boost_ci//ci/drone/:functions.star", "linux_cxx","windows_cxx","osx_cxx","freebsd_cxx") From 15ffc9a4c70b46067c2772e405aa1fd7fc906687 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Thu, 8 Jun 2023 18:42:58 +0800 Subject: [PATCH 55/76] made it UB for the pool to be moved with pending ops. --- include/boost/requests/connection_pool.hpp | 5 +---- include/boost/requests/impl/connection_pool.ipp | 13 +++++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/include/boost/requests/connection_pool.hpp b/include/boost/requests/connection_pool.hpp index b8640a1..cf0107a 100644 --- a/include/boost/requests/connection_pool.hpp +++ b/include/boost/requests/connection_pool.hpp @@ -90,7 +90,7 @@ struct connection_pool : context_(ctx), mutex_(std::forward(exec)), limit_(limit) {} /// Move constructor - connection_pool(connection_pool && ) = default; + connection_pool(connection_pool && lhs) ; void lookup(urls::url_view av) { @@ -223,11 +223,8 @@ struct connection_pool struct async_lookup_op; struct async_get_connection_op; - struct async_ropen_op_body; - struct async_ropen_op_body_base; - struct async_ropen_op; }; diff --git a/include/boost/requests/impl/connection_pool.ipp b/include/boost/requests/impl/connection_pool.ipp index dc68859..b3891bf 100644 --- a/include/boost/requests/impl/connection_pool.ipp +++ b/include/boost/requests/impl/connection_pool.ipp @@ -320,6 +320,19 @@ stream connection_pool::async_ropen_op::resume( return stream{get_executor(), nullptr}; } +connection_pool::connection_pool(connection_pool && lhs) + : use_ssl_(lhs.use_ssl_), context_(lhs.context_), mutex_(std::move(lhs.mutex_)), host_(std::move(lhs.host_)), + endpoints_(std::move(lhs.endpoints_)), limit_(lhs.limit_) +{ + BOOST_ASSERT(std::count_if( + conns_.begin(), conns_.end(), + [](const std::pair> & p) + { + return !p.second.unique(); + }) == 0u); + +} + } } From f57b04c3f788bdda95fa650573cb0aeff2a20bf9 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 22 Aug 2023 18:00:10 +0800 Subject: [PATCH 56/76] cmake links to beast in inline mode only. --- CMakeLists.txt | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6af4a3f..12fa1f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,18 +73,26 @@ endif() add_library(boost_requests src/src.cpp) target_include_directories(boost_requests PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") -target_link_libraries( + +if (BOOST_ASYNC_BUILD_INLINE) + target_link_libraries( boost_requests PUBLIC - Boost::beast - Boost::container - Boost::filesystem - Boost::json - Boost::system - Boost::url - OpenSSL::SSL - OpenSSL::Crypto - Threads::Threads) + Boost::beast) +endif() + +target_link_libraries( + boost_requests + PUBLIC + Boost::container + Boost::filesystem + Boost::json + Boost::system + Boost::url + OpenSSL::SSL + OpenSSL::Crypto + Threads::Threads) + target_compile_definitions(boost_requests PUBLIC BOOST_REQUESTS_SEPARATE_COMPILATION=1 BOOST_FILESYSTEM_NO_DEPRECATED=1) add_library(Boost::requests ALIAS boost_requests) @@ -104,7 +112,7 @@ if(BOOST_REQUESTS_INSTALL AND NOT BOOST_SUPERPROJECT_VERSION) endif() if (OPENSSL_FOUND) - include_directories (${OPENSSL_INCLUDE_DIR}) + include_directories(${OPENSSL_INCLUDE_DIR}) endif() if(BOOST_REQUESTS_BUILD_TESTS) From 7d489bf58eae56387ad43777691d4e76cb722e9d Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 22 Aug 2023 20:23:08 +0800 Subject: [PATCH 57/76] added timer based condition_variable. --- .../requests/detail/condition_variable.hpp | 83 +++++++++++++++++++ .../detail/impl/condition_variable.ipp | 57 +++++++++++++ include/boost/requests/src.hpp | 1 + test/detail/condition_variable.cpp | 59 +++++++++++++ 4 files changed, 200 insertions(+) create mode 100644 include/boost/requests/detail/condition_variable.hpp create mode 100644 include/boost/requests/detail/impl/condition_variable.ipp create mode 100644 test/detail/condition_variable.cpp diff --git a/include/boost/requests/detail/condition_variable.hpp b/include/boost/requests/detail/condition_variable.hpp new file mode 100644 index 0000000..512d729 --- /dev/null +++ b/include/boost/requests/detail/condition_variable.hpp @@ -0,0 +1,83 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_REQUESTS_DETAIL_CONDITION_VARIABLE_HPP +#define BOOST_REQUESTS_DETAIL_CONDITION_VARIABLE_HPP + +#include +#include +#include +#include + +namespace boost { +namespace requests { +namespace detail { + +struct condition_variable +{ + using executor_type = asio::any_io_executor; + + explicit condition_variable(executor_type exec) + : timer_(exec, std::chrono::steady_clock::time_point::max()) {} + + template + explicit condition_variable(ExecutionContext & ctx, + typename std::enable_if< + std::is_convertible< + ExecutionContext&, + asio::execution_context&>::value + >::type * = nullptr) + : timer_(ctx, std::chrono::steady_clock::time_point::max()) + { + } + + BOOST_REQUESTS_DECL void + async_wait(std::unique_lock & lock, faux_token_t tk); + + condition_variable& operator=(const condition_variable&) = delete; + condition_variable& operator=(condition_variable&& lhs) noexcept = default; + condition_variable(const condition_variable&) = delete; + condition_variable(condition_variable&& mi) noexcept = default; + BOOST_REQUESTS_DECL void wait(std::unique_lock & lock, system::error_code & ec); + void wait(std::unique_lock & lock) + { + system::error_code ec; + wait(lock, ec); + if (ec) + boost::throw_exception(system::system_error(ec, "lock")); + } + void notify_one() { if (timer_.cancel_one() == 0u) cv_.notify_one(); } + void notify_all() { timer_.cancel(); cv_.notify_all();} + + template + struct rebind_executor + { + /// The mutex type when rebound to the specified executor. + typedef condition_variable other; + }; + + executor_type + get_executor() noexcept {return timer_.get_executor();} + + BOOST_REQUESTS_DECL ~condition_variable(); + + private: + asio::steady_timer timer_; + std::condition_variable cv_; + std::shared_ptr shutdown_indicator_{std::make_shared()}; +}; + +} +} +} + +#if defined(BOOST_REQUESTS_HEADER_ONLY) +#include +#endif + + +#endif // BOOST_REQUESTS_DETAIL_CONDITION_VARIABLE_HPP diff --git a/include/boost/requests/detail/impl/condition_variable.ipp b/include/boost/requests/detail/impl/condition_variable.ipp new file mode 100644 index 0000000..9411dc6 --- /dev/null +++ b/include/boost/requests/detail/impl/condition_variable.ipp @@ -0,0 +1,57 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_REQUESTS_DETAIL_IMPL_CONDITION_VARIABLE_IPP +#define BOOST_REQUESTS_DETAIL_IMPL_CONDITION_VARIABLE_IPP + +#include + +namespace boost +{ +namespace requests +{ +namespace detail +{ + +void condition_variable::wait(std::unique_lock & lock, system::error_code & ec) +{ + std::weak_ptr indicator = this->shutdown_indicator_; + cv_.wait(lock); + if (indicator.expired()) + ec = asio::error::operation_aborted; + +} +void condition_variable::async_wait(std::unique_lock & lock, + faux_token_t tk) +{ + std::weak_ptr indicator = this->shutdown_indicator_; + lock.unlock(); + timer_.async_wait( + asio::deferred( + [&lock, indicator](system::error_code ec_) + { + lock.lock(); + if (!indicator.expired()) + ec_.clear(); + return asio::deferred.values(ec_); + })) + (std::move(tk)); +} + + + +condition_variable::~condition_variable() +{ + shutdown_indicator_.reset(); + timer_.cancel(); + cv_.notify_all(); + +} +} +} +} +#endif // BOOST_REQUESTS_DETAIL_IMPL_CONDITION_VARIABLE_IPP diff --git a/include/boost/requests/src.hpp b/include/boost/requests/src.hpp index 461d7c4..cf756b0 100644 --- a/include/boost/requests/src.hpp +++ b/include/boost/requests/src.hpp @@ -16,6 +16,7 @@ #endif #include +#include #include #include #include diff --git a/test/detail/condition_variable.cpp b/test/detail/condition_variable.cpp new file mode 100644 index 0000000..65f2317 --- /dev/null +++ b/test/detail/condition_variable.cpp @@ -0,0 +1,59 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include + +#include "../string_maker.hpp" + + +TEST_SUITE_BEGIN("condition_variable"); + +namespace asio = boost::asio; +namespace requests = boost::requests; + +TEST_CASE("sync") +{ + asio::thread_pool ctx; + std::thread thr; + + std::mutex mtx; + std::unique_lock lock{mtx}; + + int pos = 0; + { + requests::detail::condition_variable cv{ctx}; + + thr = std::thread{[&] { + cv.wait(lock); + pos = 1; + cv.wait(lock); + pos = 2; + CHECK_THROWS(cv.wait(lock)); + pos = 3; + }}; + + CHECK(pos == 0); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + cv.notify_one(); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + CHECK(pos == 1); + cv.notify_all(); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + CHECK(pos == 2); + + } + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + CHECK(pos == 3); + + + thr.join(); + ctx.join(); +} + +TEST_SUITE_END(); \ No newline at end of file From e3aaf2d1ad6b858d51142d716ff8221b0f09bfa3 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 23 Aug 2023 13:35:19 +0800 Subject: [PATCH 58/76] switched pool to explicity pooling. --- include/boost/requests/connection_pool.hpp | 42 +++- .../boost/requests/impl/connection_pool.hpp | 8 +- .../boost/requests/impl/connection_pool.ipp | 208 +++++++----------- 3 files changed, 112 insertions(+), 146 deletions(-) diff --git a/include/boost/requests/connection_pool.hpp b/include/boost/requests/connection_pool.hpp index cf0107a..6f6fad4 100644 --- a/include/boost/requests/connection_pool.hpp +++ b/include/boost/requests/connection_pool.hpp @@ -7,11 +7,13 @@ #define BOOST_REQUESTS_CONNECTION_POOL_HPP #include +#include namespace boost { namespace requests { -namespace detail { +namespace detail +{ struct endpoint_hash { @@ -22,7 +24,6 @@ struct endpoint_hash } }; - } struct connection_pool @@ -45,7 +46,7 @@ struct connection_pool /// Get the executor executor_type get_executor() noexcept { - return mutex_.get_executor(); + return cv_.get_executor(); } /// The protocol-type of the lowest layer. @@ -65,7 +66,7 @@ struct connection_pool : context_( asio::use_service( asio::query(exec, asio::execution::context) - ).get()), mutex_(exec), limit_(limit) {} + ).get()), cv_(exec), limit_(limit) {} template explicit connection_pool(ExecutionContext &context, @@ -75,7 +76,7 @@ struct connection_pool >::type limit = BOOST_REQUESTS_DEFAULT_POOL_SIZE) : context_( asio::use_service(context).get()), - mutex_(context), limit_(limit) {} + cv_(context), limit_(limit) {} /// Construct a stream. /** @@ -87,7 +88,7 @@ struct connection_pool explicit connection_pool(Exec && exec, asio::ssl::context & ctx, std::size_t limit = BOOST_REQUESTS_DEFAULT_POOL_SIZE) - : context_(ctx), mutex_(std::forward(exec)), limit_(limit) {} + : context_(ctx), cv_(std::forward(exec)), limit_(limit) {} /// Move constructor connection_pool(connection_pool && lhs) ; @@ -212,7 +213,8 @@ struct connection_pool private: bool use_ssl_{true}; asio::ssl::context & context_; - detail::mutex mutex_; + std::mutex mtx_; + detail::condition_variable cv_; std::string host_; std::vector endpoints_; std::size_t limit_; @@ -221,11 +223,37 @@ struct connection_pool std::shared_ptr, detail::endpoint_hash> conns_; + std::vector> free_conns_; + struct async_lookup_op; struct async_get_connection_op; struct async_ropen_op_body; struct async_ropen_op_body_base; struct async_ropen_op; + + void return_connection_(std::shared_ptr conn) + { + std::lock_guard lock{mtx_}; + free_conns_.push_back(std::move(conn)); + cv_.notify_all(); + } + + void drop_connection_(const std::shared_ptr & conn) + { + std::lock_guard lock{mtx_}; + auto itr = std::find_if(conns_.begin(), conns_.end(), + [&](const std::pair> & e) + { + return e.second == conn; + }); + if (itr != conns_.end()) + { + conns_.erase(itr); + cv_.notify_all(); + } + } + + friend struct stream; }; template diff --git a/include/boost/requests/impl/connection_pool.hpp b/include/boost/requests/impl/connection_pool.hpp index 0e54721..e6eb44a 100644 --- a/include/boost/requests/impl/connection_pool.hpp +++ b/include/boost/requests/impl/connection_pool.hpp @@ -26,10 +26,7 @@ struct connection_pool::async_lookup_op : asio::coroutine urls::string_view scheme = "https"; urls::string_view service; - using mutex_type = detail::mutex; - using lock_type = detail::lock_guard; - - lock_type lock; + std::unique_lock lock{this_->mtx_, std::defer_lock}; async_lookup_op(connection_pool * this_, urls::url_view sv, executor_type exec) : this_(this_), sv(sv), resolver(exec) {} @@ -61,7 +58,6 @@ struct connection_pool::async_get_connection_op : asio::coroutine connection_pool * this_; async_get_connection_op(connection_pool * this_) : this_(this_) {} - using lock_type = detail::lock_guard; using conn_t = boost::unordered_multimap, detail::endpoint_hash>; @@ -69,7 +65,7 @@ struct connection_pool::async_get_connection_op : asio::coroutine std::shared_ptr nconn = nullptr; - lock_type lock; + std::unique_lock lock{this_->mtx_, std::defer_lock}; endpoint_type ep; using completion_signature_type = void(system::error_code, connection); diff --git a/include/boost/requests/impl/connection_pool.ipp b/include/boost/requests/impl/connection_pool.ipp index b3891bf..6668c27 100644 --- a/include/boost/requests/impl/connection_pool.ipp +++ b/include/boost/requests/impl/connection_pool.ipp @@ -20,13 +20,10 @@ void connection_pool::lookup(urls::url_view sv, system::error_code & ec) if (sv.has_scheme()) scheme = sv.scheme(); - detail::lock_guard lock; - if (scheme == "unix") // all good, no lookup needed { - - lock = detail::lock(mutex_, ec); + std::lock_guard lock{mtx_}; if (ec) return; @@ -51,7 +48,7 @@ void connection_pool::lookup(urls::url_view sv, system::error_code & ec) if (ec) return; - lock = detail::lock(mutex_, ec); + std::lock_guard lock{mtx_}; if (ec) return; use_ssl_ = scheme == "https"; @@ -96,14 +93,7 @@ void connection_pool::async_lookup_op::resume(requests::detail::faux_token_tmutex_.try_lock()) - { - BOOST_ASIO_CORO_YIELD this_->mutex_.async_lock(std::move(self)); - } - if (ec) - return; - lock = lock_type{this_->mutex_, std::adopt_lock}; + lock.lock(); this_->use_ssl_ = false; this_->host_ = "localhost"; @@ -123,13 +113,7 @@ void connection_pool::async_lookup_op::resume(requests::detail::faux_token_tmutex_.try_lock()) - { - BOOST_ASIO_CORO_YIELD this_->mutex_.async_lock(std::move(self)); - } - if (ec) - return; - lock = lock_type{this_->mutex_, std::adopt_lock}; + lock.lock(); this_->use_ssl_ = scheme == "https"; this_->host_ = eps->host_name(); const auto r = @@ -163,69 +147,48 @@ void connection_pool::async_lookup_op::resume(requests::detail::faux_token_t connection { - - auto lock = detail::lock(mutex_, ec); - if (ec) - return connection(); - - // find an idle connection - auto itr = std::find_if(conns_.begin(), conns_.end(), - [](const std::pair> & conn) - { - return (conn.second->working_requests() == 0u) && conn.second->is_open(); - }); - - if (itr != conns_.end()) - return connection(itr->second); - - // check if we can make more connections. -> open another connection. - // the race here is that we might open one too many - if (conns_.size() <= limit_) // open another connection then -> we block the entire + std::unique_lock lock{mtx_}; + do { - if (endpoints_.empty()) + if (!free_conns_.empty()) { - BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); - return connection(); + auto pp = std::move(free_conns_.front()); + free_conns_.erase(free_conns_.begin()); + return connection(pp); } + // check if we can make more connections. -> open another connection. + // the race here is that we might open one too many + else if (conns_.size() <= limit_) // open another connection then -> we block the entire + { + if (endpoints_.empty()) { + BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); + return connection(); + } - //sort the endpoints by connections that use it - std::sort(endpoints_.begin(), endpoints_.end(), - [this](const endpoint_type & a, const endpoint_type & b) - { - return conns_.count(a) < conns_.count(b); - }); - const auto ep = endpoints_.front(); - - std::shared_ptr nconn = std::make_shared(get_executor(), context_); - nconn->use_ssl(use_ssl_); - nconn->set_host(host_); - nconn->connect(ep, ec); - if (ec) - return connection(); - - if (ec) - return connection(); + // sort the endpoints by connections that use it + std::sort(endpoints_.begin(), endpoints_.end(), + [this](const endpoint_type &a, const endpoint_type &b) { return conns_.count(a) < conns_.count(b); }); + const auto ep = endpoints_.front(); + + auto nconn = conns_.emplace(ep, std::make_shared(get_executor(), context_))->second; + // no one else will grab it from there, bc it's not in free_conns_ + lock.unlock(); + nconn->use_ssl(use_ssl_); + nconn->set_host(host_); + nconn->connect(ep, ec); + if (ec) + return connection(); - conns_.emplace(ep, nconn); - return connection(nconn); + if (ec) + return connection(); + return connection(nconn); + } + cv_.wait(lock); } + while(!ec); - // find the one with the lowest usage - itr = std::min_element(conns_.begin(), conns_.end(), - [](const std::pair> & lhs, - const std::pair> & rhs) - { - return (lhs.second->working_requests() + (lhs.second->is_open() ? 0 : 1)) - < (rhs.second->working_requests() + (rhs.second->is_open() ? 0 : 1)); - }); - if (itr == conns_.end()) - { - BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); - return connection(); - } - else - return connection(itr->second); + return connection(); } @@ -235,67 +198,46 @@ auto connection_pool::async_get_connection_op::resume( { BOOST_ASIO_CORO_REENTER (this) { - if (!this_->mutex_.try_lock()) + lock.lock(); + do { - BOOST_ASIO_CORO_YIELD this_->mutex_.async_lock(std::move(self)); - } - if (ec) - return connection(); - - lock = {this_->mutex_, std::adopt_lock}; - - // find an idle connection - itr = std::find_if(this_->conns_.begin(), this_->conns_.end(), - [](const std::pair> & conn) - { - return (conn.second->working_requests() == 0u) && conn.second->is_open();; - }); - - if (itr != this_->conns_.end()) - return connection(itr->second); - - // check if we can make more connections. -> open another connection. - // the race here is that we might open one too many - if (this_->conns_.size() < this_->limit_) // open another connection then -> we block the entire - { - if (this_->endpoints_.empty()) + if (!this_->free_conns_.empty()) { - BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); - return connection(); + auto pp = std::move(this_->free_conns_.front()); + this_->free_conns_.erase(this_->free_conns_.begin()); + return connection(pp); } - - //sort the endpoints by connections that use it - std::sort(this_->endpoints_.begin(), this_->endpoints_.end(), - [this](const endpoint_type & a, const endpoint_type & b) - { - return this_->conns_.count(a) < this_->conns_.count(b); - }); - ep = this_->endpoints_.front(); - nconn = std::make_shared(get_executor(), this_->context_); - nconn->use_ssl(this_->use_ssl_); - nconn->set_host(this_->host_); - BOOST_ASIO_CORO_YIELD nconn->async_connect(ep, std::move(self)); // don't unlock here. - if (ec) - return connection(); - - this_->conns_.emplace(ep, nconn); - return connection(std::move(nconn)); - } - // find the one with the lowest usage - itr = std::min_element(this_->conns_.begin(), this_->conns_.end(), - [](const std::pair> & lhs, - const std::pair> & rhs) - { - return (lhs.second->working_requests() + (lhs.second->is_open() ? 0 : 1)) - < (rhs.second->working_requests() + (rhs.second->is_open() ? 0 : 1)); - }); - if (itr == this_->conns_.end()) - { - BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); - return connection(); + // check if we can make more connections. -> open another connection. + // the race here is that we might open one too many + else if (this_->conns_.size() <= this_->limit_) // open another connection then -> we block the entire + { + if (this_->endpoints_.empty()) { + BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); + return connection(); + } + + // sort the endpoints by connections that use it + std::sort(this_->endpoints_.begin(), this_->endpoints_.end(), + [this](const endpoint_type &a, const endpoint_type &b) { return this_->conns_.count(a) < this_->conns_.count(b); }); + ep = this_->endpoints_.front(); + + nconn = this_->conns_.emplace(ep, std::make_shared(get_executor(), this_->context_))->second; + // no one else will grab it from there, bc it's not in free_conns_ + lock.unlock(); + nconn->use_ssl(this_->use_ssl_); + nconn->set_host(this_->host_); + BOOST_ASIO_CORO_YIELD nconn->async_connect(ep, std::move(self)); + if (ec) + return connection(); + + if (ec) + return connection(); + + return connection(nconn); + } + this_->cv_.async_wait(lock, std::move(self)); } - else - return connection(itr->second); + while(!ec); } return connection(); } @@ -321,7 +263,7 @@ stream connection_pool::async_ropen_op::resume( } connection_pool::connection_pool(connection_pool && lhs) - : use_ssl_(lhs.use_ssl_), context_(lhs.context_), mutex_(std::move(lhs.mutex_)), host_(std::move(lhs.host_)), + : use_ssl_(lhs.use_ssl_), context_(lhs.context_), cv_(lhs.cv_.get_executor()), host_(std::move(lhs.host_)), endpoints_(std::move(lhs.endpoints_)), limit_(lhs.limit_) { BOOST_ASSERT(std::count_if( From f8a061444e8161986786302e7328d550e56ea22f Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 23 Aug 2023 13:48:14 +0800 Subject: [PATCH 59/76] removed tracker. --- include/boost/requests/connection.hpp | 2 - .../boost/requests/detail/connection_impl.hpp | 3 -- .../requests/detail/impl/connection_impl.hpp | 4 -- .../requests/detail/impl/connection_impl.ipp | 3 -- include/boost/requests/detail/tracker.hpp | 44 ------------------- include/boost/requests/stream.hpp | 7 +-- 6 files changed, 2 insertions(+), 61 deletions(-) delete mode 100644 include/boost/requests/detail/tracker.hpp diff --git a/include/boost/requests/connection.hpp b/include/boost/requests/connection.hpp index e2bd89b..7c541c7 100644 --- a/include/boost/requests/connection.hpp +++ b/include/boost/requests/connection.hpp @@ -134,8 +134,6 @@ struct connection bool is_open() const { return impl_ && impl_->is_open(); } endpoint_type endpoint() const { return impl_->endpoint(); } - - std::size_t working_requests() const { return impl_->working_requests(); } void reserve(std::size_t size) { impl_->reserve(size); } void set_host(core::string_view sv) diff --git a/include/boost/requests/detail/connection_impl.hpp b/include/boost/requests/detail/connection_impl.hpp index 98f3efd..004de9f 100644 --- a/include/boost/requests/detail/connection_impl.hpp +++ b/include/boost/requests/detail/connection_impl.hpp @@ -112,8 +112,6 @@ struct connection_impl : std::enable_shared_from_this // Endpoint endpoint_type endpoint() const {return endpoint_;} - std::size_t working_requests() const { return ongoing_requests_; } - // Reserve memory for the internal buffer. void reserve(std::size_t size) { @@ -193,7 +191,6 @@ struct connection_impl : std::enable_shared_from_this std::string host_; beast::flat_buffer buffer_; - std::atomic ongoing_requests_{0u}; endpoint_type endpoint_; struct async_close_op; diff --git a/include/boost/requests/detail/impl/connection_impl.hpp b/include/boost/requests/detail/impl/connection_impl.hpp index 291821c..f00932a 100644 --- a/include/boost/requests/detail/impl/connection_impl.hpp +++ b/include/boost/requests/detail/impl/connection_impl.hpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -69,7 +68,6 @@ connection_impl::async_connect(endpoint_type ep, CompletionToken && completion_t struct connection_impl::async_close_op : asio::coroutine { connection_impl * this_; - detail::tracker t{this_->ongoing_requests_}; using executor_type = asio::any_io_executor; executor_type get_executor() const {return this_->get_executor();} @@ -166,8 +164,6 @@ struct connection_impl::async_ropen_op std::shared_ptr this_; optional str; - detail::tracker t{this_->ongoing_requests_}; - beast::http::verb method; urls::pct_string_view path; http::fields & headers; diff --git a/include/boost/requests/detail/impl/connection_impl.ipp b/include/boost/requests/detail/impl/connection_impl.ipp index 3fedaaf..fdaa152 100644 --- a/include/boost/requests/detail/impl/connection_impl.ipp +++ b/include/boost/requests/detail/impl/connection_impl.ipp @@ -110,7 +110,6 @@ auto connection_impl::ropen(beast::http::verb method, { const auto is_secure = use_ssl_; using lock_type = detail::lock_guard; - detail::tracker t{this->ongoing_requests_}; lock_type read_lock; if (jar) { @@ -218,7 +217,6 @@ auto connection_impl::ropen(beast::http::verb method, && (rc != http::status::permanent_redirect))) { // GO - str.t_ = std::move(t); str.lock_ = std::move(read_lock); str.history_ = std::move(history); return str; @@ -396,7 +394,6 @@ auto connection_impl::async_ropen_op::resume( (rc != http::status::temporary_redirect) && (rc != http::status::permanent_redirect))) { // GO - str->t_ = std::move(t); str->lock_ = std::move(lock); str->history_ = std::move(history); return *std::move(str); diff --git a/include/boost/requests/detail/tracker.hpp b/include/boost/requests/detail/tracker.hpp deleted file mode 100644 index 5cd1ebd..0000000 --- a/include/boost/requests/detail/tracker.hpp +++ /dev/null @@ -1,44 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BOOST_REQUESTS_TRACKER_HPP -#define BOOST_REQUESTS_TRACKER_HPP - -#include -#include - -namespace boost { -namespace requests { -namespace detail { - -struct tracker -{ - std::atomic *cnt = nullptr; - tracker() = default; - tracker(std::atomic &cnt) : cnt(&cnt) {++cnt;} - ~tracker() - { - if (cnt) --(*cnt); - } - - tracker(const tracker &) = delete; - tracker(tracker && lhs) noexcept : cnt(boost::exchange(lhs.cnt, nullptr)) - { - } - - tracker& operator=(tracker && lhs) noexcept - { - std::swap(cnt, lhs.cnt); - return *this; - } -}; - -} -} -} - -#endif // BOOST_REQUESTS_TRACKER_HPP diff --git a/include/boost/requests/stream.hpp b/include/boost/requests/stream.hpp index f5cfc48..080e4a1 100644 --- a/include/boost/requests/stream.hpp +++ b/include/boost/requests/stream.hpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include @@ -146,10 +145,9 @@ struct stream bool done() const {return !parser_ || parser_->is_done();} explicit stream(executor_type executor, std::nullptr_t ) : executor_{executor}, impl_(nullptr) {} - explicit stream(executor_type executor, std::shared_ptr impl, detail::tracker t = {}) + explicit stream(executor_type executor, std::shared_ptr impl) : executor_{executor}, - impl_(std::move(impl)), - t_(std::move(t)) + impl_(std::move(impl)) {} http::response_header &&headers() && @@ -179,7 +177,6 @@ struct stream std::unique_ptr, detail::pmr_deleter> parser_; history_type history_; - detail::tracker t_; template struct async_read_op; From 01125e533b98a8f953fd28480fd00828b19362d6 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Thu, 24 Aug 2023 22:55:32 +0800 Subject: [PATCH 60/76] added pool ptr to connection. stream still to do. --- include/boost/requests/connection.hpp | 16 ++++++++++++--- include/boost/requests/connection_pool.hpp | 5 ++++- .../boost/requests/impl/connection_pool.ipp | 20 +++++++++++++++---- include/boost/requests/impl/stream.ipp | 2 ++ 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/include/boost/requests/connection.hpp b/include/boost/requests/connection.hpp index 7c541c7..76bee36 100644 --- a/include/boost/requests/connection.hpp +++ b/include/boost/requests/connection.hpp @@ -63,9 +63,6 @@ struct connection /// The endpoint of the lowest lowest layer. using endpoint_type = typename protocol_type::endpoint; - - explicit connection(std::shared_ptr impl) : impl_(std::move(impl)) {} - connection() = default; connection(const connection & lhs) = default; connection & operator=(const connection & lhs) = default; @@ -227,9 +224,22 @@ struct connection void use_ssl(bool use_ssl = true) {impl_->use_ssl(use_ssl);} operator bool() const {return impl_ != nullptr;} + + struct connection_pool * pool() {return borrowed_from_; } + + BOOST_REQUESTS_DECL void return_to_pool(); + BOOST_REQUESTS_DECL void remove_from_pool(); + private: + + + explicit connection(std::shared_ptr impl, + struct connection_pool * borrowed_from) : impl_(std::move(impl)), borrowed_from_(borrowed_from) {} + std::shared_ptr impl_; + struct connection_pool * borrowed_from_ = nullptr; + friend struct connection_pool; friend struct stream; }; diff --git a/include/boost/requests/connection_pool.hpp b/include/boost/requests/connection_pool.hpp index 6f6fad4..636aa5f 100644 --- a/include/boost/requests/connection_pool.hpp +++ b/include/boost/requests/connection_pool.hpp @@ -233,6 +233,9 @@ struct connection_pool void return_connection_(std::shared_ptr conn) { + if (!conn->is_open()) + return drop_connection_(conn); + std::lock_guard lock{mtx_}; free_conns_.push_back(std::move(conn)); cv_.notify_all(); @@ -252,7 +255,7 @@ struct connection_pool cv_.notify_all(); } } - + friend struct connection; friend struct stream; }; diff --git a/include/boost/requests/impl/connection_pool.ipp b/include/boost/requests/impl/connection_pool.ipp index 6668c27..44316cf 100644 --- a/include/boost/requests/impl/connection_pool.ipp +++ b/include/boost/requests/impl/connection_pool.ipp @@ -154,7 +154,7 @@ auto connection_pool::get_connection(error_code & ec) -> connection { auto pp = std::move(free_conns_.front()); free_conns_.erase(free_conns_.begin()); - return connection(pp); + return connection(std::move(pp), this); } // check if we can make more connections. -> open another connection. // the race here is that we might open one too many @@ -182,7 +182,7 @@ auto connection_pool::get_connection(error_code & ec) -> connection if (ec) return connection(); - return connection(nconn); + return connection(std::move(nconn), this); } cv_.wait(lock); } @@ -205,7 +205,7 @@ auto connection_pool::async_get_connection_op::resume( { auto pp = std::move(this_->free_conns_.front()); this_->free_conns_.erase(this_->free_conns_.begin()); - return connection(pp); + return connection(std::move(pp), this_); } // check if we can make more connections. -> open another connection. // the race here is that we might open one too many @@ -233,7 +233,7 @@ auto connection_pool::async_get_connection_op::resume( if (ec) return connection(); - return connection(nconn); + return connection(std::move(nconn), this_); } this_->cv_.async_wait(lock, std::move(self)); } @@ -275,6 +275,18 @@ connection_pool::connection_pool(connection_pool && lhs) } +void connection::return_to_pool() +{ + if (borrowed_from_) + borrowed_from_->return_connection_(std::move(impl_)); +} + +void connection::remove_from_pool() +{ + if (borrowed_from_) + borrowed_from_->drop_connection_(impl_); +} + } } diff --git a/include/boost/requests/impl/stream.ipp b/include/boost/requests/impl/stream.ipp index 9f82043..cd40ff7 100644 --- a/include/boost/requests/impl/stream.ipp +++ b/include/boost/requests/impl/stream.ipp @@ -39,6 +39,8 @@ void stream::dump(system::error_code & ec) boost::system::error_code ec_; impl_->do_close_(ec_); } + /*else + impl_->do_return_();*/ } From 655d5aa25db1d2ccdbb57f0a5ce9e602db876fa6 Mon Sep 17 00:00:00 2001 From: Fernando Pelliccioni Date: Thu, 31 Aug 2023 17:01:56 +0200 Subject: [PATCH 61/76] encoded_host_name() returns an empty string if the host type is not host_type::name --- include/boost/requests/impl/connection_pool.ipp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/requests/impl/connection_pool.ipp b/include/boost/requests/impl/connection_pool.ipp index 44316cf..64c6b68 100644 --- a/include/boost/requests/impl/connection_pool.ipp +++ b/include/boost/requests/impl/connection_pool.ipp @@ -40,7 +40,7 @@ void connection_pool::lookup(urls::url_view sv, system::error_code & ec) asio::ip::tcp::resolver resolver{get_executor()}; const auto service = sv.has_port() ? sv.port() : scheme; auto eps = resolver.resolve( - std::string(sv.encoded_host_name().data(), sv.encoded_host_name().size()), + std::string(sv.encoded_host_address().data(), sv.encoded_host_address().size()), std::string(service.data(), service.size()), ec); if (!ec && eps.empty()) @@ -105,7 +105,7 @@ void connection_pool::async_lookup_op::resume(requests::detail::faux_token_tasync_resolve( - std::string(sv.encoded_host_name().data(), sv.encoded_host_name().size()), + std::string(sv.encoded_host_address().data(), sv.encoded_host_address().size()), std::string(service.data(), service.size()), std::move(self)); if (!ec && eps.empty()) From 7e2a8698448466631f8ef1d3e0654a7db161bfb0 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 5 Sep 2023 23:13:04 +0800 Subject: [PATCH 62/76] added pool return functions to connection_impl. --- include/boost/requests/connection.hpp | 22 ++++++++------- include/boost/requests/connection_pool.hpp | 15 ++++++---- .../boost/requests/detail/connection_impl.hpp | 16 +++++++++-- .../detail/impl/condition_variable.ipp | 6 ++-- .../requests/detail/impl/connection_impl.ipp | 14 ++++++++++ .../boost/requests/impl/connection_pool.hpp | 1 + .../boost/requests/impl/connection_pool.ipp | 28 +++++++++---------- include/boost/requests/impl/stream.ipp | 8 ++++++ include/boost/requests/stream.hpp | 5 ++-- 9 files changed, 77 insertions(+), 38 deletions(-) diff --git a/include/boost/requests/connection.hpp b/include/boost/requests/connection.hpp index 76bee36..97055a0 100644 --- a/include/boost/requests/connection.hpp +++ b/include/boost/requests/connection.hpp @@ -225,20 +225,22 @@ struct connection operator bool() const {return impl_ != nullptr;} - struct connection_pool * pool() {return borrowed_from_; } - - BOOST_REQUESTS_DECL void return_to_pool(); - BOOST_REQUESTS_DECL void remove_from_pool(); - + struct connection_pool * pool() {return impl_ ? impl_->pool() : nullptr; } + ~connection() + { + if (impl_.use_count() == 2u && impl_->pool() != nullptr) + { + if (impl_->is_open()) + impl_->return_to_pool(); + else + impl_->remove_from_pool(); + } + } private: - - - explicit connection(std::shared_ptr impl, - struct connection_pool * borrowed_from) : impl_(std::move(impl)), borrowed_from_(borrowed_from) {} + explicit connection(std::shared_ptr impl) : impl_(std::move(impl)) {} std::shared_ptr impl_; - struct connection_pool * borrowed_from_ = nullptr; friend struct connection_pool; friend struct stream; }; diff --git a/include/boost/requests/connection_pool.hpp b/include/boost/requests/connection_pool.hpp index 636aa5f..10793c1 100644 --- a/include/boost/requests/connection_pool.hpp +++ b/include/boost/requests/connection_pool.hpp @@ -15,6 +15,8 @@ namespace requests { namespace detail { +struct connection_impl; + struct endpoint_hash { std::size_t operator()(const asio::generic::stream_protocol::endpoint & be) const @@ -91,8 +93,10 @@ struct connection_pool : context_(ctx), cv_(std::forward(exec)), limit_(limit) {} /// Move constructor + BOOST_REQUESTS_DECL connection_pool(connection_pool && lhs) ; - + BOOST_REQUESTS_DECL + ~connection_pool(); void lookup(urls::url_view av) { boost::system::error_code ec; @@ -223,7 +227,7 @@ struct connection_pool std::shared_ptr, detail::endpoint_hash> conns_; - std::vector> free_conns_; + std::vector free_conns_; struct async_lookup_op; struct async_get_connection_op; @@ -231,7 +235,7 @@ struct connection_pool struct async_ropen_op_body_base; struct async_ropen_op; - void return_connection_(std::shared_ptr conn) + void return_connection_(detail::connection_impl * conn) { if (!conn->is_open()) return drop_connection_(conn); @@ -241,13 +245,13 @@ struct connection_pool cv_.notify_all(); } - void drop_connection_(const std::shared_ptr & conn) + void drop_connection_(const detail::connection_impl * conn) { std::lock_guard lock{mtx_}; auto itr = std::find_if(conns_.begin(), conns_.end(), [&](const std::pair> & e) { - return e.second == conn; + return e.second.get() == conn; }); if (itr != conns_.end()) { @@ -257,6 +261,7 @@ struct connection_pool } friend struct connection; friend struct stream; + friend struct detail::connection_impl; }; template diff --git a/include/boost/requests/detail/connection_impl.hpp b/include/boost/requests/detail/connection_impl.hpp index 004de9f..867a8f0 100644 --- a/include/boost/requests/detail/connection_impl.hpp +++ b/include/boost/requests/detail/connection_impl.hpp @@ -25,6 +25,7 @@ namespace boost { namespace requests { struct stream; +struct connection_pool; namespace detail { @@ -54,8 +55,10 @@ struct connection_impl : std::enable_shared_from_this connection_impl(connection_impl && lhs) = delete; connection_impl & operator=(connection_impl && lhs) = delete; template - explicit connection_impl(ExecutorOrContext && exec_or_ctx, asio::ssl::context & ctx) - : next_layer_(std::forward(exec_or_ctx), ctx), use_ssl_{true} {} + explicit connection_impl(ExecutorOrContext && exec_or_ctx, asio::ssl::context & ctx, + connection_pool * borrowed_from = nullptr) + : next_layer_(std::forward(exec_or_ctx), ctx), use_ssl_{true}, + borrowed_from_{borrowed_from} {} template explicit connection_impl(ExecutionContext &context, @@ -182,6 +185,11 @@ struct connection_impl : std::enable_shared_from_this CompletionToken && completion_token); bool uses_ssl() const {return use_ssl_;} void use_ssl(bool use_ssl = true) {use_ssl_ = use_ssl;} + + BOOST_REQUESTS_DECL void return_to_pool(); + BOOST_REQUESTS_DECL void remove_from_pool(); + + struct connection_pool * pool() const {return borrowed_from_; } private: next_layer_type next_layer_; @@ -193,6 +201,8 @@ struct connection_impl : std::enable_shared_from_this beast::flat_buffer buffer_; endpoint_type endpoint_; + connection_pool * borrowed_from_{nullptr}; + struct async_close_op; struct async_connect_op; @@ -208,7 +218,9 @@ struct connection_impl : std::enable_shared_from_this BOOST_REQUESTS_DECL void do_async_close_(detail::faux_token_t); BOOST_REQUESTS_DECL void do_close_(system::error_code & ec); + friend struct boost::requests::stream; + friend struct requests::connection_pool; }; diff --git a/include/boost/requests/detail/impl/condition_variable.ipp b/include/boost/requests/detail/impl/condition_variable.ipp index 9411dc6..920ca5a 100644 --- a/include/boost/requests/detail/impl/condition_variable.ipp +++ b/include/boost/requests/detail/impl/condition_variable.ipp @@ -23,8 +23,8 @@ void condition_variable::wait(std::unique_lock & lock, system::error cv_.wait(lock); if (indicator.expired()) ec = asio::error::operation_aborted; - } + void condition_variable::async_wait(std::unique_lock & lock, faux_token_t tk) { @@ -42,15 +42,13 @@ void condition_variable::async_wait(std::unique_lock & lock, (std::move(tk)); } - - condition_variable::~condition_variable() { shutdown_indicator_.reset(); timer_.cancel(); cv_.notify_all(); - } + } } } diff --git a/include/boost/requests/detail/impl/connection_impl.ipp b/include/boost/requests/detail/impl/connection_impl.ipp index fdaa152..b21bffe 100644 --- a/include/boost/requests/detail/impl/connection_impl.ipp +++ b/include/boost/requests/detail/impl/connection_impl.ipp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -575,6 +576,19 @@ void connection_impl::do_close_(system::error_code & ec) next_layer_.next_layer().close(ec); } +void connection_impl::return_to_pool() +{ + if (borrowed_from_) + borrowed_from_->return_connection_(this); +} + +void connection_impl::remove_from_pool() +{ + if (borrowed_from_) + borrowed_from_->drop_connection_(this); +} + + } } } diff --git a/include/boost/requests/impl/connection_pool.hpp b/include/boost/requests/impl/connection_pool.hpp index e6eb44a..fea04c5 100644 --- a/include/boost/requests/impl/connection_pool.hpp +++ b/include/boost/requests/impl/connection_pool.hpp @@ -132,6 +132,7 @@ struct connection_pool::async_ropen_op : asio::coroutine auto resume(requests::detail::faux_token_t self, boost::system::error_code & ec, variant2::variant res = variant2::monostate()) -> stream; + }; diff --git a/include/boost/requests/impl/connection_pool.ipp b/include/boost/requests/impl/connection_pool.ipp index 64c6b68..662ec2e 100644 --- a/include/boost/requests/impl/connection_pool.ipp +++ b/include/boost/requests/impl/connection_pool.ipp @@ -154,7 +154,7 @@ auto connection_pool::get_connection(error_code & ec) -> connection { auto pp = std::move(free_conns_.front()); free_conns_.erase(free_conns_.begin()); - return connection(std::move(pp), this); + return connection(pp->shared_from_this()); } // check if we can make more connections. -> open another connection. // the race here is that we might open one too many @@ -170,7 +170,7 @@ auto connection_pool::get_connection(error_code & ec) -> connection [this](const endpoint_type &a, const endpoint_type &b) { return conns_.count(a) < conns_.count(b); }); const auto ep = endpoints_.front(); - auto nconn = conns_.emplace(ep, std::make_shared(get_executor(), context_))->second; + auto nconn = conns_.emplace(ep, std::make_shared(get_executor(), context_, this))->second; // no one else will grab it from there, bc it's not in free_conns_ lock.unlock(); nconn->use_ssl(use_ssl_); @@ -182,7 +182,7 @@ auto connection_pool::get_connection(error_code & ec) -> connection if (ec) return connection(); - return connection(std::move(nconn), this); + return connection(std::move(nconn)); } cv_.wait(lock); } @@ -205,7 +205,7 @@ auto connection_pool::async_get_connection_op::resume( { auto pp = std::move(this_->free_conns_.front()); this_->free_conns_.erase(this_->free_conns_.begin()); - return connection(std::move(pp), this_); + return connection(pp->shared_from_this()); } // check if we can make more connections. -> open another connection. // the race here is that we might open one too many @@ -221,7 +221,7 @@ auto connection_pool::async_get_connection_op::resume( [this](const endpoint_type &a, const endpoint_type &b) { return this_->conns_.count(a) < this_->conns_.count(b); }); ep = this_->endpoints_.front(); - nconn = this_->conns_.emplace(ep, std::make_shared(get_executor(), this_->context_))->second; + nconn = this_->conns_.emplace(ep, std::make_shared(get_executor(), this_->context_, this_))->second; // no one else will grab it from there, bc it's not in free_conns_ lock.unlock(); nconn->use_ssl(this_->use_ssl_); @@ -233,7 +233,7 @@ auto connection_pool::async_get_connection_op::resume( if (ec) return connection(); - return connection(std::move(nconn), this_); + return connection(std::move(nconn)); } this_->cv_.async_wait(lock, std::move(self)); } @@ -264,7 +264,8 @@ stream connection_pool::async_ropen_op::resume( connection_pool::connection_pool(connection_pool && lhs) : use_ssl_(lhs.use_ssl_), context_(lhs.context_), cv_(lhs.cv_.get_executor()), host_(std::move(lhs.host_)), - endpoints_(std::move(lhs.endpoints_)), limit_(lhs.limit_) + endpoints_(std::move(lhs.endpoints_)), limit_(lhs.limit_), + conns_(std::move(lhs.conns_)), free_conns_(std::move(lhs.free_conns_)) { BOOST_ASSERT(std::count_if( conns_.begin(), conns_.end(), @@ -273,19 +274,16 @@ connection_pool::connection_pool(connection_pool && lhs) return !p.second.unique(); }) == 0u); + for (auto & p : conns_) + p.second->borrowed_from_ = this; } -void connection::return_to_pool() +connection_pool::~connection_pool() { - if (borrowed_from_) - borrowed_from_->return_connection_(std::move(impl_)); + for (auto & p : conns_) + p.second->borrowed_from_ = nullptr; } -void connection::remove_from_pool() -{ - if (borrowed_from_) - borrowed_from_->drop_connection_(impl_); -} } } diff --git a/include/boost/requests/impl/stream.ipp b/include/boost/requests/impl/stream.ipp index cd40ff7..3d994f6 100644 --- a/include/boost/requests/impl/stream.ipp +++ b/include/boost/requests/impl/stream.ipp @@ -49,6 +49,14 @@ stream::~stream() if (parser_ && parser_->is_header_done() && !parser_->is_done() && parser_->get().body().more && impl_ && impl_->is_open()) dump(); + + if (impl_.use_count() == 2u && impl_->pool() != nullptr) + { + if (impl_->is_open()) + impl_->return_to_pool(); + else + impl_->remove_from_pool(); + } } diff --git a/include/boost/requests/stream.hpp b/include/boost/requests/stream.hpp index 080e4a1..c1d9fc1 100644 --- a/include/boost/requests/stream.hpp +++ b/include/boost/requests/stream.hpp @@ -166,8 +166,8 @@ struct stream void prepend_history(history_type && pre_history) { history_.insert(history_.begin(), - std::make_move_iterator(history_.begin()), - std::make_move_iterator(history_.end())); + std::make_move_iterator(pre_history.begin()), + std::make_move_iterator(pre_history.end())); } private: executor_type executor_; @@ -184,6 +184,7 @@ struct stream struct async_read_some_op; friend struct detail::connection_impl; + friend struct connection; }; template From bdfc27adfdf4788def1f811132156351f0c2ec7e Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 6 Sep 2023 03:24:05 +0800 Subject: [PATCH 63/76] made borrowed_from atomic. --- include/boost/requests/detail/connection_impl.hpp | 3 ++- include/boost/requests/detail/impl/connection_impl.ipp | 4 ++-- include/boost/requests/impl/connection_pool.ipp | 4 +++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/boost/requests/detail/connection_impl.hpp b/include/boost/requests/detail/connection_impl.hpp index 867a8f0..eee8d5a 100644 --- a/include/boost/requests/detail/connection_impl.hpp +++ b/include/boost/requests/detail/connection_impl.hpp @@ -201,7 +201,8 @@ struct connection_impl : std::enable_shared_from_this beast::flat_buffer buffer_; endpoint_type endpoint_; - connection_pool * borrowed_from_{nullptr}; + // atomic so moving the pool can be thread-safe + std::atomic borrowed_from_{nullptr}; struct async_close_op; struct async_connect_op; diff --git a/include/boost/requests/detail/impl/connection_impl.ipp b/include/boost/requests/detail/impl/connection_impl.ipp index b21bffe..fe7b7ed 100644 --- a/include/boost/requests/detail/impl/connection_impl.ipp +++ b/include/boost/requests/detail/impl/connection_impl.ipp @@ -579,13 +579,13 @@ void connection_impl::do_close_(system::error_code & ec) void connection_impl::return_to_pool() { if (borrowed_from_) - borrowed_from_->return_connection_(this); + borrowed_from_.load()->return_connection_(this); } void connection_impl::remove_from_pool() { if (borrowed_from_) - borrowed_from_->drop_connection_(this); + borrowed_from_.load()->drop_connection_(this); } diff --git a/include/boost/requests/impl/connection_pool.ipp b/include/boost/requests/impl/connection_pool.ipp index 662ec2e..5c164c6 100644 --- a/include/boost/requests/impl/connection_pool.ipp +++ b/include/boost/requests/impl/connection_pool.ipp @@ -265,7 +265,7 @@ stream connection_pool::async_ropen_op::resume( connection_pool::connection_pool(connection_pool && lhs) : use_ssl_(lhs.use_ssl_), context_(lhs.context_), cv_(lhs.cv_.get_executor()), host_(std::move(lhs.host_)), endpoints_(std::move(lhs.endpoints_)), limit_(lhs.limit_), - conns_(std::move(lhs.conns_)), free_conns_(std::move(lhs.free_conns_)) + conns_(std::move(lhs.conns_)) { BOOST_ASSERT(std::count_if( conns_.begin(), conns_.end(), @@ -276,6 +276,8 @@ connection_pool::connection_pool(connection_pool && lhs) for (auto & p : conns_) p.second->borrowed_from_ = this; + + free_conns_ = std::move(lhs.free_conns_); } connection_pool::~connection_pool() From 7a1f9724d3c62173a18d80e63e73d85ee6147c80 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 6 Sep 2023 04:03:44 +0800 Subject: [PATCH 64/76] removed the ipp nonsense. --- CMakeLists.txt | 28 +++++++++++- build/Jamfile | 28 +++++++++++- include/boost/requests/cookie_jar.hpp | 5 --- .../requests/detail/condition_variable.hpp | 5 --- include/boost/requests/detail/config.hpp | 14 +++--- .../requests/detail/impl/connection_impl.hpp | 4 -- include/boost/requests/detail/mutex.hpp | 3 -- include/boost/requests/error.hpp | 4 -- include/boost/requests/fields/keep_alive.hpp | 6 --- include/boost/requests/fields/link.hpp | 6 --- include/boost/requests/fields/location.hpp | 4 -- include/boost/requests/fields/set_cookie.hpp | 4 -- .../boost/requests/impl/connection_pool.hpp | 6 --- .../boost/requests/impl/request_settings.ipp | 25 ----------- include/boost/requests/impl/response.ipp | 25 ----------- include/boost/requests/impl/session.hpp | 5 --- include/boost/requests/impl/source.hpp | 8 ++-- include/boost/requests/impl/stream.hpp | 9 ++-- include/boost/requests/mime_types.hpp | 4 -- include/boost/requests/public_suffix.hpp | 4 -- include/boost/requests/redirect.hpp | 4 -- include/boost/requests/request_options.hpp | 3 -- include/boost/requests/response.hpp | 4 -- include/boost/requests/rfc/dates.hpp | 4 -- include/boost/requests/rfc/link.hpp | 4 -- include/boost/requests/rfc/quoted_string.hpp | 6 --- include/boost/requests/sources/buffer.hpp | 4 -- include/boost/requests/sources/empty.hpp | 4 -- include/boost/requests/sources/file.hpp | 5 --- include/boost/requests/sources/form.hpp | 4 -- include/boost/requests/sources/json.hpp | 5 --- include/boost/requests/src.hpp | 45 ------------------- include/boost/requests/stream.hpp | 14 +++--- .../connection_pool.cpp | 5 --- .../impl/cookie_jar.ipp => src/cookie_jar.cpp | 5 --- .../detail/condition_variable.cpp | 6 +-- .../detail/connection_impl.cpp | 7 +-- .../impl/mutex.ipp => src/detail/mutex.cpp | 7 +-- .../requests/impl/error.ipp => src/error.cpp | 9 +--- .../fields/keep_alive.cpp | 5 --- .../impl/link.ipp => src/fields/link.cpp | 13 +++--- .../location.ipp => src/fields/location.cpp | 5 --- .../fields/set_cookie.cpp | 3 -- .../impl/mime_types.ipp => src/mime_types.cpp | 5 --- .../public_suffix.cpp | 5 +-- .../requests/impl => src}/public_suffix.inc | 0 .../impl/redirect.ipp => src/redirect.cpp | 5 --- .../request_options.cpp | 5 --- .../rfc/impl/dates.ipp => src/rfc/dates.cpp | 3 -- .../rfc/impl/link.ipp => src/rfc/link.cpp | 4 -- .../rfc/quoted_string.cpp | 4 -- .../impl/session.ipp => src/session.cpp | 7 +-- .../impl/buffer.ipp => src/sources/buffer.cpp | 5 --- .../impl/empty.ipp => src/sources/empty.cpp | 5 --- .../impl/file.ipp => src/sources/file.cpp | 4 -- .../impl/form.ipp => src/sources/form.cpp | 9 +--- .../impl/json.ipp => src/sources/json.cpp | 4 -- src/src.cpp | 8 ---- .../impl/stream.ipp => src/stream.cpp | 11 +++-- test/cookie_grammar.cpp | 2 +- test/cookie_jar.cpp | 4 +- test/keep_alive.cpp | 2 +- 62 files changed, 103 insertions(+), 357 deletions(-) delete mode 100644 include/boost/requests/impl/request_settings.ipp delete mode 100644 include/boost/requests/impl/response.ipp delete mode 100644 include/boost/requests/src.hpp rename include/boost/requests/impl/connection_pool.ipp => src/connection_pool.cpp (98%) rename include/boost/requests/impl/cookie_jar.ipp => src/cookie_jar.cpp (97%) rename include/boost/requests/detail/impl/condition_variable.ipp => src/detail/condition_variable.cpp (87%) rename include/boost/requests/detail/impl/connection_impl.ipp => src/detail/connection_impl.cpp (99%) rename include/boost/requests/detail/impl/mutex.ipp => src/detail/mutex.cpp (96%) rename include/boost/requests/impl/error.ipp => src/error.cpp (95%) rename include/boost/requests/fields/impl/keep_alive.ipp => src/fields/keep_alive.cpp (95%) rename include/boost/requests/fields/impl/link.ipp => src/fields/link.cpp (94%) rename include/boost/requests/fields/impl/location.ipp => src/fields/location.cpp (90%) rename include/boost/requests/fields/impl/set_cookie.ipp => src/fields/set_cookie.cpp (96%) rename include/boost/requests/impl/mime_types.ipp => src/mime_types.cpp (97%) rename include/boost/requests/impl/public_suffix.ipp => src/public_suffix.cpp (93%) rename {include/boost/requests/impl => src}/public_suffix.inc (100%) rename include/boost/requests/impl/redirect.ipp => src/redirect.cpp (97%) rename include/boost/requests/impl/request_options.ipp => src/request_options.cpp (75%) rename include/boost/requests/rfc/impl/dates.ipp => src/rfc/dates.cpp (98%) rename include/boost/requests/rfc/impl/link.ipp => src/rfc/link.cpp (95%) rename include/boost/requests/rfc/impl/quoted_string.ipp => src/rfc/quoted_string.cpp (90%) rename include/boost/requests/impl/session.ipp => src/session.cpp (98%) rename include/boost/requests/sources/impl/buffer.ipp => src/sources/buffer.cpp (79%) rename include/boost/requests/sources/impl/empty.ipp => src/sources/empty.cpp (84%) rename include/boost/requests/sources/impl/file.ipp => src/sources/file.cpp (84%) rename include/boost/requests/sources/impl/form.ipp => src/sources/form.cpp (95%) rename include/boost/requests/sources/impl/json.ipp => src/sources/json.cpp (92%) delete mode 100644 src/src.cpp rename include/boost/requests/impl/stream.ipp => src/stream.cpp (94%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 12fa1f4..ae925b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,9 +71,34 @@ if (NOT MSVC) link_libraries(${OPENSSL_LIBRARIES}) endif() -add_library(boost_requests src/src.cpp) +add_library(boost_requests + src/detail/condition_variable.cpp + src/detail/connection_impl.cpp + src/detail/mutex.cpp + src/fields/keep_alive.cpp + src/fields/link.cpp + src/fields/location.cpp + src/fields/set_cookie.cpp + src/rfc/dates.cpp + src/rfc/link.cpp + src/rfc/quoted_string.cpp + src/sources/buffer.cpp + src/sources/empty.cpp + src/sources/file.cpp + src/sources/form.cpp + src/sources/json.cpp + src/connection_pool.cpp + src/cookie_jar.cpp + src/error.cpp + src/mime_types.cpp + src/public_suffix.cpp + src/redirect.cpp + src/request_options.cpp + src/session.cpp + src/stream.cpp) target_include_directories(boost_requests PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") + if (BOOST_ASYNC_BUILD_INLINE) target_link_libraries( boost_requests @@ -94,6 +119,7 @@ target_link_libraries( Threads::Threads) +target_compile_definitions(boost_requests PRIVATE BOOST_REQUESTS_SOURCE=1) target_compile_definitions(boost_requests PUBLIC BOOST_REQUESTS_SEPARATE_COMPILATION=1 BOOST_FILESYSTEM_NO_DEPRECATED=1) add_library(Boost::requests ALIAS boost_requests) diff --git a/build/Jamfile b/build/Jamfile index ae39c03..5443160 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -6,6 +6,7 @@ project boost/requests static:BOOST_REQUESTS_STATIC_LINK=1 BOOST_REQUESTS_SOURCE=1 BOOST_FILESYSTEM_NO_DEPRECATED=1 + BOOST_JSON_SOURCE : usage-requirements shared:BOOST_REQUESTS_DYN_LINK=1 static:BOOST_REQUESTS_STATIC_LINK=1 @@ -13,6 +14,31 @@ project boost/requests ; -alias requests_sources : src.cpp ; +alias requests_sources : + detail/condition_variable.cpp + detail/connection_impl.cpp + detail/mutex.cpp + fields/keep_alive.cpp + fields/link.cpp + fields/location.cpp + fields/set_cookie.cpp + rfc/dates.cpp + rfc/link.cpp + rfc/quoted_string.cpp + sources/buffer.cpp + sources/empty.cpp + sources/file.cpp + sources/form.cpp + sources/json.cpp + connection_pool.cpp + cookie_jar.cpp + error.cpp + mime_types.cpp + public_suffix.cpp + redirect.cpp + request_options.cpp + session.cpp + stream.cpp + ; lib boost_requests : requests_sources /openssl//ssl ; boost-install boost_requests ; \ No newline at end of file diff --git a/include/boost/requests/cookie_jar.hpp b/include/boost/requests/cookie_jar.hpp index b4a6a1c..6e0dfec 100644 --- a/include/boost/requests/cookie_jar.hpp +++ b/include/boost/requests/cookie_jar.hpp @@ -91,9 +91,4 @@ struct cookie_jar final } } -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - - #endif //BOOST_REQUESTS_COOKIES_JAR_HPP diff --git a/include/boost/requests/detail/condition_variable.hpp b/include/boost/requests/detail/condition_variable.hpp index 512d729..6a15444 100644 --- a/include/boost/requests/detail/condition_variable.hpp +++ b/include/boost/requests/detail/condition_variable.hpp @@ -75,9 +75,4 @@ struct condition_variable } } -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - - #endif // BOOST_REQUESTS_DETAIL_CONDITION_VARIABLE_HPP diff --git a/include/boost/requests/detail/config.hpp b/include/boost/requests/detail/config.hpp index f852a32..89a1a58 100644 --- a/include/boost/requests/detail/config.hpp +++ b/include/boost/requests/detail/config.hpp @@ -10,16 +10,14 @@ #include -#ifndef BOOST_REQUESTS_HEADER_ONLY -# ifndef BOOST_REQUESTS_SEPARATE_COMPILATION -# define BOOST_REQUESTS_HEADER_ONLY 1 +# if (defined(BOOST_REQUESTS_DYN_LINK) || defined(BOOST_ALL_DYN_LINK)) && !defined(BOOST_JSON_STATIC_LINK) +# if defined(BOOST_REQUESTS_SOURCE) +# define BOOST_REQUESTS_DECL BOOST_SYMBOL_EXPORT +# else +# define BOOST_REQUESTS_DECL BOOST_SYMBOL_IMPORT # endif -#endif - -#if defined(BOOST_REQUESTS_HEADER_ONLY) -# define BOOST_REQUESTS_DECL inline #else -# define BOOST_REQUESTS_DECL BOOST_SYMBOL_EXPORT +# define BOOST_REQUESTS_DECL #endif #define BOOST_REQUESTS_RETURN_EC(ev) \ diff --git a/include/boost/requests/detail/impl/connection_impl.hpp b/include/boost/requests/detail/impl/connection_impl.hpp index f00932a..2c5060e 100644 --- a/include/boost/requests/detail/impl/connection_impl.hpp +++ b/include/boost/requests/detail/impl/connection_impl.hpp @@ -278,9 +278,5 @@ connection_impl::async_ropen(beast::http::verb method, } -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - #endif // BOOST_REQUESTS_IMPL_CONNECTION_HPP diff --git a/include/boost/requests/detail/mutex.hpp b/include/boost/requests/detail/mutex.hpp index 978fab6..cbfc57f 100644 --- a/include/boost/requests/detail/mutex.hpp +++ b/include/boost/requests/detail/mutex.hpp @@ -94,8 +94,5 @@ struct mutex } } -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif #endif // BOOST_REQUESTS_MUTEX_HPP diff --git a/include/boost/requests/error.hpp b/include/boost/requests/error.hpp index 46f62db..6d503bc 100644 --- a/include/boost/requests/error.hpp +++ b/include/boost/requests/error.hpp @@ -70,8 +70,4 @@ struct is_error_code_enum<::boost::requests::error> } // system } // boost -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - #endif // BOOST_REQUESTS_ERROR_HPP diff --git a/include/boost/requests/fields/keep_alive.hpp b/include/boost/requests/fields/keep_alive.hpp index 3e209c7..f7f2fd1 100644 --- a/include/boost/requests/fields/keep_alive.hpp +++ b/include/boost/requests/fields/keep_alive.hpp @@ -31,10 +31,4 @@ BOOST_REQUESTS_DECL system::result parse_keep_alive_field( } } - -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - - #endif //BOOST_REQUESTS_FIELDS_KEEP_ALIVE_HPP diff --git a/include/boost/requests/fields/link.hpp b/include/boost/requests/fields/link.hpp index 1528b80..7590572 100644 --- a/include/boost/requests/fields/link.hpp +++ b/include/boost/requests/fields/link.hpp @@ -79,10 +79,4 @@ BOOST_REQUESTS_DECL system::result> parse_link_field( } } - -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - - #endif // BOOST_REQUESTS_FIELDS_LINK_HPP diff --git a/include/boost/requests/fields/location.hpp b/include/boost/requests/fields/location.hpp index e35264f..0f117f6 100644 --- a/include/boost/requests/fields/location.hpp +++ b/include/boost/requests/fields/location.hpp @@ -28,8 +28,4 @@ BOOST_REQUESTS_DECL system::result interpret_location( } } -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - #endif // BOOST_REQUESTS_FIELDS_LOCATION_HPP diff --git a/include/boost/requests/fields/set_cookie.hpp b/include/boost/requests/fields/set_cookie.hpp index 77edc90..3072ba2 100644 --- a/include/boost/requests/fields/set_cookie.hpp +++ b/include/boost/requests/fields/set_cookie.hpp @@ -54,8 +54,4 @@ system::result parse_set_cookie_field(core::string_view value); } } -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - #endif //BOOST_REQUESTS_COOKIES_SET_COOKIE_HPP diff --git a/include/boost/requests/impl/connection_pool.hpp b/include/boost/requests/impl/connection_pool.hpp index fea04c5..3a6e822 100644 --- a/include/boost/requests/impl/connection_pool.hpp +++ b/include/boost/requests/impl/connection_pool.hpp @@ -203,10 +203,4 @@ connection_pool::async_ropen(beast::http::verb method, } } - -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - - #endif // BOOST_REQUESTS_IMPL_CONNECTION_POOL_HPP diff --git a/include/boost/requests/impl/request_settings.ipp b/include/boost/requests/impl/request_settings.ipp deleted file mode 100644 index 0d2e258..0000000 --- a/include/boost/requests/impl/request_settings.ipp +++ /dev/null @@ -1,25 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BOOST_REQUESTS_IMPL_REQUEST_IPP -#define BOOST_REQUESTS_IMPL_REQUEST_IPP - - -#if defined(BOOST_REQUESTS_SOURCE) - -#include - -namespace boost { -namespace requests { - -} -} - -#endif - - -#endif // BOOST_REQUESTS_IMPL_REQUEST_IPP diff --git a/include/boost/requests/impl/response.ipp b/include/boost/requests/impl/response.ipp deleted file mode 100644 index 017a48a..0000000 --- a/include/boost/requests/impl/response.ipp +++ /dev/null @@ -1,25 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BOOST_REQUESTS_IMPL_RESPONSE_IPP -#define BOOST_REQUESTS_IMPL_RESPONSE_IPP - - -#if defined(BOOST_REQUESTS_SOURCE) - -#include - -namespace boost { -namespace requests { - -} -} - -#endif - - -#endif // BOOST_REQUESTS_IMPL_RESPONSE_IPP diff --git a/include/boost/requests/impl/session.hpp b/include/boost/requests/impl/session.hpp index b584809..69ab03b 100644 --- a/include/boost/requests/impl/session.hpp +++ b/include/boost/requests/impl/session.hpp @@ -173,9 +173,4 @@ session::async_ropen(urls::url_view url, } } - -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - #endif // BOOST_REQUESTS_IMPL_SESSION_HPP diff --git a/include/boost/requests/impl/source.hpp b/include/boost/requests/impl/source.hpp index cee9d88..b8f9bd6 100644 --- a/include/boost/requests/impl/source.hpp +++ b/include/boost/requests/impl/source.hpp @@ -5,13 +5,15 @@ #ifndef BOOST_REQUESTS_IMPL_SOURCE_HPP #define BOOST_REQUESTS_IMPL_SOURCE_HPP -#include -#include -#include #include #include #include +#include +#include +#include +#include + namespace boost { namespace requests diff --git a/include/boost/requests/impl/stream.hpp b/include/boost/requests/impl/stream.hpp index 2d91f1b..b22646d 100644 --- a/include/boost/requests/impl/stream.hpp +++ b/include/boost/requests/impl/stream.hpp @@ -8,9 +8,11 @@ #ifndef BOOST_REQUESTS_IMPL_STREAM_HPP #define BOOST_REQUESTS_IMPL_STREAM_HPP +#include +#include + #include #include -#include namespace boost @@ -263,9 +265,4 @@ stream::async_dump(CompletionToken && token) } } - -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - #endif // BOOST_REQUESTS_IMPL_STREAM_HPP diff --git a/include/boost/requests/mime_types.hpp b/include/boost/requests/mime_types.hpp index 094517a..b7e63d8 100644 --- a/include/boost/requests/mime_types.hpp +++ b/include/boost/requests/mime_types.hpp @@ -27,8 +27,4 @@ BOOST_REQUESTS_DECL const mime_type_map & default_mime_type_map(); } -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - #endif // BOOST_REQUESTS_MIME_TYPES_HPP diff --git a/include/boost/requests/public_suffix.hpp b/include/boost/requests/public_suffix.hpp index ee6e11d..4eae093 100644 --- a/include/boost/requests/public_suffix.hpp +++ b/include/boost/requests/public_suffix.hpp @@ -31,8 +31,4 @@ BOOST_REQUESTS_DECL bool is_public_suffix(core::string_view value, } } -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - #endif //BOOST_REQUESTS_COOKIES_PUBLIC_SUFFIX_HPP diff --git a/include/boost/requests/redirect.hpp b/include/boost/requests/redirect.hpp index 0a6d230..cd8e1b5 100644 --- a/include/boost/requests/redirect.hpp +++ b/include/boost/requests/redirect.hpp @@ -57,8 +57,4 @@ BOOST_REQUESTS_DECL bool same_endpoint_on_host(const urls::url_view current, con } } -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - #endif //BOOST_REQUESTS_REDIRECT_HPP diff --git a/include/boost/requests/request_options.hpp b/include/boost/requests/request_options.hpp index 29747dc..b97be77 100644 --- a/include/boost/requests/request_options.hpp +++ b/include/boost/requests/request_options.hpp @@ -26,8 +26,5 @@ BOOST_REQUESTS_DECL request_options & default_options(); } } -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif #endif // BOOST_REQUESTS_REQUEST_OPTIONS_HPP diff --git a/include/boost/requests/response.hpp b/include/boost/requests/response.hpp index f3121b4..c1c42af 100644 --- a/include/boost/requests/response.hpp +++ b/include/boost/requests/response.hpp @@ -186,8 +186,4 @@ struct response : response_base } } -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - #endif // BOOST_REQUESTS_RESPONSE_HPP diff --git a/include/boost/requests/rfc/dates.hpp b/include/boost/requests/rfc/dates.hpp index ca40f3a..50e22a1 100644 --- a/include/boost/requests/rfc/dates.hpp +++ b/include/boost/requests/rfc/dates.hpp @@ -183,8 +183,4 @@ constexpr http_date_t http_date; } } -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - #endif //BOOST_REQUESTS_RFC_DATES_HPP diff --git a/include/boost/requests/rfc/link.hpp b/include/boost/requests/rfc/link.hpp index 3742a70..db447c5 100644 --- a/include/boost/requests/rfc/link.hpp +++ b/include/boost/requests/rfc/link.hpp @@ -49,8 +49,4 @@ constexpr link_value_t link_value{}; } } -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - #endif // BOOST_REQUESTS_RFC_LINK_HPP diff --git a/include/boost/requests/rfc/quoted_string.hpp b/include/boost/requests/rfc/quoted_string.hpp index 648195c..0d0a5d3 100644 --- a/include/boost/requests/rfc/quoted_string.hpp +++ b/include/boost/requests/rfc/quoted_string.hpp @@ -86,10 +86,4 @@ auto unquote_string( } } - -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - - #endif // BOOST_REQUESTS_RFC_QUOTED_STRING_HPP diff --git a/include/boost/requests/sources/buffer.hpp b/include/boost/requests/sources/buffer.hpp index 4a7535f..acca839 100644 --- a/include/boost/requests/sources/buffer.hpp +++ b/include/boost/requests/sources/buffer.hpp @@ -44,8 +44,4 @@ source_ptr tag_invoke(make_source_tag, asio::const_buffer cb, container::pmr::me } } -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - #endif //BOOST_REQUESTS_SOURCES_BUFFER_HPP diff --git a/include/boost/requests/sources/empty.hpp b/include/boost/requests/sources/empty.hpp index 61c61de..8676d44 100644 --- a/include/boost/requests/sources/empty.hpp +++ b/include/boost/requests/sources/empty.hpp @@ -47,8 +47,4 @@ BOOST_REQUESTS_DECL source_ptr tag_invoke(const make_source_tag&, const none_t & } } -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - #endif // BOOST_REQUESTS_SOURCES_EMPTY_HPP diff --git a/include/boost/requests/sources/file.hpp b/include/boost/requests/sources/file.hpp index bf2de76..dd73c8d 100644 --- a/include/boost/requests/sources/file.hpp +++ b/include/boost/requests/sources/file.hpp @@ -77,9 +77,4 @@ struct file_source : source } } - -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - #endif //BOOST_REQUESTS_SOURCES_FILE_HPP diff --git a/include/boost/requests/sources/form.hpp b/include/boost/requests/sources/form.hpp index 278d7f0..5f10c70 100644 --- a/include/boost/requests/sources/form.hpp +++ b/include/boost/requests/sources/form.hpp @@ -100,8 +100,4 @@ source_ptr tag_invoke(make_source_tag, multi_part_form mpf, container::pmr::memo } } -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - #endif //BOOST_REQUESTS_SOURCES_FILE_HPP diff --git a/include/boost/requests/sources/json.hpp b/include/boost/requests/sources/json.hpp index e6c738a..d7b144e 100644 --- a/include/boost/requests/sources/json.hpp +++ b/include/boost/requests/sources/json.hpp @@ -32,9 +32,4 @@ struct json_source : source } } - -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#include -#endif - #endif //BOOST_REQUESTS_SOURCES_JSON_HPP diff --git a/include/boost/requests/src.hpp b/include/boost/requests/src.hpp deleted file mode 100644 index cf756b0..0000000 --- a/include/boost/requests/src.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2021 Klemens D. Morgenstern -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#ifndef BOOST_REQUESTS_SRC_IPP -#define BOOST_REQUESTS_SRC_IPP - -#include - -#if defined(BOOST_REQUESTS_HEADER_ONLY) -#error "You can't include this in header only mode" -#endif - -#ifndef BOOST_REQUESTS_SOURCE -#define BOOST_REQUESTS_SOURCE -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#endif //BOOST_REQUESTS_SRC_IPP diff --git a/include/boost/requests/stream.hpp b/include/boost/requests/stream.hpp index c1d9fc1..67d771b 100644 --- a/include/boost/requests/stream.hpp +++ b/include/boost/requests/stream.hpp @@ -5,12 +5,9 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -// this is on purpose #ifndef BOOST_REQUESTS_STREAM_HPP - #define BOOST_REQUESTS_STREAM_HPP -#include #include #include #include @@ -19,11 +16,16 @@ #include #include #include +#include namespace boost { namespace requests { +namespace detail +{ +struct connection_impl; +} struct stream { @@ -50,10 +52,7 @@ struct stream /// Check if the underlying connection is open. - bool is_open() const - { - return impl_ && impl_->is_open() && !done(); - } + BOOST_REQUESTS_DECL bool is_open() const; /// Read some data from the request body. template @@ -220,7 +219,6 @@ struct stream::defaulted : stream } } -#include #include diff --git a/include/boost/requests/impl/connection_pool.ipp b/src/connection_pool.cpp similarity index 98% rename from include/boost/requests/impl/connection_pool.ipp rename to src/connection_pool.cpp index 5c164c6..c754d9a 100644 --- a/include/boost/requests/impl/connection_pool.ipp +++ b/src/connection_pool.cpp @@ -5,9 +5,6 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_IMPL_CONNECTION_POOL_IPP -#define BOOST_REQUESTS_IMPL_CONNECTION_POOL_IPP - #include namespace boost { @@ -290,5 +287,3 @@ connection_pool::~connection_pool() } } - -#endif // BOOST_REQUESTS_IMPL_CONNECTION_POOL_IPP diff --git a/include/boost/requests/impl/cookie_jar.ipp b/src/cookie_jar.cpp similarity index 97% rename from include/boost/requests/impl/cookie_jar.ipp rename to src/cookie_jar.cpp index 04f11d6..51f1b85 100644 --- a/include/boost/requests/impl/cookie_jar.ipp +++ b/src/cookie_jar.cpp @@ -5,10 +5,6 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_IMPL_COOKIE_JAR_IPP -#define BOOST_REQUESTS_IMPL_COOKIE_JAR_IPP - - #include namespace boost { @@ -155,4 +151,3 @@ void cookie_jar::drop_expired(const std::chrono::system_clock::time_point nw) } } -#endif // BOOST_REQUESTS_COOKIE_JAR_IPP diff --git a/include/boost/requests/detail/impl/condition_variable.ipp b/src/detail/condition_variable.cpp similarity index 87% rename from include/boost/requests/detail/impl/condition_variable.ipp rename to src/detail/condition_variable.cpp index 920ca5a..398a4f0 100644 --- a/include/boost/requests/detail/impl/condition_variable.ipp +++ b/src/detail/condition_variable.cpp @@ -5,10 +5,9 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_DETAIL_IMPL_CONDITION_VARIABLE_IPP -#define BOOST_REQUESTS_DETAIL_IMPL_CONDITION_VARIABLE_IPP - #include +#include + namespace boost { @@ -52,4 +51,3 @@ condition_variable::~condition_variable() } } } -#endif // BOOST_REQUESTS_DETAIL_IMPL_CONDITION_VARIABLE_IPP diff --git a/include/boost/requests/detail/impl/connection_impl.ipp b/src/detail/connection_impl.cpp similarity index 99% rename from include/boost/requests/detail/impl/connection_impl.ipp rename to src/detail/connection_impl.cpp index fe7b7ed..1485ef1 100644 --- a/include/boost/requests/detail/impl/connection_impl.ipp +++ b/src/detail/connection_impl.cpp @@ -5,12 +5,9 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_IMPL_CONNECTION_IPP -#define BOOST_REQUESTS_IMPL_CONNECTION_IPP - +#include #include #include -#include #include #include @@ -594,5 +591,3 @@ void connection_impl::remove_from_pool() } #include - -#endif // BOOST_REQUESTS_REQUESTS_CONNECTION_IPP diff --git a/include/boost/requests/detail/impl/mutex.ipp b/src/detail/mutex.cpp similarity index 96% rename from include/boost/requests/detail/impl/mutex.ipp rename to src/detail/mutex.cpp index ef05e91..d2c9280 100644 --- a/include/boost/requests/detail/impl/mutex.ipp +++ b/src/detail/mutex.cpp @@ -5,15 +5,12 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_DETAIL_IMPL_MUTEX_IPP -#define BOOST_REQUESTS_DETAIL_IMPL_MUTEX_IPP - #include #include #include #include -#include #include +#include #include namespace boost @@ -139,5 +136,3 @@ mutex::~mutex() } } } - -#endif // BOOST_REQUESTS_DETAIL_IMPL_MUTEX_IPP diff --git a/include/boost/requests/impl/error.ipp b/src/error.cpp similarity index 95% rename from include/boost/requests/impl/error.ipp rename to src/error.cpp index c3f4961..d423f67 100644 --- a/include/boost/requests/impl/error.ipp +++ b/src/error.cpp @@ -5,13 +5,10 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_IMPL_ERROR_IPP -#define BOOST_REQUESTS_IMPL_ERROR_IPP - -#include #include -#include #include +#include +#include namespace boost { namespace requests { @@ -111,5 +108,3 @@ make_error_code(error e) } // requests } // boost - -#endif // BOOST_REQUESTS_IMPL_ERROR_IPP diff --git a/include/boost/requests/fields/impl/keep_alive.ipp b/src/fields/keep_alive.cpp similarity index 95% rename from include/boost/requests/fields/impl/keep_alive.ipp rename to src/fields/keep_alive.cpp index 1cbd779..22fde32 100644 --- a/include/boost/requests/fields/impl/keep_alive.ipp +++ b/src/fields/keep_alive.cpp @@ -5,9 +5,6 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_IMPL_KEEP_ALIVE_IPP -#define BOOST_REQUESTS_IMPL_KEEP_ALIVE_IPP - #include #include @@ -89,5 +86,3 @@ system::result parse_keep_alive_field( } } - -#endif //BOOST_REQUESTS_IMPL_KEEP_ALIVE_IPP diff --git a/include/boost/requests/fields/impl/link.ipp b/src/fields/link.cpp similarity index 94% rename from include/boost/requests/fields/impl/link.ipp rename to src/fields/link.cpp index 3be636c..a0d6e88 100644 --- a/include/boost/requests/fields/impl/link.ipp +++ b/src/fields/link.cpp @@ -5,17 +5,19 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_FIELDS_IMPL_LINK_IPP -#define BOOST_REQUESTS_FIELDS_IMPL_LINK_IPP - -#include #include +#include #include #include #include -#include +#include +#include #include +#include +#include +#include #include +#include namespace boost { namespace requests { @@ -139,4 +141,3 @@ system::result link::anchor() const } } -#endif // BOOST_REQUESTS_FIELDS_IMPL_LINK_IPP diff --git a/include/boost/requests/fields/impl/location.ipp b/src/fields/location.cpp similarity index 90% rename from include/boost/requests/fields/impl/location.ipp rename to src/fields/location.cpp index c714565..e9a184b 100644 --- a/include/boost/requests/fields/impl/location.ipp +++ b/src/fields/location.cpp @@ -5,9 +5,6 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_FIELDS_IMPL_LOCATION_IPP -#define BOOST_REQUESTS_FIELDS_IMPL_LOCATION_IPP - #include namespace boost @@ -65,5 +62,3 @@ catch(system_error & se) } } - -#endif // BOOST_REQUESTS_FIELDS_IMPL_LOCATION_IPP diff --git a/include/boost/requests/fields/impl/set_cookie.ipp b/src/fields/set_cookie.cpp similarity index 96% rename from include/boost/requests/fields/impl/set_cookie.ipp rename to src/fields/set_cookie.cpp index 7564133..4ee0f74 100644 --- a/include/boost/requests/fields/impl/set_cookie.ipp +++ b/src/fields/set_cookie.cpp @@ -2,8 +2,6 @@ // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#ifndef BOOST_REQUESTS_SET_COOKIE_IPP -#define BOOST_REQUESTS_SET_COOKIE_IPP #include #include @@ -102,4 +100,3 @@ system::result parse_set_cookie_field(core::string_view input) } } -#endif //BOOST_REQUESTS_SET_COOKIE_IPP diff --git a/include/boost/requests/impl/mime_types.ipp b/src/mime_types.cpp similarity index 97% rename from include/boost/requests/impl/mime_types.ipp rename to src/mime_types.cpp index a2a1f4d..70b2774 100644 --- a/include/boost/requests/impl/mime_types.ipp +++ b/src/mime_types.cpp @@ -5,9 +5,6 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_IMPL_MIME_TYPES_IPP -#define BOOST_REQUESTS_IMPL_MIME_TYPES_IPP - #include namespace boost { @@ -97,5 +94,3 @@ const mime_type_map & default_mime_type_map() } } - -#endif // BOOST_REQUESTS_IMPL_MIME_TYPES_IPP diff --git a/include/boost/requests/impl/public_suffix.ipp b/src/public_suffix.cpp similarity index 93% rename from include/boost/requests/impl/public_suffix.ipp rename to src/public_suffix.cpp index 1b23a67..5b3e82d 100644 --- a/include/boost/requests/impl/public_suffix.ipp +++ b/src/public_suffix.cpp @@ -2,8 +2,6 @@ // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#ifndef BOOST_REQUESTS_COOKIES_IMPL_PUBLIC_SUFFIX_IPP -#define BOOST_REQUESTS_COOKIES_IMPL_PUBLIC_SUFFIX_IPP #include #include @@ -20,7 +18,7 @@ const public_suffix_list & default_public_suffix_list() { const static public_suffix_list def_list = { #include "public_suffix.inc" - }; + }; return def_list; } @@ -90,4 +88,3 @@ bool is_public_suffix(core::string_view value, } } -#endif //BOOST_REQUESTS_COOKIES_IMPL_PUBLIC_SUFFIX_IPP diff --git a/include/boost/requests/impl/public_suffix.inc b/src/public_suffix.inc similarity index 100% rename from include/boost/requests/impl/public_suffix.inc rename to src/public_suffix.inc diff --git a/include/boost/requests/impl/redirect.ipp b/src/redirect.cpp similarity index 97% rename from include/boost/requests/impl/redirect.ipp rename to src/redirect.cpp index 2cc1937..81736fc 100644 --- a/include/boost/requests/impl/redirect.ipp +++ b/src/redirect.cpp @@ -5,9 +5,6 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_IMPL_REDIRECT_IPP -#define BOOST_REQUESTS_IMPL_REDIRECT_IPP - #include namespace boost @@ -135,5 +132,3 @@ bool same_endpoint_on_host(const urls::url_view current, const asio::generic::st } } - -#endif //BOOST_REQUESTS_IMPL_REDIRECT_IPP diff --git a/include/boost/requests/impl/request_options.ipp b/src/request_options.cpp similarity index 75% rename from include/boost/requests/impl/request_options.ipp rename to src/request_options.cpp index 904e344..08d1f9d 100644 --- a/include/boost/requests/impl/request_options.ipp +++ b/src/request_options.cpp @@ -5,9 +5,6 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_REQUEST_OPTIONS_IPP -#define BOOST_REQUESTS_REQUEST_OPTIONS_IPP - #include namespace boost { @@ -21,5 +18,3 @@ request_options & default_options() } } - -#endif // BOOST_REQUESTS_REQUEST_OPTIONS_IPP diff --git a/include/boost/requests/rfc/impl/dates.ipp b/src/rfc/dates.cpp similarity index 98% rename from include/boost/requests/rfc/impl/dates.ipp rename to src/rfc/dates.cpp index a32dc8d..cceccf3 100644 --- a/include/boost/requests/rfc/impl/dates.ipp +++ b/src/rfc/dates.cpp @@ -5,8 +5,6 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_RFC_IMPL_DATES_IPP -#define BOOST_REQUESTS_RFC_IMPL_DATES_IPP #include #include @@ -302,4 +300,3 @@ http_date_t::parse( } } } -#endif //BOOST_REQUESTS_RFC_IMPL_RFC_1123_IPP diff --git a/include/boost/requests/rfc/impl/link.ipp b/src/rfc/link.cpp similarity index 95% rename from include/boost/requests/rfc/impl/link.ipp rename to src/rfc/link.cpp index 5c04d6b..a5ba286 100644 --- a/include/boost/requests/rfc/impl/link.ipp +++ b/src/rfc/link.cpp @@ -5,8 +5,6 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_RFC_IMPL_LINK_IPP -#define BOOST_REQUESTS_RFC_IMPL_LINK_IPP #include #include @@ -107,5 +105,3 @@ link_value_t::parse( } } } - -#endif // BOOST_REQUESTS_RFC_IMPL_LINK_IPP diff --git a/include/boost/requests/rfc/impl/quoted_string.ipp b/src/rfc/quoted_string.cpp similarity index 90% rename from include/boost/requests/rfc/impl/quoted_string.ipp rename to src/rfc/quoted_string.cpp index 21b9441..74360e7 100644 --- a/include/boost/requests/rfc/impl/quoted_string.ipp +++ b/src/rfc/quoted_string.cpp @@ -5,9 +5,6 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_RFC_IMPL_QUOTED_STRING_IPP -#define BOOST_REQUESTS_RFC_IMPL_QUOTED_STRING_IPP - #include #include #include @@ -78,4 +75,3 @@ bool unquoted_size(core::string_view sv) } } } -#endif // BOOST_REQUESTS_RFC_IMPL_QUOTED_STRING_IPP diff --git a/include/boost/requests/impl/session.ipp b/src/session.cpp similarity index 98% rename from include/boost/requests/impl/session.ipp rename to src/session.cpp index 3a214e5..e7a3a45 100644 --- a/include/boost/requests/impl/session.ipp +++ b/src/session.cpp @@ -5,12 +5,10 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_IMPL_SESSION_IPP -#define BOOST_REQUESTS_IMPL_SESSION_IPP - -#include #include +#include +#include namespace boost { namespace requests { @@ -330,4 +328,3 @@ auto session::async_ropen_op::resume(requests::detail::faux_token_t -#endif // BOOST_REQUESTS_IMPL_SESSION_IPP diff --git a/include/boost/requests/sources/impl/buffer.ipp b/src/sources/buffer.cpp similarity index 79% rename from include/boost/requests/sources/impl/buffer.ipp rename to src/sources/buffer.cpp index f75ebe2..5605069 100644 --- a/include/boost/requests/sources/impl/buffer.ipp +++ b/src/sources/buffer.cpp @@ -5,9 +5,6 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_SOURCES_IMPL_BUFFER_IPP -#define BOOST_REQUESTS_SOURCES_IMPL_BUFFER_IPP - #include namespace boost @@ -26,5 +23,3 @@ source_ptr tag_invoke(make_source_tag, asio::const_buffer cb, container::pmr::me } } } - -#endif // BOOST_REQUESTS_SOURCES_IMPL_BUFFER_IPP diff --git a/include/boost/requests/sources/impl/empty.ipp b/src/sources/empty.cpp similarity index 84% rename from include/boost/requests/sources/impl/empty.ipp rename to src/sources/empty.cpp index 30a6e7c..ac1412a 100644 --- a/include/boost/requests/sources/impl/empty.ipp +++ b/src/sources/empty.cpp @@ -5,9 +5,6 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_EMPTY_IPP -#define BOOST_REQUESTS_EMPTY_IPP - #include namespace boost @@ -29,5 +26,3 @@ source_ptr tag_invoke(const make_source_tag& tag, const none_t &) } } - -#endif // BOOST_REQUESTS_EMPTY_IPP diff --git a/include/boost/requests/sources/impl/file.ipp b/src/sources/file.cpp similarity index 84% rename from include/boost/requests/sources/impl/file.ipp rename to src/sources/file.cpp index a2ad43a..8f2f188 100644 --- a/include/boost/requests/sources/impl/file.ipp +++ b/src/sources/file.cpp @@ -5,8 +5,6 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_SOURCES_IMPL_FILE_IPP -#define BOOST_REQUESTS_SOURCES_IMPL_FILE_IPP #include #include @@ -29,5 +27,3 @@ source_ptr tag_invoke(const make_source_tag &tag, const filesystem::path & path, } } - -#endif // BOOST_REQUESTS_SOURCES_IMPL_FILE_IPP diff --git a/include/boost/requests/sources/impl/form.ipp b/src/sources/form.cpp similarity index 95% rename from include/boost/requests/sources/impl/form.ipp rename to src/sources/form.cpp index 4526e80..f7bed89 100644 --- a/include/boost/requests/sources/impl/form.ipp +++ b/src/sources/form.cpp @@ -5,11 +5,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_SOURCE_IMPL_FORM_IPP -#define BOOST_REQUESTS_SOURCE_IMPL_FORM_IPP - -#include -#include +#include "boost/requests/sources/file.hpp" +#include "boost/requests/sources/form.hpp" #include namespace boost @@ -191,5 +188,3 @@ multi_part_form_source::multi_part_form_source(const multi_part_form & mpf) : mp } } - -#endif // BOOST_REQUESTS_SOURCE_IMPL_FORM_IPP diff --git a/include/boost/requests/sources/impl/json.ipp b/src/sources/json.cpp similarity index 92% rename from include/boost/requests/sources/impl/json.ipp rename to src/sources/json.cpp index 74d9f1a..7177012 100644 --- a/include/boost/requests/sources/impl/json.ipp +++ b/src/sources/json.cpp @@ -5,8 +5,6 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_REQUESTS_SOURCES_IMPL_JSON_IPP -#define BOOST_REQUESTS_SOURCES_IMPL_JSON_IPP #include #include @@ -63,5 +61,3 @@ source_ptr tag_invoke(make_source_tag, const boost::json::string & arr, containe } } - -#endif // BOOST_REQUESTS_SOURCES_IMPL_JSON_IPP diff --git a/src/src.cpp b/src/src.cpp deleted file mode 100644 index c885d86..0000000 --- a/src/src.cpp +++ /dev/null @@ -1,8 +0,0 @@ -// -// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#include diff --git a/include/boost/requests/impl/stream.ipp b/src/stream.cpp similarity index 94% rename from include/boost/requests/impl/stream.ipp rename to src/stream.cpp index 3d994f6..3c7f5fd 100644 --- a/include/boost/requests/impl/stream.ipp +++ b/src/stream.cpp @@ -2,10 +2,10 @@ // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#ifndef BOOST_REQUESTS_STREAM_IPP -#define BOOST_REQUESTS_STREAM_IPP #include +#include +#include namespace boost { @@ -131,9 +131,12 @@ void stream::async_dump_op::resume(requests::detail::faux_token_tis_open() && !done(); +} + } } - -#endif //BOOST_REQUESTS_STREAM_IPP diff --git a/test/cookie_grammar.cpp b/test/cookie_grammar.cpp index a170039..bbae47c 100644 --- a/test/cookie_grammar.cpp +++ b/test/cookie_grammar.cpp @@ -20,7 +20,7 @@ using namespace boost; -#include "boost/requests/cookie.hpp" +#include #include "doctest.h" #include "string_maker.hpp" diff --git a/test/cookie_jar.cpp b/test/cookie_jar.cpp index ad03e71..73c85e1 100644 --- a/test/cookie_jar.cpp +++ b/test/cookie_jar.cpp @@ -3,8 +3,8 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#include "boost/requests/cookie_jar.hpp" -#include "boost/requests/public_suffix.hpp" +#include +#include #include namespace rcp = boost::requests; diff --git a/test/keep_alive.cpp b/test/keep_alive.cpp index 724ac81..9ef2e7f 100644 --- a/test/keep_alive.cpp +++ b/test/keep_alive.cpp @@ -5,7 +5,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include "boost/requests/fields/keep_alive.hpp" +#include #include #include "doctest.h" From 906c8b46ae282af7337526dfdf940c9f6fca2ff5 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 6 Sep 2023 15:03:24 +0800 Subject: [PATCH 65/76] simplification #1. --- .../requests/detail/condition_variable.hpp | 16 +++++++++++-- .../boost/requests/detail/connection_impl.hpp | 2 -- .../requests/detail/impl/connection_impl.hpp | 20 ++++++++++++---- include/boost/requests/detail/mutex.hpp | 15 +++++++++--- .../boost/requests/impl/connection_pool.hpp | 20 ++++++++++++---- include/boost/requests/impl/session.hpp | 7 +++--- include/boost/requests/session.hpp | 13 ++++------ src/detail/condition_variable.cpp | 10 ++++---- src/detail/mutex.cpp | 24 +++++++++---------- 9 files changed, 84 insertions(+), 43 deletions(-) diff --git a/include/boost/requests/detail/condition_variable.hpp b/include/boost/requests/detail/condition_variable.hpp index 6a15444..900c4d3 100644 --- a/include/boost/requests/detail/condition_variable.hpp +++ b/include/boost/requests/detail/condition_variable.hpp @@ -9,6 +9,7 @@ #define BOOST_REQUESTS_DETAIL_CONDITION_VARIABLE_HPP #include +#include #include #include #include @@ -35,8 +36,15 @@ struct condition_variable { } - BOOST_REQUESTS_DECL void - async_wait(std::unique_lock & lock, faux_token_t tk); + template + auto + async_wait(std::unique_lock & lock, CompletionToken && tk) + { + return asio::async_initiate + ( + async_wait_impl_, tk, this, std::ref(lock) + ); + } condition_variable& operator=(const condition_variable&) = delete; condition_variable& operator=(condition_variable&& lhs) noexcept = default; @@ -66,6 +74,10 @@ struct condition_variable BOOST_REQUESTS_DECL ~condition_variable(); private: + BOOST_REQUESTS_DECL static + void async_wait_impl_(asio::any_completion_handler h, + condition_variable * this_, std::unique_lock & lock); + asio::steady_timer timer_; std::condition_variable cv_; std::shared_ptr shutdown_indicator_{std::make_shared()}; diff --git a/include/boost/requests/detail/connection_impl.hpp b/include/boost/requests/detail/connection_impl.hpp index eee8d5a..51ba1e4 100644 --- a/include/boost/requests/detail/connection_impl.hpp +++ b/include/boost/requests/detail/connection_impl.hpp @@ -208,9 +208,7 @@ struct connection_impl : std::enable_shared_from_this struct async_connect_op; struct async_ropen_op; - struct async_ropen_op_body; - struct async_ropen_op_body_base; BOOST_REQUESTS_DECL std::size_t do_read_some_(beast::http::basic_parser & parser); diff --git a/include/boost/requests/detail/impl/connection_impl.hpp b/include/boost/requests/detail/impl/connection_impl.hpp index 2c5060e..bfd0666 100644 --- a/include/boost/requests/detail/impl/connection_impl.hpp +++ b/include/boost/requests/detail/impl/connection_impl.hpp @@ -249,10 +249,22 @@ connection_impl::async_ropen( RequestBody && body, request_parameters req, CompletionToken && completion_token) { - return detail::faux_run_with_allocator( - std::forward(completion_token), - shared_from_this(), method, path, std::forward(body), - std::move(req)); + return asio::async_initiate( + [this](auto handler, + beast::http::verb method, + urls::url_view path, + RequestBody && body, request_parameters req) + { + auto source_ptr = requests::make_source(std::forward(body)); + auto & source = *source_ptr; + auto alloc = asio::get_associated_allocator(handler, asio::recycling_allocator()); + auto header_ptr = allocate_unique(alloc, std::move(req.fields)); + auto & headers = *header_ptr; + async_ropen(method, path, headers, source, std::move(req.opts), req.jar, + asio::consign(std::move(handler), std::move(source_ptr), std::move(header_ptr))); + }, + completion_token, method, path, std::forward(body), std::move(req) + ); } template diff --git a/include/boost/requests/detail/mutex.hpp b/include/boost/requests/detail/mutex.hpp index cbfc57f..0d5e707 100644 --- a/include/boost/requests/detail/mutex.hpp +++ b/include/boost/requests/detail/mutex.hpp @@ -8,6 +8,7 @@ #ifndef BOOST_REQUESTS_DETAIL_MUTEX_HPP #define BOOST_REQUESTS_DETAIL_MUTEX_HPP +#include #include #include #include @@ -39,8 +40,13 @@ struct mutex { } - BOOST_REQUESTS_DECL void - async_lock(faux_token_t tk); + template + BOOST_REQUESTS_DECL auto + async_lock(CompletionToken && tk) + { + return asio::async_initiate + (async_lock_impl_, tk, this); + } mutex& operator=(const mutex&) = delete; mutex& operator=(mutex&& lhs) noexcept @@ -84,10 +90,13 @@ struct mutex BOOST_REQUESTS_DECL ~mutex(); private: + BOOST_REQUESTS_DECL static + void async_lock_impl_(asio::any_completion_handler handler, mutex * this_); + asio::any_io_executor exec_; std::atomic locked_{false}; std::mutex mtx_; - std::list> waiters_; + std::list> waiters_; }; } diff --git a/include/boost/requests/impl/connection_pool.hpp b/include/boost/requests/impl/connection_pool.hpp index 3a6e822..58ba61b 100644 --- a/include/boost/requests/impl/connection_pool.hpp +++ b/include/boost/requests/impl/connection_pool.hpp @@ -194,10 +194,22 @@ connection_pool::async_ropen(beast::http::verb method, RequestBody && body, request_parameters req, CompletionToken && completion_token) { - return detail::faux_run_with_allocator( - std::forward(completion_token), - this, method, path, std::forward(body), - std::move(req)); + return asio::async_initiate( + [this](auto handler, + beast::http::verb method, + urls::url_view path, + RequestBody && body, request_parameters req) + { + auto source_ptr = requests::make_source(std::forward(body)); + auto & source = *source_ptr; + auto alloc = asio::get_associated_allocator(handler, asio::recycling_allocator()); + auto header_ptr = allocate_unique(alloc, std::move(req.fields)); + auto & headers = *header_ptr; + async_ropen(method, path, headers, source, std::move(req.opts), req.jar, + asio::consign(std::move(handler), std::move(source_ptr), std::move(header_ptr))); + }, + completion_token, method, path, std::forward(body), std::move(req) + ); } } diff --git a/include/boost/requests/impl/session.hpp b/include/boost/requests/impl/session.hpp index 69ab03b..b207ac9 100644 --- a/include/boost/requests/impl/session.hpp +++ b/include/boost/requests/impl/session.hpp @@ -159,16 +159,15 @@ session::async_ropen(beast::http::verb method, template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, basic_stream)) -session::async_ropen(urls::url_view url, - http::verb method, + void (boost::system::error_code, stream)) +session::async_ropen(http::verb method, urls::url_view path, http::fields & headers, source & src, CompletionToken && completion_token) { return detail::faux_run(std::forward(completion_token), - this, url, method, path, headers, src); + this, method, path, headers, src); } } diff --git a/include/boost/requests/session.hpp b/include/boost/requests/session.hpp index 81d06d0..9094f02 100644 --- a/include/boost/requests/session.hpp +++ b/include/boost/requests/session.hpp @@ -150,8 +150,7 @@ struct session template< BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code, stream)) CompletionToken> BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, stream)) - async_ropen(urls::url_view url, - http::verb method, + async_ropen(http::verb method, urls::url_view path, http::fields & headers, source & src, @@ -212,14 +211,13 @@ struct session::defaulted : session template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, stream)) - async_ropen(urls::url_view url, - http::verb method, + async_ropen(http::verb method, urls::url_view path, http::fields & headers, source & src, CompletionToken && completion_token ) { - return session::async_ropen(url, method, path, headers, src, + return session::async_ropen(method, path, headers, src, detail::with_defaulted_token(std::forward(completion_token))); } @@ -232,13 +230,12 @@ struct session::defaulted : session return async_ropen(method, path, std::forward(body), std::move(req), default_token()); } - auto async_ropen(urls::url_view url, - http::verb method, + auto async_ropen(http::verb method, urls::url_view path, http::fields & headers, source & src) { - return async_ropen(url, method, path, headers, src, default_token()); + return async_ropen(method, path, headers, src, default_token()); } diff --git a/src/detail/condition_variable.cpp b/src/detail/condition_variable.cpp index 398a4f0..c3c0f80 100644 --- a/src/detail/condition_variable.cpp +++ b/src/detail/condition_variable.cpp @@ -24,12 +24,14 @@ void condition_variable::wait(std::unique_lock & lock, system::error ec = asio::error::operation_aborted; } -void condition_variable::async_wait(std::unique_lock & lock, - faux_token_t tk) +void condition_variable::async_wait_impl_( + asio::any_completion_handler tk, + condition_variable * this_, + std::unique_lock & lock) { - std::weak_ptr indicator = this->shutdown_indicator_; + std::weak_ptr indicator = this_->shutdown_indicator_; lock.unlock(); - timer_.async_wait( + this_->timer_.async_wait( asio::deferred( [&lock, indicator](system::error_code ec_) { diff --git a/src/detail/mutex.cpp b/src/detail/mutex.cpp index d2c9280..4759e13 100644 --- a/src/detail/mutex.cpp +++ b/src/detail/mutex.cpp @@ -20,31 +20,31 @@ namespace requests namespace detail { -void mutex::async_lock(faux_token_t tk) +void mutex::async_lock_impl_(asio::any_completion_handler handler, + mutex * this_) { - std::lock_guard lock{mtx_}; - if (try_lock()) - return asio::post( - exec_, - asio::append(std::move(tk), system::error_code())); - + std::lock_guard lock{this_->mtx_}; + if (this_->try_lock()) + return asio::dispatch( + asio::get_associated_executor(handler, this_->exec_), + asio::append(std::move(handler), system::error_code())); - auto itr = waiters_.insert(waiters_.end(), std::move(tk)); + auto itr = this_->waiters_.insert(this_->waiters_.end(), std::move(handler)); auto slot = itr->get_cancellation_slot(); if (slot.is_connected()) { slot.assign( - [this, itr](asio::cancellation_type type) + [this_, itr](asio::cancellation_type type) { if (type != asio::cancellation_type::none) { - std::lock_guard lock{mtx_}; + std::lock_guard lock{this_->mtx_}; ignore_unused(lock); system::error_code ec; BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::operation_aborted); - asio::defer(exec_, asio::append(std::move(*itr), ec)); - waiters_.erase(itr); + asio::defer(this_->exec_, asio::append(std::move(*itr), ec)); + this_->waiters_.erase(itr); } }); } From aa4bc846afe7049f78d9d470f9d2572b1c57448e Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 6 Sep 2023 15:20:00 +0800 Subject: [PATCH 66/76] simplification #2. --- include/boost/requests/connection_pool.hpp | 2 - .../boost/requests/detail/connection_impl.hpp | 3 - .../requests/detail/impl/connection_impl.hpp | 29 --------- .../boost/requests/impl/connection_pool.hpp | 33 ---------- include/boost/requests/impl/session.hpp | 60 +++++++------------ include/boost/requests/session.hpp | 14 ++--- 6 files changed, 25 insertions(+), 116 deletions(-) diff --git a/include/boost/requests/connection_pool.hpp b/include/boost/requests/connection_pool.hpp index 10793c1..6d2a857 100644 --- a/include/boost/requests/connection_pool.hpp +++ b/include/boost/requests/connection_pool.hpp @@ -231,8 +231,6 @@ struct connection_pool struct async_lookup_op; struct async_get_connection_op; - struct async_ropen_op_body; - struct async_ropen_op_body_base; struct async_ropen_op; void return_connection_(detail::connection_impl * conn) diff --git a/include/boost/requests/detail/connection_impl.hpp b/include/boost/requests/detail/connection_impl.hpp index 51ba1e4..513bb5e 100644 --- a/include/boost/requests/detail/connection_impl.hpp +++ b/include/boost/requests/detail/connection_impl.hpp @@ -206,10 +206,7 @@ struct connection_impl : std::enable_shared_from_this struct async_close_op; struct async_connect_op; - struct async_ropen_op; - struct async_ropen_op_body; - struct async_ropen_op_body_base; BOOST_REQUESTS_DECL std::size_t do_read_some_(beast::http::basic_parser & parser); BOOST_REQUESTS_DECL std::size_t do_read_some_(beast::http::basic_parser & parser, system::error_code & ec) ; diff --git a/include/boost/requests/detail/impl/connection_impl.hpp b/include/boost/requests/detail/impl/connection_impl.hpp index bfd0666..e87d20d 100644 --- a/include/boost/requests/detail/impl/connection_impl.hpp +++ b/include/boost/requests/detail/impl/connection_impl.hpp @@ -210,35 +210,6 @@ struct connection_impl::async_ropen_op system::error_code & ec, std::size_t res_ = 0u) -> stream; }; -struct connection_impl::async_ropen_op_body_base -{ - source_ptr source_impl; - http::fields headers; - - template - async_ropen_op_body_base( - container::pmr::polymorphic_allocator alloc, - RequestBody && body, http::fields headers) - : source_impl(requests::make_source(std::forward(body), alloc.resource())), headers(std::move(headers)) - { - } -}; - -struct connection_impl::async_ropen_op_body : async_ropen_op_body_base, async_ropen_op -{ - template - async_ropen_op_body(container::pmr::polymorphic_allocator alloc, - std::shared_ptr this_, - beast::http::verb method, - urls::url_view path, - RequestBody && body, - request_parameters req) - : async_ropen_op_body_base{alloc, std::forward(body), std::move(req.fields)}, - async_ropen_op(std::move(this_), method, path, async_ropen_op_body_base::headers, - *this->source_impl, std::move(req.opts), req.jar) - {} -}; - template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, diff --git a/include/boost/requests/impl/connection_pool.hpp b/include/boost/requests/impl/connection_pool.hpp index 58ba61b..5b82056 100644 --- a/include/boost/requests/impl/connection_pool.hpp +++ b/include/boost/requests/impl/connection_pool.hpp @@ -152,39 +152,6 @@ connection_pool::async_ropen(beast::http::verb method, this, method, path, std::ref(headers), std::ref(src), std::move(opt), jar); } -struct connection_pool::async_ropen_op_body_base -{ - source_ptr source_impl; - http::fields headers; - - template - async_ropen_op_body_base( - container::pmr::polymorphic_allocator alloc, - RequestBody && body, http::fields headers) - : source_impl(requests::make_source(std::forward(body), alloc.resource())), headers(std::move(headers)) - { - } -}; - - -struct connection_pool::async_ropen_op_body : async_ropen_op_body_base, async_ropen_op -{ - template - async_ropen_op_body( - container::pmr::polymorphic_allocator alloc, - connection_pool * this_, - beast::http::verb method, - urls::url_view path, - RequestBody && body, - request_parameters req) - : async_ropen_op_body_base{alloc, std::forward(body), std::move(req.fields)}, - async_ropen_op{this_, method, path.encoded_resource(), async_ropen_op_body_base::headers, - *this->source_impl, - std::move(req.opts), req.jar} - {} -}; - - template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, diff --git a/include/boost/requests/impl/session.hpp b/include/boost/requests/impl/session.hpp index b207ac9..5b5e41c 100644 --- a/include/boost/requests/impl/session.hpp +++ b/include/boost/requests/impl/session.hpp @@ -97,8 +97,8 @@ struct session::async_ropen_op : asio::coroutine async_ropen_op(session * this_, http::verb method, urls::url_view path, - http::fields & headers, - source & src) + source & src, + http::fields & headers) : this_(this_), method(method), url(path), opts(this_->options_), headers(headers), src(src) { } @@ -110,39 +110,6 @@ struct session::async_ropen_op : asio::coroutine system::error_code & ec, variant2::variant, stream> s) -> stream; }; - -struct session::async_ropen_op_body_base -{ - source_ptr source_impl; - http::fields headers; - - template - async_ropen_op_body_base( - container::pmr::polymorphic_allocator alloc, - RequestBody && body, http::fields headers) - : source_impl(requests::make_source(std::forward(body), alloc.resource())), - headers(std::move(headers)) - { - } -}; - -struct session::async_ropen_op_body : async_ropen_op_body_base, async_ropen_op -{ - template - async_ropen_op_body( - container::pmr::polymorphic_allocator alloc, - session * this_, - beast::http::verb method, - urls::url_view path, - RequestBody && body, - http::fields req) - : async_ropen_op_body_base{alloc, std::forward(body), std::move(req)}, - async_ropen_op(this_, method, path, async_ropen_op_body_base::headers, - *this->source_impl) - {} -}; - - template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, basic_stream)) @@ -152,9 +119,22 @@ session::async_ropen(beast::http::verb method, http::fields req, CompletionToken && completion_token) { - return detail::faux_run_with_allocator( - std::forward(completion_token), - this, method, path, std::forward(body), std::move(req)); + return asio::async_initiate( + [this](auto handler, + beast::http::verb method, + urls::url_view path, + RequestBody && body, http::fields req) + { + auto source_ptr = requests::make_source(std::forward(body)); + auto & source = *source_ptr; + auto alloc = asio::get_associated_allocator(handler, asio::recycling_allocator()); + auto req_ptr = allocate_unique(alloc, std::move(req)); + auto & req_ref = *req_ptr; + async_ropen(method, path, source, req_ref, + asio::consign(std::move(handler), std::move(source_ptr), std::move(req_ptr))); + }, + completion_token, method, path, std::forward(body), std::move(req) + ); } template @@ -162,12 +142,12 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, stream)) session::async_ropen(http::verb method, urls::url_view path, - http::fields & headers, source & src, + http::fields & headers, CompletionToken && completion_token) { return detail::faux_run(std::forward(completion_token), - this, method, path, headers, src); + this, method, path, src, headers); } } diff --git a/include/boost/requests/session.hpp b/include/boost/requests/session.hpp index 9094f02..c344679 100644 --- a/include/boost/requests/session.hpp +++ b/include/boost/requests/session.hpp @@ -152,8 +152,8 @@ struct session void (boost::system::error_code, stream)) async_ropen(http::verb method, urls::url_view path, - http::fields & headers, source & src, + http::fields & headers, CompletionToken && completion_token); private: @@ -171,12 +171,8 @@ struct session cookie_jar jar_{boost::container::pmr::polymorphic_allocator(&pmr_)}; struct async_get_pool_op; - struct async_ropen_op; - struct async_ropen_op_body; - - struct async_ropen_op_body_base; BOOST_REQUESTS_DECL auto make_request_(http::fields fields) -> requests::request_parameters; BOOST_REQUESTS_DECL static urls::url normalize_(urls::url_view in); @@ -213,11 +209,11 @@ struct session::defaulted : session BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, stream)) async_ropen(http::verb method, urls::url_view path, - http::fields & headers, source & src, + http::fields & headers, CompletionToken && completion_token ) { - return session::async_ropen(method, path, headers, src, + return session::async_ropen(method, path, src, headers, detail::with_defaulted_token(std::forward(completion_token))); } @@ -232,8 +228,8 @@ struct session::defaulted : session auto async_ropen(http::verb method, urls::url_view path, - http::fields & headers, - source & src) + source & src, + http::fields & headers) { return async_ropen(method, path, headers, src, default_token()); } From d7f831356cf30050895d250e77d21813a216f03b Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 6 Sep 2023 22:20:35 +0800 Subject: [PATCH 67/76] simplification #3. (mutex) --- include/boost/requests/detail/mutex.hpp | 11 ++--- src/detail/mutex.cpp | 62 +++++-------------------- 2 files changed, 15 insertions(+), 58 deletions(-) diff --git a/include/boost/requests/detail/mutex.hpp b/include/boost/requests/detail/mutex.hpp index 0d5e707..3ea7c96 100644 --- a/include/boost/requests/detail/mutex.hpp +++ b/include/boost/requests/detail/mutex.hpp @@ -44,16 +44,14 @@ struct mutex BOOST_REQUESTS_DECL auto async_lock(CompletionToken && tk) { - return asio::async_initiate - (async_lock_impl_, tk, this); + return asio::async_initiate(async_lock_impl_, tk, this); } mutex& operator=(const mutex&) = delete; mutex& operator=(mutex&& lhs) noexcept { - std::lock_guard _(mtx_); + std::lock_guard _(waiters_mtx_); exec_ = std::move(lhs.exec_); - locked_ = locked_.exchange(lhs.locked_.load()); waiters_ = std::move(lhs.waiters_); return *this; } @@ -61,10 +59,10 @@ struct mutex mutex(const mutex&) = delete; mutex(mutex&& mi) noexcept : exec_(std::move(mi.exec_)), - locked_(mi.locked_.exchange(false)), waiters_(std::move(mi.waiters_)) { } + BOOST_REQUESTS_DECL void lock(system::error_code & ec); void lock() { @@ -94,8 +92,7 @@ struct mutex void async_lock_impl_(asio::any_completion_handler handler, mutex * this_); asio::any_io_executor exec_; - std::atomic locked_{false}; - std::mutex mtx_; + std::mutex mutex_, waiters_mtx_; std::list> waiters_; }; diff --git a/src/detail/mutex.cpp b/src/detail/mutex.cpp index 4759e13..a518247 100644 --- a/src/detail/mutex.cpp +++ b/src/detail/mutex.cpp @@ -23,10 +23,10 @@ namespace detail void mutex::async_lock_impl_(asio::any_completion_handler handler, mutex * this_) { - std::lock_guard lock{this_->mtx_}; + std::lock_guard lock{this_->waiters_mtx_}; if (this_->try_lock()) return asio::dispatch( - asio::get_associated_executor(handler, this_->exec_), + asio::get_associated_immediate_executor(handler, this_->exec_), asio::append(std::move(handler), system::error_code())); auto itr = this_->waiters_.insert(this_->waiters_.end(), std::move(handler)); @@ -39,7 +39,7 @@ void mutex::async_lock_impl_(asio::any_completion_handler lock{this_->mtx_}; + std::lock_guard lock{this_->waiters_mtx_}; ignore_unused(lock); system::error_code ec; BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::operation_aborted); @@ -51,59 +51,19 @@ void mutex::async_lock_impl_(asio::any_completion_handlermutex_.lock(); +} +catch (std::system_error & se) { - if (try_lock()) - return ; - - using token_type = faux_token_t; - struct impl final : token_type::base - { - using allocator_type = container::pmr::polymorphic_allocator; - container::pmr::memory_resource * resource; - asio::any_io_executor executor; - allocator_type get_allocator() override - { - return container::pmr::polymorphic_allocator{resource}; - } - - void resume(faux_token_t tk, system::error_code ec) override - { - done = true; - this->ec = ec; - var.notify_all(); - } - - impl(container::pmr::memory_resource * res, asio::any_io_executor executor) : resource(res), executor(executor) {} - system::error_code ec; - bool done = false; - std::condition_variable var; - - void wait(std::unique_lock & lock) - { - var.wait(lock, [this]{ return done;}); - } - }; - - char buf[4096]; - container::pmr::monotonic_buffer_resource res{buf, sizeof(buf)}; - - impl ip{&res, get_executor()}; - - std::unique_lock lock(mtx_); - std::shared_ptr ptr{&ip, [](impl * ) {}}; - token_type ft{ptr}; - - waiters_.push_back(std::move(ft)); - - ip.wait(lock); - ec = ip.ec; + ec = se.code(); } void mutex::unlock() { - std::lock_guard lock{mtx_}; + std::lock_guard lock{waiters_mtx_}; if (waiters_.empty()) - locked_ = false; + this->mutex_.unlock(); else if (!waiters_.empty()) { auto h = std::move(waiters_.front()); @@ -115,7 +75,7 @@ void mutex::unlock() bool mutex::try_lock() { - return !locked_.exchange(true); + return this->mutex_.try_lock(); } mutex::~mutex() From 64ead043f55626e3f7cdfb9389fa3b53a8c61fb0 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 13 Sep 2023 10:55:19 +0800 Subject: [PATCH 68/76] removed pmr. --- doc/requests/high-level.adoc | 40 ----- doc/requests/reference/cookie.adoc | 2 +- doc/requests/reference/cookie_jar.adoc | 2 +- doc/requests/reference/http.adoc | 8 +- doc/requests/reference/json.adoc | 12 +- .../reference/request_parameters.adoc | 8 +- doc/requests/reference/response.adoc | 8 +- example/github-issues.hpp | 22 ++- example/link.cpp | 11 +- example/xml.cpp | 7 +- include/boost/requests/cookie.hpp | 48 +----- include/boost/requests/cookie_jar.hpp | 4 +- .../boost/requests/detail/faux_coroutine.hpp | 60 +------ .../requests/detail/impl/connection_impl.hpp | 1 - include/boost/requests/detail/pmr.hpp | 50 ------ include/boost/requests/download.hpp | 4 +- include/boost/requests/fields/location.hpp | 1 - include/boost/requests/form.hpp | 3 +- include/boost/requests/http.hpp | 7 +- include/boost/requests/impl/request.hpp | 4 +- include/boost/requests/impl/source.hpp | 1 - include/boost/requests/json.hpp | 155 ++++++++++-------- include/boost/requests/method.hpp | 32 ++-- include/boost/requests/request_parameters.hpp | 12 +- include/boost/requests/response.hpp | 18 +- include/boost/requests/session.hpp | 8 +- include/boost/requests/source.hpp | 37 ++--- include/boost/requests/sources/buffer.hpp | 2 +- include/boost/requests/sources/form.hpp | 4 +- include/boost/requests/sources/string.hpp | 6 +- .../boost/requests/sources/string_view.hpp | 33 ++-- include/boost/requests/stream.hpp | 4 +- src/cookie_jar.cpp | 2 +- src/detail/connection_impl.cpp | 18 +- src/detail/mutex.cpp | 1 - src/session.cpp | 15 +- src/sources/buffer.cpp | 4 +- src/sources/file.cpp | 5 +- src/sources/form.cpp | 12 +- src/sources/json.cpp | 16 +- test/detail/mutex.cpp | 18 +- test/json.cpp | 16 +- test/source.cpp | 3 +- 43 files changed, 231 insertions(+), 493 deletions(-) delete mode 100644 include/boost/requests/detail/pmr.hpp diff --git a/doc/requests/high-level.adoc b/doc/requests/high-level.adoc index 2675746..4175a18 100644 --- a/doc/requests/high-level.adoc +++ b/doc/requests/high-level.adoc @@ -150,44 +150,6 @@ requests::post( requests::headers({{"Content-Type", "application/json"}})); ---- - -## Allocations - -All types used in requests, responses & headers use a `boost::container::pmr::polymorphic_allocator`. -Any response object will take the allocator from the request object. -E.g. if a user wishes to use a `monotonic_resource` for a request, his code might look like this: - -[source,cpp] ----- -char buf[8096]; -container::pmr::monotonic_buffer_resource memres{buf, 8096}; -requests::response res = requests::get( - urls::url_view("https://boost.org/index.html"), - requests::headers({}, &memres)); - -assert(res.headers.get_allocator().resource() == &memres); ----- - -This allows users to modify the allocation strategy without introducing more templates than necessary. - -The types used from `boost::beast` are aliased in `requests::http` with the proper allocator: - -[source,cpp] ----- -using fields = boost::beast::http::basic_fields>; - -using request_header = beast::http::request_header ; -using response_header = beast::http::response_header; - -template using request = beast::http::request ; -template using response = beast::http::response; - -template using request_parser = beast::http::request_parser >; -template using response_parser = beast::http::response_parser>; ----- - -NOTE: This does nto apply to the memory allocated for async operations, which wills till use the `asio::associated_allocator`. - ## Request settings <> dictate how a request performs. It contains the `headers`, <> and a pointer to the cookie_jar. @@ -239,8 +201,6 @@ json::response> oes = json::delete_("https:// Similarly, the request_body will be treated as if it is `json`, i.e. it will attempt to use `boost::json::value_from` to send json data. -Since `json::storage_ptr` uses `boost::container::pmr::memory_resource`, the result will use the same resource. - [source,cpp] ---- auto ptr = boost::json::make_shared_resource(); diff --git a/doc/requests/reference/cookie.adoc b/doc/requests/reference/cookie.adoc index 5b20c0e..acbd592 100644 --- a/doc/requests/reference/cookie.adoc +++ b/doc/requests/reference/cookie.adoc @@ -8,7 +8,7 @@ namespace boost::requests struct cookie { - using allocator_type = boost::container::pmr::polymorphic_allocator; + using allocator_type = std::allocator; using string_type = std::basic_string, allocator_type>; cookie(allocator_type && alloc) : name(alloc), value(alloc), domain(alloc), path(alloc) {} diff --git a/doc/requests/reference/cookie_jar.adoc b/doc/requests/reference/cookie_jar.adoc index 52830c4..e3e582c 100644 --- a/doc/requests/reference/cookie_jar.adoc +++ b/doc/requests/reference/cookie_jar.adoc @@ -15,7 +15,7 @@ bool path_match(core::string_view full, core::string_view pattern); struct cookie_jar final { - using allocator_type = boost::container::pmr::polymorphic_allocator; + using allocator_type = std::allocator; // The set used to store the boost::unordered_set::template rebind_alloc> content; diff --git a/doc/requests/reference/http.adoc b/doc/requests/reference/http.adoc index e80f590..446de96 100644 --- a/doc/requests/reference/http.adoc +++ b/doc/requests/reference/http.adoc @@ -9,7 +9,7 @@ namespace boost::requests::http using boost::beast::http::field; using boost::beast::http::status; using boost::beast::http::verb; -using fields = boost::beast::http::basic_fields>; +using fields = boost::beast::http::basic_fields<>; using file_body = beast::http::file_body; using empty_body = beast::http::empty_body; @@ -21,12 +21,8 @@ using response_header = beast::http::response_header; template using request = beast::http::request ; template using response = beast::http::response; - -template using request_parser = beast::http::request_parser >; -template using response_parser = beast::http::response_parser>; - } ---- -The http header contains aliases for beast::http members with pmr allocators. +The http header contains aliases for beast::http members. diff --git a/doc/requests/reference/json.adoc b/doc/requests/reference/json.adoc index 7437cfc..2615b86 100644 --- a/doc/requests/reference/json.adoc +++ b/doc/requests/reference/json.adoc @@ -44,7 +44,7 @@ auto async_read_optional_json( template struct response : response_base { - using allocator_type = container::pmr::polymorphic_allocator; + using allocator_type = std::allocator; using fields_type = http::fields; response(allocator_type alloc, history_type history, Value && value); @@ -64,7 +64,8 @@ template response; + typename Connection::request_type req = {}, + json::storage_ptr ptr = {}) -> response; template response; template()); // requests on the default_session @@ -91,13 +94,15 @@ template auto ~method~(urls::url_view target, RequestBody && request_body, // omitted for some methods. - http::fields req) -> response; + http::fields req, + json::storage_ptr ptr = {}) -> response; template auto ~method~(urls::url_view target, RequestBody && request_body, // omitted for some methods. http::fields req, + json::storage_ptr ptr, system::error_code & ec) -> response; template fields, - boost::container::pmr::memory_resource * res = boost::container::pmr::get_default_resource()); +auto headers(std::initializer_list fields); // The full request settings used in the connection. struct request_parameters { - using allocator_type = boost::container::pmr::polymorphic_allocator; - allocator_type get_allocator() const {return fields.get_allocator();} - - using fields_type = beast::http::basic_fields; + using fields_type = beast::http::fields; fields_type fields; request_options opts{}; cookie_jar * jar = nullptr; diff --git a/doc/requests/reference/response.adoc b/doc/requests/reference/response.adoc index 0185ee8..f17730a 100644 --- a/doc/requests/reference/response.adoc +++ b/doc/requests/reference/response.adoc @@ -11,8 +11,8 @@ namespace boost::requests struct response_base { - using allocator_type = container::pmr::polymorphic_allocator; - using buffer_type = beast::basic_flat_buffer; +using allocator_type = std::allocator; + using buffer_type = beast::basic_flat_buffer; using body_type = beast::http::basic_dynamic_body; http::response_header headers; @@ -21,8 +21,8 @@ struct response_base http::status result() const {return headers.result(); } using string_body_type = typename beast::http::basic_string_body, allocator_type>; - using vector_alloc = boost::container::pmr::polymorphic_allocator>; - using history_type = std::vector, vector_alloc>; +using allocator_type = std::allocator; + using history_type = std::vector, vector_alloc>; // The history contains all the redirect messages. history_type history; diff --git a/example/github-issues.hpp b/example/github-issues.hpp index d6c7df7..c415814 100644 --- a/example/github-issues.hpp +++ b/example/github-issues.hpp @@ -11,7 +11,6 @@ #include #include #include -#include #include namespace github @@ -510,7 +509,7 @@ struct issue_client settings_.fields = boost::requests::headers({ {boost::requests::http::field::content_type, "application/vnd.github+json"}, boost::requests::bearer(auth_token) - }, &memory_); + }); } // List issues assigned to the authenticated user @@ -520,7 +519,7 @@ struct issue_client } response> list_issues(list_issues_query opt, error_code & ec) { - return boost::requests::json::get>(conn_, opt.make_query("/issues"), settings_, ec); + return boost::requests::json::get>(conn_, opt.make_query("/issues"), settings_, {}, ec); } template>)) CompletionToken> @@ -545,7 +544,7 @@ struct issue_client return boost::requests::json::get>( conn_, opt.make_query(urls::format("/repos/{owner}/issues", owner).encoded_target()), - settings_, ec); + settings_, {}, ec); } template>)) CompletionToken> @@ -568,7 +567,7 @@ struct issue_client { return boost::requests::json::get>( conn_, opt.make_query(urls::format("/repos/{owner}/{repository}/issues", owner, repository).encoded_target()), - settings_, ec); + settings_, {}, ec); } template>)) CompletionToken> @@ -594,7 +593,7 @@ struct issue_client return boost::requests::json::post( conn_, urls::format("/repos/{owner}/{repository}/issues", owner, repository), - boost::json::value_from(opts, storage()), settings_, ec); + boost::json::value_from(opts, storage()), settings_, {}, ec); } template)) CompletionToken> BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) @@ -622,7 +621,7 @@ struct issue_client return boost::requests::json::get( conn_, urls::format("/repos/{owner}/{repository}/issues/{issue_number}", owner, repository, issue_number), - settings_, ec); + settings_, {}, ec); } template)) CompletionToken> @@ -652,7 +651,7 @@ struct issue_client conn_, urls::format("/repos/{owner}/{repository}/issues/{issue_number}", owner, repository, issue_number), boost::json::value_from(opts, storage()), - settings_, ec); + settings_, {}, ec); } template)) CompletionToken> @@ -729,7 +728,7 @@ struct issue_client } response> list_user_issues(list_issues_query opt, error_code & ec) { - return boost::requests::json::get>(conn_, opt.make_query("/user/issues"), settings_, ec); + return boost::requests::json::get>(conn_, opt.make_query("/user/issues"), settings_, {}, ec); } @@ -745,13 +744,12 @@ struct issue_client boost::json::storage_ptr storage() { - return boost::json::storage_ptr(&memory_); + return boost::json::storage_ptr(); } private: - boost::container::pmr::unsynchronized_pool_resource memory_; boost::requests::cookie_jar jar_; - boost::requests::request_parameters settings_{boost::requests::http::fields {&memory_}, + boost::requests::request_parameters settings_{boost::requests::http::fields{}, {true, boost::requests::redirect_mode::none, 0}, &jar_}; diff --git a/example/link.cpp b/example/link.cpp index b834b7b..4162c5e 100644 --- a/example/link.cpp +++ b/example/link.cpp @@ -6,7 +6,6 @@ // #include #include -#include #include #include #include @@ -41,13 +40,13 @@ int main(int argc, char * argv[]) auto r = requests::get(u); if (r.ok()) { - std::cerr << "Erorr getting events: " << r.headers << r.string_view() << std::endl; + std::cerr << "Error getting events: " << r.headers << r.string_view() << std::endl; return 1; } { - char buf[8096]; - container::pmr::monotonic_buffer_resource res{buf, 8096}; + unsigned char buf[8096]; + json::monotonic_resource res{buf, 8096}; auto j = as_json(r, &res); const json::array & arr = j.as_array(); @@ -66,8 +65,8 @@ int main(int argc, char * argv[]) return 1; } - char buf[8096]; - container::pmr::monotonic_buffer_resource res{buf, 8096}; + unsigned char buf[8096]; + json::monotonic_resource res{buf, 8096}; auto j = as_json(r, &res); const json::array & arr = j.as_array(); diff --git a/example/xml.cpp b/example/xml.cpp index 10c4c6e..3fdf69e 100644 --- a/example/xml.cpp +++ b/example/xml.cpp @@ -62,12 +62,9 @@ struct xml_ref_source final : requests::source namespace pugi { -requests::source_ptr tag_invoke(const requests::make_source_tag&, const pugi::xml_document &doc, - container::pmr::memory_resource * res) +requests::source_ptr tag_invoke(const requests::make_source_tag&, const pugi::xml_document &doc) { - return std::allocate_shared( - container::pmr::polymorphic_allocator(res), - std::move(doc)); + return std::make_shared(std::move(doc)); } diff --git a/include/boost/requests/cookie.hpp b/include/boost/requests/cookie.hpp index 205338c..c344d49 100644 --- a/include/boost/requests/cookie.hpp +++ b/include/boost/requests/cookie.hpp @@ -6,7 +6,6 @@ #define BOOST_REQUESTS_COOKIES_COOKIE_HPP #include -#include #include #include #include @@ -16,26 +15,9 @@ namespace requests { struct cookie { - using allocator_type = boost::container::pmr::polymorphic_allocator; - using string_type = std::basic_string, allocator_type>; + using string_type = std::basic_string>; - cookie(allocator_type && alloc) : name(alloc), value(alloc), domain(alloc), path(alloc) {} cookie(cookie &&) noexcept = default; - - cookie(cookie && val, allocator_type && alloc) - : name(std::move(val.name), alloc), - value(std::move(val.value), alloc), - expiry_time(val.expiry_time), - domain(std::move(val.domain), alloc), - path(std::move(val.path), alloc), - creation_time(val.creation_time), - last_access_time(val.last_access_time), - persistent_flag(val.persistent_flag), - host_only_flag(val.host_only_flag), - secure_only_flag(val.secure_only_flag), - http_only_flag(val.http_only_flag) - {} - string_type name, value; std::chrono::system_clock::time_point expiry_time; string_type domain, path; @@ -121,34 +103,6 @@ auto make_cookie_field(Range && range, StringToken && token = {}) return token.result(); } -struct monotonic_token : urls::grammar::string_token::arg -{ - unsigned char buf[4096]; - boost::container::pmr::monotonic_buffer_resource memres{buf, sizeof(buf)}; - - char * data = nullptr; - std::size_t size = 0u; - - monotonic_token() = default; - monotonic_token(const monotonic_token &) = delete; - - using result_type = core::string_view; - - char * prepare(std::size_t size_) - { - if (size_ == 0u) - return nullptr; - else - return data = static_cast(memres.allocate(size = size_, 1u)); - } - - core::string_view result() const - { - return core::string_view(data, size); - } - -}; - } } diff --git a/include/boost/requests/cookie_jar.hpp b/include/boost/requests/cookie_jar.hpp index 6e0dfec..73964cc 100644 --- a/include/boost/requests/cookie_jar.hpp +++ b/include/boost/requests/cookie_jar.hpp @@ -5,7 +5,6 @@ #ifndef BOOST_REQUESTS_COOKIES_JAR_HPP #define BOOST_REQUESTS_COOKIES_JAR_HPP -#include #include #include #include @@ -17,7 +16,6 @@ #include #include -#include #include namespace boost { @@ -53,7 +51,7 @@ struct cookie_equal struct cookie_jar final { - using allocator_type = boost::container::pmr::polymorphic_allocator; + using allocator_type = std::allocator; boost::unordered_set::template rebind_alloc> content; diff --git a/include/boost/requests/detail/faux_coroutine.hpp b/include/boost/requests/detail/faux_coroutine.hpp index 9957fd9..fd5bfba 100644 --- a/include/boost/requests/detail/faux_coroutine.hpp +++ b/include/boost/requests/detail/faux_coroutine.hpp @@ -17,8 +17,6 @@ #include #include #include -#include -#include #include namespace boost @@ -40,9 +38,6 @@ struct faux_token_t using cancellation_slot_type = asio::cancellation_slot; cancellation_slot_type get_cancellation_slot() const {BOOST_ASSERT(impl_ != nullptr); return impl_->slot;} - using allocator_type = container::pmr::polymorphic_allocator; - allocator_type get_allocator() const {BOOST_ASSERT(impl_ != nullptr); return impl_->get_allocator();} - void operator()() { auto & base = *impl_; @@ -53,7 +48,6 @@ struct faux_token_t { virtual void resume(faux_token_t impl) = 0; asio::cancellation_slot slot; - virtual container::pmr::polymorphic_allocator get_allocator() = 0; }; faux_token_t(const faux_token_t & ) = delete; @@ -77,9 +71,6 @@ struct faux_token_t using cancellation_slot_type = asio::cancellation_slot; cancellation_slot_type get_cancellation_slot() const {BOOST_ASSERT(impl_ != nullptr); return impl_->slot;} - using allocator_type = container::pmr::polymorphic_allocator; - allocator_type get_allocator() const {BOOST_ASSERT(impl_ != nullptr); return impl_->get_allocator();} - void operator()(T1 t1 = {}) { auto & base = *impl_; @@ -124,13 +115,6 @@ struct faux_token_t return impl_->slot; } - using allocator_type = container::pmr::polymorphic_allocator; - allocator_type get_allocator() const - { - BOOST_ASSERT(impl_ != nullptr); - return impl_->get_allocator(); - } - void operator()(T1 t1 = {}, T2 t2 = {}) { auto & base = *impl_; @@ -263,35 +247,8 @@ struct faux_runner } Handler handler; - container::pmr::resource_adaptor_imp< - typename std::allocator_traits>::template rebind_alloc - > alloc_re{asio::get_associated_allocator(handler)}; - Implementation impl; - template - typename token_type::allocator_type get_allocator_impl(T ) - { - - } - - template - typename token_type::allocator_type get_allocator_impl(std::allocator) - { - return {}; - } - - template - typename token_type::allocator_type get_allocator_impl(container::pmr::polymorphic_allocator alloc) - { - return alloc; - } - - typename token_type::allocator_type get_allocator() override - { - return get_allocator_impl(asio::get_associated_allocator(handler)); - } - template impl_(Handler_ && h, Args_ && ... args) @@ -305,7 +262,7 @@ struct faux_runner template impl_(Handler_ && h, const with_allocator_t &, Args_ && ... args) : handler(std::forward(h)) - , impl(get_allocator(), std::forward(args)...) + , impl(std::forward(args)...) { this->token_type::base::slot = asio::get_associated_cancellation_slot(handler); } @@ -313,7 +270,7 @@ struct faux_runner template impl_(Handler_ && h, with_allocator_t &&, Args_ && ... args) : handler(std::forward(h)) - , impl(get_allocator(), std::forward(args)...) + , impl(std::forward(args)...) { this->token_type::base::slot = asio::get_associated_cancellation_slot(handler); } @@ -343,19 +300,6 @@ auto faux_run(Token && token, Args && ... args) faux_runner{}, token, std::forward(args)...); } -template -auto faux_run_with_allocator(Token && token, Args && ... args) -{ - static_assert(std::is_constructible, - Args&&...>::value, - "Can't construct implementation from those args"); - return asio::async_initiate( - faux_runner{}, token, with_allocator, std::forward(args)...); -} - } } diff --git a/include/boost/requests/detail/impl/connection_impl.hpp b/include/boost/requests/detail/impl/connection_impl.hpp index e87d20d..abecbd1 100644 --- a/include/boost/requests/detail/impl/connection_impl.hpp +++ b/include/boost/requests/detail/impl/connection_impl.hpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/include/boost/requests/detail/pmr.hpp b/include/boost/requests/detail/pmr.hpp deleted file mode 100644 index bb4bc80..0000000 --- a/include/boost/requests/detail/pmr.hpp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2022 Klemens D. Morgenstern -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#ifndef BOOST_REQUESTS_DETAIL_PMR_HPP -#define BOOST_REQUESTS_DETAIL_PMR_HPP - -#include -#include - -namespace boost { -namespace requests { -namespace detail { - -struct pmr_deleter -{ - container::pmr::memory_resource *res; - - constexpr pmr_deleter(container::pmr::memory_resource *res = container::pmr::get_default_resource()) noexcept: res( - res) {} - - template - void operator()(T *ptr) - { - ptr->~T(); - res->deallocate(ptr, sizeof(T), alignof(T)); - } -}; - -template -std::unique_ptr make_pmr(container::pmr::memory_resource *res, Args &&... args) -{ - void *raw = res->allocate(sizeof(T), alignof(T)); - try - { - return {new(raw) T(std::forward(args)...), res}; - - } - catch (...) - { - res->deallocate(raw, sizeof(T), alignof(T)); - throw; - } -} - -} -} -} - -#endif //BOOST_REQUESTS_DETAIL_PMR_HPP diff --git a/include/boost/requests/download.hpp b/include/boost/requests/download.hpp index 84db4af..ae8e5d0 100644 --- a/include/boost/requests/download.hpp +++ b/include/boost/requests/download.hpp @@ -237,7 +237,7 @@ struct download_response : response_base { filesystem::path download_path; - download_response(allocator_type allocator = {}) : response_base(allocator) {} + download_response() = default; download_response(http::response_header header, response_base::history_type history, filesystem::path file) : response_base(std::move(header), std::move(history)), download_path(std::move(file)) {} download_response(response_base header, filesystem::path file) : response_base(std::move(header)), download_path(std::move(file)) {} @@ -320,7 +320,7 @@ struct async_download_op : asio::coroutine filesystem::path download_path) : conn(*conn), target(target), req(std::move(req)), download_path(std::move(download_path)) {} - download_response rb{req.get_allocator()}; + download_response rb{}; optional str_; using completion_signature_type = void(system::error_code, download_response); diff --git a/include/boost/requests/fields/location.hpp b/include/boost/requests/fields/location.hpp index 0f117f6..06b6a9b 100644 --- a/include/boost/requests/fields/location.hpp +++ b/include/boost/requests/fields/location.hpp @@ -9,7 +9,6 @@ #define BOOST_REQUESTS_FIELDS_LOCATION_HPP #include -#include #include #include #include diff --git a/include/boost/requests/form.hpp b/include/boost/requests/form.hpp index 9b3316c..eb176ae 100644 --- a/include/boost/requests/form.hpp +++ b/include/boost/requests/form.hpp @@ -48,9 +48,8 @@ struct multi_part_form template form_data(core::string_view name, Source && source, - container::pmr::memory_resource * resource = container::pmr::get_default_resource(), decltype(make_source(std::declval())) * = nullptr) - : name(name), source(make_source(std::forward(source), resource)) + : name(name), source(make_source(std::forward(source))) { } }; diff --git a/include/boost/requests/http.hpp b/include/boost/requests/http.hpp index bb38b28..c50fc92 100644 --- a/include/boost/requests/http.hpp +++ b/include/boost/requests/http.hpp @@ -18,7 +18,6 @@ #include #include #include -#include namespace boost { namespace requests { @@ -30,7 +29,7 @@ using boost::beast::http::status_class; using boost::beast::http::to_status_class; using boost::beast::http::to_string; using boost::beast::http::verb; -using fields = boost::beast::http::basic_fields>; +using fields = boost::beast::http::fields; using file_body = beast::http::file_body; using empty_body = beast::http::empty_body; @@ -43,8 +42,8 @@ using response_header = beast::http::response_header; template using request = beast::http::request ; template using response = beast::http::response; -template using request_parser = beast::http::request_parser >; -template using response_parser = beast::http::response_parser>; +template using request_parser = beast::http::request_parser ; +template using response_parser = beast::http::response_parser; } diff --git a/include/boost/requests/impl/request.hpp b/include/boost/requests/impl/request.hpp index 1b5c357..452aea2 100644 --- a/include/boost/requests/impl/request.hpp +++ b/include/boost/requests/impl/request.hpp @@ -27,7 +27,7 @@ auto request(Connection & conn, system::error_code & ec) -> response { auto s = conn.ropen(method, target, std::forward(body), std::move(req), ec); - response rb{req.get_allocator()}; + response rb{}; if (!ec) s.read( rb.buffer, ec); @@ -56,7 +56,7 @@ struct async_request_op : asio::coroutine typename Connection::request_type req; optional str_; - response rb{req.get_allocator()}; + response rb{}; template async_request_op(Connection * conn, diff --git a/include/boost/requests/impl/source.hpp b/include/boost/requests/impl/source.hpp index b8f9bd6..855a73e 100644 --- a/include/boost/requests/impl/source.hpp +++ b/include/boost/requests/impl/source.hpp @@ -6,7 +6,6 @@ #define BOOST_REQUESTS_IMPL_SOURCE_HPP #include -#include #include #include diff --git a/include/boost/requests/json.hpp b/include/boost/requests/json.hpp index b50aa83..b0c480a 100644 --- a/include/boost/requests/json.hpp +++ b/include/boost/requests/json.hpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -74,13 +73,13 @@ using ::boost::json::storage_ptr; template struct response : response_base { - using allocator_type = container::pmr::polymorphic_allocator; + using allocator_type = std::allocator; using fields_type = http::fields; - response(allocator_type alloc, history_type history, Value && value) : response_base(std::move(alloc), std::move(history)), value(std::move(value)) {} + response(history_type history, Value && value) : response_base(std::move(history)), value(std::move(value)) {} response(http::response_header header, history_type history, Value && value) : response_base(std::move(header), std::move(history)), value(std::move(value)) {} - response(allocator_type alloc , Value && value = {}) : response_base(std::move(alloc)), value(std::move(value)) {} + response(Value && value = {}) : value(std::move(value)) {} response(http::response_header header, Value && value = {}) : response_base(std::move(header)), value(std::move(value)) {} using value_type = Value; @@ -210,11 +209,11 @@ template auto get(Connection & conn, urls::url_view target, - typename Connection::request_type req = {}) -> response + typename Connection::request_type req = {}, + json::storage_ptr ptr = {}) -> response { set_accept_headers(req); // this might be a bet idea - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = conn.ropen(http::verb::get, target, empty{}, std::move(req)); return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr) }; } @@ -225,10 +224,10 @@ template response { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = conn.ropen(http::verb::get, target, empty{}, std::move(req), ec); return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr, ec) }; @@ -244,10 +243,10 @@ template response + typename Connection::request_type req = {}, + json::storage_ptr ptr = {}) -> response { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = conn.ropen(http::verb::post, target, ::boost::json::value_from(std::forward(request_body), ptr), std::move(req)); @@ -261,10 +260,10 @@ auto post(Connection & conn, urls::url_view target, RequestBody && request_body, typename Connection::request_type req, + json::storage_ptr ptr, system::error_code & ec) -> response { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = conn.ropen(http::verb::post, target, ::boost::json::value_from(std::forward(request_body), ptr), std::move(req), ec); @@ -295,10 +294,10 @@ template response + typename Connection::request_type req = {}, + json::storage_ptr ptr = {}) -> response { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = conn.ropen(http::verb::patch, target, ::boost::json::value_from(std::forward(request_body), ptr), std::move(req)); @@ -312,10 +311,10 @@ template response> + typename Connection::request_type req = {}, + json::storage_ptr ptr = {}) -> response> { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = conn.ropen(http::verb::put, target, ::boost::json::value_from(std::forward(request_body), ptr), std::move(req)); @@ -329,10 +328,10 @@ auto put(Connection & conn, urls::url_view target, RequestBody && request_body, typename Connection::request_type req, + json::storage_ptr ptr, system::error_code & ec) -> response> { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = conn.ropen(http::verb::put, target, ::boost::json::value_from(std::forward(request_body), ptr, ec), std::move(req)); @@ -346,10 +345,10 @@ template response> + typename Connection::request_type req = {}, + json::storage_ptr ptr = {}) -> response> { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = conn.ropen(http::verb::delete_, target, ::boost::json::value_from(std::forward(request_body), ptr), std::move(req)); @@ -363,10 +362,10 @@ auto delete_(Connection & conn, urls::url_view target, RequestBody && request_body, typename Connection::request_type req, + json::storage_ptr ptr, system::error_code & ec) -> response> { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = conn.ropen(http::verb::delete_, target, ::boost::json::value_from(std::forward(request_body), ptr, ec), std::move(req)); @@ -380,10 +379,10 @@ template auto delete_(Connection & conn, urls::url_view target, - typename Connection::request_type req = {}) -> response> + typename Connection::request_type req = {}, + json::storage_ptr ptr = {}) -> response> { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = conn.ropen(http::verb::delete_, target, empty{}, std::move(req)); @@ -396,10 +395,10 @@ template response> { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = conn.ropen(http::verb::delete_, target, empty{}, std::move(req), ec); @@ -412,10 +411,10 @@ template auto options(Connection & conn, urls::url_view target, - typename Connection::request_type req = {}) -> response + typename Connection::request_type req = {}, + json::storage_ptr ptr = {}) -> response { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = conn.ropen(http::verb::options, target, empty{}, std::move(req)); return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr) }; } @@ -426,10 +425,10 @@ template response { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = conn.ropen(http::verb::options, target, empty{}, std::move(req), ec); return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr, ec) }; } @@ -437,11 +436,11 @@ auto options(Connection & conn, template auto get(urls::url_view target, - http::fields req = {}) -> response + http::fields req = {}, + json::storage_ptr ptr = {}) -> response { set_accept_headers(req); // this might be a bet idea - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = default_session().ropen(http::verb::get, target, empty{}, std::move(req)); return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr) }; } @@ -450,10 +449,10 @@ auto get(urls::url_view target, template auto get(urls::url_view target, http::fields req, + json::storage_ptr ptr, system::error_code & ec) -> response { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = default_session().ropen(http::verb::get, target, empty{}, std::move(req), ec); return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr, ec) }; @@ -467,10 +466,10 @@ template auto post(urls::url_view target, RequestBody && request_body, - http::fields req = {}) -> response + http::fields req = {}, + json::storage_ptr ptr = {}) -> response { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = default_session().ropen(http::verb::post, target, ::boost::json::value_from(std::forward(request_body), ptr), std::move(req)); @@ -482,10 +481,10 @@ template response { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = default_session().ropen(http::verb::post, target, ::boost::json::value_from(std::forward(request_body), ptr), std::move(req), ec); @@ -497,10 +496,10 @@ template response { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = default_session().ropen(http::verb::patch, target, ::boost::json::value_from(std::forward(request_body), ptr), std::move(req), ec); @@ -512,10 +511,10 @@ template auto patch(urls::url_view target, RequestBody && request_body, - http::fields req = {}) -> response + http::fields req = {}, + json::storage_ptr ptr = {}) -> response { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = default_session().ropen(http::verb::patch, target, ::boost::json::value_from(std::forward(request_body), ptr), std::move(req)); @@ -527,10 +526,10 @@ template auto put(urls::url_view target, RequestBody && request_body, - http::fields req = {}) -> response> + http::fields req = {}, + json::storage_ptr ptr = {}) -> response> { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = default_session().ropen(http::verb::put, target, ::boost::json::value_from(std::forward(request_body), ptr), std::move(req)); @@ -542,10 +541,10 @@ template response> { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = default_session().ropen(http::verb::put, target, ::boost::json::value_from(std::forward(request_body), ptr), std::move(req)); @@ -557,10 +556,10 @@ template auto delete_(urls::url_view target, RequestBody && request_body, - http::fields req = {}) -> response> + http::fields req = {}, + json::storage_ptr ptr = {}) -> response> { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = default_session().ropen(http::verb::delete_, target, ::boost::json::value_from(std::forward(request_body), ptr), std::move(req)); @@ -572,10 +571,10 @@ template response> { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = default_session().ropen(http::verb::delete_, target, ::boost::json::value_from(std::forward(request_body), ptr), std::move(req), ec); @@ -587,10 +586,10 @@ auto delete_(urls::url_view target, template auto delete_(urls::url_view target, - http::fields req = {}) -> response> + http::fields req = {}, + json::storage_ptr ptr = {}) -> response> { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = default_session().ropen(http::verb::delete_, target, empty{}, std::move(req)); @@ -601,10 +600,10 @@ template auto delete_(urls::url_view target, http::fields req, + json::storage_ptr ptr, system::error_code & ec) -> response> { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = default_session().ropen(http::verb::delete_, target, empty{}, std::move(req), ec); @@ -616,10 +615,10 @@ auto delete_(urls::url_view target, template auto options(urls::url_view target, - http::fields req = {}) -> response + http::fields req = {}, + json::storage_ptr ptr = {}) -> response { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = default_session().ropen(http::verb::options, target, empty{}, std::move(req)); return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr) }; } @@ -628,10 +627,10 @@ auto options(urls::url_view target, template auto options(urls::url_view target, http::fields req, + json::storage_ptr ptr, system::error_code & ec) -> response { set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; auto s = default_session().ropen(http::verb::options, target, empty{}, std::move(req), ec); return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr, ec) }; } @@ -770,10 +769,10 @@ struct async_request_json_op : asio::coroutine urls::url_view target; RequestBody && request_body; typename Connection::request_type req; - json::storage_ptr ptr{req.get_allocator().resource()}; + json::storage_ptr ptr{}; optional str_; - response rb{req.get_allocator()}; + response rb; json::value value_from(RequestBody && request_body, std::false_type) const { @@ -789,9 +788,10 @@ struct async_request_json_op : asio::coroutine http::verb method, urls::url_view target, RequestBody && request_body, - typename Connection::request_type req) + typename Connection::request_type req, + json::storage_ptr ptr) : conn(*conn), method(method), target(target), - request_body(std::forward(request_body)), req(std::move(req)) {} + request_body(std::forward(request_body)), req(std::move(req)), ptr(std::move(ptr)) {} response & resume(requests::detail::faux_token_t self, system::error_code & ec, @@ -844,19 +844,20 @@ struct async_request_optional_json_op : asio::coroutine urls::url_view target; RequestBody && request_body; typename Connection::request_type req; - json::storage_ptr ptr{req.get_allocator().resource()}; + json::storage_ptr ptr; optional str_; - response> rb{req.get_allocator()}; + response> rb{}; template async_request_optional_json_op(Connection * conn, http::verb method, urls::url_view target, RequestBody_ && request_body, - typename Connection::request_type req) + typename Connection::request_type req, + json::storage_ptr ptr) : conn(*conn), method(method), target(target), - request_body(std::forward(request_body)), req(std::move(req)) {} + request_body(std::forward(request_body)), req(std::move(req)), ptr(std::move(ptr)) {} response> & resume(requests::detail::faux_token_t self, system::error_code & ec, @@ -905,11 +906,12 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_c async_get(Connection & conn, urls::url_view target, typename Connection::request_type req = {}, + json::storage_ptr ptr = {}, CompletionToken && completion_token = typename Connection::default_token()) { return requests::detail::faux_run>(std::forward(completion_token), - &conn, http::verb::get, target, empty{}, std::move(req)); + &conn, http::verb::get, target, empty{}, std::move(req), std::move(ptr)); } template>>(std::forward(completion_token), - &conn, http::verb::post, target, std::forward(request_body), std::move(req)); + &conn, http::verb::post, target, std::forward(request_body), + std::move(req), std::move(ptr)); } @@ -940,11 +944,13 @@ async_patch(Connection & conn, urls::url_view target, RequestBody && request_body, typename Connection::request_type req = {}, + json::storage_ptr ptr = {}, CompletionToken && completion_token = typename Connection::default_token()) { return requests::detail::faux_run>>(std::forward(completion_token), - &conn, http::verb::patch, target, std::forward(request_body), std::move(req)); + &conn, http::verb::patch, target, std::forward(request_body), + std::move(req), std::move(ptr)); } template>>(std::forward(completion_token), - &conn, http::verb::put, target, std::forward(request_body), std::move(req)); + &conn, http::verb::put, target, std::forward(request_body), + std::move(req), std::move(ptr)); } @@ -975,11 +983,13 @@ async_delete(Connection & conn, urls::url_view target, RequestBody && request_body, typename Connection::request_type req = {}, + json::storage_ptr ptr = {}, CompletionToken && completion_token = typename Connection::default_token()) { return requests::detail::faux_run>>(std::forward(completion_token), - &conn, http::verb::delete_, target, std::forward(request_body), std::move(req)); + &conn, http::verb::delete_, target, std::forward(request_body), + std::move(req), std::move(ptr)); } @@ -992,11 +1002,13 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_c async_delete(Connection & conn, urls::url_view target, typename Connection::request_type req = {}, + json::storage_ptr ptr = {}, CompletionToken && completion_token = typename Connection::default_token()) { return requests::detail::faux_run>(std::forward(completion_token), - &conn, http::verb::delete_, target, empty{}, std::move(req)); + &conn, http::verb::delete_, target, empty{}, + std::move(req), std::move(ptr)); } @@ -1008,11 +1020,13 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_c async_options(Connection & conn, urls::url_view target, typename Connection::request_type req = {}, + json::storage_ptr ptr = {}, CompletionToken && completion_token = typename Connection::default_token()) { return requests::detail::faux_run>(std::forward(completion_token), - &conn, http::verb::options, target, empty{}, std::move(req)); + &conn, http::verb::options, target, empty{}, + std::move(req), std::move(ptr)); } namespace detail @@ -1048,12 +1062,13 @@ template)) async_get(urls::url_view target, http::fields req, + json::storage_ptr ptr, CompletionToken && completion_token) { return asio::async_initiate( detail::async_free_request_op{}, completion_token, - http::verb::get, target, empty{}, std::move(req)); + http::verb::get, target, empty{}, std::move(req), std::move(ptr)); } template( detail::async_free_request_op{}, completion_token, - http::verb::post, target, std::forward(request_body), std::move(req)); + http::verb::post, target, std::forward(request_body), std::move(req), std::move(ptr)); } @@ -1080,12 +1096,13 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_c async_patch(urls::url_view target, RequestBody && request_body, http::fields req, + json::storage_ptr ptr, CompletionToken && completion_token) { return asio::async_initiate)>( detail::async_free_request_op{}, completion_token, - http::verb::patch, target, std::forward(request_body), std::move(req)); + http::verb::patch, target, std::forward(request_body), std::move(req), std::move(ptr)); } template)>( detail::async_free_request_op{}, completion_token, - http::verb::put, target, std::forward(request_body), std::move(req)); + http::verb::put, target, std::forward(request_body), std::move(req), std::move(ptr)); } @@ -1112,12 +1130,13 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_c async_delete(urls::url_view target, RequestBody && request_body, http::fields req, + json::storage_ptr ptr, CompletionToken && completion_token) { return asio::async_initiate)>( detail::async_free_request_op{}, completion_token, - http::verb::delete_, target, std::forward(request_body), std::move(req)); + http::verb::delete_, target, std::forward(request_body), std::move(req), std::move(ptr)); } @@ -1127,12 +1146,13 @@ template)) async_delete(urls::url_view target, http::fields req, + json::storage_ptr ptr, CompletionToken && completion_token) { return asio::async_initiate)>( detail::async_free_request_op{}, completion_token, - http::verb::delete_, target, empty{}, std::move(req)); + http::verb::delete_, target, empty{}, std::move(req), std::move(ptr)); } @@ -1141,12 +1161,13 @@ template)) async_options(urls::url_view target, http::fields req, + json::storage_ptr ptr, CompletionToken && completion_token) { return asio::async_initiate)>( detail::async_free_request_op{}, completion_token, - http::verb::options, target, empty{}, std::move(req)); + http::verb::options, target, empty{}, std::move(req), std::move(ptr)); } diff --git a/include/boost/requests/method.hpp b/include/boost/requests/method.hpp index 0a2cf26..26949b7 100644 --- a/include/boost/requests/method.hpp +++ b/include/boost/requests/method.hpp @@ -24,7 +24,7 @@ auto get(Connection & conn, urls::url_view target, typename Connection::request_type req = {}) -> response { - response res{req.get_allocator()}; + response res{}; auto s = conn.ropen(http::verb::get, target, empty{}, std::move(req)); s.read(res.buffer); res.headers = std::move(s).headers(); @@ -39,7 +39,7 @@ auto get(Connection & conn, typename Connection::request_type req, system::error_code & ec) -> response { - response res{req.get_allocator()}; + response res{}; auto s = conn.ropen(http::verb::get, target, empty{}, std::move(req), ec); if (!ec) s.read(res.buffer, ec); @@ -74,7 +74,7 @@ auto post(Connection & conn, RequestBody && request_body, typename Connection::request_type req = {}) -> response { - response res{req.get_allocator()}; + response res{}; auto s = conn.ropen(http::verb::post, target, std::forward(request_body), std::move(req)); s.read(res.buffer); res.headers = std::move(s).headers(); @@ -89,7 +89,7 @@ auto post(Connection & conn, typename Connection::request_type req, system::error_code & ec) -> response { - response res{req.get_allocator()}; + response res{}; auto s = conn.ropen(http::verb::post, target, std::forward(request_body), std::move(req), ec); if (!ec) s.read(res.buffer, ec); @@ -104,7 +104,7 @@ auto patch(Connection & conn, urls::url_view target, typename Connection::request_type req = {}) -> response { - response res{req.get_allocator()}; + response res{}; auto s = conn.ropen(http::verb::patch, target, empty{}, std::move(req)); s.read(res.buffer); res.headers = std::move(s).headers(); @@ -118,7 +118,7 @@ auto patch(Connection & conn, typename Connection::request_type req, system::error_code & ec) -> response { - response res{req.get_allocator()}; + response res{}; auto s = conn.ropen(http::verb::patch, target, empty{}, std::move(req), ec); if (!ec) s.read(res.buffer, ec); @@ -133,7 +133,7 @@ auto patch(Connection & conn, RequestBody && request_body, typename Connection::request_type req = {}) -> response { - response res{req.get_allocator()}; + response res{}; auto s = conn.ropen(http::verb::patch, target, std::forward(request_body), std::move(req)); s.read(res.buffer); res.headers = std::move(s).headers(); @@ -148,7 +148,7 @@ auto patch(Connection & conn, typename Connection::request_type req, system::error_code & ec) -> response { - response res{req.get_allocator()}; + response res{}; auto s = conn.ropen(http::verb::patch, target, std::forward(request_body), std::move(req), ec); if (!ec) s.read(res.buffer, ec); @@ -164,7 +164,7 @@ auto put(Connection & conn, RequestBody && request_body, typename Connection::request_type req = {}) -> response { - response res{req.get_allocator()}; + response res{}; auto s = conn.ropen(http::verb::put, target, std::forward(request_body), std::move(req)); s.read(res.buffer); res.headers = std::move(s).headers(); @@ -179,7 +179,7 @@ auto put(Connection & conn, typename Connection::request_type req, system::error_code & ec) -> response { - response res{req.get_allocator()}; + response res{}; auto s = conn.ropen(http::verb::put, target, std::forward(request_body), std::move(req), ec); if (!ec) s.read(res.buffer, ec); @@ -193,7 +193,7 @@ auto delete_(Connection & conn, urls::url_view target, typename Connection::request_type req = {}) -> response { - response res{req.get_allocator()}; + response res{}; auto s = conn.ropen(http::verb::delete_, target, empty{}, std::move(req)); s.read(res.buffer); res.headers = std::move(s).headers(); @@ -207,7 +207,7 @@ auto delete_(Connection & conn, typename Connection::request_type req, system::error_code & ec) -> response { - response res{req.get_allocator()}; + response res{}; auto s = conn.ropen(http::verb::delete_, target, empty{}, std::move(req), ec); if (!ec) s.read(res.buffer, ec); @@ -225,7 +225,7 @@ auto delete_(Connection & conn, -> typename std::enable_if_t, typename Connection::request_type>::value, response> { - response res{req.get_allocator()}; + response res{}; auto s = conn.ropen(http::verb::delete_, target, std::forward(request_body), std::move(req)); s.read(res.buffer); res.headers = std::move(s).headers(); @@ -240,7 +240,7 @@ auto delete_(Connection & conn, typename Connection::request_type req, system::error_code & ec) -> response { - response res{req.get_allocator()}; + response res{}; auto s = conn.ropen(http::verb::delete_, target, std::forward(request_body), std::move(req), ec); if (!ec) s.read(res.buffer, ec); @@ -273,7 +273,7 @@ auto options(Connection & conn, urls::url_view target, typename Connection::request_type req = {}) -> response { - response res{req.get_allocator()}; + response res{}; auto s = conn.ropen(http::verb::options, target, empty{}, std::move(req)); s.read(res.buffer); res.headers = std::move(s).headers(); @@ -287,7 +287,7 @@ auto options(Connection & conn, typename Connection::request_type req, system::error_code & ec) -> response { - response res{req.get_allocator()}; + response res{}; auto s = conn.ropen(http::verb::options, target, empty{}, std::move(req), ec); if (!ec) s.read(res.buffer, ec); diff --git a/include/boost/requests/request_parameters.hpp b/include/boost/requests/request_parameters.hpp index df72423..e252e50 100644 --- a/include/boost/requests/request_parameters.hpp +++ b/include/boost/requests/request_parameters.hpp @@ -54,12 +54,10 @@ inline field_entry bearer(core::string_view token) return fe; } -inline auto headers(std::initializer_list fields, - boost::container::pmr::memory_resource * res = boost::container::pmr::get_default_resource()) - -> beast::http::basic_fields> +inline auto headers(std::initializer_list fields) + -> beast::http::fields { - using allocator_type = boost::container::pmr::polymorphic_allocator; - beast::http::basic_fields f{allocator_type{res}}; + beast::http::fields f; for (const auto & init : fields) if (init.field != http::field::unknown) f.set(init.field, init.value); @@ -71,9 +69,7 @@ inline auto headers(std::initializer_list fields, struct request_parameters { //Allocator - using allocator_type = boost::container::pmr::polymorphic_allocator; - allocator_type get_allocator() const {return fields.get_allocator();} - using fields_type = beast::http::basic_fields; + using fields_type = beast::http::fields; fields_type fields; request_options opts{}; cookie_jar * jar = nullptr; diff --git a/include/boost/requests/response.hpp b/include/boost/requests/response.hpp index c1c42af..6ec1bc3 100644 --- a/include/boost/requests/response.hpp +++ b/include/boost/requests/response.hpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -36,7 +35,7 @@ namespace requests struct response_base { - using allocator_type = container::pmr::polymorphic_allocator; + using allocator_type = std::allocator; using buffer_type = beast::basic_flat_buffer; using body_type = beast::http::basic_dynamic_body; @@ -45,16 +44,15 @@ struct response_base int result_code() const {return headers.result_int(); } http::status result() const {return headers.result(); } - using string_body_type = typename beast::http::basic_string_body, allocator_type>; - using vector_alloc = boost::container::pmr::polymorphic_allocator>; - using history_type = std::vector, vector_alloc>; - history_type history{vector_alloc{headers.get_allocator()}}; + using string_body_type = typename beast::http::basic_string_body>; + using history_type = std::vector>; + history_type history{}; - response_base(allocator_type alloc, history_type history) : headers(alloc), history(std::move(history)) {} + response_base(history_type history) : history(std::move(history)) {} response_base(http::response_header header, history_type history) : headers(std::move(header)), history(std::move(history)) {} + response_base(http::response_header header) : headers(std::move(header)) {} - response_base(allocator_type alloc ) : headers(alloc), history (vector_alloc{alloc}) {} - response_base(http::response_header header) : headers(std::move(header)), history (vector_alloc{headers.get_allocator()}) {} + response_base() = default; ~response_base() = default; @@ -135,7 +133,7 @@ struct response : response_base { buffer_type buffer{headers.get_allocator()}; - response(allocator_type alloc = {}) : response_base(alloc), buffer(alloc) {} + response() = default; response(http::response_header header, buffer_type buffer) : response_base(std::move(header)), buffer(std::move(buffer)) {} response(response_base header, buffer_type buffer) : response_base(std::move(header)), buffer(std::move(buffer)) {} diff --git a/include/boost/requests/session.hpp b/include/boost/requests/session.hpp index c344679..ad9a914 100644 --- a/include/boost/requests/session.hpp +++ b/include/boost/requests/session.hpp @@ -7,9 +7,6 @@ #define BOOST_REQUESTS_BASIC_SESSION_HPP #include -#include -#include -#include namespace boost @@ -167,8 +164,7 @@ struct session detail::url_hash> pools_; // this isn't great - boost::container::pmr::synchronized_pool_resource pmr_; - cookie_jar jar_{boost::container::pmr::polymorphic_allocator(&pmr_)}; + cookie_jar jar_{}; struct async_get_pool_op; struct async_ropen_op; @@ -211,7 +207,7 @@ struct session::defaulted : session urls::url_view path, source & src, http::fields & headers, - CompletionToken && completion_token ) + CompletionToken && completion_token) { return session::async_ropen(method, path, src, headers, detail::with_defaulted_token(std::forward(completion_token))); diff --git a/include/boost/requests/source.hpp b/include/boost/requests/source.hpp index 27594ee..8c6b36e 100644 --- a/include/boost/requests/source.hpp +++ b/include/boost/requests/source.hpp @@ -7,7 +7,6 @@ #include #include -#include #include #include #include @@ -38,28 +37,15 @@ inline auto tag_invoke(const make_source_tag&, source_ptr s) return s; } -inline auto tag_invoke(const make_source_tag&, source_ptr s, - container::pmr::memory_resource *) -{ - return s; -} template -auto make_source(Source && source, container::pmr::memory_resource * = container::pmr::get_default_resource()) +auto make_source(Source && source) -> decltype(tag_invoke(make_source_tag{}, std::declval())) { return tag_invoke(make_source_tag{}, std::forward(source)); } -template -auto make_source(Source && source, container::pmr::memory_resource * res = container::pmr::get_default_resource()) - -> decltype(tag_invoke(make_source_tag{}, std::declval(), res)) -{ - return tag_invoke(make_source_tag{}, std::forward(source), res); -} - - template std::size_t write_request( Stream & stream, @@ -101,32 +87,31 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(system::error_code, std using empty = beast::http::empty_body::value_type; BOOST_REQUESTS_DECL -source_ptr tag_invoke(make_source_tag, asio::const_buffer cb, container::pmr::memory_resource * res); +source_ptr tag_invoke(make_source_tag, asio::const_buffer cb); BOOST_REQUESTS_DECL source_ptr tag_invoke(const make_source_tag&, const empty &); BOOST_REQUESTS_DECL source_ptr tag_invoke(const make_source_tag&, const none_t &); BOOST_REQUESTS_DECL -source_ptr tag_invoke(const make_source_tag&, const filesystem::path & path, container::pmr::memory_resource * res); -source_ptr tag_invoke(const make_source_tag&, filesystem::path &&, container::pmr::memory_resource * res) = delete; +source_ptr tag_invoke(const make_source_tag&, const filesystem::path & path); +source_ptr tag_invoke(const make_source_tag&, filesystem::path &&) = delete; BOOST_REQUESTS_DECL -source_ptr tag_invoke(make_source_tag, struct form form_, container::pmr::memory_resource * res); +source_ptr tag_invoke(make_source_tag, struct form form_); BOOST_REQUESTS_DECL -source_ptr tag_invoke(make_source_tag, struct multi_part_form mpf, container::pmr::memory_resource * res); +source_ptr tag_invoke(make_source_tag, struct multi_part_form mpf); BOOST_REQUESTS_DECL -source_ptr tag_invoke(make_source_tag, boost::json::value value, container::pmr::memory_resource * res); +source_ptr tag_invoke(make_source_tag, boost::json::value value); BOOST_REQUESTS_DECL -source_ptr tag_invoke(make_source_tag, boost::json::object obj, container::pmr::memory_resource * res); +source_ptr tag_invoke(make_source_tag, boost::json::object obj); BOOST_REQUESTS_DECL -source_ptr tag_invoke(make_source_tag, boost::json::array arr, container::pmr::memory_resource * res); +source_ptr tag_invoke(make_source_tag, boost::json::array arr); BOOST_REQUESTS_DECL -source_ptr tag_invoke(make_source_tag, const boost::json::string & arr, - container::pmr::memory_resource * res); -source_ptr tag_invoke(make_source_tag, boost::json::string &&str, container::pmr::memory_resource * res) = delete; +source_ptr tag_invoke(make_source_tag, const boost::json::string & str); +source_ptr tag_invoke(make_source_tag, boost::json::string &&str) = delete; diff --git a/include/boost/requests/sources/buffer.hpp b/include/boost/requests/sources/buffer.hpp index acca839..30320e6 100644 --- a/include/boost/requests/sources/buffer.hpp +++ b/include/boost/requests/sources/buffer.hpp @@ -39,7 +39,7 @@ struct buffer_source : source }; BOOST_REQUESTS_DECL -source_ptr tag_invoke(make_source_tag, asio::const_buffer cb, container::pmr::memory_resource * res); +source_ptr tag_invoke(make_source_tag, asio::const_buffer cb); } } diff --git a/include/boost/requests/sources/form.hpp b/include/boost/requests/sources/form.hpp index 5f10c70..e914607 100644 --- a/include/boost/requests/sources/form.hpp +++ b/include/boost/requests/sources/form.hpp @@ -91,10 +91,10 @@ struct multi_part_form_source final : source }; BOOST_REQUESTS_DECL -source_ptr tag_invoke(make_source_tag, form form_, container::pmr::memory_resource * res); +source_ptr tag_invoke(make_source_tag, form form_); BOOST_REQUESTS_DECL -source_ptr tag_invoke(make_source_tag, multi_part_form mpf, container::pmr::memory_resource * res); +source_ptr tag_invoke(make_source_tag, multi_part_form mpf); } diff --git a/include/boost/requests/sources/string.hpp b/include/boost/requests/sources/string.hpp index bfebdd5..fa746af 100644 --- a/include/boost/requests/sources/string.hpp +++ b/include/boost/requests/sources/string.hpp @@ -60,11 +60,9 @@ struct basic_string_source final : source template inline source_ptr tag_invoke( - make_source_tag, std::basic_string data, - container::pmr::memory_resource * res) + make_source_tag, std::basic_string data) { - return std::allocate_shared>( - container::pmr::polymorphic_allocator(res), std::move(data)); + return std::allocate_shared>(std::move(data)); } } diff --git a/include/boost/requests/sources/string_view.hpp b/include/boost/requests/sources/string_view.hpp index b71e905..15dce85 100644 --- a/include/boost/requests/sources/string_view.hpp +++ b/include/boost/requests/sources/string_view.hpp @@ -61,49 +61,38 @@ struct basic_string_view_source final : source }; template -source_ptr tag_invoke(make_source_tag, const char (&data)[N], container::pmr::memory_resource * res) +source_ptr tag_invoke(make_source_tag, const char (&data)[N]) { - return std::allocate_shared>( - container::pmr::polymorphic_allocator(res), data); + return std::make_shared>(data); } template -source_ptr tag_invoke(make_source_tag, const core::basic_string_view & data, - container::pmr::memory_resource * res) +source_ptr tag_invoke(make_source_tag, const core::basic_string_view & data) { - return std::allocate_shared>( - container::pmr::polymorphic_allocator(res), std::move(data)); + return std::make_shared>(std::move(data)); } template -source_ptr tag_invoke(make_source_tag, core::basic_string_view && data, - container::pmr::memory_resource * res) = delete; +source_ptr tag_invoke(make_source_tag, core::basic_string_view && data) = delete; template -source_ptr tag_invoke(make_source_tag, const boost::basic_string_view & data, - container::pmr::memory_resource * res) +source_ptr tag_invoke(make_source_tag, const boost::basic_string_view & data) { - return std::allocate_shared>( - container::pmr::polymorphic_allocator(res), std::move(data)); + return std::make_shared>(std::move(data)); } template -source_ptr tag_invoke(make_source_tag, boost::basic_string_view && data, - container::pmr::memory_resource * res) = delete; +source_ptr tag_invoke(make_source_tag, boost::basic_string_view && data) = delete; #if defined(__cpp_lib_string_view) template -source_ptr tag_invoke(make_source_tag, const std::basic_string_view & data, - container::pmr::memory_resource * res) +source_ptr tag_invoke(make_source_tag, const std::basic_string_view & data) { - return std::allocate_shared>( - container::pmr::polymorphic_allocator(res), - core::basic_string_view(data.data(), data.size())); + return std::make_shared>(core::basic_string_view(data.data(), data.size())); } template -source_ptr tag_invoke(make_source_tag, std::basic_string_view && data, - container::pmr::memory_resource * res) = delete; +source_ptr tag_invoke(make_source_tag, std::basic_string_view && data) = delete; #endif } diff --git a/include/boost/requests/stream.hpp b/include/boost/requests/stream.hpp index 67d771b..8ef958e 100644 --- a/include/boost/requests/stream.hpp +++ b/include/boost/requests/stream.hpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -173,8 +172,7 @@ struct stream std::shared_ptr impl_; detail::lock_guard lock_; - std::unique_ptr, - detail::pmr_deleter> parser_; + std::unique_ptr> parser_; history_type history_; template diff --git a/src/cookie_jar.cpp b/src/cookie_jar.cpp index 51f1b85..105a87f 100644 --- a/src/cookie_jar.cpp +++ b/src/cookie_jar.cpp @@ -50,7 +50,7 @@ bool cookie_jar::set(const set_cookie & set, // https://www.rfc-editor.org/rfc/rfc6265#section-5.3 // 2. Create a new cookie with name cookie-name, value cookie-value. - cookie sc{content.get_allocator()}; + cookie sc{}; sc.name = set.name; sc.value = set.value; const auto now = std::chrono::system_clock::now(); diff --git a/src/detail/connection_impl.cpp b/src/detail/connection_impl.cpp index 1485ef1..4b673b6 100644 --- a/src/detail/connection_impl.cpp +++ b/src/detail/connection_impl.cpp @@ -111,8 +111,7 @@ auto connection_impl::ropen(beast::http::verb method, lock_type read_lock; if (jar) { - detail::monotonic_token mv; - auto cc = jar->get(host(), is_secure, path, mv); + auto cc = jar->get(host(), is_secure, path); if (!cc.empty()) headers.set(http::field::cookie, cc); else @@ -178,8 +177,7 @@ auto connection_impl::ropen(beast::http::verb method, return stream{get_executor(), nullptr}; stream str{get_executor(), shared_from_this()}; - str.parser_ = detail::make_pmr>(headers.get_allocator().resource(), - http::response_header{http::fields(headers.get_allocator())}); + str.parser_ = std::make_unique>(http::response_header{http::fields(headers.get_allocator())}); str.parser_->body_limit(boost::none); if (use_ssl_) beast::http::read_header(next_layer_, buffer_, *str.parser_, ec); @@ -261,8 +259,7 @@ auto connection_impl::ropen(beast::http::verb method, path = url->encoded_resource(); if (jar) { - detail::monotonic_token mv; - auto cc = jar->get(host(), is_secure, url->encoded_path(), mv); + auto cc = jar->get(host(), is_secure, url->encoded_path()); if (!cc.empty()) headers.set(http::field::cookie, cc); @@ -302,8 +299,7 @@ auto connection_impl::async_ropen_op::resume( if (jar) { - detail::monotonic_token mv; - auto cc = jar->get(this_->host(), this_->use_ssl_, path, mv); + auto cc = jar->get(this_->host(), this_->use_ssl_, path); if (!cc.empty()) headers.set(http::field::cookie, cc); else @@ -354,8 +350,7 @@ auto connection_impl::async_ropen_op::resume( // END OF write impl str.emplace(this_->get_executor(), this_); // , req.get_allocator().resource() - str->parser_ = detail::make_pmr>(headers.get_allocator().resource(), - http::response_header{http::fields(headers.get_allocator())}); + str->parser_ = std::make_unique>(http::response_header{http::fields(headers.get_allocator())}); str->parser_->body_limit(boost::none); if (this_->use_ssl_) { @@ -442,8 +437,7 @@ auto connection_impl::async_ropen_op::resume( path = url->encoded_resource(); if (jar) { - detail::monotonic_token mv; - auto cc = jar->get(this_->host(), this_->use_ssl_, url->encoded_path(), mv); + auto cc = jar->get(this_->host(), this_->use_ssl_, url->encoded_path()); if (!cc.empty()) headers.set(http::field::cookie, cc); else diff --git a/src/detail/mutex.cpp b/src/detail/mutex.cpp index a518247..87da645 100644 --- a/src/detail/mutex.cpp +++ b/src/detail/mutex.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include diff --git a/src/session.cpp b/src/session.cpp index e7a3a45..38f4c6c 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -48,8 +48,7 @@ auto session::ropen( } { - detail::monotonic_token mv; - auto cc = jar_.get(host, is_secure, url.encoded_path(), mv); + auto cc = jar_.get(host, is_secure, url.encoded_path()); if (!cc.empty()) headers.set(http::field::cookie, cc); } @@ -118,8 +117,7 @@ auto session::ropen( { - detail::monotonic_token mv; - auto cc = jar_.get(host, is_secure, url.encoded_path(), mv); + auto cc = jar_.get(host, is_secure, url.encoded_path()); if (!cc.empty()) headers.set(http::field::cookie, cc); } @@ -168,9 +166,6 @@ urls::url session::normalize_(urls::url_view in) auto session::get_pool(urls::url_view url, error_code & ec) -> std::shared_ptr { - // can be optimized to ellide the string allocation, blabla (pmr?) - char buf[1024]; - container::pmr::monotonic_buffer_resource res{buf, sizeof(buf)}; auto host_key = normalize_(url); auto lock = detail::lock(mutex_, ec); @@ -233,8 +228,7 @@ auto session::async_ropen_op::resume(requests::detail::faux_token_tjar_.get(url.encoded_host(), is_secure, url.encoded_path(), mv); + auto cc = this_->jar_.get(url.encoded_host(), is_secure, url.encoded_path()); if (!cc.empty()) headers.set(http::field::cookie, cc); } @@ -301,8 +295,7 @@ auto session::async_ropen_op::resume(requests::detail::faux_token_tjar_.get(url.encoded_host(), is_secure, url.encoded_path(), mv); + auto cc = this_->jar_.get(url.encoded_host(), is_secure, url.encoded_path()); if (!cc.empty()) headers.set(http::field::cookie, cc); else diff --git a/src/sources/buffer.cpp b/src/sources/buffer.cpp index 5605069..cbe7bea 100644 --- a/src/sources/buffer.cpp +++ b/src/sources/buffer.cpp @@ -14,9 +14,9 @@ namespace requests namespace sources { -source_ptr tag_invoke(make_source_tag, asio::const_buffer cb, container::pmr::memory_resource * res) +source_ptr tag_invoke(make_source_tag, asio::const_buffer cb) { - return std::allocate_shared(container::pmr::polymorphic_allocator(res), cb); + return std::make_shared(cb); } diff --git a/src/sources/file.cpp b/src/sources/file.cpp index 8f2f188..105dedd 100644 --- a/src/sources/file.cpp +++ b/src/sources/file.cpp @@ -19,10 +19,9 @@ file_source::file_source(const filesystem::path & file) : path(file.c_str()) this->file.open(file.string().c_str(), beast::file_mode::read, ec); } -source_ptr tag_invoke(const make_source_tag &tag, const filesystem::path & path, - container::pmr::memory_resource * res) +source_ptr tag_invoke(const make_source_tag &tag, const filesystem::path & path) { - return std::allocate_shared(container::pmr::polymorphic_allocator(res), path); + return std::make_shared(path); } } diff --git a/src/sources/form.cpp b/src/sources/form.cpp index f7bed89..4cb92a0 100644 --- a/src/sources/form.cpp +++ b/src/sources/form.cpp @@ -33,18 +33,14 @@ std::array make_boundary_value() } -source_ptr tag_invoke(make_source_tag, form form_, container::pmr::memory_resource * res) +source_ptr tag_invoke(make_source_tag, form form_) { - return std::allocate_shared( - container::pmr::polymorphic_allocator(res), - std::move(form_)); + return std::make_shared(std::move(form_)); } -source_ptr tag_invoke(make_source_tag, multi_part_form mpf, container::pmr::memory_resource * res) +source_ptr tag_invoke(make_source_tag, multi_part_form mpf) { - return std::allocate_shared( - container::pmr::polymorphic_allocator(res), - std::move(mpf)); + return std::make_shared(std::move(mpf)); } optional multi_part_form_source::size() const diff --git a/src/sources/json.cpp b/src/sources/json.cpp index 7177012..f1cb5c2 100644 --- a/src/sources/json.cpp +++ b/src/sources/json.cpp @@ -35,27 +35,27 @@ json_source::~json_source() = default; BOOST_REQUESTS_DECL -source_ptr tag_invoke(make_source_tag, boost::json::value value, container::pmr::memory_resource * res) +source_ptr tag_invoke(make_source_tag, boost::json::value value) { - return std::allocate_shared(container::pmr::polymorphic_allocator(res), std::move(value)); + return std::make_shared(std::move(value)); } BOOST_REQUESTS_DECL -source_ptr tag_invoke(make_source_tag, boost::json::object obj, container::pmr::memory_resource * res) +source_ptr tag_invoke(make_source_tag, boost::json::object obj) { - return std::allocate_shared(container::pmr::polymorphic_allocator(res), std::move(obj)); + return std::make_shared(std::move(obj)); } BOOST_REQUESTS_DECL -source_ptr tag_invoke(make_source_tag, boost::json::array arr, container::pmr::memory_resource * res) +source_ptr tag_invoke(make_source_tag, boost::json::array arr) { - return std::allocate_shared(container::pmr::polymorphic_allocator(res), std::move(arr)); + return std::make_shared(std::move(arr)); } BOOST_REQUESTS_DECL -source_ptr tag_invoke(make_source_tag, const boost::json::string & arr, container::pmr::memory_resource * res) +source_ptr tag_invoke(make_source_tag, const boost::json::string & arr) { - return std::allocate_shared(container::pmr::polymorphic_allocator(res), std::move(arr)); + return std::make_shared(std::move(arr)); } diff --git a/test/detail/mutex.cpp b/test/detail/mutex.cpp index 6b383b9..352bfa2 100644 --- a/test/detail/mutex.cpp +++ b/test/detail/mutex.cpp @@ -188,12 +188,6 @@ TEST_CASE("cancel_twice") { std::vector ecs; - using allocator_type = container::pmr::polymorphic_allocator; - allocator_type get_allocator() override - { - return container::pmr::polymorphic_allocator{container::pmr::get_default_resource()}; - } - void resume(boost::requests::detail::faux_token_t tk, error_code ec) override { ecs.push_back(ec); @@ -247,11 +241,7 @@ TEST_CASE("cancel_lock") { std::vector ecs; - using allocator_type = container::pmr::polymorphic_allocator; - allocator_type get_allocator() override - { - return container::pmr::polymorphic_allocator{container::pmr::get_default_resource()}; - } + void resume(boost::requests::detail::faux_token_t tk, error_code ec) override { @@ -302,12 +292,6 @@ TEST_CASE("cancel_one") { std::vector ecs; - using allocator_type = container::pmr::polymorphic_allocator; - allocator_type get_allocator() override - { - return container::pmr::polymorphic_allocator{container::pmr::get_default_resource()}; - } - void resume(boost::requests::detail::faux_token_t tk, error_code ec) override { ecs.push_back(ec); diff --git a/test/json.cpp b/test/json.cpp index 7c6e9ef..f36efc5 100644 --- a/test/json.cpp +++ b/test/json.cpp @@ -114,7 +114,7 @@ void json_request_connection(bool https) { error_code ec; auto res = requests::json::get(hc, urls::url_view("/redirect/10"), - {{}, {false, requests::redirect_mode::private_domain, 5}}, ec); + {{}, {false, requests::redirect_mode::private_domain, 5}}, {}, ec); CHECK_HTTP_RESULT(res.headers); CHECK(res.history.size() == 5); CHECK(res.headers.begin() == res.headers.end()); @@ -184,7 +184,7 @@ void run_json_tests(error_code ec, { requests::json::async_get( hc, urls::url_view("/get"), - requests::request_parameters{requests::headers({{"Test-Header", "it works"}}), {false}}, + requests::request_parameters{requests::headers({{"Test-Header", "it works"}}), {false}}, {}, tracker( [url](error_code ec, requests::json::response<> hdr) { @@ -199,7 +199,7 @@ void run_json_tests(error_code ec, requests::json::async_get( hc, urls::url_view("/redirect-to?url=%2Fget"), - requests::request_parameters{requests::headers({{"Test-Header", "it works"}}), {false}}, + requests::request_parameters{requests::headers({{"Test-Header", "it works"}}), {false}}, {}, tracker( [url](error_code ec, requests::json::response<> hdr) { @@ -216,7 +216,7 @@ void run_json_tests(error_code ec, requests::json::async_get( - hc, urls::url_view("/redirect/10"), {{}, {false, requests::redirect_mode::private_domain, 5}}, + hc, urls::url_view("/redirect/10"), {{}, {false, requests::redirect_mode::private_domain, 5}},{}, tracker( [url](error_code ec, requests::json::response<> res) { @@ -228,7 +228,7 @@ void run_json_tests(error_code ec, requests::json::async_delete( - hc, urls::url_view("/delete"), json::value{{"test-key", "test-value"}}, {{}, {false}}, + hc, urls::url_view("/delete"), json::value{{"test-key", "test-value"}}, {{}, {false}}, {}, tracker( [url](error_code ec, requests::json::response<> hdr) { @@ -240,7 +240,7 @@ void run_json_tests(error_code ec, })); requests::json::async_patch( - hc, urls::url_view("/patch"), json::value {{"test-key", "test-value"}}, {{}, {false}}, + hc, urls::url_view("/patch"), json::value {{"test-key", "test-value"}}, {{}, {false}}, {}, tracker( [url](error_code ec, requests::json::response<> hdr) { @@ -253,7 +253,7 @@ void run_json_tests(error_code ec, })); requests::json::async_put( - hc, urls::url_view("/put"), json::value{{"test-key", "test-value"}}, {{}, {false}}, + hc, urls::url_view("/put"), json::value{{"test-key", "test-value"}}, {{}, {false}}, {}, tracker( [url](error_code ec, requests::json::response> hdr) { @@ -267,7 +267,7 @@ void run_json_tests(error_code ec, })); requests::json::async_post( - hc, urls::url_view("/post"), json::value{{"test-key", "test-value"}}, {{}, {false}}, + hc, urls::url_view("/post"), json::value{{"test-key", "test-value"}}, {{}, {false}}, {}, tracker( [url](error_code ec, requests::json::response<> hdr) { diff --git a/test/source.cpp b/test/source.cpp index 470ec00..f8e49d1 100644 --- a/test/source.cpp +++ b/test/source.cpp @@ -31,8 +31,7 @@ TEST_CASE("sync") std::thread thr{ [&]{ system::error_code ec; - auto sp = tag_invoke(requests::make_source_tag{}, json::value{"foobaria"}, - container::pmr::get_default_resource()); + auto sp = tag_invoke(requests::make_source_tag{}, json::value{"foobaria"}); requests::http::fields hd; write_request(wp, requests::http::verb::post, "/test", hd, From c5c9c9b5f60f424635ae82329ab9dd7530483f22 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 19 Sep 2023 08:31:36 +0800 Subject: [PATCH 69/76] redid connection ownership (intrusive_ptr solution). --- include/boost/requests/connection.hpp | 22 +- include/boost/requests/connection_pool.hpp | 36 +-- .../requests/detail/condition_variable.hpp | 40 +++- .../boost/requests/detail/connection_impl.hpp | 36 ++- .../boost/requests/detail/faux_coroutine.hpp | 11 +- .../requests/detail/impl/connection_impl.hpp | 10 +- .../boost/requests/impl/connection_pool.hpp | 3 +- include/boost/requests/stream.hpp | 11 +- src/connection_pool.cpp | 108 +++++++-- src/detail/condition_variable.cpp | 33 ++- src/detail/connection_impl.cpp | 43 ++-- src/detail/mutex.cpp | 7 +- src/stream.cpp | 12 +- test/CMakeLists.txt | 4 +- test/connection.cpp | 223 +++++++++++++++--- test/connection_pool.cpp | 191 ++++++++++++--- test/json.cpp | 13 +- 17 files changed, 591 insertions(+), 212 deletions(-) diff --git a/include/boost/requests/connection.hpp b/include/boost/requests/connection.hpp index 97055a0..6070ca6 100644 --- a/include/boost/requests/connection.hpp +++ b/include/boost/requests/connection.hpp @@ -73,16 +73,16 @@ struct connection /// Construct a stream. template explicit connection(ExecutorOrContext && exec_or_ctx, asio::ssl::context & ctx) - : impl_(std::make_shared(std::forward(exec_or_ctx), ctx)) {} + : impl_(new detail::connection_impl(std::forward(exec_or_ctx), ctx)) {} template explicit connection(ExecutionContext &context, typename asio::constraint< asio::is_convertible::value >::type = 0) - : impl_(std::make_shared(context)) {} + : impl_(new detail::connection_impl(context)) {} - explicit connection(asio::any_io_executor exec) : impl_(std::make_shared(exec)) {} + explicit connection(asio::any_io_executor exec) : impl_(new detail::connection_impl(std::move(exec))) {} void connect(endpoint_type ep) { @@ -226,20 +226,10 @@ struct connection operator bool() const {return impl_ != nullptr;} struct connection_pool * pool() {return impl_ ? impl_->pool() : nullptr; } - ~connection() - { - if (impl_.use_count() == 2u && impl_->pool() != nullptr) - { - if (impl_->is_open()) - impl_->return_to_pool(); - else - impl_->remove_from_pool(); - } - } -private: - explicit connection(std::shared_ptr impl) : impl_(std::move(impl)) {} + private: + explicit connection(boost::intrusive_ptr impl) : impl_(std::move(impl)) {} - std::shared_ptr impl_; + boost::intrusive_ptr impl_; friend struct connection_pool; friend struct stream; diff --git a/include/boost/requests/connection_pool.hpp b/include/boost/requests/connection_pool.hpp index 6d2a857..afa72ff 100644 --- a/include/boost/requests/connection_pool.hpp +++ b/include/boost/requests/connection_pool.hpp @@ -26,6 +26,13 @@ struct endpoint_hash } }; +struct connection_deleter +{ + constexpr connection_deleter() = default; + BOOST_REQUESTS_DECL + void operator()(connection_impl * ptr); +}; + } struct connection_pool @@ -224,7 +231,7 @@ struct connection_pool std::size_t limit_; boost::unordered_multimap, + std::unique_ptr, detail::endpoint_hash> conns_; std::vector free_conns_; @@ -233,30 +240,11 @@ struct connection_pool struct async_get_connection_op; struct async_ropen_op; - void return_connection_(detail::connection_impl * conn) - { - if (!conn->is_open()) - return drop_connection_(conn); - - std::lock_guard lock{mtx_}; - free_conns_.push_back(std::move(conn)); - cv_.notify_all(); - } + BOOST_REQUESTS_DECL + void return_connection_(detail::connection_impl * conn); + BOOST_REQUESTS_DECL + void drop_connection_(const detail::connection_impl * conn); - void drop_connection_(const detail::connection_impl * conn) - { - std::lock_guard lock{mtx_}; - auto itr = std::find_if(conns_.begin(), conns_.end(), - [&](const std::pair> & e) - { - return e.second.get() == conn; - }); - if (itr != conns_.end()) - { - conns_.erase(itr); - cv_.notify_all(); - } - } friend struct connection; friend struct stream; friend struct detail::connection_impl; diff --git a/include/boost/requests/detail/condition_variable.hpp b/include/boost/requests/detail/condition_variable.hpp index 900c4d3..41987b9 100644 --- a/include/boost/requests/detail/condition_variable.hpp +++ b/include/boost/requests/detail/condition_variable.hpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -40,16 +41,15 @@ struct condition_variable auto async_wait(std::unique_lock & lock, CompletionToken && tk) { - return asio::async_initiate - ( - async_wait_impl_, tk, this, std::ref(lock) - ); + return asio::async_compose(async_wait_op{this, lock}, tk, timer_); } condition_variable& operator=(const condition_variable&) = delete; - condition_variable& operator=(condition_variable&& lhs) noexcept = default; + BOOST_REQUESTS_DECL + condition_variable& operator=(condition_variable&& lhs) noexcept; condition_variable(const condition_variable&) = delete; - condition_variable(condition_variable&& mi) noexcept = default; + BOOST_REQUESTS_DECL + condition_variable(condition_variable&& mi) noexcept; BOOST_REQUESTS_DECL void wait(std::unique_lock & lock, system::error_code & ec); void wait(std::unique_lock & lock) { @@ -74,13 +74,33 @@ struct condition_variable BOOST_REQUESTS_DECL ~condition_variable(); private: - BOOST_REQUESTS_DECL static - void async_wait_impl_(asio::any_completion_handler h, - condition_variable * this_, std::unique_lock & lock); - asio::steady_timer timer_; std::condition_variable cv_; std::shared_ptr shutdown_indicator_{std::make_shared()}; + + struct async_wait_op + { + condition_variable * this_; + std::unique_lock & lock; + std::weak_ptr indicator; + + template + void operator()(Self && self) + { + indicator = this_->shutdown_indicator_; + this_->timer_.async_wait(std::move(self)); + lock.unlock(); + } + + template + void operator()(Self && self, system::error_code ec) + { + lock.lock(); + if (!indicator.expired() && self.get_cancellation_state().cancelled() == asio::cancellation_type::none) + ec.clear(); + self.complete(ec); + } + }; }; } diff --git a/include/boost/requests/detail/connection_impl.hpp b/include/boost/requests/detail/connection_impl.hpp index 513bb5e..2707f21 100644 --- a/include/boost/requests/detail/connection_impl.hpp +++ b/include/boost/requests/detail/connection_impl.hpp @@ -21,6 +21,7 @@ #include #include + namespace boost { namespace requests { @@ -29,7 +30,8 @@ struct connection_pool; namespace detail { -struct connection_impl : std::enable_shared_from_this +struct connection_deleter; +struct connection_impl { typedef asio::ssl::stream next_layer_type; typedef typename next_layer_type::executor_type executor_type; @@ -186,8 +188,6 @@ struct connection_impl : std::enable_shared_from_this bool uses_ssl() const {return use_ssl_;} void use_ssl(bool use_ssl = true) {use_ssl_ = use_ssl;} - BOOST_REQUESTS_DECL void return_to_pool(); - BOOST_REQUESTS_DECL void remove_from_pool(); struct connection_pool * pool() const {return borrowed_from_; } private: @@ -203,6 +203,7 @@ struct connection_impl : std::enable_shared_from_this // atomic so moving the pool can be thread-safe std::atomic borrowed_from_{nullptr}; + std::atomic borrow_count_{0u}; struct async_close_op; struct async_connect_op; @@ -214,9 +215,34 @@ struct connection_impl : std::enable_shared_from_this BOOST_REQUESTS_DECL void do_async_close_(detail::faux_token_t); BOOST_REQUESTS_DECL void do_close_(system::error_code & ec); + BOOST_REQUESTS_DECL void return_to_pool_(); + BOOST_REQUESTS_DECL void remove_from_pool_(); + + friend stream; + friend connection_pool; + friend connection_deleter; - friend struct boost::requests::stream; - friend struct requests::connection_pool; + friend void intrusive_ptr_add_ref(connection_impl* ptr) + { + ptr->borrow_count_++; + } + + friend void intrusive_ptr_release(connection_impl* ptr) + { + // borrow usage + if (--ptr->borrow_count_ == 0u) + { + if (ptr->borrowed_from_ != nullptr) + { + if (ptr->is_open()) + ptr->return_to_pool_(); + else + ptr->remove_from_pool_(); + } + else + delete ptr; + } + } }; diff --git a/include/boost/requests/detail/faux_coroutine.hpp b/include/boost/requests/detail/faux_coroutine.hpp index fd5bfba..54456ca 100644 --- a/include/boost/requests/detail/faux_coroutine.hpp +++ b/include/boost/requests/detail/faux_coroutine.hpp @@ -181,9 +181,9 @@ struct faux_runner if (buf.use_count() == 0u) return ; auto h = std::move(handler); - BOOST_ASSERT(buf.use_count() >= 1); + BOOST_ASSERT(buf.use_count() == 1); auto exec = asio::get_associated_executor(h, impl.get_executor()); - buf = nullptr; + buf.reset(); asio::dispatch(exec, asio::append(std::move(h), ec)); } } @@ -197,10 +197,10 @@ struct faux_runner if (buf.use_count() == 0u) return ; auto h = std::move(handler); - BOOST_ASSERT(buf.use_count() >= 1); + BOOST_ASSERT(buf.use_count() == 1); auto tmp = std::move(res); auto exec = asio::get_associated_executor(h, impl.get_executor()); - buf = nullptr; + buf.reset(); asio::dispatch(exec, asio::append(std::move(h), ec, std::move(tmp))); } } @@ -224,6 +224,7 @@ struct faux_runner auto h = std::move(handler); auto exec = asio::get_associated_executor(h, impl.get_executor()); + BOOST_ASSERT(buf.use_count() == 1); buf.reset(); asio::post(exec, asio::append(std::move(h), ec)); } @@ -242,6 +243,8 @@ struct faux_runner auto h = std::move(handler); auto tmp = std::move(res); auto exec = asio::get_associated_executor(h, impl.get_executor()); + BOOST_ASSERT(buf.use_count() == 1); + buf.reset(); asio::post(exec, asio::append(std::move(h), ec, std::move(tmp))); } } diff --git a/include/boost/requests/detail/impl/connection_impl.hpp b/include/boost/requests/detail/impl/connection_impl.hpp index abecbd1..6a13f82 100644 --- a/include/boost/requests/detail/impl/connection_impl.hpp +++ b/include/boost/requests/detail/impl/connection_impl.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -59,7 +60,6 @@ template( std::forward(completion_token), this, ep); } @@ -160,7 +160,7 @@ struct connection_impl::async_ropen_op using lock_type = detail::lock_guard; - std::shared_ptr this_; + boost::intrusive_ptr this_; optional str; beast::http::verb method; @@ -177,7 +177,7 @@ struct connection_impl::async_ropen_op response_base::history_type history; system::error_code ec_; - async_ropen_op(std::shared_ptr this_, + async_ropen_op(boost::intrusive_ptr this_, beast::http::verb method, urls::pct_string_view path, http::fields & headers, @@ -188,7 +188,7 @@ struct connection_impl::async_ropen_op { } - async_ropen_op(std::shared_ptr this_, + async_ropen_op(boost::intrusive_ptr this_, beast::http::verb method, urls::url_view path, http::fields & headers, @@ -250,7 +250,7 @@ connection_impl::async_ropen(beast::http::verb method, CompletionToken && completion_token) { return detail::faux_run(std::forward(completion_token), - shared_from_this(), method, path, + this, method, path, std::ref(headers), std::ref(src), std::move(opt), jar); } diff --git a/include/boost/requests/impl/connection_pool.hpp b/include/boost/requests/impl/connection_pool.hpp index 5b82056..cb39c66 100644 --- a/include/boost/requests/impl/connection_pool.hpp +++ b/include/boost/requests/impl/connection_pool.hpp @@ -52,6 +52,7 @@ connection_pool::async_lookup(urls::url_view av, CompletionToken && completion_t struct connection_pool::async_get_connection_op : asio::coroutine { + int i = 0; using executor_type = asio::any_io_executor; executor_type get_executor() {return this_->get_executor(); } @@ -64,7 +65,7 @@ struct connection_pool::async_get_connection_op : asio::coroutine typename conn_t::iterator itr; - std::shared_ptr nconn = nullptr; + boost::intrusive_ptr nconn = nullptr; std::unique_lock lock{this_->mtx_, std::defer_lock}; endpoint_type ep; diff --git a/include/boost/requests/stream.hpp b/include/boost/requests/stream.hpp index 8ef958e..d89bdb2 100644 --- a/include/boost/requests/stream.hpp +++ b/include/boost/requests/stream.hpp @@ -9,14 +9,17 @@ #define BOOST_REQUESTS_STREAM_HPP #include -#include -#include #include #include #include #include #include +#include +#include +#include + + namespace boost { namespace requests @@ -143,7 +146,7 @@ struct stream bool done() const {return !parser_ || parser_->is_done();} explicit stream(executor_type executor, std::nullptr_t ) : executor_{executor}, impl_(nullptr) {} - explicit stream(executor_type executor, std::shared_ptr impl) + explicit stream(executor_type executor, boost::intrusive_ptr impl) : executor_{executor}, impl_(std::move(impl)) {} @@ -169,7 +172,7 @@ struct stream } private: executor_type executor_; - std::shared_ptr impl_; + boost::intrusive_ptr impl_; detail::lock_guard lock_; std::unique_ptr> parser_; diff --git a/src/connection_pool.cpp b/src/connection_pool.cpp index c754d9a..2b4a960 100644 --- a/src/connection_pool.cpp +++ b/src/connection_pool.cpp @@ -10,6 +10,19 @@ namespace boost { namespace requests { +void detail::connection_deleter::operator()(connection_impl * ptr) +{ + if (ptr->borrow_count_ == 0u) + delete ptr; + else + { + ptr->borrowed_from_ = nullptr; + if (ptr->borrow_count_ == 0u) + delete ptr; + } +} + + void connection_pool::lookup(urls::url_view sv, system::error_code & ec) { @@ -37,8 +50,9 @@ void connection_pool::lookup(urls::url_view sv, system::error_code & ec) asio::ip::tcp::resolver resolver{get_executor()}; const auto service = sv.has_port() ? sv.port() : scheme; auto eps = resolver.resolve( - std::string(sv.encoded_host_address().data(), sv.encoded_host_address().size()), - std::string(service.data(), service.size()), ec); + std::string(sv.encoded_host_address().data(), sv.encoded_host_address().size()), + std::string(service.data(), service.size()), ec); + if (!ec && eps.empty()) BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found) @@ -91,7 +105,6 @@ void connection_pool::async_lookup_op::resume(requests::detail::faux_token_tuse_ssl_ = false; this_->host_ = "localhost"; this_->endpoints_ = {asio::local::stream_protocol::endpoint( @@ -101,6 +114,7 @@ void connection_pool::async_lookup_op::resume(requests::detail::faux_token_tasync_resolve( std::string(sv.encoded_host_address().data(), sv.encoded_host_address().size()), std::string(service.data(), service.size()), std::move(self)); @@ -126,6 +140,7 @@ void connection_pool::async_lookup_op::resume(requests::detail::faux_token_tconns_.erase(itr); } + lock.unlock(); } } @@ -147,17 +163,19 @@ auto connection_pool::get_connection(error_code & ec) -> connection std::unique_lock lock{mtx_}; do { + if (!free_conns_.empty()) { auto pp = std::move(free_conns_.front()); free_conns_.erase(free_conns_.begin()); - return connection(pp->shared_from_this()); + return connection(pp); } // check if we can make more connections. -> open another connection. // the race here is that we might open one too many else if (conns_.size() <= limit_) // open another connection then -> we block the entire { - if (endpoints_.empty()) { + if (endpoints_.empty()) + { BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); return connection(); } @@ -167,7 +185,7 @@ auto connection_pool::get_connection(error_code & ec) -> connection [this](const endpoint_type &a, const endpoint_type &b) { return conns_.count(a) < conns_.count(b); }); const auto ep = endpoints_.front(); - auto nconn = conns_.emplace(ep, std::make_shared(get_executor(), context_, this))->second; + boost::intrusive_ptr nconn = conns_.emplace(ep, new detail::connection_impl(get_executor(), context_, this))->second.get(); // no one else will grab it from there, bc it's not in free_conns_ lock.unlock(); nconn->use_ssl(use_ssl_); @@ -175,11 +193,8 @@ auto connection_pool::get_connection(error_code & ec) -> connection nconn->connect(ep, ec); if (ec) return connection(); - - if (ec) - return connection(); - - return connection(std::move(nconn)); + else + return connection(std::move(nconn)); } cv_.wait(lock); } @@ -195,20 +210,29 @@ auto connection_pool::async_get_connection_op::resume( { BOOST_ASIO_CORO_REENTER (this) { + static int j = 0; + this-> i = j++; + lock.lock(); do { + if (this_->endpoints_.empty()) + { + BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); + return connection{}; + } if (!this_->free_conns_.empty()) { auto pp = std::move(this_->free_conns_.front()); this_->free_conns_.erase(this_->free_conns_.begin()); - return connection(pp->shared_from_this()); + return connection(pp); } // check if we can make more connections. -> open another connection. // the race here is that we might open one too many - else if (this_->conns_.size() <= this_->limit_) // open another connection then -> we block the entire + else if (this_->conns_.size() < this_->limit_) // open another connection then { - if (this_->endpoints_.empty()) { + if (this_->endpoints_.empty()) + { BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); return connection(); } @@ -218,21 +242,32 @@ auto connection_pool::async_get_connection_op::resume( [this](const endpoint_type &a, const endpoint_type &b) { return this_->conns_.count(a) < this_->conns_.count(b); }); ep = this_->endpoints_.front(); - nconn = this_->conns_.emplace(ep, std::make_shared(get_executor(), this_->context_, this_))->second; + nconn = this_->conns_.emplace(ep, new detail::connection_impl(get_executor(), this_->context_, this_))->second.get(); + // no one else will grab it from there, bc it's not in free_conns_ lock.unlock(); nconn->use_ssl(this_->use_ssl_); nconn->set_host(this_->host_); - BOOST_ASIO_CORO_YIELD nconn->async_connect(ep, std::move(self)); - if (ec) - return connection(); - if (ec) + BOOST_ASIO_CORO_YIELD nconn->async_connect(ep, std::move(self)); + if (ec == system::errc::address_not_available) + { + lock.lock(); + auto itr = std::find(this_->endpoints_.begin(), this_->endpoints_.end(), ep); + if (itr != this_->endpoints_.end()) + this_->endpoints_.erase(itr); + + if (this_->endpoints_.empty()) + this_->cv_.notify_all(); + ec.clear(); + continue; + } + else if (ec) return connection(); - - return connection(std::move(nconn)); + else + return connection(std::move(nconn)); } - this_->cv_.async_wait(lock, std::move(self)); + BOOST_ASIO_CORO_YIELD this_->cv_.async_wait(lock, std::move(self)); } while(!ec); } @@ -266,9 +301,9 @@ connection_pool::connection_pool(connection_pool && lhs) { BOOST_ASSERT(std::count_if( conns_.begin(), conns_.end(), - [](const std::pair> & p) + [](const std::pair> & p) { - return !p.second.unique(); + return p.second->borrow_count_ > 0u; }) == 0u); for (auto & p : conns_) @@ -283,6 +318,31 @@ connection_pool::~connection_pool() p.second->borrowed_from_ = nullptr; } +void connection_pool::return_connection_(detail::connection_impl * conn) +{ + if (!conn->is_open()) + return drop_connection_(conn); + + std::lock_guard lock{mtx_}; + free_conns_.push_back(std::move(conn)); + cv_.notify_all(); +} + +void connection_pool::drop_connection_(const detail::connection_impl * conn) +{ + std::lock_guard lock{mtx_}; + auto itr = std::find_if(conns_.begin(), conns_.end(), + [&](const std::pair> & e) + { + return e.second.get() == conn; + }); + if (itr != conns_.end()) + { + conns_.erase(itr); + cv_.notify_all(); + } +} } } diff --git a/src/detail/condition_variable.cpp b/src/detail/condition_variable.cpp index c3c0f80..7cae6f8 100644 --- a/src/detail/condition_variable.cpp +++ b/src/detail/condition_variable.cpp @@ -23,31 +23,26 @@ void condition_variable::wait(std::unique_lock & lock, system::error if (indicator.expired()) ec = asio::error::operation_aborted; } - -void condition_variable::async_wait_impl_( - asio::any_completion_handler tk, - condition_variable * this_, - std::unique_lock & lock) +condition_variable::~condition_variable() { - std::weak_ptr indicator = this_->shutdown_indicator_; - lock.unlock(); - this_->timer_.async_wait( - asio::deferred( - [&lock, indicator](system::error_code ec_) - { - lock.lock(); - if (!indicator.expired()) - ec_.clear(); - return asio::deferred.values(ec_); - })) - (std::move(tk)); + shutdown_indicator_.reset(); + timer_.cancel(); + cv_.notify_all(); } -condition_variable::~condition_variable() +condition_variable& condition_variable::operator=(condition_variable&& lhs) noexcept { - shutdown_indicator_.reset(); + shutdown_indicator_ = std::make_shared(); timer_.cancel(); cv_.notify_all(); + timer_ = asio::steady_timer(lhs.timer_.get_executor()); + return *this; +} + + +condition_variable::condition_variable(condition_variable&& mi) noexcept + : timer_(mi.timer_.get_executor()) +{ } } diff --git a/src/detail/connection_impl.cpp b/src/detail/connection_impl.cpp index 4b673b6..662fe7d 100644 --- a/src/detail/connection_impl.cpp +++ b/src/detail/connection_impl.cpp @@ -153,6 +153,7 @@ auto connection_impl::ropen(beast::http::verb method, } alock.reset(); + if (use_ssl_) write_request(next_layer_, method, path, headers, src); else @@ -176,9 +177,13 @@ auto connection_impl::ropen(beast::http::verb method, if (ec) return stream{get_executor(), nullptr}; - stream str{get_executor(), shared_from_this()}; + stream str{get_executor(), this}; str.parser_ = std::make_unique>(http::response_header{http::fields(headers.get_allocator())}); str.parser_->body_limit(boost::none); + + if (method == http::verb::head || method == http::verb::connect) + str.parser_->skip(true); + if (use_ssl_) beast::http::read_header(next_layer_, buffer_, *str.parser_, ec); else @@ -257,10 +262,12 @@ auto connection_impl::ropen(beast::http::verb method, } path = url->encoded_resource(); - if (jar) - { - auto cc = jar->get(host(), is_secure, url->encoded_path()); + if (!url->encoded_host_and_port().empty()) + headers.set(http::field::host, url->encoded_host_and_port()); + if (jar && headers.count(http::field::host) > 0u) + { + auto cc = jar->get(headers[http::field::host], is_secure, url->encoded_path()); if (!cc.empty()) headers.set(http::field::cookie, cc); else @@ -272,7 +279,7 @@ auto connection_impl::ropen(beast::http::verb method, read_lock = {}; } - stream str{get_executor(), shared_from_this()}; + stream str{get_executor(), this}; str.history_ = std::move(history); return str; } @@ -299,6 +306,7 @@ auto connection_impl::async_ropen_op::resume( if (jar) { + auto cc = jar->get(this_->host(), this_->use_ssl_, path); if (!cc.empty()) headers.set(http::field::cookie, cc); @@ -352,6 +360,10 @@ auto connection_impl::async_ropen_op::resume( str.emplace(this_->get_executor(), this_); // , req.get_allocator().resource() str->parser_ = std::make_unique>(http::response_header{http::fields(headers.get_allocator())}); str->parser_->body_limit(boost::none); + + if (method == http::verb::head || method == http::verb::connect) + str->parser_->skip(true); + if (this_->use_ssl_) { BOOST_ASIO_CORO_YIELD beast::http::async_read_header(this_->next_layer_, this_->buffer_, *str->parser_, std::move(self)); @@ -435,9 +447,12 @@ auto connection_impl::async_ropen_op::resume( } path = url->encoded_resource(); - if (jar) + if (!url->encoded_host_and_port().empty()) + headers.set(http::field::host, url->encoded_host_and_port()); + + if (jar && headers.count(http::field::host) > 0u) { - auto cc = jar->get(this_->host(), this_->use_ssl_, url->encoded_path()); + auto cc = jar->get(headers[http::field::host], this_->use_ssl_, url->encoded_path()); if (!cc.empty()) headers.set(http::field::cookie, cc); else @@ -530,6 +545,7 @@ void connection_impl::async_connect_op::resume(requests::detail::faux_token_twrite_mtx_, write_lock); BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, read_lock); BOOST_ASIO_CORO_YIELD this_->next_layer_.next_layer().async_connect(this_->endpoint_ = ep, std::move(self)); + if (!ec && this_->use_ssl_) { BOOST_ASIO_CORO_YIELD this_->next_layer_.async_handshake(asio::ssl::stream_base::client, std::move(self)); @@ -567,19 +583,18 @@ void connection_impl::do_close_(system::error_code & ec) next_layer_.next_layer().close(ec); } -void connection_impl::return_to_pool() +void connection_impl::return_to_pool_() { - if (borrowed_from_) - borrowed_from_.load()->return_connection_(this); + if (auto ptr = borrowed_from_.load()) + ptr->return_connection_(this); } -void connection_impl::remove_from_pool() +void connection_impl::remove_from_pool_() { - if (borrowed_from_) - borrowed_from_.load()->drop_connection_(this); + if (auto ptr = borrowed_from_.load()) + ptr->drop_connection_(this); } - } } } diff --git a/src/detail/mutex.cpp b/src/detail/mutex.cpp index 87da645..b2b5068 100644 --- a/src/detail/mutex.cpp +++ b/src/detail/mutex.cpp @@ -24,9 +24,10 @@ void mutex::async_lock_impl_(asio::any_completion_handler lock{this_->waiters_mtx_}; if (this_->try_lock()) - return asio::dispatch( - asio::get_associated_immediate_executor(handler, this_->exec_), - asio::append(std::move(handler), system::error_code())); + { + auto exec = asio::get_associated_immediate_executor(handler, this_->exec_); + return asio::dispatch(exec, asio::append(std::move(handler), system::error_code())); + } auto itr = this_->waiters_.insert(this_->waiters_.end(), std::move(handler)); diff --git a/src/stream.cpp b/src/stream.cpp index 3c7f5fd..3c8b303 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -14,11 +14,11 @@ namespace requests void stream::dump(system::error_code & ec) { - if (!parser_ || !parser_->get().body().more) + if (!parser_ || !parser_->get().body().more || parser_->is_done()) return; char data[65535]; - while (!ec && parser_->get().body().more) + while (!ec && parser_->get().body().more && !parser_->is_done()) { parser_->get().body().data = data; parser_->get().body().size = sizeof(data); @@ -49,14 +49,6 @@ stream::~stream() if (parser_ && parser_->is_header_done() && !parser_->is_done() && parser_->get().body().more && impl_ && impl_->is_open()) dump(); - - if (impl_.use_count() == 2u && impl_->pool() != nullptr) - { - if (impl_->is_open()) - impl_->return_to_pool(); - else - impl_->remove_from_pool(); - } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 675e6f0..82375f9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -6,4 +6,6 @@ target_link_libraries(boost_requests_tests PUBLIC Boost::requests Boost::filesys target_compile_definitions(boost_requests_tests PUBLIC BOOST_REQUESTS_SEPARATE_COMPILATION=1 - BOOST_FILESYSTEM_NO_DEPRECATED=1) \ No newline at end of file + BOOST_FILESYSTEM_NO_DEPRECATED=1) + + diff --git a/test/connection.cpp b/test/connection.cpp index cb8554a..840c985 100644 --- a/test/connection.cpp +++ b/test/connection.cpp @@ -43,23 +43,28 @@ inline std::string httpbin() TEST_SUITE_BEGIN("connection"); -void http_request_connection(bool https) +TEST_CASE("sync-http") { - auto url = httpbin(); - + urls::url u = urls::parse_uri("http://" + httpbin()).value(); + auto url = u.encoded_host_and_port(); asio::io_context ctx; - asio::ssl::context sslctx{asio::ssl::context_base::tls_client}; sslctx.set_verify_mode(asio::ssl::verify_peer); sslctx.set_default_verify_paths(); - auto hc = https ? requests::connection(ctx.get_executor(), sslctx) : requests::connection(ctx.get_executor()); + requests::connection hc(ctx.get_executor()); hc.set_host(url); - hc.use_ssl(https); - asio::ip::tcp::resolver rslvr{ctx}; - asio::ip::tcp::endpoint ep = *rslvr.resolve(url, https ? "https" : "http").begin(); - hc.use_ssl(https); + hc.use_ssl(false); + + asio::ip::tcp::endpoint ep; + if (url == "localhost") + ep = {asio::ip::make_address("127.0.0.1"), u.port_number()}; + else + { + asio::ip::tcp::resolver rslvr{ctx}; + ep = *rslvr.resolve(u.host_name(), u.has_port() ? u.port() : u.scheme()).begin(); + } hc.connect(ep); // headers @@ -150,7 +155,6 @@ void http_request_connection(bool https) CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); auto hd = as_json(hdr).at("headers"); - CHECK(hd.at("Host") == json::value(url)); CHECK(hd.at("Test-Header") == json::value("it works")); } @@ -327,10 +331,70 @@ void http_request_connection(bool https) } } -TEST_CASE("sync-connection-request") +TEST_CASE("sync-https") { - SUBCASE("http") { http_request_connection(false);} - SUBCASE("https") { http_request_connection(true);} + asio::io_context ctx; + + asio::ssl::context sslctx{asio::ssl::context_base::tlsv13_client}; + sslctx.set_default_verify_paths(); + + requests::connection hc (ctx.get_executor(), sslctx); + + const auto host = "google.com"; + hc.set_host(host); + hc.use_ssl(true); + asio::ip::tcp::resolver rslvr{ctx}; + asio::ip::tcp::endpoint ep = *rslvr.resolve(host, "https").begin(); + error_code ec; + hc.connect(ep, ec); + CHECK_FALSE(ec); + + // header + { + auto hdr = request(hc, requests::http::verb::head, urls::url_view("/"), requests::empty{}, {{}, {false}}, ec); + CHECK_FALSE(ec); + CHECK_HTTP_RESULT(hdr.headers); + } + + // stream + { + auto str = hc.ropen(requests::http::verb::get, urls::url_view("/"), + requests::empty{}, {{}, {false}}); + + CHECK_HTTP_RESULT(str.headers()); + json::stream_parser sp; + + char buf[32]; + while (!str.done()) + auto sz = str.read_some(asio::buffer(buf)); + } + + + // stream-all + { + auto str = hc.ropen(requests::http::verb::get, urls::url_view("/"), + requests::empty{}, {{}, {false}}); + + CHECK_HTTP_RESULT(str.headers()); + std::string buf; + auto bb = asio::dynamic_buffer(buf); + CHECK(str.read(bb) > 0u); + } + + + // stream-dump + { + auto str = hc.ropen(requests::http::verb::get, urls::url_view("/"), + requests::empty{}, {{}, {false}}); + CHECK_HTTP_RESULT(str.headers()); + str.dump(); + } + + // get + { + auto hdr = get(hc, urls::url_view("/"), {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); + } } @@ -546,8 +610,7 @@ void run_tests(error_code ec, TEST_CASE("async-connection-request") { - urls::url url; - url.set_host(httpbin()); + urls::url url = urls::parse_uri("http://" + httpbin()).value(); asio::io_context ctx; asio::ssl::context sslctx{asio::ssl::context_base::tls_client}; sslctx.set_verify_mode(asio::ssl::verify_peer); @@ -569,8 +632,6 @@ TEST_CASE("async-connection-request") .otherwise(asio::post(ctx, asio::append(asio::deferred, ec))); }); - SUBCASE("http") - { conn = requests::connection(ctx); asio::ip::tcp::resolver rslvr{ctx}; url.set_scheme("http"); @@ -578,24 +639,120 @@ TEST_CASE("async-connection-request") CHECK(!conn.uses_ssl()); conn.set_host(url.encoded_host()); - rslvr.async_resolve(std::string(url.encoded_host().data(), url.encoded_host().size()), - "80", do_the_thing) - (asio::append(&run_tests, std::ref(conn), urls::url_view(url))); + if (url.encoded_host() == "localhost") + conn.async_connect( + asio::ip::tcp::endpoint{asio::ip::make_address("127.0.0.1"), url.port_number()}, + asio::append(&run_tests, std::ref(conn), urls::url_view(url))); + else + rslvr.async_resolve(std::string(url.encoded_host().data(), url.encoded_host().size()), + "80", do_the_thing) + (asio::append(&run_tests, std::ref(conn), urls::url_view(url))); ctx.run(); - } - SUBCASE("https") - { - conn = requests::connection(ctx, sslctx); - asio::ip::tcp::resolver rslvr{ctx}; - url.set_scheme("https"); - CHECK(conn.uses_ssl()); - conn.set_host(url.encoded_host()); - rslvr.async_resolve(std::string(url.encoded_host().data(), url.encoded_host().size()), - "443", do_the_thing) - (asio::append(&run_tests, std::ref(conn), urls::url_view(url))); - ctx.run(); - } } +void async_https_impl(requests::connection & hc) +{ + + // header + async_request(hc, requests::http::verb::head, urls::url_view("/"), requests::empty{}, {{}, {false}}, + tracker([](error_code ec, requests::response hdr) + { + CHECK_FALSE(ec); + CHECK_HTTP_RESULT(hdr.headers); + })); + + // stream + hc.async_ropen(requests::http::verb::get, urls::url_view("/"), + requests::empty{}, {{}, {false}}, + tracker([](error_code ec, requests::stream str) + { + CHECK_FALSE(ec); + CHECK_HTTP_RESULT(str.headers()); + + struct ht + { + requests::stream str; + std::unique_ptr buf = std::make_unique(32); + void operator()(error_code ec, std::size_t n) + { + CHECK_FALSE(ec); + if (!ec && !str.done()) + str.async_read_some(asio::buffer(buf.get(), 32), tracker(std::move(*this))); + } + }; + ht h{std::move(str)}; + h.str.async_read_some(asio::buffer(h.buf.get(), 32), tracker(std::move(h))); + })); + + + // stream-all + hc.async_ropen(requests::http::verb::get, urls::url_view("/"), + requests::empty{}, {{}, {false}}, + tracker( + [](error_code ec, requests::stream str) + { + CHECK_FALSE(ec); + CHECK_HTTP_RESULT(str.headers()); + thread_local static std::string buf; + auto bb = asio::dynamic_buffer(buf); + str.async_read( + bb, + tracker( + [](error_code ec, std::size_t n) + { + CHECK(n > 0u); + CHECK_FALSE(ec); + })); + })); + + + // stream-dump + hc.async_ropen(requests::http::verb::get, urls::url_view("/"), + requests::empty{}, {{}, {false}}, + tracker( + [](error_code ec, requests::stream str) + { + CHECK_FALSE(ec); + CHECK_HTTP_RESULT(str.headers()); + str.async_dump(tracker([](error_code ec) {CHECK_FALSE(ec);})); + })); + + // get + async_get(hc, urls::url_view("/"), {{}, {false}}, + tracker( + [](error_code ec, requests::response hdr) + { + CHECK_FALSE(ec); + CHECK_HTTP_RESULT(hdr.headers); + })); +} + +TEST_CASE("async-https") +{ + asio::io_context ctx; + + asio::ssl::context sslctx{asio::ssl::context_base::tls_client}; + + sslctx.set_default_verify_paths(); + + requests::connection hc (ctx.get_executor(), sslctx); + + const auto host = "google.com"; + hc.set_host(host); + hc.use_ssl(true); + asio::ip::tcp::resolver rslvr{ctx}; + asio::ip::tcp::endpoint ep = *rslvr.resolve(host, "https").begin(); + error_code ec; + hc.async_connect( + ep, + tracker([&](error_code ec) + { + CHECK_FALSE(ec); + if (!ec) + async_https_impl(hc); + })); + + ctx.run(); +} TEST_SUITE_END(); \ No newline at end of file diff --git a/test/connection_pool.cpp b/test/connection_pool.cpp index e7ac82b..2606625 100644 --- a/test/connection_pool.cpp +++ b/test/connection_pool.cpp @@ -41,9 +41,9 @@ inline std::string httpbin() TEST_SUITE_BEGIN("connection-pool"); -void http_request_connection_pool(bool https) +TEST_CASE("sync-http") { - auto url = urls::url((https ? "https://" : "http://") + httpbin()); + auto url = urls::url("http://" + httpbin()); asio::io_context ctx; @@ -52,10 +52,9 @@ void http_request_connection_pool(bool https) sslctx.set_verify_mode(asio::ssl::verify_peer); sslctx.set_default_verify_paths(); - requests::connection_pool hc(ctx.get_executor(), sslctx); hc.lookup(url); - CHECK(https == hc.uses_ssl()); + CHECK(!hc.uses_ssl()); // headers @@ -306,15 +305,58 @@ void http_request_connection_pool(bool https) CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); } - } -TEST_CASE("sync-connection-request") -{ - SUBCASE("http") { http_request_connection_pool(false);} - SUBCASE("https") { http_request_connection_pool(true);} -} +TEST_CASE("sync-https") { + asio::io_context ctx; + + asio::ssl::context sslctx{asio::ssl::context_base::tlsv13_client}; + sslctx.set_default_verify_paths(); + + requests::connection_pool hc(ctx.get_executor(), sslctx); + hc.lookup("https://google.com"); + CHECK(hc.uses_ssl()); + // header + { + auto hdr = request(hc, requests::http::verb::head, urls::url_view("/"), requests::empty{}, {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); + } + + // stream + { + auto str = hc.ropen(requests::http::verb::get, urls::url_view("/"), requests::empty{}, {{}, {false}}); + + CHECK_HTTP_RESULT(str.headers()); + json::stream_parser sp; + + char buf[32]; + while (!str.done()) + auto sz = str.read_some(asio::buffer(buf)); + } + // stream-all + { + auto str = hc.ropen(requests::http::verb::get, urls::url_view("/"), requests::empty{}, {{}, {false}}); + + CHECK_HTTP_RESULT(str.headers()); + std::string buf; + auto bb = asio::dynamic_buffer(buf); + CHECK(str.read(bb) > 0u); + } + + // stream-dump + { + auto str = hc.ropen(requests::http::verb::get, urls::url_view("/"), requests::empty{}, {{}, {false}}); + CHECK_HTTP_RESULT(str.headers()); + str.dump(); + } + + // get + { + auto hdr = get(hc, urls::url_view("/"), {{}, {false}}); + CHECK_HTTP_RESULT(hdr.headers); + } +} void run_tests(error_code ec, requests::connection_pool & conn, @@ -526,33 +568,122 @@ void run_tests(error_code ec, } -TEST_CASE("async-connection-pool-request") +TEST_CASE("async-http") { - urls::url url; - url.set_host(httpbin()); + auto url = urls::url("http://" + httpbin()); + asio::io_context ctx; asio::ssl::context sslctx{asio::ssl::context_base::tls_client}; - sslctx.set_verify_mode(asio::ssl::verify_peer); sslctx.set_default_verify_paths(); requests::connection_pool conn(ctx, sslctx); - SUBCASE("http") - { - url.set_scheme("http"); - conn.async_lookup(url, asio::append(&run_tests, std::ref(conn), urls::url_view(url))); - ctx.run(); - CHECK(!conn.uses_ssl()); - CHECK(conn.limit() >= conn.active()); - } + url.set_scheme("http"); + conn.async_lookup(url, asio::append(&run_tests, std::ref(conn), urls::url_view(url))); + ctx.run(); + CHECK(!conn.uses_ssl()); + CHECK(conn.limit() >= conn.active()); +} - SUBCASE("https") - { - url.set_scheme("https"); - CHECK(conn.uses_ssl()); - conn.async_lookup(url, asio::append(&run_tests, std::ref(conn), urls::url_view(url))); - ctx.run(); - CHECK(conn.limit() >= conn.active()); - } + +void async_https_impl(requests::connection_pool & hc) +{ + + // header + async_request(hc, requests::http::verb::head, urls::url_view("/"), requests::empty{}, {{}, {false}}, + tracker([](error_code ec, requests::response hdr) + { + CHECK_FALSE(ec); + CHECK_HTTP_RESULT(hdr.headers); + })); + + // stream + hc.async_ropen(requests::http::verb::get, urls::url_view("/"), + requests::empty{}, {{}, {false}}, + tracker([](error_code ec, requests::stream str) + { + CHECK_FALSE(ec); + CHECK_HTTP_RESULT(str.headers()); + + struct ht + { + requests::stream str; + std::unique_ptr buf = std::make_unique(32); + void operator()(error_code ec, std::size_t n) + { + CHECK_FALSE(ec); + if (!ec && !str.done()) + str.async_read_some(asio::buffer(buf.get(), 32), tracker(std::move(*this))); + } + }; + ht h{std::move(str)}; + h.str.async_read_some(asio::buffer(h.buf.get(), 32), tracker(std::move(h))); + })); + + + // stream-all + hc.async_ropen(requests::http::verb::get, urls::url_view("/"), + requests::empty{}, {{}, {false}}, + tracker( + [](error_code ec, requests::stream str) + { + CHECK_FALSE(ec); + CHECK_HTTP_RESULT(str.headers()); + thread_local static std::string buf; + auto bb = asio::dynamic_buffer(buf); + str.async_read( + bb, + tracker( + [](error_code ec, std::size_t n) + { + CHECK(n > 0u); + CHECK_FALSE(ec); + })); + })); + + + // stream-dump + hc.async_ropen(requests::http::verb::get, urls::url_view("/"), + requests::empty{}, {{}, {false}}, + tracker( + [](error_code ec, requests::stream str) + { + CHECK_FALSE(ec); + CHECK_HTTP_RESULT(str.headers()); + str.async_dump(tracker([](error_code ec) {CHECK_FALSE(ec);})); + })); + + // get + async_get(hc, urls::url_view("/"), {{}, {false}}, + tracker( + [](error_code ec, requests::response hdr) + { + CHECK_FALSE(ec); + CHECK_HTTP_RESULT(hdr.headers); + })); } +TEST_CASE("async-https") +{ + asio::io_context ctx; + + asio::ssl::context sslctx{asio::ssl::context_base::tls_client}; + + sslctx.set_default_verify_paths(); + + requests::connection_pool hc (ctx.get_executor(), sslctx); + + error_code ec; + hc.async_lookup( + "https://google.com", + tracker([&](error_code ec) + { + CHECK_FALSE(ec); + if (!ec) + async_https_impl(hc); + })); + + ctx.run(); +} + + TEST_SUITE_END(); \ No newline at end of file diff --git a/test/json.cpp b/test/json.cpp index f36efc5..97e5984 100644 --- a/test/json.cpp +++ b/test/json.cpp @@ -36,7 +36,7 @@ inline std::string httpbin() TEST_SUITE_BEGIN("json-connection"); -void json_request_connection(bool https) +TEST_CASE("sync-http") { auto url = httpbin(); @@ -50,11 +50,11 @@ void json_request_connection(bool https) requests::connection hc(ctx.get_executor(), sslctx); hc.set_host(url); - hc.use_ssl(https); - CHECK(hc.uses_ssl() == https); + hc.use_ssl(false); + CHECK(hc.uses_ssl() == false); asio::ip::tcp::resolver rslvr{ctx}; - asio::ip::tcp::endpoint ep = *rslvr.resolve(url, https ? "https" : "http").begin(); + asio::ip::tcp::endpoint ep = *rslvr.resolve(url, false ? "https" : "http").begin(); hc.connect(ep); @@ -172,11 +172,6 @@ void json_request_connection(bool https) } -TEST_CASE("sync-connection-request") -{ - SUBCASE("http") { json_request_connection(false);} - SUBCASE("https") { json_request_connection(true);} -} void run_json_tests(error_code ec, requests::connection & hc, From b35c5d2999b69c9efb13a0220c315b542559a31c Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 20 Sep 2023 23:50:45 +0800 Subject: [PATCH 70/76] removed faux_routine entirely. --- include/boost/requests/connection_pool.hpp | 19 + .../requests/detail/condition_variable.hpp | 1 - .../boost/requests/detail/connection_impl.hpp | 21 +- .../boost/requests/detail/faux_coroutine.hpp | 311 ---------------- .../requests/detail/impl/connection_impl.hpp | 124 +------ include/boost/requests/detail/mutex.hpp | 1 - include/boost/requests/download.hpp | 278 +++++++++----- .../boost/requests/impl/connection_pool.hpp | 121 +----- include/boost/requests/impl/request.hpp | 95 +++-- include/boost/requests/impl/session.hpp | 73 +--- include/boost/requests/impl/source.hpp | 147 +++++--- include/boost/requests/impl/stream.hpp | 99 ++--- include/boost/requests/json.hpp | 346 ++++++++++++------ include/boost/requests/session.hpp | 11 + include/boost/requests/stream.hpp | 9 +- src/connection_pool.cpp | 208 +++++++++-- src/detail/connection_impl.cpp | 231 +++++++++--- src/detail/mutex.cpp | 3 + src/session.cpp | 275 +++++++++----- src/stream.cpp | 138 +++++-- test/connection.cpp | 2 +- test/detail/faux_coroutine.cpp | 99 ----- test/detail/mutex.cpp | 116 ++---- 23 files changed, 1373 insertions(+), 1355 deletions(-) delete mode 100644 include/boost/requests/detail/faux_coroutine.hpp delete mode 100644 test/detail/faux_coroutine.cpp diff --git a/include/boost/requests/connection_pool.hpp b/include/boost/requests/connection_pool.hpp index afa72ff..bc6ac64 100644 --- a/include/boost/requests/connection_pool.hpp +++ b/include/boost/requests/connection_pool.hpp @@ -248,6 +248,25 @@ struct connection_pool friend struct connection; friend struct stream; friend struct detail::connection_impl; + + BOOST_REQUESTS_DECL + static void async_ropen_impl(asio::any_completion_handler handler, + connection_pool * this_, http::verb method, + urls::pct_string_view path, http::fields & headers, + source & src, request_options opt, cookie_jar * jar); + + + BOOST_REQUESTS_DECL + static void async_get_connection_impl( + asio::any_completion_handler handler, + connection_pool * this_); + + + BOOST_REQUESTS_DECL + static void async_lookup_impl( + asio::any_completion_handler handler, + connection_pool * this_, urls::url_view av); + }; template diff --git a/include/boost/requests/detail/condition_variable.hpp b/include/boost/requests/detail/condition_variable.hpp index 41987b9..a7e01e7 100644 --- a/include/boost/requests/detail/condition_variable.hpp +++ b/include/boost/requests/detail/condition_variable.hpp @@ -8,7 +8,6 @@ #ifndef BOOST_REQUESTS_DETAIL_CONDITION_VARIABLE_HPP #define BOOST_REQUESTS_DETAIL_CONDITION_VARIABLE_HPP -#include #include #include #include diff --git a/include/boost/requests/detail/connection_impl.hpp b/include/boost/requests/detail/connection_impl.hpp index 2707f21..682e39e 100644 --- a/include/boost/requests/detail/connection_impl.hpp +++ b/include/boost/requests/detail/connection_impl.hpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -211,8 +210,8 @@ struct connection_impl BOOST_REQUESTS_DECL std::size_t do_read_some_(beast::http::basic_parser & parser); BOOST_REQUESTS_DECL std::size_t do_read_some_(beast::http::basic_parser & parser, system::error_code & ec) ; - BOOST_REQUESTS_DECL void do_async_read_some_(beast::http::basic_parser & parser, detail::faux_token_t) ; - BOOST_REQUESTS_DECL void do_async_close_(detail::faux_token_t); + BOOST_REQUESTS_DECL void do_async_read_some_(beast::http::basic_parser & parser, asio::any_completion_handler) ; + BOOST_REQUESTS_DECL void do_async_close_(asio::any_completion_handler); BOOST_REQUESTS_DECL void do_close_(system::error_code & ec); BOOST_REQUESTS_DECL void return_to_pool_(); @@ -222,6 +221,7 @@ struct connection_impl friend connection_pool; friend connection_deleter; + // borrow usage is done through intrusive_ptrs friend void intrusive_ptr_add_ref(connection_impl* ptr) { ptr->borrow_count_++; @@ -229,7 +229,6 @@ struct connection_impl friend void intrusive_ptr_release(connection_impl* ptr) { - // borrow usage if (--ptr->borrow_count_ == 0u) { if (ptr->borrowed_from_ != nullptr) @@ -243,6 +242,20 @@ struct connection_impl delete ptr; } } + + BOOST_REQUESTS_DECL + static void async_connect_impl(asio::any_completion_handler handler, + connection_impl * this_, endpoint_type ep); + + BOOST_REQUESTS_DECL + static void async_close_impl(asio::any_completion_handler handler, + connection_impl * this_); + + BOOST_REQUESTS_DECL + static void async_ropen_impl(asio::any_completion_handler handler, + connection_impl * this_, http::verb method, + urls::pct_string_view path, http::fields & headers, + source & src, request_options opt, cookie_jar * jar); }; diff --git a/include/boost/requests/detail/faux_coroutine.hpp b/include/boost/requests/detail/faux_coroutine.hpp deleted file mode 100644 index 54456ca..0000000 --- a/include/boost/requests/detail/faux_coroutine.hpp +++ /dev/null @@ -1,311 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BOOST_REQUESTS_DETAIL_ASYNC_COROUTINE_HPP -#define BOOST_REQUESTS_DETAIL_ASYNC_COROUTINE_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace boost -{ -namespace requests -{ -namespace detail -{ - -template -struct faux_runner; - -template -struct faux_token_t; - -template<> -struct faux_token_t -{ - using cancellation_slot_type = asio::cancellation_slot; - cancellation_slot_type get_cancellation_slot() const {BOOST_ASSERT(impl_ != nullptr); return impl_->slot;} - - void operator()() - { - auto & base = *impl_; - base.resume(std::move(*this)); - } - - struct base - { - virtual void resume(faux_token_t impl) = 0; - asio::cancellation_slot slot; - }; - - faux_token_t(const faux_token_t & ) = delete; - faux_token_t( faux_token_t && lhs) : impl_(lhs.impl_) { } - - explicit faux_token_t(std::shared_ptr impl) : impl_(std::move(impl)) {} - private: - template - friend struct faux_token_t; - - - std::shared_ptr impl_; - - template friend struct faux_runner; -}; - - -template -struct faux_token_t -{ - using cancellation_slot_type = asio::cancellation_slot; - cancellation_slot_type get_cancellation_slot() const {BOOST_ASSERT(impl_ != nullptr); return impl_->slot;} - - void operator()(T1 t1 = {}) - { - auto & base = *impl_; - base.resume(std::move(*this), std::move(t1)); - } - - operator faux_token_t () && - { - return faux_token_t{impl_}; - } - - struct base : faux_token_t::base - { - virtual void resume(faux_token_t tk, T1 t1) = 0; - void resume(faux_token_t tk) - { - resume(faux_token_t{std::static_pointer_cast(std::move(tk.impl_))}, T1{}); - } - }; - - faux_token_t(const faux_token_t & ) = delete; - faux_token_t( faux_token_t && lhs) : impl_(lhs.impl_) { } - - explicit faux_token_t(std::shared_ptr impl) : impl_(std::move(impl)) {} -private: - template - friend struct faux_token_t; - - - std::shared_ptr impl_; - - template friend struct faux_runner; -}; - -template -struct faux_token_t -{ - using cancellation_slot_type = asio::cancellation_slot; - cancellation_slot_type get_cancellation_slot() const - { - BOOST_ASSERT(impl_ != nullptr); - return impl_->slot; - } - - void operator()(T1 t1 = {}, T2 t2 = {}) - { - auto & base = *impl_; - base.resume(std::move(*this), std::move(t1), std::move(t2)); - } - - struct base : faux_token_t::base - { - virtual void resume(faux_token_t tk, T1 t1, T2 t2) = 0; - void resume(faux_token_t tk) final - { - resume(faux_token_t{std::static_pointer_cast(std::move(tk.impl_))}, T1{}, T2{}); - } - void resume(faux_token_t tk, T1 t1) - { - resume(faux_token_t{std::static_pointer_cast(std::move(tk.impl_)) }, std::move(t1), T2{}); - } - }; - - faux_token_t( faux_token_t && lhs) : impl_(lhs.impl_) { } - - operator faux_token_t () && - { - return faux_token_t{std::move(impl_)}; - } - - operator faux_token_t () && - { - return faux_token_t{std::move(impl_)}; - } - explicit faux_token_t(std::shared_ptr impl) : impl_(std::move(impl)) {} - - private: - template - friend struct faux_token_t; - - std::shared_ptr impl_; - template friend struct faux_runner; -}; - -struct with_allocator_t {}; -constexpr static with_allocator_t with_allocator{}; - -template -struct faux_runner -{ - - using token_type = faux_token_t; - template - struct impl_ final : token_type::base - { - void resume(token_type tk, system::error_code ec, Args ... args) override - { - using result_type = decltype(impl.resume(std::move(tk), ec, std::move(args)...)); - resume_impl(std::is_void{}, std::move(tk), std::move(ec), std::move(args)...); - } - - void resume_impl(std::true_type, token_type tk, system::error_code ec, Args ... args) - { - auto buf = tk.impl_; - impl.resume(std::move(tk), ec, std::move(args)...); - if (impl.is_complete()) - { - if (buf.use_count() == 0u) - return ; - auto h = std::move(handler); - BOOST_ASSERT(buf.use_count() == 1); - auto exec = asio::get_associated_executor(h, impl.get_executor()); - buf.reset(); - asio::dispatch(exec, asio::append(std::move(h), ec)); - } - } - - void resume_impl(std::false_type, token_type tk, system::error_code ec, Args ... args) - { - auto buf = tk.impl_; - decltype(auto) res = impl.resume(std::move(tk), ec, std::move(args)...); - if (impl.is_complete()) - { - if (buf.use_count() == 0u) - return ; - auto h = std::move(handler); - BOOST_ASSERT(buf.use_count() == 1); - auto tmp = std::move(res); - auto exec = asio::get_associated_executor(h, impl.get_executor()); - buf.reset(); - asio::dispatch(exec, asio::append(std::move(h), ec, std::move(tmp))); - } - } - - void initiate(token_type tk) - { - using result_type = decltype(impl.resume(std::move(tk), std::declval(), std::declval()...)); - BOOST_ASSERT(tk.impl_); - initiate_impl(std::is_void{}, std::move(tk), {}, Args{}...); - } - - void initiate_impl(std::true_type, token_type tk, system::error_code ec, Args ... args) - { - BOOST_ASSERT(tk.impl_.get() == this); - auto buf = tk.impl_; - impl.resume(std::move(tk), ec, std::move(args)...); - if (impl.is_complete()) - { - if (buf.use_count() == 0u) - return ; - - auto h = std::move(handler); - auto exec = asio::get_associated_executor(h, impl.get_executor()); - BOOST_ASSERT(buf.use_count() == 1); - buf.reset(); - asio::post(exec, asio::append(std::move(h), ec)); - } - } - - void initiate_impl(std::false_type, token_type tk, system::error_code ec, Args ... args) - { - BOOST_ASSERT(tk.impl_.get() == this); - auto buf = tk.impl_; - decltype(auto) res = impl.resume(std::move(tk), ec, std::move(args)...); - if (impl.is_complete()) - { - if (buf.use_count() == 0u) - return ; - - auto h = std::move(handler); - auto tmp = std::move(res); - auto exec = asio::get_associated_executor(h, impl.get_executor()); - BOOST_ASSERT(buf.use_count() == 1); - buf.reset(); - asio::post(exec, asio::append(std::move(h), ec, std::move(tmp))); - } - } - - Handler handler; - Implementation impl; - - - template - impl_(Handler_ && h, Args_ && ... args) - : handler(std::forward(h)) - , impl(std::forward(args)...) - { - this->token_type::base::slot = asio::get_associated_cancellation_slot(handler); - } - - - template - impl_(Handler_ && h, const with_allocator_t &, Args_ && ... args) - : handler(std::forward(h)) - , impl(std::forward(args)...) - { - this->token_type::base::slot = asio::get_associated_cancellation_slot(handler); - } - - template - impl_(Handler_ && h, with_allocator_t &&, Args_ && ... args) - : handler(std::forward(h)) - , impl(std::forward(args)...) - { - this->token_type::base::slot = asio::get_associated_cancellation_slot(handler); - } - }; - - template - void operator()(Handler && h, Args_ &&... args) - { - auto alloc = asio::get_associated_allocator(h, asio::recycling_allocator()); - using impl_t = impl_>; - auto ptr = std::allocate_shared(alloc, std::forward(h), std::forward(args)...); - auto * impl = ptr.get(); - impl->initiate(token_type{std::move(ptr)}); - } - - -}; - -template -auto faux_run(Token && token, Args && ... args) -{ - static_assert(std::is_constructible::value, - "Can't construct implementation from those args"); - return asio::async_initiate( - faux_runner{}, token, std::forward(args)...); -} - - -} -} -} - -#endif // BOOST_REQUESTS_DETAIL_ASYNC_COROUTINE_HPP diff --git a/include/boost/requests/detail/impl/connection_impl.hpp b/include/boost/requests/detail/impl/connection_impl.hpp index 6a13f82..8d59973 100644 --- a/include/boost/requests/detail/impl/connection_impl.hpp +++ b/include/boost/requests/detail/impl/connection_impl.hpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -32,65 +31,23 @@ namespace boost { namespace requests { namespace detail { -struct connection_impl::async_connect_op : asio::coroutine -{ - using executor_type = asio::any_io_executor; - executor_type get_executor() {return this_->get_executor(); } - - connection_impl * this_; - endpoint_type ep; - - asio::coroutine inner_coro; - - async_connect_op(connection_impl * this_, endpoint_type ep) : this_(this_), ep(ep) {} - - using lock_type = detail::lock_guard; - - lock_type read_lock, write_lock; - - using completion_signature_type = void(system::error_code); - using step_signature_type = void(system::error_code); - - BOOST_REQUESTS_DECL - void resume(requests::detail::faux_token_t self, - system::error_code & ec); -}; - template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code)) connection_impl::async_connect(endpoint_type ep, CompletionToken && completion_token) { - return detail::faux_run( - std::forward(completion_token), this, ep); + return asio::async_initiate( + &async_connect_impl, completion_token, + this, ep); } -struct connection_impl::async_close_op : asio::coroutine -{ - connection_impl * this_; - - using executor_type = asio::any_io_executor; - executor_type get_executor() const {return this_->get_executor();} - - using lock_type = detail::lock_guard; - lock_type read_lock, write_lock; - - - async_close_op(connection_impl * this_) : this_(this_) {} - - using completion_signature_type = void(system::error_code); - using step_signature_type = void(system::error_code); - - BOOST_REQUESTS_DECL - void resume(requests::detail::faux_token_t self, - system::error_code & ec); -}; template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code)) connection_impl::async_close(CompletionToken && completion_token) { - return detail::faux_run( - std::forward(completion_token), this); + return asio::async_initiate( + &async_close_impl, completion_token, + this); } BOOST_REQUESTS_DECL bool check_endpoint( @@ -151,65 +108,6 @@ auto connection_impl::ropen( return ropen(method, path.encoded_target(), req.fields, *src, std::move(req.opts), req.jar, ec); } - -struct connection_impl::async_ropen_op - : boost::asio::coroutine -{ - using executor_type = asio::any_io_executor; - executor_type get_executor() {return this_->get_executor(); } - - using lock_type = detail::lock_guard; - - boost::intrusive_ptr this_; - optional str; - - beast::http::verb method; - urls::pct_string_view path; - http::fields & headers; - source & src; - request_options opts; - cookie_jar * jar{nullptr}; - response_base::buffer_type buf{headers.get_allocator()}; - - lock_type lock; - boost::optional alock; - - response_base::history_type history; - system::error_code ec_; - - async_ropen_op(boost::intrusive_ptr this_, - beast::http::verb method, - urls::pct_string_view path, - http::fields & headers, - source & src, - request_options opts, - cookie_jar * jar) - : this_(std::move(this_)), method(method), path(path), headers(headers), src(src), opts(std::move(opts)), jar(jar) - { - } - - async_ropen_op(boost::intrusive_ptr this_, - beast::http::verb method, - urls::url_view path, - http::fields & headers, - source & src, - request_options opt, - cookie_jar * jar) - : this_(this_), method(method), path(path.encoded_resource()), - headers(headers), src(src), opts(std::move(opt)), jar(jar) - { - detail::check_endpoint(path, this_->endpoint(), this_->host(), this_->use_ssl_, ec_); - } - - using completion_signature_type = void(system::error_code, stream); - using step_signature_type = void(system::error_code, std::size_t); - - BOOST_REQUESTS_DECL - auto resume(requests::detail::faux_token_t self, - system::error_code & ec, std::size_t res_ = 0u) -> stream; -}; - - template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, stream)) @@ -239,8 +137,7 @@ connection_impl::async_ropen( template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, - stream)) + void (boost::system::error_code, stream)) connection_impl::async_ropen(beast::http::verb method, urls::pct_string_view path, http::fields & headers, @@ -249,9 +146,10 @@ connection_impl::async_ropen(beast::http::verb method, cookie_jar * jar, CompletionToken && completion_token) { - return detail::faux_run(std::forward(completion_token), - this, method, path, - std::ref(headers), std::ref(src), std::move(opt), jar); + return asio::async_initiate( + &async_ropen_impl, completion_token, + this, method, path, + std::ref(headers), std::ref(src), std::move(opt), jar); } } diff --git a/include/boost/requests/detail/mutex.hpp b/include/boost/requests/detail/mutex.hpp index 3ea7c96..41537bb 100644 --- a/include/boost/requests/detail/mutex.hpp +++ b/include/boost/requests/detail/mutex.hpp @@ -13,7 +13,6 @@ #include #include #include -#include #include namespace boost diff --git a/include/boost/requests/download.hpp b/include/boost/requests/download.hpp index ae8e5d0..a24b1d3 100644 --- a/include/boost/requests/download.hpp +++ b/include/boost/requests/download.hpp @@ -9,10 +9,10 @@ #define BOOST_REQUESTS_DOWNLOAD_HPP #include -#include #include #include #include +#include #include #if defined(BOOST_ASIO_HAS_FILE) @@ -70,54 +70,72 @@ template struct async_write_to_file_op : asio::coroutine { using executor_type = typename std::decay_t::executor_type; - executor_type get_executor() {return str.get_executor(); } + executor_type get_executor() {return state->str.get_executor(); } using completion_signature_type = void(system::error_code, std::size_t); using step_signature_type = void(system::error_code, std::size_t); - Stream str; - asio::basic_stream_file::executor_type> f{str.get_executor()}; - const std::filesystem::path & file; - char buffer[BOOST_REQUESTS_CHUNK_SIZE]; + const filesystem::path & file; + + struct state_t + { + state_t(Stream * str) : str(*str)) {} - std::size_t written = 0u; - system::error_code ec_read; + Stream & str; + asio::basic_stream_file::executor_type> f{str.get_executor()}; + char buffer[BOOST_REQUESTS_CHUNK_SIZE]; + + std::size_t written = 0u; + system::error_code ec_read; + }; + + using allocator_type = asio::any_completion_handler_allocator; + std::unique_ptr> state; template - async_write_to_file_op(Stream_ && str, const std::filesystem::path & pt) - : str(std::forward(str)), file(pt) + async_write_to_file_op(allocator_type alloc, Stream_ * str, const filesystem::path & pt) + : file(pt), state(allocate_unique(alloc, str)) { } - std::size_t resume(requests::detail::faux_token_t self, - system::error_code & ec, std::size_t n = 0u) + template + void operator()(Self && self, system::error_code ec = {}, std::size_t n = 0u) { - BOOST_ASIO_CORO_REENTER(this) - { - f.open(file.string().c_str(), asio::file_base::write_only | asio::file_base::create, ec); - if (ec) - return 0u; - - while (!str.done() && !ec) - { - // KDM: this could be in parallel to write using parallel_group. - BOOST_ASIO_CORO_YIELD { - auto b = asio::buffer(buffer); - str.async_read_some(b, std::move(self)); + auto st = state.get(); + if (!ec) + BOOST_ASIO_CORO_REENTER(this) + { + st->f.open(file.string().c_str(), asio::file_base::write_only | asio::file_base::create, ec); + if (ec) + { + st->written = 0u; + break; + } + + while (!st->str.done() && !ec) + { + // KDM: this could be in parallel to write using parallel_group. + BOOST_ASIO_CORO_YIELD { + auto b = asio::buffer(st->buffer); + st->str.async_read_some(b, std::move(self)); + } + + if (n == 0 && ec) + break; + + st->ec_read = exchange(ec, {}); + BOOST_ASIO_CORO_YIELD asio::async_write(st->f, asio::buffer(st->buffer, n), std::move(self)); + + st->written += n; + if (st->ec_read && !ec) + ec = st->ec_read; + } } - - if (n == 0 && ec) - return written; - - ec_read = exchange(ec, {}); - BOOST_ASIO_CORO_YIELD asio::async_write(f, asio::buffer(buffer, n), std::move(self)); - - written += n; - if (ec_read && !ec) - ec = ec_read; - } + if (is_complete()) + { + state.reset(); + self.complete(ec, st->written); } - return written; } }; @@ -165,47 +183,83 @@ template struct async_write_to_file_op : asio::coroutine { using executor_type = typename std::decay_t::executor_type; - executor_type get_executor() {return str.get_executor(); } + executor_type get_executor() {return state->str.get_executor(); } using completion_signature_type = void(system::error_code, std::size_t); using step_signature_type = void(system::error_code, std::size_t); - Stream &str; - beast::file f; const filesystem::path & file; - char buffer[BOOST_REQUESTS_CHUNK_SIZE]; - std::size_t written = 0u; + struct state_t + { + state_t(Stream * str) : str(*str) {} + Stream &str; + beast::file f; + char buffer[BOOST_REQUESTS_CHUNK_SIZE]; + + std::size_t written = 0u; + }; + + using allocator_type = asio::any_completion_handler_allocator; + std::unique_ptr> state; + - async_write_to_file_op(Stream * str, const filesystem::path & pt) : str(*str), file(pt) {} + async_write_to_file_op(allocator_type alloc, Stream * str, const filesystem::path & pt) + : file(pt), state(allocate_unique(alloc, str)) {} - std::size_t resume(requests::detail::faux_token_t self, - system::error_code & ec, std::size_t n = 0u) + template + void operator()(Self && self, + system::error_code ec = {}, std::size_t n = 0u) { + auto st = state.get(); + if (!ec) BOOST_ASIO_CORO_REENTER(this) { - f.open(file.string().c_str(), beast::file_mode::write_new, ec); + state->f.open(file.string().c_str(), beast::file_mode::write_new, ec); if (ec) - return 0u; + { + state->written = 0u; + BOOST_ASIO_CORO_YIELD { + auto exec = asio::get_associated_immediate_executor(self, state->str.get_executor()); + asio::dispatch(exec, std::move(self)); + } + break; + } + - while (!ec && !str.done()) + while (!ec && !state->str.done()) { - BOOST_ASIO_CORO_YIELD str.async_read_some(asio::buffer(buffer), std::move(self)); + BOOST_ASIO_CORO_YIELD state->str.async_read_some(asio::buffer(state->buffer), std::move(self)); if (n == 0 && ec) - return written; + break; system::error_code ec_read = exchange(ec, {}); - written += f.write(buffer, n, ec); + state->written += state->f.write(state->buffer, n, ec); if (ec_read && !ec) ec = ec_read; } } - return written; + if (is_complete()) + { + state.reset(); + self.complete(ec, st->written); + } } }; +template +void async_write_to_file_impl( + asio::any_completion_handler handler, + Stream * str, const filesystem::path & file) +{ + return asio::async_compose, + void(boost::system::error_code, std::size_t)>( + detail::async_write_to_file_op{asio::get_associated_allocator(handler), str, file}, + handler, str->get_executor()); +} + } @@ -219,8 +273,9 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_c async_write_to_file(Stream & str, const filesystem::path & file, CompletionToken && completion_token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN( typename Stream::executor_type)) { - return requests::detail::faux_run< - detail::async_write_to_file_op>(std::forward(completion_token), &str, file); + return asio::async_initiate( + &detail::async_write_to_file_impl, + completion_token, &str, file); } template @@ -307,58 +362,89 @@ template struct async_download_op : asio::coroutine { using executor_type = typename Connection::executor_type; - executor_type get_executor() {return conn.get_executor(); } + executor_type get_executor() {return state->str.get_executor(); } + + + struct state_t + { + Connection & conn; + urls::url_view target; + typename Connection::request_type req; + filesystem::path download_path; + + download_response rb{}; + optional str_; + + state_t(Connection * conn, + urls::url_view target, + typename Connection::request_type req, + filesystem::path download_path) + : conn(*conn), target(target), req(std::move(req)), download_path(std::move(download_path)) {} + }; + + using allocator_type = asio::any_completion_handler_allocator; + std::unique_ptr> state; - Connection & conn; - urls::url_view target; - typename Connection::request_type req; - filesystem::path download_path; - async_download_op(Connection * conn, + async_download_op(allocator_type alloc, Connection * conn, urls::url_view target, typename Connection::request_type req, filesystem::path download_path) - : conn(*conn), target(target), req(std::move(req)), download_path(std::move(download_path)) {} + : state(allocate_unique(alloc, conn, target, req, std::move(download_path))) + {} - download_response rb{}; - optional str_; - - using completion_signature_type = void(system::error_code, download_response); - using step_signature_type = void(system::error_code, optional); - - download_response & resume(requests::detail::faux_token_t self, - system::error_code & ec, - optional s = none) + template + void operator()(Self && self, system::error_code ec = {}, optional s = none) { - BOOST_ASIO_CORO_REENTER(this) + auto st = state.get(); + if (!ec) + BOOST_ASIO_CORO_REENTER(this) + { + BOOST_ASIO_CORO_YIELD st->conn.async_ropen(http::verb::get, st->target, empty{}, + std::move(st->req), std::move(self)); + if (ec) + { + st->rb.history = std::move(*s).history(); + st->rb.headers = std::move(*s).headers(); + break; + } + st->str_ = *std::move(s); + if (filesystem::exists(st->download_path, ec) && filesystem::is_directory(st->download_path, ec) && !st->target.segments().empty()) + st->rb.download_path = st->download_path / st->target.segments().back(); // so we can download to a folder + else + st->rb.download_path = std::move(st->download_path); + ec.clear(); + if (!ec) + { + BOOST_ASIO_CORO_YIELD async_write_to_file(*st->str_, st->rb.download_path, + asio::deferred([](system::error_code ec, std::size_t){return asio::deferred.values(ec);})) + (std::move(self)); + } + + st->rb.history = std::move(*st->str_).history(); + st->rb.headers = std::move(*st->str_).headers(); + } + if (is_complete()) { - BOOST_ASIO_CORO_YIELD conn.async_ropen(http::verb::get, target, empty{}, std::move(req), std::move(self)); - if (ec) - { - rb.history = std::move(*s).history(); - rb.headers = std::move(*s).headers(); - return rb; - } - str_ = std::move(s); - if (filesystem::exists(download_path, ec) && filesystem::is_directory(download_path, ec) && !target.segments().empty()) - rb.download_path = download_path / target.segments().back(); // so we can download to a folder - else - rb.download_path = std::move(download_path); - ec.clear(); - if (!ec) - { - BOOST_ASIO_CORO_YIELD async_write_to_file(*str_, rb.download_path, - asio::deferred([](system::error_code ec, std::size_t){return asio::deferred.values(ec);})) - (std::move(self)); - } - - rb.history = std::move(*str_).history(); - rb.headers = std::move(*str_).headers(); + state.reset(); + self.complete(ec, st->rb); } - return rb; } }; +template +void async_download_impl( + asio::any_completion_handler handler, + Connection * conn, urls::url_view target, typename Connection::request_type req, filesystem::path download_path) +{ + return asio::async_compose, + void(boost::system::error_code, download_response)>( + detail::async_download_op{ + asio::get_associated_allocator(handler), + conn, target, std::move(req), std::move(download_path)}, + handler, conn->get_executor()); +} + } @@ -372,9 +458,9 @@ async_download(Connection & conn, filesystem::path download_path, CompletionToken && completion_token = typename Connection::default_token()) { - return detail::faux_run>( - std::forward(completion_token), - &conn, target, std::move(req), std::move(download_path)); + return asio::async_initiate( + &detail::async_download_impl, completion_token, + &conn, target, std::move(req), std::move(download_path)); } namespace detail diff --git a/include/boost/requests/impl/connection_pool.hpp b/include/boost/requests/impl/connection_pool.hpp index cb39c66..64cbd6d 100644 --- a/include/boost/requests/impl/connection_pool.hpp +++ b/include/boost/requests/impl/connection_pool.hpp @@ -14,129 +14,30 @@ namespace boost { namespace requests { -struct connection_pool::async_lookup_op : asio::coroutine -{ - using executor_type = asio::any_io_executor; - executor_type get_executor() {return this_->get_executor(); } - - connection_pool * this_; - const urls::url_view sv; - optional resolver; - - urls::string_view scheme = "https"; - urls::string_view service; - - std::unique_lock lock{this_->mtx_, std::defer_lock}; - - async_lookup_op(connection_pool * this_, urls::url_view sv, executor_type exec) - : this_(this_), sv(sv), resolver(exec) {} - - using completion_signature_type = void(system::error_code); - using step_signature_type = void(system::error_code, typename asio::ip::tcp::resolver::results_type); - - - BOOST_REQUESTS_DECL void resume(requests::detail::faux_token_t self, - system::error_code & ec, typename asio::ip::tcp::resolver::results_type eps = {}); -}; template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code)) connection_pool::async_lookup(urls::url_view av, CompletionToken && completion_token) { - return detail::faux_run( - std::forward(completion_token), - this, av, get_executor()); + return asio::async_initiate( + &async_lookup_impl, + completion_token, + this, av); } -struct connection_pool::async_get_connection_op : asio::coroutine -{ - int i = 0; - using executor_type = asio::any_io_executor; - executor_type get_executor() {return this_->get_executor(); } - - connection_pool * this_; - async_get_connection_op(connection_pool * this_) : this_(this_) {} - - using conn_t = boost::unordered_multimap, - detail::endpoint_hash>; - typename conn_t::iterator itr; - - - boost::intrusive_ptr nconn = nullptr; - std::unique_lock lock{this_->mtx_, std::defer_lock}; - endpoint_type ep; - - using completion_signature_type = void(system::error_code, connection); - using step_signature_type = void(system::error_code); - - BOOST_REQUESTS_DECL auto resume(requests::detail::faux_token_t self, - system::error_code & ec) -> connection; -}; template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, connection)) connection_pool::async_get_connection(CompletionToken && completion_token) { - // async_get_connection_op - return detail::faux_run( - std::forward(completion_token), - this - ); + return asio::async_initiate( + &async_get_connection_impl, + completion_token, + this); } -struct connection_pool::async_ropen_op : asio::coroutine -{ - using executor_type = asio::any_io_executor; - executor_type get_executor() {return this_->get_executor(); } - - connection_pool * this_; - beast::http::verb method; - urls::pct_string_view path; - http::fields & headers; - source & src; - request_options opt; - cookie_jar * jar; - - connection conn; - - async_ropen_op(connection_pool * this_, - beast::http::verb method, - urls::pct_string_view path, - http::fields & headers, - source & src, - request_options opts, - cookie_jar * jar) - : this_(this_), method(method), path(path), headers(headers), src(src), opt(std::move(opts)), jar(jar) - { - } - - - async_ropen_op(connection_pool * this_, - beast::http::verb method, - urls::url_view path, - http::fields & headers, - source & src, - request_options opt, - cookie_jar * jar) - : this_(this_), method(method), path(path.encoded_resource()), - headers(headers), src(src), opt(std::move(opt)), jar(jar) - { - } - - using completion_signature_type = void(boost::system::error_code, stream); - using step_signature_type = void(boost::system::error_code, variant2::variant); - - BOOST_REQUESTS_DECL - auto resume(requests::detail::faux_token_t self, - boost::system::error_code & ec, - variant2::variant res = variant2::monostate()) -> stream; - -}; - - template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, stream)) @@ -148,8 +49,10 @@ connection_pool::async_ropen(beast::http::verb method, cookie_jar * jar, CompletionToken && completion_token) { - return detail::faux_run( - std::forward(completion_token), + + return asio::async_initiate( + &async_ropen_impl, + completion_token, this, method, path, std::ref(headers), std::ref(src), std::move(opt), jar); } diff --git a/include/boost/requests/impl/request.hpp b/include/boost/requests/impl/request.hpp index 452aea2..4fcfd19 100644 --- a/include/boost/requests/impl/request.hpp +++ b/include/boost/requests/impl/request.hpp @@ -46,50 +46,63 @@ struct async_request_op : asio::coroutine using executor_type = typename Connection::executor_type; executor_type get_executor() {return conn.get_executor(); } - using completion_signature_type = void(system::error_code, response); - using step_signature_type = void(system::error_code, variant2::variant); - Connection & conn; http::verb method; urls::url_view target; RequestBody && request_body; - typename Connection::request_type req; - optional str_; - response rb{}; + struct state_t + { + state_t(typename Connection::request_type req) : req(std::move(req)) {} + typename Connection::request_type req; + optional str_; + + response rb{}; + }; + + using allocator_type = asio::any_completion_handler_allocator; + std::unique_ptr> state; template - async_request_op(Connection * conn, + async_request_op(allocator_type alloc, + Connection * conn, http::verb method, urls::url_view target, RequestBody_ && request_body, typename Connection::request_type req) - : conn(*conn), method(method), target(target), - request_body(static_cast(request_body)), req(std::move(req)) {} - - response & resume(requests::detail::faux_token_t self, - system::error_code & ec, - variant2::variant s) + : conn(*conn), method(method), target(target) + , request_body(static_cast(request_body)) + , state(allocate_unique(alloc, std::move(req))) {} + + template + void operator()(Self && self, + system::error_code ec = {}, + variant2::variant s = 0u) { - BOOST_ASIO_CORO_REENTER(this) - { - BOOST_ASIO_CORO_YIELD conn.async_ropen(method, target, - std::forward(request_body), - std::move(req), std::move(self)); - str_.emplace(std::move(variant2::get<1>(s))); - if (!ec) + auto st = state.get(); + + if (!ec) + BOOST_ASIO_CORO_REENTER(this) { - BOOST_ASIO_CORO_YIELD str_->async_read( rb.buffer, std::move(self)); + BOOST_ASIO_CORO_YIELD conn.async_ropen(method, target, + std::forward(request_body), + std::move(st->req), std::move(self)); + st->str_.emplace(std::move(variant2::get<1>(s))); + if (!ec) + { + BOOST_ASIO_CORO_YIELD st->str_->async_read( st->rb.buffer, std::move(self)); + } + st->rb.headers = std::move(*st->str_).headers(); + st->rb.history = std::move(*st->str_).history(); } - - rb.headers = std::move(*str_).headers(); - rb.history = std::move(*str_).history(); + if (is_complete()) + { + state.reset(); + self.complete(ec, std::move(st->rb)); } - return rb; } }; - struct async_free_request_op { template +inline void async_request_impl( + asio::any_completion_handler handler, + Connection * conn, + beast::http::verb method, + urls::url_view target, + RequestBody * body, + typename Connection::request_type req) +{ + return asio::async_compose< + asio::any_completion_handler, + void(system::error_code, response)>( + async_request_op{ + asio::get_associated_allocator(handler), + conn, method, target, std::forward(*body), std::move(req) + }, + handler, conn->get_executor()); +} + + + } template + BOOST_ASIO_COMPLETION_TOKEN_FOR(void(system::error_code, response)) CompletionToken> BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (system::error_code, response)) async_request(Connection & conn, beast::http::verb method, @@ -120,9 +154,10 @@ async_request(Connection & conn, typename Connection::request_type req, CompletionToken && completion_token) { - return requests::detail::faux_run>( - std::forward(completion_token), - &conn, method, target, std::forward(body), std::move(req)); + return asio::async_initiate( + &detail::async_request_impl, + completion_token, + &conn, method, target, &body, std::move(req)); } templateget_executor(); } - - session *this_; - urls::url_view url; - const bool is_https; - - detail::lock_guard lock; - - async_get_pool_op(session *this_, urls::url_view url) - : this_(this_), url(url), - is_https((url.scheme_id() == urls::scheme::https) || (url.scheme_id() == urls::scheme::wss)) - {} - - std::shared_ptr p; - - using completion_signature_type = void(system::error_code, std::shared_ptr); - using step_signature_type = void(system::error_code); - - BOOST_REQUESTS_DECL - std::shared_ptr resume(requests::detail::faux_token_t self, error_code ec); -}; template< BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code, std::shared_ptr)) CompletionToken> BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, pool_ptr)) session::async_get_pool(urls::url_view url, CompletionToken && completion_token) { - return detail::faux_run(std::forward(completion_token), this, url); + return asio::async_initiate< + CompletionToken, void (boost::system::error_code, std::shared_ptr)>( + &async_get_pool_impl, completion_token, this, url); } @@ -69,46 +47,6 @@ auto session::ropen( return ropen(method, url, fields, *src, ec); } -struct session::async_ropen_op : asio::coroutine -{ - using executor_type = asio::any_io_executor; - executor_type get_executor() {return this_->get_executor(); } - - session * this_; - - http::verb method; - - urls::url url; - struct request_options opts; - core::string_view default_mime_type; - - system::error_code ec_; - - bool is_secure = (url.scheme_id() == urls::scheme::https) - || (url.scheme_id() == urls::scheme::wss); - - response_base::history_type history; - - http::fields & headers; - source & src; - - urls::url url_cache; - - async_ropen_op(session * this_, - http::verb method, - urls::url_view path, - source & src, - http::fields & headers) - : this_(this_), method(method), url(path), opts(this_->options_), headers(headers), src(src) - { - } - - using completion_signature_type = void(system::error_code, stream); - using step_signature_type = void(system::error_code, variant2::variant, stream>); - - BOOST_REQUESTS_DECL auto resume(requests::detail::faux_token_t self, - system::error_code & ec, variant2::variant, stream> s) -> stream; -}; template @@ -146,8 +84,9 @@ session::async_ropen(http::verb method, http::fields & headers, CompletionToken && completion_token) { - return detail::faux_run(std::forward(completion_token), - this, method, path, src, headers); + return asio::async_initiate( + &async_ropen_impl, completion_token, + this, method, path, &src, &headers); } } diff --git a/include/boost/requests/impl/source.hpp b/include/boost/requests/impl/source.hpp index 855a73e..7828126 100644 --- a/include/boost/requests/impl/source.hpp +++ b/include/boost/requests/impl/source.hpp @@ -5,12 +5,14 @@ #ifndef BOOST_REQUESTS_IMPL_SOURCE_HPP #define BOOST_REQUESTS_IMPL_SOURCE_HPP -#include #include +#include +#include #include #include #include +#include #include namespace boost @@ -186,77 +188,98 @@ struct async_write_request_op : asio::coroutine core::string_view target; http::fields &header; - source &src; + struct state_t + { + source &src; + + state_t(source &src) : src(src) {} + + optional sz = src.size(); + variant2::variant, + http::request, + http::request> freq; + + char prebuffer[BOOST_REQUESTS_CHUNK_SIZE]; + std::pair init; - optional sz = src.size(); - variant2::variant, - http::request, - http::request> freq; + }; - char prebuffer[BOOST_REQUESTS_CHUNK_SIZE]; - std::pair init; + using allocator_type = asio::any_completion_handler_allocator; + std::unique_ptr> state; - async_write_request_op(Stream & stream, + async_write_request_op(allocator_type alloc, + Stream & stream, http::verb method, core::string_view target, http::fields &header, source &src) - : stream(stream), method(method), target(target), header(header), src(src) + : stream(stream), method(method), target(target), header(header) + , state(allocate_unique(alloc, src)) {} - std::size_t resume(requests::detail::faux_token_t self, - system::error_code ec = {}, std::size_t n = 0u) + template + void operator()(Self && self, + system::error_code ec = {}, std::size_t n = 0u) { - BOOST_ASIO_CORO_REENTER(this) - { + auto st = state.get(); + if (!ec) + BOOST_ASIO_CORO_REENTER(this) { - const auto itr = header.find(beast::http::field::content_type); - if (itr == header.end()) { - auto def = src.default_content_type(); - if (!def.empty()) - header.set(beast::http::field::content_type, def); + const auto itr = header.find(beast::http::field::content_type); + if (itr == header.end()) + { + auto def = st->src.default_content_type(); + if (!def.empty()) + header.set(beast::http::field::content_type, def); + } } - } - src.reset(); + st->src.reset(); - if (sz) - { - if (*sz == 0) + if (st->sz) { - freq.emplace<1>(method, target, 11, http::empty_body::value_type(), std::move(header)).prepare_payload(); - BOOST_ASIO_CORO_YIELD beast::http::async_write(stream, variant2::get<1>(freq), std::move(self)); - header = std::move(variant2::get<1>(freq).base()); + if (*st->sz == 0) + { + st->freq.template emplace<1>(method, target, 11, http::empty_body::value_type(), std::move(header)).prepare_payload(); + BOOST_ASIO_CORO_YIELD beast::http::async_write(stream, variant2::get<1>(st->freq), std::move(self)); + header = std::move(variant2::get<1>(st->freq).base()); + } + else + { + st->freq.template emplace<2>(method, target, 11, st->src, std::move(header)).prepare_payload(); + BOOST_ASIO_CORO_YIELD beast::http::async_write(stream, variant2::get<2>(st->freq), std::move(self)); + header = std::move(variant2::get<2>(st->freq).base()); + } + } else { - freq.emplace<2>(method, target, 11, src, std::move(header)).prepare_payload(); - BOOST_ASIO_CORO_YIELD beast::http::async_write(stream, variant2::get<2>(freq), std::move(self)); - header = std::move(variant2::get<2>(freq).base()); + st->init = st->src.read_some(st->prebuffer, sizeof(st->prebuffer), ec); + if (ec) + { + n = 0u; + break; + } + + st->freq.template emplace<3>(method, target, 11, + detail::source_body::value_type{st->src, + asio::buffer(st->prebuffer, st->init.first), + st->init.second}, std::move(header)); + + if (!st->init.second) + variant2::get<3>(st->freq).set(beast::http::field::content_length, std::to_string(st->init.first)); + else + variant2::get<3>(st->freq).prepare_payload(); + BOOST_ASIO_CORO_YIELD beast::http::async_write(stream, variant2::get<3>(st->freq), std::move(self)); + header = std::move(variant2::get<3>(st->freq).base()); } - - } - else - { - init = src.read_some(prebuffer, sizeof(prebuffer), ec); - if (ec) - return 0u; - - freq.emplace<3>(method, target, 11, - detail::source_body::value_type{src, - asio::buffer(prebuffer, init.first), - init.second}, std::move(header)); - - if (!init.second) - variant2::get<3>(freq).set(beast::http::field::content_length, std::to_string(init.first)); - else - variant2::get<3>(freq).prepare_payload(); - BOOST_ASIO_CORO_YIELD beast::http::async_write(stream, variant2::get<3>(freq), std::move(self)); - header = std::move(variant2::get<3>(freq).base()); } + if (is_complete()) + { + state.reset(); + self.complete(ec, n); } - return n; } }; @@ -264,8 +287,8 @@ struct async_write_request_op : asio::coroutine } template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(system::error_code, std::size_t)) + BOOST_ASIO_COMPLETION_TOKEN_FOR(void(boost::system::error_code, std::size_t)) CompletionToken> +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::system::error_code, std::size_t)) async_write_request( Stream & stream, http::verb method, @@ -274,9 +297,21 @@ async_write_request( source &src, CompletionToken && token) { - return detail::faux_run>( - std::forward(token), std::ref(stream), - method, target, std::ref(header), std::ref(src)); + return asio::async_initiate( + [](asio::any_completion_handler handler, + Stream * stream, + http::verb method, + core::string_view target, + http::fields *header, + source *src) + { + asio::async_compose, + void(boost::system::error_code, std::size_t)>( + detail::async_write_request_op{ + asio::get_associated_allocator(handler), + *stream, method, target, *header, *src}, + handler, stream->get_executor()); + }, token, &stream, method, target, &header, &src); } diff --git a/include/boost/requests/impl/stream.hpp b/include/boost/requests/impl/stream.hpp index b22646d..3be7fa8 100644 --- a/include/boost/requests/impl/stream.hpp +++ b/include/boost/requests/impl/stream.hpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -112,8 +113,7 @@ struct stream::async_read_op : asio::coroutine stream * this_; DynamicBuffer & buffer; - template - async_read_op(stream * this_, DynamicBuffer_ && buffer) : this_(this_), buffer(buffer) + async_read_op(stream * this_, DynamicBuffer & buffer) : this_(this_), buffer(buffer) { } @@ -122,13 +122,10 @@ struct stream::async_read_op : asio::coroutine system::error_code ec_; std::size_t res = 0u; - - using completion_signature_type = void(system::error_code, std::size_t); - using step_signature_type = void(system::error_code, std::size_t); - - std::size_t resume(requests::detail::faux_token_t self, - system::error_code & ec, std::size_t n = 0u) + template + void operator()(Self && self, error_code ec = {}, std::size_t n = 0u) { + if (!ec) BOOST_ASIO_CORO_REENTER(this) { if (!this_->parser_) @@ -166,10 +163,9 @@ struct stream::async_read_op : asio::coroutine std::swap(ec, ec_); } } - - return res; } - return 0u; + if (is_complete()) + self.complete(ec, res); } }; @@ -183,43 +179,11 @@ stream::async_read( DynamicBuffer & buffers, CompletionToken && token) { - return detail::faux_run>(std::forward(token), this, buffers); + return asio::async_compose( + async_read_op{this, buffers}, + token, get_executor()); } -struct stream::async_read_some_op : asio::coroutine -{ - using executor_type = asio::any_io_executor; - executor_type get_executor() {return this_->get_executor(); } - - stream * this_; - asio::mutable_buffer buffer; - - template - async_read_some_op(stream * this_, const MutableBufferSequence & buffer) : this_(this_) - { - auto itr = boost::asio::buffer_sequence_begin(buffer); - const auto end = boost::asio::buffer_sequence_end(buffer); - - while (itr != end) - { - if (itr->size() != 0u) - { - this->buffer = *itr; - break; - } - } - } - system::error_code ec_; - - using completion_signature_type = void(system::error_code, std::size_t); - using step_signature_type = void(system::error_code, std::size_t); - - BOOST_REQUESTS_DECL - std::size_t resume(requests::detail::faux_token_t self, - system::error_code & ec, std::size_t res = 0u); - -}; - template< typename MutableBufferSequence, BOOST_ASIO_COMPLETION_TOKEN_FOR(void (system::error_code, std::size_t)) CompletionToken> @@ -228,40 +192,35 @@ stream::async_read_some( const MutableBufferSequence & buffers, CompletionToken && token) { - return detail::faux_run(std::forward(token), this, buffers); -} - - -struct stream::async_dump_op : asio::coroutine -{ - using executor_type = asio::any_io_executor; - executor_type get_executor() {return this_->get_executor(); } - - stream * this_; - - char buffer[BOOST_REQUESTS_CHUNK_SIZE]; - system::error_code ec_; + return asio::async_initiate( + [this](asio::any_completion_handler handler, + const MutableBufferSequence & buffers) + { + auto itr = boost::asio::buffer_sequence_begin(buffers); + const auto end = boost::asio::buffer_sequence_end(buffers); - async_dump_op(stream * this_) : this_(this_) {} + asio::mutable_buffer buffer; + while (itr != end) + { + if (itr->size() != 0u) + { + buffer = *itr; + break; + } + } + async_read_some_impl(std::move(handler), buffer); + }, token, buffers); +} - using completion_signature_type = void(system::error_code); - using step_signature_type = void(system::error_code, std::size_t); - BOOST_REQUESTS_DECL - void resume(requests::detail::faux_token_t self, - system::error_code ec = {}, std::size_t n = 0u); -}; template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code)) stream::async_dump(CompletionToken && token) { - return detail::faux_run(std::forward(token), this); + return asio::async_initiate(&async_dump_impl, token, this); } - - - } } diff --git a/include/boost/requests/json.hpp b/include/boost/requests/json.hpp index b0c480a..e3c4596 100644 --- a/include/boost/requests/json.hpp +++ b/include/boost/requests/json.hpp @@ -8,8 +8,6 @@ #ifndef BOOST_REQUESTS_JSON_HPP #define BOOST_REQUESTS_JSON_HPP - -#include #include #include #include @@ -644,38 +642,60 @@ struct async_read_json_op : asio::coroutine using executor_type = typename Stream::executor_type; executor_type get_executor() {return str.get_executor(); } - using completion_signature_type = void(system::error_code, value); - using step_signature_type = void(system::error_code, std::size_t); - Stream &str; - ::boost::json::stream_parser sp; - char buffer[BOOST_REQUESTS_CHUNK_SIZE]; - async_read_json_op(Stream * str, json::storage_ptr ptr) : str(*str), sp{ptr} {} + struct state_t + { + state_t(json::storage_ptr ptr) : sp(std::move(ptr)) {} + ::boost::json::stream_parser sp; + char buffer[BOOST_REQUESTS_CHUNK_SIZE]; + }; + + using allocator_type = asio::any_completion_handler_allocator; + std::unique_ptr> state; - value resume(requests::detail::faux_token_t self, - system::error_code & ec, std::size_t n = 0u) + async_read_json_op(allocator_type alloc, Stream * str, json::storage_ptr ptr) + : str(*str), state{allocate_unique(alloc, std::move(ptr))} {} + + template + void operator()(Self && self, system::error_code ec = {}, std::size_t n = 0u) { BOOST_ASIO_CORO_REENTER(this) { - while (!sp.done() && !str.done() && !ec) + while (!state->sp.done() && !str.done() && !ec) { - BOOST_ASIO_CORO_YIELD str.async_read_some(asio::buffer(buffer), std::move(self)); + BOOST_ASIO_CORO_YIELD str.async_read_some(asio::buffer(state->buffer), std::move(self)); if (ec) - return nullptr; - sp.write_some(buffer, n, ec); + return self.complete(ec, nullptr); + state->sp.write_some(state->buffer, n, ec); } if (!ec) - sp.finish(ec); + state->sp.finish(ec); if (ec) - return nullptr; + return self.complete(ec, nullptr); - return sp.release(); + auto r = state->sp.release(); + state.reset(); + return self.complete(ec, std::move(r)); } - return nullptr; } }; +template +inline void async_read_json_impl( + asio::any_completion_handler handler, + Stream * str, json::storage_ptr ptr) +{ + return asio::async_compose, + void(error_code, json::value)>( + async_read_json_op{ + asio::get_associated_allocator(handler), + str, std::move(ptr)}, + handler, + str->get_executor()); +} + + template struct async_read_optional_json_op : asio::coroutine @@ -683,69 +703,87 @@ struct async_read_optional_json_op : asio::coroutine using executor_type = typename Stream::executor_type; executor_type get_executor() {return str.get_executor(); } - using completion_signature_type = void(system::error_code, response>); - using step_signature_type = void(system::error_code, std::size_t); - Stream & str; ::boost::json::stream_parser sp; - char buffer[BOOST_REQUESTS_CHUNK_SIZE]; + using allocator_type = asio::any_completion_handler_allocator)>; + std::vector buffer; - async_read_optional_json_op(Stream * str, json::storage_ptr ptr) : str(*str), sp{ptr} {} + async_read_optional_json_op(allocator_type alloc, Stream * str, json::storage_ptr ptr) + : str(*str), sp{ptr}, buffer(BOOST_REQUESTS_CHUNK_SIZE, std::move(alloc)){} - optional resume(requests::detail::faux_token_t self, - system::error_code & ec, std::size_t n = 0u) + template + void operator()(Self && self, system::error_code ec = {}, std::size_t n = 0u) { BOOST_ASIO_CORO_REENTER(this) { BOOST_ASIO_CORO_YIELD str.async_read_some(asio::buffer(buffer), std::move(self)); if (ec || (n == 0 && str.done())) - return boost::none; - sp.write_some(buffer, n, ec); + return self.complete(ec, boost::none); + sp.write_some(buffer.data(), n, ec); + if (ec) + return self.complete(ec, boost::none); while (!sp.done() && !str.done()) { BOOST_ASIO_CORO_YIELD str.async_read_some(asio::buffer(buffer), std::move(self)); if (ec) - return boost::none; - sp.write_some(buffer, n, ec); + return self.complete(ec, boost::none); + sp.write_some(buffer.data(), n, ec); } if (!ec) sp.finish(ec); if (ec) - return boost::none; - return sp.release(); + return self.complete(ec, boost::none); + return self.complete(ec, sp.release()); } - return boost::none; } }; + +template +inline void async_read_optional_json_impl( + asio::any_completion_handler)> handler, + Stream * str, json::storage_ptr ptr) +{ + return asio::async_compose)>, + void(error_code, optional)>( + async_read_optional_json_op{ + asio::get_associated_allocator(handler), + str, std::move(ptr)}, + handler, + str->get_executor()); } -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, Value)) +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, json::value)) async_read_json(Stream & str, json::storage_ptr ptr = {}, CompletionToken && completion_token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN( typename Stream::executor_type)) { - return requests::detail::faux_run< - detail::async_read_json_op>(std::forward(completion_token), &str, ptr); + return asio::async_initiate( + &detail::async_read_json_impl, + completion_token, + &str, std::move(ptr)); } -template)) CompletionToken +template)) CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(typename Stream::executor_type)> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::Stream::error_code, optional)) +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::Stream::error_code, optional)) async_read_optional_json(Stream & str, json::storage_ptr ptr = {}, CompletionToken && completion_token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN( typename Stream::executor_type)) { - return requests::detail::faux_run< - detail::async_read_optional_json_op>(std::forward(completion_token), &str, ptr); + + return asio::async_initiate)>( + &detail::async_read_optional_json_impl, + completion_token, + &str, std::move(ptr)); } using requests::async_head; @@ -760,23 +798,34 @@ struct async_request_json_op : asio::coroutine using executor_type = typename Connection::executor_type; executor_type get_executor() {return conn.get_executor(); } - using completion_signature_type = void(system::error_code, response); - using step_signature_type = void(system::error_code, variant2::variant); Connection & conn; http::verb method; urls::url_view target; - RequestBody && request_body; - typename Connection::request_type req; - json::storage_ptr ptr{}; - optional str_; - response rb; + struct state_t + { + state_t(RequestBody && request_body, + typename Connection::request_type req, + json::storage_ptr ptr) + : request_body(std::forward(request_body)), req(std::move(req)), ptr(std::move(ptr)) {} + + RequestBody && request_body; + typename Connection::request_type req; + json::storage_ptr ptr; + + optional str_; + response rb; + + }; + + using allocator_type = asio::any_completion_handler_allocator)>; + std::unique_ptr> state; + json::value value_from(RequestBody && request_body, std::false_type) const { - return ::boost::json::value_from(std::forward(request_body), ptr); + return ::boost::json::value_from(std::forward(request_body), state->ptr); } empty value_from(RequestBody && request_body, std::true_type) const @@ -784,48 +833,53 @@ struct async_request_json_op : asio::coroutine return {}; } - async_request_json_op(Connection * conn, + async_request_json_op( + allocator_type alloc, + Connection * conn, http::verb method, urls::url_view target, RequestBody && request_body, typename Connection::request_type req, json::storage_ptr ptr) : conn(*conn), method(method), target(target), - request_body(std::forward(request_body)), req(std::move(req)), ptr(std::move(ptr)) {} + state(allocate_unique(alloc, std::forward(request_body), std::move(req), std::move(ptr))) {} - response & resume(requests::detail::faux_token_t self, - system::error_code & ec, - variant2::variant s) + template + void operator()(Self && self, + system::error_code ec = {}, + variant2::variant s = variant2::monostate{}) { BOOST_ASIO_CORO_REENTER(this) { BOOST_ASIO_CORO_YIELD conn.async_ropen(method, target, - value_from(std::forward(request_body), + value_from(std::forward(state->request_body), std::is_same>{}), - std::move(req), std::move(self)); + std::move(state->req), std::move(self)); if (!ec) { - str_.emplace(std::move(variant2::get<1>(s))); - rb.headers = std::move(*str_).headers(); - rb.history = std::move(*str_).history(); - BOOST_ASIO_CORO_YIELD async_read_json(*str_, ptr, std::move(self)); + state->str_.emplace(std::move(variant2::get<1>(s))); + state->rb.headers = std::move(*state->str_).headers(); + state->rb.history = std::move(*state->str_).history(); + BOOST_ASIO_CORO_YIELD async_read_json(*state->str_, state->ptr, std::move(self)); if (ec) - return rb; + break; } else { - rb.headers = std::move(std::move(variant2::get<1>(s))).headers(); - rb.history = std::move(std::move(variant2::get<1>(s))).history(); + state->rb.headers = std::move(std::move(variant2::get<1>(s))).headers(); + state->rb.history = std::move(std::move(variant2::get<1>(s))).history(); break; } auto v = ::boost::json::try_value_to(variant2::get<2>(s)); if (v) - rb.value = std::move(*v); + state->rb.value = std::move(*v); else ec = v.error(); } - return rb; + + if (is_complete()) + return self.complete(ec, std::move(state->rb)); } }; @@ -836,49 +890,61 @@ struct async_request_optional_json_op : asio::coroutine using executor_type = typename Connection::executor_type; executor_type get_executor() {return conn.get_executor(); } - using completion_signature_type = void(system::error_code, response>); - using step_signature_type = void(system::error_code, variant2::variant>); - Connection & conn; http::verb method; urls::url_view target; - RequestBody && request_body; - typename Connection::request_type req; - json::storage_ptr ptr; - optional str_; + struct state_t + { + state_t(RequestBody && request_body, + typename Connection::request_type req, + json::storage_ptr ptr) + : request_body(std::forward(request_body)), req(std::move(req)), ptr(std::move(ptr)) {} + + RequestBody && request_body; + typename Connection::request_type req; + json::storage_ptr ptr; + optional str_; + + response> rb{}; + }; + + using allocator_type = asio::any_completion_handler_allocator>)>; + std::unique_ptr> state; + - response> rb{}; template - async_request_optional_json_op(Connection * conn, + async_request_optional_json_op(allocator_type alloc, + Connection * conn, http::verb method, urls::url_view target, RequestBody_ && request_body, typename Connection::request_type req, json::storage_ptr ptr) : conn(*conn), method(method), target(target), - request_body(std::forward(request_body)), req(std::move(req)), ptr(std::move(ptr)) {} + state(allocate_unique(alloc, std::forward(request_body), std::move(req), std::move(ptr))) {} - response> & resume(requests::detail::faux_token_t self, - system::error_code & ec, - variant2::variant> s) + template + void operator()(Self && self, + system::error_code ec = {}, + variant2::variant> s = variant2::monostate{}) { BOOST_ASIO_CORO_REENTER(this) { BOOST_ASIO_CORO_YIELD conn.async_ropen(method, target, - ::boost::json::value_from(std::forward(request_body), ptr), - std::move(req), std::move(self)); + ::boost::json::value_from(std::forward(state->request_body), state->ptr), + std::move(state->req), std::move(self)); if (!ec) { - str_.emplace(std::move(variant2::get<1>(s))); - rb.headers = std::move(*str_).headers(); - rb.history = std::move(*str_).history(); - BOOST_ASIO_CORO_YIELD async_read_json(*str_, ptr, std::move(self)); + state->str_.emplace(std::move(variant2::get<1>(s))); + state->rb.headers = std::move(*state->str_).headers(); + state->rb.history = std::move(*state->str_).history(); + BOOST_ASIO_CORO_YIELD async_read_json(*state->str_, state->ptr, std::move(self)); } else { - rb.headers = std::move(std::move(variant2::get<1>(s))).headers(); - rb.history = std::move(std::move(variant2::get<1>(s))).history(); + state->rb.headers = std::move(std::move(variant2::get<1>(s))).headers(); + state->rb.history = std::move(std::move(variant2::get<1>(s))).history(); break; } @@ -887,15 +953,56 @@ struct async_request_optional_json_op : asio::coroutine { auto v = ::boost::json::try_value_to(*variant2::get<2>(s)); if (v) - rb.value = std::move(*v); + state->rb.value = std::move(*v); else ec = v.error(); } } - return rb; + if (is_complete()) + self.complete(ec, std::move(state->rb)); } }; +template +inline void async_request_json_impl( + asio::any_completion_handler)> handler, + Connection * conn, + http::verb method, + urls::url_view target, + RequestBody && request_body, + typename Connection::request_type req, + json::storage_ptr ptr) +{ + return asio::async_compose)>, + void(error_code, response)>( + async_request_json_op{ + asio::get_associated_allocator(handler), + conn, method, target, std::forward(request_body), std::move(req), + std::move(ptr)}, + handler, + conn->get_executor()); +} + +template +inline void async_request_optional_json_impl( + asio::any_completion_handler>)> handler, + Connection * conn, + http::verb method, + urls::url_view target, + RequestBody && request_body, + typename Connection::request_type req, + json::storage_ptr ptr) +{ + return asio::async_compose>)>, + void(error_code, response>)>( + async_request_optional_json_op{ + asio::get_associated_allocator(handler), + conn, method, target, std::forward(request_body), std::move(req), + std::move(ptr)}, + handler, + conn->get_executor()); +} + } template>(std::forward(completion_token), - &conn, http::verb::get, target, empty{}, std::move(req), std::move(ptr)); + return asio::async_initiate)>( + &detail::async_request_json_impl, completion_token, + &conn, http::verb::get, target, empty{}, std::move(req), std::move(ptr)); } template>>(std::forward(completion_token), + return asio::async_initiate)>( + &detail::async_request_json_impl, completion_token, &conn, http::verb::post, target, std::forward(request_body), std::move(req), std::move(ptr)); } @@ -947,10 +1054,9 @@ async_patch(Connection & conn, json::storage_ptr ptr = {}, CompletionToken && completion_token = typename Connection::default_token()) { - return requests::detail::faux_run>>(std::forward(completion_token), - &conn, http::verb::patch, target, std::forward(request_body), - std::move(req), std::move(ptr)); + return asio::async_initiate)>( + &detail::async_request_json_impl, completion_token, + &conn, http::verb::patch, target, std::forward(request_body), std::move(req), std::move(ptr)); } template>>(std::forward(completion_token), - &conn, http::verb::put, target, std::forward(request_body), - std::move(req), std::move(ptr)); + return asio::async_initiate>)>( + &detail::async_request_optional_json_impl, completion_token, + &conn, http::verb::put, target, std::forward(request_body), std::move(req), std::move(ptr)); } @@ -986,10 +1091,9 @@ async_delete(Connection & conn, json::storage_ptr ptr = {}, CompletionToken && completion_token = typename Connection::default_token()) { - return requests::detail::faux_run>>(std::forward(completion_token), - &conn, http::verb::delete_, target, std::forward(request_body), - std::move(req), std::move(ptr)); + return asio::async_initiate)>( + &detail::async_request_json_impl, completion_token, + &conn, http::verb::delete_, target, std::forward(request_body), std::move(req), std::move(ptr)); } @@ -1005,10 +1109,10 @@ async_delete(Connection & conn, json::storage_ptr ptr = {}, CompletionToken && completion_token = typename Connection::default_token()) { - return requests::detail::faux_run>(std::forward(completion_token), - &conn, http::verb::delete_, target, empty{}, - std::move(req), std::move(ptr)); + return asio::async_initiate)>( + &detail::async_request_json_impl, + completion_token, &conn, http::verb::delete_, target, empty{}, + std::move(req), std::move(ptr)); } @@ -1023,10 +1127,9 @@ async_options(Connection & conn, json::storage_ptr ptr = {}, CompletionToken && completion_token = typename Connection::default_token()) { - return requests::detail::faux_run>(std::forward(completion_token), - &conn, http::verb::options, target, empty{}, - std::move(req), std::move(ptr)); + return asio::async_initiate)>( + &detail::async_request_json_impl, completion_token, + &conn, http::verb::options, target, empty{}, std::move(req), std::move(ptr)); } namespace detail @@ -1046,11 +1149,10 @@ struct async_free_request_op typename Connection::request_type req) { auto & sess = default_session(asio::get_associated_executor(handler)); - return requests::detail::faux_run< - async_request_json_op>( - std::forward(handler), - &sess, method, target, - std::forward(request_body),std::move(req)); + detail::async_read_json_impl( + std::forward(handler), + &sess, method, target, + std::forward(request_body), std::move(req)); } }; diff --git a/include/boost/requests/session.hpp b/include/boost/requests/session.hpp index ad9a914..09b2d08 100644 --- a/include/boost/requests/session.hpp +++ b/include/boost/requests/session.hpp @@ -169,6 +169,17 @@ struct session struct async_get_pool_op; struct async_ropen_op; + BOOST_REQUESTS_DECL + static void async_ropen_impl( + asio::any_completion_handler handler, + session * sess, http::verb method, urls::url_view path, source * src, http::fields * headers); + + BOOST_REQUESTS_DECL + static void async_get_pool_impl( + asio::any_completion_handler)> handler, + session * sess, urls::url_view url); + + BOOST_REQUESTS_DECL auto make_request_(http::fields fields) -> requests::request_parameters; BOOST_REQUESTS_DECL static urls::url normalize_(urls::url_view in); diff --git a/include/boost/requests/stream.hpp b/include/boost/requests/stream.hpp index d89bdb2..84ae992 100644 --- a/include/boost/requests/stream.hpp +++ b/include/boost/requests/stream.hpp @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -183,6 +182,14 @@ struct stream struct async_dump_op; struct async_read_some_op; + BOOST_REQUESTS_DECL + void async_read_some_impl( + asio::any_completion_handler handler, asio::mutable_buffer buffer); + + + BOOST_REQUESTS_DECL + static void async_dump_impl(asio::any_completion_handler handler, stream * this_); + friend struct detail::connection_impl; friend struct connection; }; diff --git a/src/connection_pool.cpp b/src/connection_pool.cpp index 2b4a960..9e1128d 100644 --- a/src/connection_pool.cpp +++ b/src/connection_pool.cpp @@ -23,7 +23,6 @@ void detail::connection_deleter::operator()(connection_impl * ptr) } - void connection_pool::lookup(urls::url_view sv, system::error_code & ec) { urls::string_view scheme = "https"; @@ -92,10 +91,32 @@ void connection_pool::lookup(urls::url_view sv, system::error_code & ec) } +struct connection_pool::async_lookup_op : asio::coroutine +{ + using executor_type = asio::any_io_executor; + executor_type get_executor() {return this_->get_executor(); } + + connection_pool * this_; + const urls::url_view sv; + + urls::string_view scheme = "https"; + + std::unique_lock lock{this_->mtx_, std::defer_lock}; + + async_lookup_op(connection_pool * this_, urls::url_view sv) + : this_(this_), sv(sv) {} + + template + void operator()(Self && self, system::error_code ec = {}, typename asio::ip::tcp::resolver::results_type eps = {}); +}; + -void connection_pool::async_lookup_op::resume(requests::detail::faux_token_t self, - system::error_code & ec, typename asio::ip::tcp::resolver::results_type eps) +template +void connection_pool::async_lookup_op::operator()(Self && self, + system::error_code ec, + typename asio::ip::tcp::resolver::results_type eps) { + if (!ec) BOOST_ASIO_CORO_REENTER(this) { if (sv.has_scheme()) @@ -112,12 +133,18 @@ void connection_pool::async_lookup_op::resume(requests::detail::faux_token_tasync_resolve( + BOOST_ASIO_CORO_YIELD + { + auto service = sv.has_port() ? sv.port() : scheme; + auto resolver = allocate_unique( + asio::get_associated_allocator(self), + get_executor()); + resolver->async_resolve( std::string(sv.encoded_host_address().data(), sv.encoded_host_address().size()), - std::string(service.data(), service.size()), std::move(self)); + std::string(service.data(), service.size()), + asio::consign(std::move(self), std::move(resolver))); + } if (!ec && eps.empty()) BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found) @@ -155,6 +182,8 @@ void connection_pool::async_lookup_op::resume(requests::detail::faux_token_t connection } -auto connection_pool::async_get_connection_op::resume( - requests::detail::faux_token_t self, - system::error_code & ec) -> connection + +struct connection_pool::async_get_connection_op : asio::coroutine { + using executor_type = asio::any_io_executor; + executor_type get_executor() {return this_->get_executor(); } + + connection_pool * this_; + async_get_connection_op(connection_pool * this_) : this_(this_) {} + + using conn_t = boost::unordered_multimap, + detail::endpoint_hash>; + typename conn_t::iterator itr; + boost::intrusive_ptr nconn = nullptr; + std::unique_lock lock{this_->mtx_, std::defer_lock}; + endpoint_type ep; + + template + void operator()( + Self && self, + system::error_code ec = {}); +}; + +template +void connection_pool::async_get_connection_op::operator()( + Self && self, + system::error_code ec) +{ + connection conn; + if (!ec) BOOST_ASIO_CORO_REENTER (this) { - static int j = 0; - this-> i = j++; lock.lock(); do @@ -219,13 +272,14 @@ auto connection_pool::async_get_connection_op::resume( if (this_->endpoints_.empty()) { BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); - return connection{}; + break; } if (!this_->free_conns_.empty()) { auto pp = std::move(this_->free_conns_.front()); this_->free_conns_.erase(this_->free_conns_.begin()); - return connection(pp); + conn = connection(pp); + break; } // check if we can make more connections. -> open another connection. // the race here is that we might open one too many @@ -234,7 +288,7 @@ auto connection_pool::async_get_connection_op::resume( if (this_->endpoints_.empty()) { BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); - return connection(); + break; } // sort the endpoints by connections that use it @@ -263,35 +317,94 @@ auto connection_pool::async_get_connection_op::resume( continue; } else if (ec) - return connection(); + break; else - return connection(std::move(nconn)); + { + conn = connection{std::move(nconn)}; + break; + } + } BOOST_ASIO_CORO_YIELD this_->cv_.async_wait(lock, std::move(self)); } while(!ec); } - return connection(); + + if (is_complete()) + self.complete(ec, std::move(conn)); } -stream connection_pool::async_ropen_op::resume( - requests::detail::faux_token_t self, - boost::system::error_code & ec, - variant2::variant res) + + +struct connection_pool::async_ropen_op : asio::coroutine { - BOOST_ASIO_CORO_REENTER(this) + using executor_type = asio::any_io_executor; + executor_type get_executor() {return this_->get_executor(); } + + connection_pool * this_; + beast::http::verb method; + urls::pct_string_view path; + http::fields & headers; + source & src; + request_options opt; + cookie_jar * jar; + + connection conn; + + async_ropen_op(connection_pool * this_, + beast::http::verb method, + urls::pct_string_view path, + http::fields & headers, + source & src, + request_options opts, + cookie_jar * jar) + : this_(this_), method(method), path(path), headers(headers), src(src), opt(std::move(opts)), jar(jar) { - BOOST_ASIO_CORO_YIELD this_->async_get_connection(std::move(self)); - conn = std::move(variant2::get<1>(res)); - if (!ec && !conn) - BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); - if (ec) - return stream{this_->get_executor(), nullptr}; + } - BOOST_ASIO_CORO_YIELD conn.async_ropen(method, path, headers, src, std::move(opt), jar, std::move(self)); - return variant2::get<2>(std::move(res)); + + async_ropen_op(connection_pool * this_, + beast::http::verb method, + urls::url_view path, + http::fields & headers, + source & src, + request_options opt, + cookie_jar * jar) + : this_(this_), method(method), path(path.encoded_resource()), + headers(headers), src(src), opt(std::move(opt)), jar(jar) + { } - return stream{get_executor(), nullptr}; + + template + void operator()(Self && self, + boost::system::error_code ec = {}, + variant2::variant res = variant2::monostate()); + +}; + + +template +void connection_pool::async_ropen_op::operator()( + Self && self, + boost::system::error_code ec, + variant2::variant res) +{ + if (!ec) + BOOST_ASIO_CORO_REENTER(this) + { + BOOST_ASIO_CORO_YIELD this_->async_get_connection(std::move(self)); + conn = std::move(variant2::get<1>(res)); + if (!ec && !conn) + BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); + if (ec) + break; + + BOOST_ASIO_CORO_YIELD conn.async_ropen(method, path, headers, src, std::move(opt), jar, std::move(self)); + return self.complete(ec, variant2::get<2>(std::move(res))); + } + if (is_complete()) + self.complete(ec, stream{get_executor(), nullptr}); + } connection_pool::connection_pool(connection_pool && lhs) @@ -344,6 +457,37 @@ void connection_pool::drop_connection_(const detail::connection_impl * conn) } } +void connection_pool::async_ropen_impl(asio::any_completion_handler handler, + connection_pool * this_, http::verb method, + urls::pct_string_view path, http::fields & headers, + source & src, request_options opt, cookie_jar * jar) +{ + return asio::async_compose< + asio::any_completion_handler, void(error_code, stream)>( + async_ropen_op{this_, method, path, headers, src, std::move(opt), jar}, + handler, this_->get_executor()); +} + +void connection_pool::async_get_connection_impl( + asio::any_completion_handler handler, + connection_pool * this_) +{ + return asio::async_compose< + asio::any_completion_handler, void(error_code, connection)>( + async_get_connection_op{this_}, + handler, this_->get_executor()); +} + +void connection_pool::async_lookup_impl( + asio::any_completion_handler handler, + connection_pool * this_, urls::url_view av) +{ + return asio::async_compose< + asio::any_completion_handler, void(error_code)>( + async_lookup_op{this_, av}, + handler, this_->get_executor()); +} + } } diff --git a/src/detail/connection_impl.cpp b/src/detail/connection_impl.cpp index 662fe7d..3a740fb 100644 --- a/src/detail/connection_impl.cpp +++ b/src/detail/connection_impl.cpp @@ -291,19 +291,88 @@ void connection_impl::set_host(core::string_view sv, system::error_code & ec) } -BOOST_REQUESTS_DECL -auto connection_impl::async_ropen_op::resume( - requests::detail::faux_token_t self, - system::error_code & ec, std::size_t res_) -> stream +struct connection_impl::async_ropen_op + : boost::asio::coroutine +{ + using executor_type = asio::any_io_executor; + executor_type get_executor() {return this_->get_executor(); } + + using lock_type = detail::lock_guard; + + boost::intrusive_ptr this_; + + optional str; + beast::http::verb method; + urls::pct_string_view path; + http::fields & headers; + source & src; + request_options opts; + cookie_jar * jar{nullptr}; + system::error_code ec_; + + struct state_t + { + response_base::buffer_type buf; + + lock_type lock; + boost::optional alock; + + response_base::history_type history; + }; + + using allocator_type = asio::any_completion_handler_allocator; + std::unique_ptr> state; + + async_ropen_op(allocator_type alloc, + boost::intrusive_ptr this_, + beast::http::verb method, + urls::pct_string_view path, + http::fields & headers, + source & src, + request_options opts, + cookie_jar * jar) + : this_(std::move(this_)), method(method), path(path), headers(headers), src(src), opts(std::move(opts)) + , jar(jar), state(allocate_unique(alloc)) + { + } + + async_ropen_op(allocator_type alloc, + boost::intrusive_ptr this_, + beast::http::verb method, + urls::url_view path, + http::fields & headers, + source & src, + request_options opt, + cookie_jar * jar) + : this_(this_), method(method), path(path.encoded_resource()), + headers(headers), src(src), opts(std::move(opt)), jar(jar), state(allocate_unique(alloc)) + { + detail::check_endpoint(path, this_->endpoint(), this_->host(), this_->use_ssl_, ec_); + } + + template + void operator()(Self && self, system::error_code ec = {}, std::size_t res_ = 0u); +}; + + +template +void connection_impl::async_ropen_op::operator()( + Self && self, + system::error_code ec, std::size_t res_) { + auto st = state.get(); + if (!ec) BOOST_ASIO_CORO_REENTER(this) { if (ec_) { BOOST_ASIO_CORO_YIELD asio::post(this_->get_executor(), std::move(self)); ec = ec_; + break; } + state = boost::allocate_unique(asio::get_associated_allocator(self)); + if (jar) { @@ -323,12 +392,12 @@ auto connection_impl::async_ropen_op::resume( while (!ec) { - BOOST_REQUESTS_AWAIT_LOCK(this_->write_mtx_, lock); + BOOST_REQUESTS_AWAIT_LOCK(this_->write_mtx_, st->lock); if (!this_->is_open()) { retry: - BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, alock); + BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, st->alock); BOOST_ASIO_CORO_YIELD this_->next_layer_.next_layer().async_connect(this_->endpoint_, std::move(self)); if (!ec && this_->use_ssl_) { @@ -338,7 +407,7 @@ auto connection_impl::async_ropen_op::resume( break; } - alock.reset(); + st->alock.reset(); if (this_->use_ssl_) { BOOST_ASIO_CORO_YIELD async_write_request(this_->next_layer_, method, path, headers, src, std::move(self)); @@ -354,7 +423,7 @@ auto connection_impl::async_ropen_op::resume( break; // release after acquire! - BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, lock); + BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, st->lock); // END OF write impl str.emplace(this_->get_executor(), this_); // , req.get_allocator().resource() @@ -388,7 +457,7 @@ auto connection_impl::async_ropen_op::resume( else { ec = f.error(); - return *std::move(str); + return self.complete(ec, *std::move(str)); } } } @@ -399,23 +468,23 @@ auto connection_impl::async_ropen_op::resume( (rc != http::status::temporary_redirect) && (rc != http::status::permanent_redirect))) { // GO - str->lock_ = std::move(lock); - str->history_ = std::move(history); - return *std::move(str); + str->lock_ = std::move(st->lock); + str->history_ = std::move(st->history); + return self.complete(ec, *std::move(str)); } } if (method != http::verb::head) { - BOOST_ASIO_CORO_YIELD str->async_read(buf, std::move(self)); + BOOST_ASIO_CORO_YIELD str->async_read(st->buf, std::move(self)); } if (ec) break; - history.emplace_back(std::move( str->parser_->get().base()), std::move(buf)); - lock = {}; + state->history.emplace_back(std::move(str->parser_->get().base()), std::move(st->buf)); + st->lock = {}; str.reset(); - auto & res = history.back().base(); + auto & res = state->history.back().base(); // read the body to put into history auto loc_itr = res.find(http::field::location); @@ -464,12 +533,16 @@ auto connection_impl::async_ropen_op::resume( } stream str_{this_->get_executor(), nullptr}; - str_.history_ = std::move(history); - return str_; + str_.history_ = std::move(st->history); + return self.complete(ec, std::move(str_)); } - return stream{this_->get_executor(), nullptr}; - + // coro is complete + if (is_complete()) + { + state.reset(); + self.complete(ec, stream{this_->get_executor(), nullptr}); + } } @@ -524,7 +597,7 @@ std::size_t connection_impl::do_read_some_(beast::http::basic_parser & pa return beast::http::read_some(next_layer_.next_layer(), buffer_, parser, ec); } -void connection_impl::do_async_read_some_(beast::http::basic_parser & parser, detail::faux_token_t tk) +void connection_impl::do_async_read_some_(beast::http::basic_parser & parser, asio::any_completion_handler tk) { if (use_ssl_) beast::http::async_read_some(next_layer_, buffer_, parser, std::move(tk)); @@ -532,43 +605,84 @@ void connection_impl::do_async_read_some_(beast::http::basic_parser & par beast::http::async_read_some(next_layer_.next_layer(), buffer_, parser, std::move(tk)); } -void connection_impl::do_async_close_(detail::faux_token_t tk) +void connection_impl::do_async_close_(asio::any_completion_handler tk) { - async_close(std::move(tk)); + async_close_impl(std::move(tk), this); } -void connection_impl::async_connect_op::resume(requests::detail::faux_token_t self, - system::error_code & ec) +struct connection_impl::async_connect_op : asio::coroutine { - BOOST_ASIO_CORO_REENTER(this) + using lock_type = detail::lock_guard; + struct state_t { - BOOST_REQUESTS_AWAIT_LOCK(this_->write_mtx_, write_lock); - BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, read_lock); - BOOST_ASIO_CORO_YIELD this_->next_layer_.next_layer().async_connect(this_->endpoint_ = ep, std::move(self)); + lock_type read_lock, write_lock; + }; + using allocator_type = asio::any_completion_handler_allocator; + std::unique_ptr> state; + + connection_impl * this_; + endpoint_type ep; - if (!ec && this_->use_ssl_) + template + void operator()(Self && self, system::error_code ec = {}) + { + auto st = state.get(); + if (!ec) + BOOST_ASIO_CORO_REENTER(this) + { + BOOST_REQUESTS_AWAIT_LOCK(this_->write_mtx_, st->write_lock); + BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, st->read_lock); + if (this_->use_ssl_) + { + BOOST_ASIO_CORO_YIELD this_->next_layer_.async_handshake(asio::ssl::stream_base::client, std::move(self)); + } + } + // coro is complete + if (is_complete()) { - BOOST_ASIO_CORO_YIELD this_->next_layer_.async_handshake(asio::ssl::stream_base::client, std::move(self)); + state.reset(); + self.complete(ec); } } -} +}; -void connection_impl::async_close_op::resume(requests::detail::faux_token_t self, - system::error_code & ec) +struct connection_impl::async_close_op : asio::coroutine { - BOOST_ASIO_CORO_REENTER(this) + using lock_type = detail::lock_guard; + struct state_t + { + lock_type read_lock, write_lock; + }; + using allocator_type = asio::any_completion_handler_allocator; + std::unique_ptr> state; + + connection_impl * this_; + endpoint_type ep; + + template + void operator()(Self && self, system::error_code ec = {}) { - BOOST_REQUESTS_AWAIT_LOCK(this_->write_mtx_, write_lock); - BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, read_lock); - if (!ec && this_->use_ssl_) + auto st = state.get(); + if (!ec) + BOOST_ASIO_CORO_REENTER(this) + { + BOOST_REQUESTS_AWAIT_LOCK(this_->write_mtx_, st->write_lock); + BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, st->read_lock); + if (!ec && this_->use_ssl_) + { + BOOST_ASIO_CORO_YIELD this_->next_layer_.async_shutdown(std::move(self)); + } + if (this_->next_layer_.next_layer().is_open()) + this_->next_layer_.next_layer().close(ec); + } + // coro is complete + if (is_complete()) { - BOOST_ASIO_CORO_YIELD this_->next_layer_.async_shutdown(std::move(self)); + state.reset(); + self.complete(ec); } - if (this_->next_layer_.next_layer().is_open()) - this_->next_layer_.next_layer().close(ec); } -} - +}; void connection_impl::do_close_(system::error_code & ec) { @@ -595,6 +709,37 @@ void connection_impl::remove_from_pool_() ptr->drop_connection_(this); } +BOOST_REQUESTS_DECL +void connection_impl::async_connect_impl(asio::any_completion_handler handler, + connection_impl * this_, endpoint_type ep) +{ + return asio::async_compose, void(error_code)>( + async_connect_op{{}, + allocate_unique(asio::get_associated_allocator(handler)), + this_, ep}, handler, this_->get_executor()); +} + +BOOST_REQUESTS_DECL +void connection_impl::async_close_impl(asio::any_completion_handler handler, + connection_impl * this_) +{ + return asio::async_compose, void(error_code)>( + async_close_op{{}, + allocate_unique(asio::get_associated_allocator(handler)), + this_}, handler, this_->get_executor()); +} + +BOOST_REQUESTS_DECL +void connection_impl::async_ropen_impl(asio::any_completion_handler handler, + connection_impl * this_, http::verb method, + urls::pct_string_view path, http::fields & headers, + source & src, request_options opt, cookie_jar * jar) +{ + return asio::async_compose, void(error_code, stream)>( + async_ropen_op{asio::get_associated_allocator(handler), this_, method, path, headers, src, std::move(opt), jar}, + handler, this_->get_executor()); +} + } } } diff --git a/src/detail/mutex.cpp b/src/detail/mutex.cpp index b2b5068..9b2aee7 100644 --- a/src/detail/mutex.cpp +++ b/src/detail/mutex.cpp @@ -6,8 +6,11 @@ // #include +#include #include +#include #include +#include #include #include #include diff --git a/src/session.cpp b/src/session.cpp index 38f4c6c..71b22d7 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -8,6 +8,8 @@ #include #include + +#include #include namespace boost { @@ -188,132 +190,223 @@ session::get_pool(urls::url_view url, error_code & ec) -> std::shared_ptr session::async_get_pool_op::resume( - requests::detail::faux_token_t self, - error_code ec) +struct session::async_get_pool_op : asio::coroutine { - BOOST_ASIO_CORO_REENTER(this) - { - BOOST_REQUESTS_AWAIT_LOCK(this_->mutex_, lock); + using executor_type = asio::any_io_executor; + executor_type get_executor() {return this_->get_executor(); } + session *this_; + urls::url_view url; + const bool is_https; + + detail::lock_guard lock; + + async_get_pool_op(session *this_, urls::url_view url) + : this_(this_), url(url), + is_https((url.scheme_id() == urls::scheme::https) || (url.scheme_id() == urls::scheme::wss)) + {} + + std::shared_ptr p; + + template + void operator()(Self && self, error_code ec = {}) + { + BOOST_ASIO_CORO_REENTER(this) { - auto itr = this_->pools_.find(url); - if (itr != this_->pools_.end()) - return itr->second; - } - p = std::make_shared(this_->get_executor(), this_->sslctx_); - BOOST_ASIO_CORO_YIELD p->async_lookup(url, std::move(self)); - if (!ec) - { - this_->pools_.emplace(url, p); - return p; + BOOST_REQUESTS_AWAIT_LOCK(this_->mutex_, lock); + + { + auto itr = this_->pools_.find(url); + if (itr != this_->pools_.end()) + return self.complete(ec, std::move(itr->second)); + } + p = std::make_shared(this_->get_executor(), this_->sslctx_); + BOOST_ASIO_CORO_YIELD p->async_lookup(url, std::move(self)); + if (!ec) + { + this_->pools_.emplace(url, p); + return self.complete(ec, p); + } } } - return nullptr; -} -auto session::async_ropen_op::resume(requests::detail::faux_token_t self, - system::error_code & ec, - variant2::variant, stream> s) -> stream +}; + + +struct session::async_ropen_op : asio::coroutine { - BOOST_ASIO_CORO_REENTER(this) - { - headers.set(beast::http::field::host, url.encoded_host_and_port()); - headers.set(beast::http::field::user_agent, "Requests-" BOOST_BEAST_VERSION_STRING); + using executor_type = asio::any_io_executor; + executor_type get_executor() {return this_->get_executor(); } - if (!is_secure && this_->options_.enforce_tls) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::insecure); - return stream{get_executor(), nullptr}; - } + session * this_; - { - auto cc = this_->jar_.get(url.encoded_host(), is_secure, url.encoded_path()); - if (!cc.empty()) - headers.set(http::field::cookie, cc); - } + http::verb method; - BOOST_ASIO_CORO_YIELD this_->async_get_pool(url, std::move(self)); - if (ec) - return stream{get_executor(), nullptr}; - BOOST_ASIO_CORO_YIELD variant2::get<1>(s)->async_ropen(method, url.encoded_resource(), - headers, src, opts, &this_->jar_, std::move(self)); + urls::url url; + struct request_options opts; + core::string_view default_mime_type; - if (!ec || opts.max_redirects == variant2::get<2>(s).history().size()) - return std::move(variant2::get<2>(s)); + system::error_code ec_; - while (ec == error::forbidden_redirect) + bool is_secure = (url.scheme_id() == urls::scheme::https) + || (url.scheme_id() == urls::scheme::wss); + + response_base::history_type history; + + http::fields & headers; + source & src; + + urls::url url_cache; + + async_ropen_op(session * this_, + http::verb method, + urls::url_view path, + source & src, + http::fields & headers) + : this_(this_), method(method), url(path), opts(this_->options_), headers(headers), src(src) + { + } + + using completion_signature_type = void(system::error_code, stream); + using step_signature_type = void(system::error_code, variant2::variant, stream>); + + template + void operator()(Self && self, system::error_code ec = {}, + variant2::variant, stream> s = variant2::monostate{}); +}; + + +template +void session::async_ropen_op::operator()( + Self && self, system::error_code ec, + variant2::variant, stream> s) +{ + if (!ec) + BOOST_ASIO_CORO_REENTER(this) { - ec.clear(); - if (variant2::get<2>(s).history().empty()) + headers.set(beast::http::field::host, url.encoded_host_and_port()); + headers.set(beast::http::field::user_agent, "Requests-" BOOST_BEAST_VERSION_STRING); + + if (!is_secure && this_->options_.enforce_tls) { - BOOST_REQUESTS_ASSIGN_EC(ec, error::invalid_redirect); - break; + BOOST_REQUESTS_ASSIGN_EC(ec, error::insecure); + BOOST_ASIO_CORO_YIELD { + auto exec = asio::get_associated_immediate_executor(self, this_->get_executor()); + asio::dispatch(exec, asio::append(std::move(self), ec)); + }; } - opts.max_redirects -= variant2::get<2>(s).history().size(); - if (opts.max_redirects == 0) + { - BOOST_REQUESTS_ASSIGN_EC(ec, error::too_many_redirects); - break ; + auto cc = this_->jar_.get(url.encoded_host(), is_secure, url.encoded_path()); + if (!cc.empty()) + headers.set(http::field::cookie, cc); } + BOOST_ASIO_CORO_YIELD this_->async_get_pool(url, std::move(self)); + BOOST_ASIO_CORO_YIELD variant2::get<1>(s)->async_ropen(method, url.encoded_resource(), + headers, src, opts, &this_->jar_, std::move(self)); + + if (!ec || opts.max_redirects == variant2::get<2>(s).history().size()) + return self.complete(ec, std::move(variant2::get<2>(s))); + + while (ec == error::forbidden_redirect) { - const auto & last = variant2::get<2>(s).history().back(); - auto loc_itr = last.base().find(http::field::location); - auto rc = last.base().result(); - if (((rc != http::status::moved_permanently) - && (rc != http::status::found) - && (rc != http::status::temporary_redirect) - && (rc != http::status::permanent_redirect)) - || (loc_itr == last.base().end())) + ec.clear(); + if (variant2::get<2>(s).history().empty()) { BOOST_REQUESTS_ASSIGN_EC(ec, error::invalid_redirect); break; } - const auto nurl = interpret_location(url.encoded_resource(), loc_itr->value()); - if (nurl.has_error()) + opts.max_redirects -= variant2::get<2>(s).history().size(); + if (opts.max_redirects == 0) { - ec = nurl.error(); - break; + BOOST_REQUESTS_ASSIGN_EC(ec, error::too_many_redirects); + break ; } - if (!should_redirect(this_->options_.redirect, url, *nurl)) { - BOOST_REQUESTS_ASSIGN_EC(ec, error::forbidden_redirect); - break ; + const auto & last = variant2::get<2>(s).history().back(); + auto loc_itr = last.base().find(http::field::location); + auto rc = last.base().result(); + if (((rc != http::status::moved_permanently) + && (rc != http::status::found) + && (rc != http::status::temporary_redirect) + && (rc != http::status::permanent_redirect)) + || (loc_itr == last.base().end())) + { + BOOST_REQUESTS_ASSIGN_EC(ec, error::invalid_redirect); + break; + } + const auto nurl = interpret_location(url.encoded_resource(), loc_itr->value()); + if (nurl.has_error()) + { + ec = nurl.error(); + break; + } + + if (!should_redirect(this_->options_.redirect, url, *nurl)) + { + BOOST_REQUESTS_ASSIGN_EC(ec, error::forbidden_redirect); + break ; + } + + if (nurl->has_authority()) + url = *nurl; + else + { + url_cache = url; + url_cache.set_encoded_path(nurl->encoded_path()); + url = url_cache; + } + } - if (nurl->has_authority()) - url = *nurl; - else { - url_cache = url; - url_cache.set_encoded_path(nurl->encoded_path()); - url = url_cache; + auto cc = this_->jar_.get(url.encoded_host(), is_secure, url.encoded_path()); + if (!cc.empty()) + headers.set(http::field::cookie, cc); + else + headers.erase(http::field::cookie); } - + history.insert(history.end(), + std::make_move_iterator(std::move(variant2::get<2>(s)).history().begin()), + std::make_move_iterator(std::move(variant2::get<2>(s)).history().end())); + BOOST_ASIO_CORO_YIELD this_->async_get_pool(url, std::move(self)); + BOOST_ASIO_CORO_YIELD variant2::get<1>(s)->async_ropen(method, url.encoded_resource(), headers, src, opts, &this_->jar_, std::move(self)); } + variant2::get<2>(s).prepend_history(std::move(history)); + return self.complete(ec, std::move(variant2::get<2>(s))); - { - auto cc = this_->jar_.get(url.encoded_host(), is_secure, url.encoded_path()); - if (!cc.empty()) - headers.set(http::field::cookie, cc); - else - headers.erase(http::field::cookie); - } - history.insert(history.end(), - std::make_move_iterator(std::move(variant2::get<2>(s)).history().begin()), - std::make_move_iterator(std::move(variant2::get<2>(s)).history().end())); - BOOST_ASIO_CORO_YIELD this_->async_get_pool(url, std::move(self)); - if (ec) - return stream{get_executor(), nullptr}; - BOOST_ASIO_CORO_YIELD variant2::get<1>(s)->async_ropen(method, url.encoded_resource(), headers, src, opts, &this_->jar_, std::move(self)); } - variant2::get<2>(s).prepend_history(std::move(history)); - return std::move(variant2::get<2>(s)); + if (is_complete() || ec) + self.complete(ec, stream{this_->get_executor(), nullptr}); +} - } - return stream{this_->get_executor(), nullptr}; +BOOST_REQUESTS_DECL +void session::async_ropen_impl( + asio::any_completion_handler handler, + session * sess, http::verb method, urls::url_view path, source * src, http::fields * headers) +{ + asio::async_compose< + asio::any_completion_handler, + void (boost::system::error_code, stream)>( + async_ropen_op{ + sess, method, path, *src, *headers}, handler, sess->get_executor() + ); +} + +BOOST_REQUESTS_DECL +void session::async_get_pool_impl( + asio::any_completion_handler)> handler, + session * sess, urls::url_view url) +{ + asio::async_compose< + asio::any_completion_handler)>, + void (boost::system::error_code, std::shared_ptr)>( + async_get_pool_op{sess, url}, + handler, + sess->get_executor() + ); } } diff --git a/src/stream.cpp b/src/stream.cpp index 3c8b303..d8a8a2c 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -7,6 +7,8 @@ #include #include +#include + namespace boost { namespace requests @@ -52,56 +54,116 @@ stream::~stream() } -std::size_t stream::async_read_some_op::resume(requests::detail::faux_token_t self, - system::error_code & ec, std::size_t res) +struct stream::async_read_some_op : asio::coroutine { + using executor_type = asio::any_io_executor; + executor_type get_executor() {return this_->get_executor(); } - BOOST_ASIO_CORO_REENTER(this) - { - if (!this_->parser_) - BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_connected) - else if (!this_->parser_->get().body().more) - BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::eof) - else if (buffer.size() == 0u) - BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::no_buffer_space) + stream * this_; + asio::mutable_buffer buffer; - if (ec) - return std::size_t(-1); - - this_->parser_->get().body().data = buffer.data(); - this_->parser_->get().body().size = buffer.size(); + template + async_read_some_op(stream * this_, const MutableBufferSequence & buffer) : this_(this_) + { + auto itr = boost::asio::buffer_sequence_begin(buffer); + const auto end = boost::asio::buffer_sequence_end(buffer); - BOOST_ASIO_CORO_YIELD this_->impl_->do_async_read_some_(*this_->parser_, std::move(self)); - if (!this_->parser_->is_done()) + while (itr != end) { - this_->parser_->get().body().more = true; - if (ec == beast::http::error::need_buffer) - ec = {}; + if (itr->size() != 0u) + { + this->buffer = *itr; + break; + } } - else + } + system::error_code ec_; + + template + void operator()(Self && self, system::error_code ec = {}, std::size_t res = 0u); +}; + + +template +void stream::async_read_some_op::operator()(Self && self, system::error_code ec, std::size_t res) +{ + if (!ec) + BOOST_ASIO_CORO_REENTER(this) { - this_->parser_->get().body().more = false; - if (!this_->parser_->get().keep_alive()) + if (!this_->parser_) + BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_connected) + else if (!this_->parser_->get().body().more) + BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::eof) + else if (buffer.size() == 0u) + BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::no_buffer_space) + + if (ec) + break; + + this_->parser_->get().body().data = buffer.data(); + this_->parser_->get().body().size = buffer.size(); + + BOOST_ASIO_CORO_YIELD this_->impl_->do_async_read_some_(*this_->parser_, std::move(self)); + if (!this_->parser_->is_done()) + { + this_->parser_->get().body().more = true; + if (ec == beast::http::error::need_buffer) + ec = {}; + } + else { - ec_ = ec ; - BOOST_ASIO_CORO_YIELD this_->impl_->do_async_close_(std::move(self)); - ec = ec_; + this_->parser_->get().body().more = false; + if (!this_->parser_->get().keep_alive()) + { + ec_ = ec ; + BOOST_ASIO_CORO_YIELD this_->impl_->do_async_close_(std::move(self)); + ec = ec_; + } } } - return res; - } - return 0u; + + if (is_complete()) + self.complete(ec, res); } +void stream::async_read_some_impl( + asio::any_completion_handler handler, + asio::mutable_buffer buffer) +{ + return asio::async_compose< + asio::any_completion_handler, + void(error_code, std::size_t)>( + async_read_some_op{this, buffer}, + handler, get_executor()); +} -void stream::async_dump_op::resume(requests::detail::faux_token_t self, - system::error_code ec, std::size_t n) + +struct stream::async_dump_op : asio::coroutine +{ + using executor_type = asio::any_io_executor; + executor_type get_executor() {return this_->get_executor(); } + + stream * this_; + + char buffer[BOOST_REQUESTS_CHUNK_SIZE]; + system::error_code ec_; + + async_dump_op(stream * this_) : this_(this_) {} + + template + void operator()(Self && self, system::error_code ec = {}, std::size_t n = 0u); +}; + +template +void stream::async_dump_op::operator()(Self && self, system::error_code ec, std::size_t n) { BOOST_ASIO_CORO_REENTER(this) { if (!this_->parser_ || !this_->parser_->is_done()) { - BOOST_ASIO_CORO_YIELD asio::post(this_->get_executor(), std::move(self)); + BOOST_ASIO_CORO_YIELD asio::dispatch( + asio::get_associated_immediate_executor(self, this_->get_executor()), + std::move(self)); ec = ec_; return; } @@ -121,6 +183,8 @@ void stream::async_dump_op::resume(requests::detail::faux_token_tis_open() && !done(); } +void stream::async_dump_impl( + asio::any_completion_handler handler, stream * this_) +{ + return asio::async_compose< + asio::any_completion_handler, + void(error_code)>( + async_dump_op{this_}, + handler, this_->get_executor()); +} + } } diff --git a/test/connection.cpp b/test/connection.cpp index 840c985..7d20118 100644 --- a/test/connection.cpp +++ b/test/connection.cpp @@ -608,7 +608,7 @@ void run_tests(error_code ec, } -TEST_CASE("async-connection-request") +TEST_CASE("async-http") { urls::url url = urls::parse_uri("http://" + httpbin()).value(); asio::io_context ctx; diff --git a/test/detail/faux_coroutine.cpp b/test/detail/faux_coroutine.cpp deleted file mode 100644 index 081981a..0000000 --- a/test/detail/faux_coroutine.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#include - -#include -#include -#include -#include -#include -#include - -#include "../doctest.h" - -using namespace boost; - -struct my_coro : asio::coroutine -{ - asio::readable_pipe source; - asio::writable_pipe sink; - - using executor_type = asio::any_io_executor; - executor_type get_executor() {return source.get_executor(); } - - char buf[4096]; - system::error_code ec_; - - my_coro(const my_coro &) = delete; - my_coro( my_coro &&) = delete; - - my_coro(asio::writable_pipe &source, asio::readable_pipe &sink) - : source(sink.get_executor()), sink(source.get_executor()) - { - asio::connect_pipe(sink, this->sink, ec_); - if (!ec_) - asio::connect_pipe(this->source, source, ec_); - } - - using completion_signature_type = void(system::error_code); - using step_signature_type = void(system::error_code, std::size_t); - - void resume(requests::detail::faux_token_t token, - system::error_code & ec, std::size_t n = {}) - { - BOOST_ASIO_CORO_REENTER(this) - { - if (ec_) - { - ec = ec_; - break; - } - while (source.is_open()) - { - BOOST_ASIO_CORO_YIELD source.async_read_some(asio::buffer(buf), std::move(token)); - if (ec && n == 0) - break; - BOOST_ASIO_CORO_YIELD sink.async_write_some(asio::buffer(buf, n), std::move(token)); - if (ec) - break; - BOOST_ASIO_CORO_YIELD { - requests::detail::faux_token_t tt = std::move(token); - asio::post(sink.get_executor(), std::move(tt)); - }; - } - } - } -}; - -TEST_SUITE_BEGIN("async_coro"); -TEST_CASE("sanity-check") -{ - asio::io_context ctx; - asio::writable_pipe source{ctx}; - asio::readable_pipe sink{ctx}; - - requests::detail::faux_run( - [](system::error_code ec) - { - printf("EC : %s\n", ec.message().c_str()); - }, source, sink); - - - char read_buf[32]; - sink.async_read_some(asio::buffer(read_buf), asio::detached); - - asio::async_write(source, asio::buffer("FOOBAR", 6), - [&](system::error_code ec, std::size_t n) - { - source.close(); - }); - - ctx.run(); -} - -TEST_SUITE_END(); \ No newline at end of file diff --git a/test/detail/mutex.cpp b/test/detail/mutex.cpp index 352bfa2..50d8108 100644 --- a/test/detail/mutex.cpp +++ b/test/detail/mutex.cpp @@ -65,17 +65,17 @@ struct basic_main using completion_signature_type = void(error_code); using step_signature_type = void(error_code, lock_guard); - - void resume(requests::detail::faux_token_t self, - error_code & ec, lock_guard l = {}) + template + void operator()(Self && self, error_code ec = {}, lock_guard l = {}) { BOOST_ASIO_CORO_REENTER(this) { BOOST_ASIO_CORO_YIELD mtx.async_lock(std::move(self)); v.push_back(i); tim = std::make_unique(mtx.get_executor(), std::chrono::milliseconds(10)); - BOOST_ASIO_CORO_YIELD tim->async_wait(std::move(self)); v.push_back(i + 1); + BOOST_ASIO_CORO_YIELD tim->async_wait(std::move(self)); + self.complete(ec); } } }; @@ -86,7 +86,8 @@ struct basic_main static auto f(std::vector< int > &v, mutex &mtx, int i) { - return boost::requests::detail::faux_run(asio::deferred, std::ref(v), std::ref(mtx), i); + return asio::async_compose( + step_impl(v, mtx, i), asio::deferred); } @@ -183,29 +184,17 @@ TEST_CASE("cancel_twice") { asio::io_context ctx; - using token_type = boost::requests::detail::faux_token_t; - struct impl final : token_type::base - { - std::vector ecs; - - void resume(boost::requests::detail::faux_token_t tk, error_code ec) override - { - ecs.push_back(ec); - } - }; - - impl ip{}; - std::shared_ptr ptr{&ip, [](impl * ) {}}; + std::vector ecs; { mutex mtx{ctx}; - mtx.async_lock(token_type{ptr}); - mtx.async_lock(token_type{ptr}); - mtx.async_lock(token_type{ptr}); - mtx.async_lock(token_type{ptr}); - mtx.async_lock(token_type{ptr}); - mtx.async_lock(token_type{ptr}); - mtx.async_lock(token_type{ptr}); + mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); + mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); + mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); + mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); + mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); + mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); + mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); ctx.run_for(std::chrono::milliseconds(10)); @@ -222,12 +211,12 @@ TEST_CASE("cancel_twice") ctx.run_for(std::chrono::milliseconds(10)); - CHECK(ip.ecs.size() == 7u); - CHECK(!ip.ecs.at(0)); - CHECK(!ip.ecs.at(1)); - CHECK(!ip.ecs.at(2)); + CHECK(ecs.size() == 7u); + CHECK(!ecs.at(0)); + CHECK(!ecs.at(1)); + CHECK(!ecs.at(2)); - CHECK(4u == std::count(ip.ecs.begin(), ip.ecs.end(), asio::error::operation_aborted)); + CHECK(4u == std::count(ecs.begin(), ecs.end(), asio::error::operation_aborted)); } @@ -236,30 +225,17 @@ TEST_CASE("cancel_lock") { asio::io_context ctx; - using token_type = boost::requests::detail::faux_token_t; - struct impl final : token_type::base - { - std::vector ecs; - - - - void resume(boost::requests::detail::faux_token_t tk, error_code ec) override - { - ecs.push_back(ec); - } - }; - impl ip{}; - std::shared_ptr ptr{&ip, [](impl * ) {}}; + std::vector ecs; { mutex mtx{ctx}; - mtx.async_lock(token_type{ptr}); - mtx.async_lock(token_type{ptr}); - mtx.async_lock(token_type{ptr}); - mtx.async_lock(token_type{ptr}); - mtx.async_lock(token_type{ptr}); - mtx.async_lock(token_type{ptr}); - mtx.async_lock(token_type{ptr}); + mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); + mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); + mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); + mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); + mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); + mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); + mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); ctx.run_for(std::chrono::milliseconds(10)); mtx.unlock(); @@ -274,54 +250,42 @@ TEST_CASE("cancel_lock") ctx.run_for(std::chrono::milliseconds(10)); - CHECK(ip.ecs.size() == 7u); - CHECK(!ip.ecs.at(0)); - CHECK(!ip.ecs.at(1)); - CHECK(!ip.ecs.at(2)); + CHECK(ecs.size() == 7u); + CHECK(!ecs.at(0)); + CHECK(!ecs.at(1)); + CHECK(!ecs.at(2)); - CHECK(4u == std::count(ip.ecs.begin(), ip.ecs.end(), asio::error::operation_aborted)); + CHECK(4u == std::count(ecs.begin(), ecs.end(), asio::error::operation_aborted)); } TEST_CASE("cancel_one") { asio::io_context ctx; - - using token_type = boost::requests::detail::faux_token_t; - struct impl final : token_type::base - { - std::vector ecs; - - void resume(boost::requests::detail::faux_token_t tk, error_code ec) override - { - ecs.push_back(ec); - } - }; - impl ip{}; - std::shared_ptr ptr{&ip, [](impl * ) {}}; asio::cancellation_signal sig; - ip.slot = sig.slot(); + std::vector ecs; + { mutex mtx{ctx}; mtx.lock(); - mtx.async_lock(token_type{ptr}); - mtx.async_lock(token_type{ptr}); + mtx.async_lock(asio::bind_cancellation_slot(sig.slot(), [&](error_code ec){ecs.push_back(ec);})); + mtx.async_lock(asio::bind_cancellation_slot(sig.slot(), [&](error_code ec){ecs.push_back(ec);})); ctx.run_for(std::chrono::milliseconds(10)); - CHECK(ip.ecs.empty()); + CHECK(ecs.empty()); sig.emit(asio::cancellation_type::all); ctx.restart(); ctx.run_for(std::chrono::milliseconds(10)); - REQUIRE(ip.ecs.size() == 1u); - CHECK(ip.ecs.front() == asio::error::operation_aborted); + REQUIRE(ecs.size() == 1u); + CHECK(ecs.front() == asio::error::operation_aborted); } ctx.restart(); ctx.run_for(std::chrono::milliseconds(10)); - CHECK(ip.ecs.size() == 2u); + CHECK(ecs.size() == 2u); } From 68de64301b198018d5fc9e9ccf569adcda060df6 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 27 Sep 2023 15:32:18 +0800 Subject: [PATCH 71/76] [WIP] Backup before I destroy everything. --- include/boost/requests/connection.hpp | 16 +- include/boost/requests/connection_pool.hpp | 12 +- .../boost/requests/detail/connection_impl.hpp | 64 +- include/boost/requests/detail/define.hpp | 11 +- .../requests/detail/impl/connection_impl.hpp | 82 +-- include/boost/requests/download.hpp | 121 ++-- include/boost/requests/impl/request.hpp | 26 +- include/boost/requests/impl/source.hpp | 85 +-- include/boost/requests/impl/stream.hpp | 8 +- include/boost/requests/json.hpp | 22 +- include/boost/requests/redirect.hpp | 10 +- include/boost/requests/response.hpp | 1 - include/boost/requests/session.hpp | 14 +- include/boost/requests/stream.hpp | 18 +- src/connection_pool.cpp | 19 +- src/detail/connection_impl.cpp | 546 ++++++------------ src/redirect.cpp | 15 +- src/session.cpp | 116 +--- src/stream.cpp | 22 +- test/string_maker.hpp | 14 +- 20 files changed, 474 insertions(+), 748 deletions(-) diff --git a/include/boost/requests/connection.hpp b/include/boost/requests/connection.hpp index 6070ca6..ad0eee8 100644 --- a/include/boost/requests/connection.hpp +++ b/include/boost/requests/connection.hpp @@ -17,7 +17,7 @@ namespace boost { namespace requests { struct stream; -struct connection +struct connection : private detail::connection_owner { /// The type of the next layer. typedef asio::ssl::stream next_layer_type; @@ -174,11 +174,10 @@ struct connection urls::pct_string_view path, http::fields & headers, source & src, - request_options opt, cookie_jar * jar, system::error_code & ec) -> stream { - return impl_->ropen(method, path, headers, src, std::move(opt), jar, ec); + return impl_->ropen(method, path, headers, src, jar, ec); } BOOST_REQUESTS_DECL @@ -186,10 +185,9 @@ struct connection urls::pct_string_view path, http::fields & headers, source & src, - request_options opt, cookie_jar * jar) -> stream { - return impl_->ropen(method, path, headers, src, std::move(opt), jar); + return impl_->ropen(method, path, headers, src, jar); } templatepool() : nullptr; } + private: explicit connection(boost::intrusive_ptr impl) : impl_(std::move(impl)) {} boost::intrusive_ptr impl_; + BOOST_REQUESTS_DECL + void return_connection_(detail::connection_impl * conn) override {} + BOOST_REQUESTS_DECL + void drop_connection_(const detail::connection_impl * conn) override {} + + friend struct connection_pool; friend struct stream; }; diff --git a/include/boost/requests/connection_pool.hpp b/include/boost/requests/connection_pool.hpp index bc6ac64..0f29ab5 100644 --- a/include/boost/requests/connection_pool.hpp +++ b/include/boost/requests/connection_pool.hpp @@ -35,7 +35,7 @@ struct connection_deleter } -struct connection_pool +struct connection_pool : private detail::connection_owner { /// The type of the executor associated with the object. typedef asio::any_io_executor executor_type; @@ -171,7 +171,6 @@ struct connection_pool urls::pct_string_view path, http::fields & headers, source & src, - request_options opt, cookie_jar * jar, system::error_code & ec) -> stream { @@ -182,18 +181,17 @@ struct connection_pool return stream{get_executor(), nullptr}; BOOST_ASSERT(conn); - return conn.ropen(method, path, headers, src, opt, jar, ec); + return conn.ropen(method, path, headers, src, jar, ec); } auto ropen(beast::http::verb method, urls::pct_string_view path, http::fields & headers, source & src, - request_options opt, cookie_jar * jar) -> stream { boost::system::error_code ec; - auto res = ropen(method, path, headers, src, opt, jar, ec); + auto res = ropen(method, path, headers, src, jar, ec); if (ec) throw_exception(system::system_error(ec, "open")); return res; @@ -241,9 +239,9 @@ struct connection_pool struct async_ropen_op; BOOST_REQUESTS_DECL - void return_connection_(detail::connection_impl * conn); + void return_connection_(detail::connection_impl * conn) override; BOOST_REQUESTS_DECL - void drop_connection_(const detail::connection_impl * conn); + void drop_connection_(const detail::connection_impl * conn) override; friend struct connection; friend struct stream; diff --git a/include/boost/requests/detail/connection_impl.hpp b/include/boost/requests/detail/connection_impl.hpp index 682e39e..597d5fa 100644 --- a/include/boost/requests/detail/connection_impl.hpp +++ b/include/boost/requests/detail/connection_impl.hpp @@ -28,6 +28,13 @@ struct stream; struct connection_pool; namespace detail { +struct connection_impl; + +struct connection_owner +{ + virtual void return_connection_(connection_impl * conn) = 0; + virtual void drop_connection_(const connection_impl * conn) = 0; +}; struct connection_deleter; struct connection_impl @@ -57,7 +64,7 @@ struct connection_impl connection_impl & operator=(connection_impl && lhs) = delete; template explicit connection_impl(ExecutorOrContext && exec_or_ctx, asio::ssl::context & ctx, - connection_pool * borrowed_from = nullptr) + connection_owner * borrowed_from = nullptr) : next_layer_(std::forward(exec_or_ctx), ctx), use_ssl_{true}, borrowed_from_{borrowed_from} {} @@ -136,43 +143,36 @@ struct connection_impl using request_type = request_parameters; - template + BOOST_REQUESTS_DECL auto ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, request_parameters req, + urls::pct_string_view path, + http::fields & headers, + source & src, + cookie_jar * jar, system::error_code & ec) -> stream; - template + BOOST_REQUESTS_DECL auto ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, request_parameters req) -> stream; + urls::pct_string_view path, + http::fields & headers, + source & src, + cookie_jar * jar) -> stream; BOOST_REQUESTS_DECL auto ropen(beast::http::verb method, - urls::pct_string_view path, + urls::url_view path, http::fields & headers, source & src, - request_options opt, cookie_jar * jar, system::error_code & ec) -> stream; BOOST_REQUESTS_DECL auto ropen(beast::http::verb method, - urls::pct_string_view path, + urls::url_view path, http::fields & headers, source & src, - request_options opt, cookie_jar * jar) -> stream; - template - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, stream)) - async_ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, request_parameters req, - CompletionToken && completion_token); - template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, @@ -181,14 +181,23 @@ struct connection_impl urls::pct_string_view path, http::fields & headers, source & src, - request_options opt, cookie_jar * jar, CompletionToken && completion_token); + + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, stream)) + async_ropen(beast::http::verb method, + urls::url_view path, + http::fields & headers, + source & src, + cookie_jar * jar, + CompletionToken && completion_token); + bool uses_ssl() const {return use_ssl_;} void use_ssl(bool use_ssl = true) {use_ssl_ = use_ssl;} - struct connection_pool * pool() const {return borrowed_from_; } + struct connection_owner * owner() const {return borrowed_from_.load(); } private: next_layer_type next_layer_; @@ -201,7 +210,7 @@ struct connection_impl endpoint_type endpoint_; // atomic so moving the pool can be thread-safe - std::atomic borrowed_from_{nullptr}; + std::atomic borrowed_from_{nullptr}; std::atomic borrow_count_{0u}; struct async_close_op; @@ -255,7 +264,14 @@ struct connection_impl static void async_ropen_impl(asio::any_completion_handler handler, connection_impl * this_, http::verb method, urls::pct_string_view path, http::fields & headers, - source & src, request_options opt, cookie_jar * jar); + source & src, cookie_jar * jar); + + BOOST_REQUESTS_DECL + static void async_ropen_impl_url(asio::any_completion_handler handler, + connection_impl * this_, http::verb method, + urls::url_view path, http::fields & headers, + source & src, cookie_jar * jar); + }; diff --git a/include/boost/requests/detail/define.hpp b/include/boost/requests/detail/define.hpp index a240f01..7b90fb4 100644 --- a/include/boost/requests/detail/define.hpp +++ b/include/boost/requests/detail/define.hpp @@ -5,16 +5,17 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#define BOOST_REQUESTS_AWAIT(coro) \ - coro = {}; \ - while (!coro.is_complete()) \ - BOOST_ASIO_CORO_YIELD +#define BOOST_REQUESTS_YIELD \ + BOOST_ASIO_CORO_YIELD \ + if (bool _once_ = true) \ + for (BOOST_ASIO_HANDLER_LOCATION((__FILE__, __LINE__, op_name)); _once_; _once_= false) #define BOOST_REQUESTS_AWAIT_LOCK(Mutex, Lock) \ if (!Mutex.try_lock()) \ { \ - BOOST_ASIO_CORO_YIELD Mutex.async_lock(std::move(self)); \ + BOOST_REQUESTS_YIELD Mutex.async_lock(std::move(self)); \ if (ec) \ break; \ } \ Lock = detail::lock_guard{Mutex, std::adopt_lock} + diff --git a/include/boost/requests/detail/impl/connection_impl.hpp b/include/boost/requests/detail/impl/connection_impl.hpp index 8d59973..33c2556 100644 --- a/include/boost/requests/detail/impl/connection_impl.hpp +++ b/include/boost/requests/detail/impl/connection_impl.hpp @@ -72,69 +72,6 @@ BOOST_REQUESTS_DECL bool check_endpoint( system::error_code & ec); -template -auto connection_impl::ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, request_parameters req) -> stream -{ - system::error_code ec; - auto res = ropen(method, path, std::forward(body), std::move(req), ec); - if (ec) - throw_exception(system::system_error(ec)); - return res; -} - -template -auto connection_impl::ropen( - beast::http::verb method, - urls::url_view path, - RequestBody && body, request_parameters req, - system::error_code & ec) -> stream -{ - const auto is_secure = use_ssl_; - - if (!detail::check_endpoint(path, endpoint_, host_, use_ssl_, ec)) - return stream{get_executor(), nullptr}; - - if (((endpoint_.protocol() == asio::ip::tcp::v4()) - || (endpoint_.protocol() == asio::ip::tcp::v6())) - && !is_secure && req.opts.enforce_tls) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::insecure); - return stream{get_executor(), nullptr}; - } - - auto src = requests::make_source(std::forward(body)); - return ropen(method, path.encoded_target(), req.fields, *src, std::move(req.opts), req.jar, ec); -} - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, - stream)) -connection_impl::async_ropen( - beast::http::verb method, - urls::url_view path, - RequestBody && body, request_parameters req, - CompletionToken && completion_token) -{ - return asio::async_initiate( - [this](auto handler, - beast::http::verb method, - urls::url_view path, - RequestBody && body, request_parameters req) - { - auto source_ptr = requests::make_source(std::forward(body)); - auto & source = *source_ptr; - auto alloc = asio::get_associated_allocator(handler, asio::recycling_allocator()); - auto header_ptr = allocate_unique(alloc, std::move(req.fields)); - auto & headers = *header_ptr; - async_ropen(method, path, headers, source, std::move(req.opts), req.jar, - asio::consign(std::move(handler), std::move(source_ptr), std::move(header_ptr))); - }, - completion_token, method, path, std::forward(body), std::move(req) - ); -} - template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, stream)) @@ -142,14 +79,29 @@ connection_impl::async_ropen(beast::http::verb method, urls::pct_string_view path, http::fields & headers, source & src, - request_options opt, cookie_jar * jar, CompletionToken && completion_token) { return asio::async_initiate( &async_ropen_impl, completion_token, this, method, path, - std::ref(headers), std::ref(src), std::move(opt), jar); + std::ref(headers), std::ref(src), jar); +} + +template +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + void (boost::system::error_code, stream)) +connection_impl::async_ropen(beast::http::verb method, + urls::url_view path, + http::fields & headers, + source & src, + cookie_jar * jar, + CompletionToken && completion_token) +{ + return asio::async_initiate( + &async_ropen_impl_url, completion_token, + this, method, path, + std::ref(headers), std::ref(src), jar); } } diff --git a/include/boost/requests/download.hpp b/include/boost/requests/download.hpp index a24b1d3..c1b8fd2 100644 --- a/include/boost/requests/download.hpp +++ b/include/boost/requests/download.hpp @@ -69,6 +69,8 @@ namespace detail template struct async_write_to_file_op : asio::coroutine { + constexpr static const char * op_name = "async_write_to_file_op"; + using executor_type = typename std::decay_t::executor_type; executor_type get_executor() {return state->str.get_executor(); } @@ -102,35 +104,34 @@ struct async_write_to_file_op : asio::coroutine void operator()(Self && self, system::error_code ec = {}, std::size_t n = 0u) { auto st = state.get(); - if (!ec) - BOOST_ASIO_CORO_REENTER(this) - { - st->f.open(file.string().c_str(), asio::file_base::write_only | asio::file_base::create, ec); - if (ec) - { - st->written = 0u; - break; - } - - while (!st->str.done() && !ec) - { - // KDM: this could be in parallel to write using parallel_group. - BOOST_ASIO_CORO_YIELD { - auto b = asio::buffer(st->buffer); - st->str.async_read_some(b, std::move(self)); - } - - if (n == 0 && ec) - break; - - st->ec_read = exchange(ec, {}); - BOOST_ASIO_CORO_YIELD asio::async_write(st->f, asio::buffer(st->buffer, n), std::move(self)); - - st->written += n; - if (st->ec_read && !ec) - ec = st->ec_read; - } + BOOST_ASIO_CORO_REENTER(this) + { + st->f.open(file.string().c_str(), asio::file_base::write_only | asio::file_base::create, ec); + if (ec) + { + st->written = 0u; + break; + } + + while (!st->str.done() && !ec) + { + // KDM: this could be in parallel to write using parallel_group. + BOOST_REQUESTS_YIELD { + auto b = asio::buffer(st->buffer); + st->str.async_read_some(b, std::move(self)); } + + if (n == 0 && ec) + break; + + st->ec_read = exchange(ec, {}); + BOOST_REQUESTS_YIELD asio::async_write(st->f, asio::buffer(st->buffer, n), std::move(self)); + + st->written += n; + if (st->ec_read && !ec) + ec = st->ec_read; + } + } if (is_complete()) { state.reset(); @@ -182,6 +183,8 @@ namespace detail template struct async_write_to_file_op : asio::coroutine { + constexpr static const char * op_name = "async_write_to_file_op"; + using executor_type = typename std::decay_t::executor_type; executor_type get_executor() {return state->str.get_executor(); } @@ -212,25 +215,22 @@ struct async_write_to_file_op : asio::coroutine system::error_code ec = {}, std::size_t n = 0u) { auto st = state.get(); - if (!ec) BOOST_ASIO_CORO_REENTER(this) { state->f.open(file.string().c_str(), beast::file_mode::write_new, ec); if (ec) { state->written = 0u; - BOOST_ASIO_CORO_YIELD { + BOOST_REQUESTS_YIELD { auto exec = asio::get_associated_immediate_executor(self, state->str.get_executor()); asio::dispatch(exec, std::move(self)); } break; } - while (!ec && !state->str.done()) { - BOOST_ASIO_CORO_YIELD state->str.async_read_some(asio::buffer(state->buffer), std::move(self)); - + BOOST_REQUESTS_YIELD state->str.async_read_some(asio::buffer(state->buffer), std::move(self)); if (n == 0 && ec) break; @@ -361,6 +361,8 @@ namespace detail template struct async_download_op : asio::coroutine { + + constexpr static const char * op_name = "async_download_op"; using executor_type = typename Connection::executor_type; executor_type get_executor() {return state->str.get_executor(); } @@ -397,33 +399,32 @@ struct async_download_op : asio::coroutine void operator()(Self && self, system::error_code ec = {}, optional s = none) { auto st = state.get(); - if (!ec) - BOOST_ASIO_CORO_REENTER(this) - { - BOOST_ASIO_CORO_YIELD st->conn.async_ropen(http::verb::get, st->target, empty{}, - std::move(st->req), std::move(self)); - if (ec) - { - st->rb.history = std::move(*s).history(); - st->rb.headers = std::move(*s).headers(); - break; - } - st->str_ = *std::move(s); - if (filesystem::exists(st->download_path, ec) && filesystem::is_directory(st->download_path, ec) && !st->target.segments().empty()) - st->rb.download_path = st->download_path / st->target.segments().back(); // so we can download to a folder - else - st->rb.download_path = std::move(st->download_path); - ec.clear(); - if (!ec) - { - BOOST_ASIO_CORO_YIELD async_write_to_file(*st->str_, st->rb.download_path, - asio::deferred([](system::error_code ec, std::size_t){return asio::deferred.values(ec);})) - (std::move(self)); - } - - st->rb.history = std::move(*st->str_).history(); - st->rb.headers = std::move(*st->str_).headers(); - } + BOOST_ASIO_CORO_REENTER(this) + { + BOOST_REQUESTS_YIELD st->conn.async_ropen(http::verb::get, st->target, empty{}, + std::move(st->req), std::move(self)); + if (ec) + { + st->rb.history = std::move(*s).history(); + st->rb.headers = std::move(*s).headers(); + break; + } + st->str_ = *std::move(s); + if (filesystem::exists(st->download_path, ec) && filesystem::is_directory(st->download_path, ec) && !st->target.segments().empty()) + st->rb.download_path = st->download_path / st->target.segments().back(); // so we can download to a folder + else + st->rb.download_path = std::move(st->download_path); + ec.clear(); + if (!ec) + { + BOOST_REQUESTS_YIELD async_write_to_file(*st->str_, st->rb.download_path, + asio::deferred([](system::error_code ec, std::size_t){return asio::deferred.values(ec);})) + (std::move(self)); + } + + st->rb.history = std::move(*st->str_).history(); + st->rb.headers = std::move(*st->str_).headers(); + } if (is_complete()) { state.reset(); diff --git a/include/boost/requests/impl/request.hpp b/include/boost/requests/impl/request.hpp index 4fcfd19..3263801 100644 --- a/include/boost/requests/impl/request.hpp +++ b/include/boost/requests/impl/request.hpp @@ -43,6 +43,8 @@ namespace detail template struct async_request_op : asio::coroutine { + constexpr static const char * op_name = "async_request_op"; + using executor_type = typename Connection::executor_type; executor_type get_executor() {return conn.get_executor(); } @@ -80,21 +82,19 @@ struct async_request_op : asio::coroutine variant2::variant s = 0u) { auto st = state.get(); - - if (!ec) - BOOST_ASIO_CORO_REENTER(this) + BOOST_ASIO_CORO_REENTER(this) + { + BOOST_REQUESTS_YIELD conn.async_ropen(method, target, + std::forward(request_body), + std::move(st->req), std::move(self)); + st->str_.emplace(std::move(variant2::get<1>(s))); + if (!ec) { - BOOST_ASIO_CORO_YIELD conn.async_ropen(method, target, - std::forward(request_body), - std::move(st->req), std::move(self)); - st->str_.emplace(std::move(variant2::get<1>(s))); - if (!ec) - { - BOOST_ASIO_CORO_YIELD st->str_->async_read( st->rb.buffer, std::move(self)); - } - st->rb.headers = std::move(*st->str_).headers(); - st->rb.history = std::move(*st->str_).history(); + BOOST_REQUESTS_YIELD st->str_->async_read( st->rb.buffer, std::move(self)); } + st->rb.headers = std::move(*st->str_).headers(); + st->rb.history = std::move(*st->str_).history(); + } if (is_complete()) { state.reset(); diff --git a/include/boost/requests/impl/source.hpp b/include/boost/requests/impl/source.hpp index 7828126..72a8530 100644 --- a/include/boost/requests/impl/source.hpp +++ b/include/boost/requests/impl/source.hpp @@ -5,6 +5,7 @@ #ifndef BOOST_REQUESTS_IMPL_SOURCE_HPP #define BOOST_REQUESTS_IMPL_SOURCE_HPP +#include #include #include @@ -175,6 +176,7 @@ namespace detail template struct async_write_request_op : asio::coroutine { + constexpr static const char * op_name = "async_write_request_op"; using executor_type = typename Stream::executor_type; executor_type get_executor() const {return stream.get_executor();} @@ -223,58 +225,57 @@ struct async_write_request_op : asio::coroutine system::error_code ec = {}, std::size_t n = 0u) { auto st = state.get(); - if (!ec) - BOOST_ASIO_CORO_REENTER(this) + BOOST_ASIO_CORO_REENTER(this) + { { + const auto itr = header.find(beast::http::field::content_type); + if (itr == header.end()) { - const auto itr = header.find(beast::http::field::content_type); - if (itr == header.end()) - { - auto def = st->src.default_content_type(); - if (!def.empty()) - header.set(beast::http::field::content_type, def); - } + auto def = st->src.default_content_type(); + if (!def.empty()) + header.set(beast::http::field::content_type, def); } - st->src.reset(); + } + st->src.reset(); - if (st->sz) + if (st->sz) + { + if (*st->sz == 0) { - if (*st->sz == 0) - { - st->freq.template emplace<1>(method, target, 11, http::empty_body::value_type(), std::move(header)).prepare_payload(); - BOOST_ASIO_CORO_YIELD beast::http::async_write(stream, variant2::get<1>(st->freq), std::move(self)); - header = std::move(variant2::get<1>(st->freq).base()); - } - else - { - st->freq.template emplace<2>(method, target, 11, st->src, std::move(header)).prepare_payload(); - BOOST_ASIO_CORO_YIELD beast::http::async_write(stream, variant2::get<2>(st->freq), std::move(self)); - header = std::move(variant2::get<2>(st->freq).base()); - } - + st->freq.template emplace<1>(method, target, 11, http::empty_body::value_type(), std::move(header)).prepare_payload(); + BOOST_REQUESTS_YIELD beast::http::async_write(stream, variant2::get<1>(st->freq), std::move(self)); + header = std::move(variant2::get<1>(st->freq).base()); } else { - st->init = st->src.read_some(st->prebuffer, sizeof(st->prebuffer), ec); - if (ec) - { - n = 0u; - break; - } - - st->freq.template emplace<3>(method, target, 11, - detail::source_body::value_type{st->src, - asio::buffer(st->prebuffer, st->init.first), - st->init.second}, std::move(header)); - - if (!st->init.second) - variant2::get<3>(st->freq).set(beast::http::field::content_length, std::to_string(st->init.first)); - else - variant2::get<3>(st->freq).prepare_payload(); - BOOST_ASIO_CORO_YIELD beast::http::async_write(stream, variant2::get<3>(st->freq), std::move(self)); - header = std::move(variant2::get<3>(st->freq).base()); + st->freq.template emplace<2>(method, target, 11, st->src, std::move(header)).prepare_payload(); + BOOST_REQUESTS_YIELD beast::http::async_write(stream, variant2::get<2>(st->freq), std::move(self)); + header = std::move(variant2::get<2>(st->freq).base()); } + } + else + { + st->init = st->src.read_some(st->prebuffer, sizeof(st->prebuffer), ec); + if (ec) + { + n = 0u; + break; + } + + st->freq.template emplace<3>(method, target, 11, + detail::source_body::value_type{st->src, + asio::buffer(st->prebuffer, st->init.first), + st->init.second}, std::move(header)); + + if (!st->init.second) + variant2::get<3>(st->freq).set(beast::http::field::content_length, std::to_string(st->init.first)); + else + variant2::get<3>(st->freq).prepare_payload(); + BOOST_REQUESTS_YIELD beast::http::async_write(stream, variant2::get<3>(st->freq), std::move(self)); + header = std::move(variant2::get<3>(st->freq).base()); + } + } if (is_complete()) { state.reset(); diff --git a/include/boost/requests/impl/stream.hpp b/include/boost/requests/impl/stream.hpp index 3be7fa8..f379519 100644 --- a/include/boost/requests/impl/stream.hpp +++ b/include/boost/requests/impl/stream.hpp @@ -107,6 +107,7 @@ std::size_t stream::read(DynamicBuffer & buffer, system::error_code & ec) template struct stream::async_read_op : asio::coroutine { + constexpr static const char * op_name = "stream::async_read_op"; using executor_type = asio::any_io_executor; executor_type get_executor() {return this_->get_executor(); } @@ -117,15 +118,12 @@ struct stream::async_read_op : asio::coroutine { } - using lock_type = detail::lock_guard; - lock_type lock; system::error_code ec_; std::size_t res = 0u; template void operator()(Self && self, error_code ec = {}, std::size_t n = 0u) { - if (!ec) BOOST_ASIO_CORO_REENTER(this) { if (!this_->parser_) @@ -141,7 +139,7 @@ struct stream::async_read_op : asio::coroutine while (!ec && !this_->parser_->is_done()) { - BOOST_ASIO_CORO_YIELD this_->async_read_some( + BOOST_REQUESTS_YIELD this_->async_read_some( buffer.prepare(this_->parser_->content_length_remaining().value_or(BOOST_REQUESTS_CHUNK_SIZE)), std::move(self)); buffer.commit(n); @@ -159,7 +157,7 @@ struct stream::async_read_op : asio::coroutine if (this_->parser_->get().keep_alive()) { std::swap(ec, ec_); - BOOST_ASIO_CORO_YIELD this_->impl_->do_async_close_(std::move(self)); + BOOST_REQUESTS_YIELD this_->impl_->do_async_close_(std::move(self)); std::swap(ec, ec_); } } diff --git a/include/boost/requests/json.hpp b/include/boost/requests/json.hpp index e3c4596..c89b059 100644 --- a/include/boost/requests/json.hpp +++ b/include/boost/requests/json.hpp @@ -639,6 +639,7 @@ namespace detail template struct async_read_json_op : asio::coroutine { + constexpr static const char * op_name = "async_read_json_op"; using executor_type = typename Stream::executor_type; executor_type get_executor() {return str.get_executor(); } @@ -664,7 +665,7 @@ struct async_read_json_op : asio::coroutine { while (!state->sp.done() && !str.done() && !ec) { - BOOST_ASIO_CORO_YIELD str.async_read_some(asio::buffer(state->buffer), std::move(self)); + BOOST_REQUESTS_YIELD str.async_read_some(asio::buffer(state->buffer), std::move(self)); if (ec) return self.complete(ec, nullptr); state->sp.write_some(state->buffer, n, ec); @@ -700,6 +701,8 @@ inline void async_read_json_impl( template struct async_read_optional_json_op : asio::coroutine { + constexpr static const char * op_name = "async_read_optional_json_op"; + using executor_type = typename Stream::executor_type; executor_type get_executor() {return str.get_executor(); } @@ -716,7 +719,7 @@ struct async_read_optional_json_op : asio::coroutine { BOOST_ASIO_CORO_REENTER(this) { - BOOST_ASIO_CORO_YIELD str.async_read_some(asio::buffer(buffer), std::move(self)); + BOOST_REQUESTS_YIELD str.async_read_some(asio::buffer(buffer), std::move(self)); if (ec || (n == 0 && str.done())) return self.complete(ec, boost::none); sp.write_some(buffer.data(), n, ec); @@ -725,7 +728,7 @@ struct async_read_optional_json_op : asio::coroutine while (!sp.done() && !str.done()) { - BOOST_ASIO_CORO_YIELD str.async_read_some(asio::buffer(buffer), std::move(self)); + BOOST_REQUESTS_YIELD str.async_read_some(asio::buffer(buffer), std::move(self)); if (ec) return self.complete(ec, boost::none); sp.write_some(buffer.data(), n, ec); @@ -795,10 +798,11 @@ namespace detail template struct async_request_json_op : asio::coroutine { + constexpr static const char * op_name = "async_request_json_op"; + using executor_type = typename Connection::executor_type; executor_type get_executor() {return conn.get_executor(); } - Connection & conn; http::verb method; urls::url_view target; @@ -851,7 +855,7 @@ struct async_request_json_op : asio::coroutine { BOOST_ASIO_CORO_REENTER(this) { - BOOST_ASIO_CORO_YIELD conn.async_ropen(method, target, + BOOST_REQUESTS_YIELD conn.async_ropen(method, target, value_from(std::forward(state->request_body), std::is_same>{}), std::move(state->req), std::move(self)); @@ -860,7 +864,7 @@ struct async_request_json_op : asio::coroutine state->str_.emplace(std::move(variant2::get<1>(s))); state->rb.headers = std::move(*state->str_).headers(); state->rb.history = std::move(*state->str_).history(); - BOOST_ASIO_CORO_YIELD async_read_json(*state->str_, state->ptr, std::move(self)); + BOOST_REQUESTS_YIELD async_read_json(*state->str_, state->ptr, std::move(self)); if (ec) break; } @@ -887,6 +891,8 @@ struct async_request_json_op : asio::coroutine template struct async_request_optional_json_op : asio::coroutine { + constexpr static const char * op_name = "async_request_optional_json_op"; + using executor_type = typename Connection::executor_type; executor_type get_executor() {return conn.get_executor(); } @@ -931,7 +937,7 @@ struct async_request_optional_json_op : asio::coroutine { BOOST_ASIO_CORO_REENTER(this) { - BOOST_ASIO_CORO_YIELD conn.async_ropen(method, target, + BOOST_REQUESTS_YIELD conn.async_ropen(method, target, ::boost::json::value_from(std::forward(state->request_body), state->ptr), std::move(state->req), std::move(self)); if (!ec) @@ -939,7 +945,7 @@ struct async_request_optional_json_op : asio::coroutine state->str_.emplace(std::move(variant2::get<1>(s))); state->rb.headers = std::move(*state->str_).headers(); state->rb.history = std::move(*state->str_).history(); - BOOST_ASIO_CORO_YIELD async_read_json(*state->str_, state->ptr, std::move(self)); + BOOST_REQUESTS_YIELD async_read_json(*state->str_, state->ptr, std::move(self)); } else { diff --git a/include/boost/requests/redirect.hpp b/include/boost/requests/redirect.hpp index cd8e1b5..a30e63a 100644 --- a/include/boost/requests/redirect.hpp +++ b/include/boost/requests/redirect.hpp @@ -51,8 +51,16 @@ BOOST_REQUESTS_DECL bool same_endpoint_on_host(const urls::url_view current, con BOOST_REQUESTS_DECL bool same_endpoint_on_host(const urls::url_view current, const asio::local::stream_protocol::endpoint); /// Check if the endpoint is the same as the endpoint -BOOST_REQUESTS_DECL bool same_endpoint_on_host(const urls::url_view current, const asio::generic::stream_protocol::endpoint); +BOOST_REQUESTS_DECL bool same_endpoint_on_host(const urls::url_view current, const asio::generic::stream_protocol::endpoint &); +/// Check if a status is a redirect +inline bool is_redirect(http::status rc) +{ + return ((rc != http::status::moved_permanently) + && (rc != http::status::found) + && (rc != http::status::temporary_redirect) + && (rc != http::status::permanent_redirect)); +} } } diff --git a/include/boost/requests/response.hpp b/include/boost/requests/response.hpp index 6ec1bc3..21f0e33 100644 --- a/include/boost/requests/response.hpp +++ b/include/boost/requests/response.hpp @@ -44,7 +44,6 @@ struct response_base int result_code() const {return headers.result_int(); } http::status result() const {return headers.result(); } - using string_body_type = typename beast::http::basic_string_body>; using history_type = std::vector>; history_type history{}; diff --git a/include/boost/requests/session.hpp b/include/boost/requests/session.hpp index 09b2d08..1419843 100644 --- a/include/boost/requests/session.hpp +++ b/include/boost/requests/session.hpp @@ -43,9 +43,8 @@ struct session }; /// Constructor. - explicit session(const executor_type &ex) : mutex_(ex) + explicit session(const executor_type &ex) : executor_(ex) { - sslctx_.set_verify_mode(asio::ssl::verify_peer); sslctx_.set_default_verify_paths(); } @@ -54,17 +53,15 @@ struct session explicit session(ExecutionContext &context, typename asio::constraint< asio::is_convertible::value - >::type = 0) - : mutex_(context.get_executor()) + >::type = 0) : executor_(context.get_executor()) { - sslctx_.set_verify_mode(asio::ssl::verify_peer); sslctx_.set_default_verify_paths(); } /// Get the executor associated with the object. executor_type get_executor() BOOST_ASIO_NOEXCEPT { - return mutex_.get_executor(); + return executor_; } struct request_options & options() {return options_;} @@ -153,9 +150,12 @@ struct session http::fields & headers, CompletionToken && completion_token); + + private: asio::ssl::context sslctx_{asio::ssl::context_base::tls_client}; - detail::mutex mutex_; + asio::any_io_executor executor_; + std::mutex mutex_; struct request_options options_{default_options()}; diff --git a/include/boost/requests/stream.hpp b/include/boost/requests/stream.hpp index 84ae992..195ca49 100644 --- a/include/boost/requests/stream.hpp +++ b/include/boost/requests/stream.hpp @@ -8,12 +8,14 @@ #ifndef BOOST_REQUESTS_STREAM_HPP #define BOOST_REQUESTS_STREAM_HPP -#include #include +#include #include #include #include +#include +#include #include #include #include @@ -129,8 +131,6 @@ struct stream stream& operator=(const stream &) = delete; BOOST_REQUESTS_DECL ~stream(); - using history_type = response_base::history_type; - const http::response_header &headers() const & { if (!parser_) @@ -140,8 +140,6 @@ struct stream } return parser_->get().base(); } - const history_type &history() const & { return history_; } - bool done() const {return !parser_ || parser_->is_done();} explicit stream(executor_type executor, std::nullptr_t ) : executor_{executor}, impl_(nullptr) {} @@ -160,22 +158,12 @@ struct stream return std::move(parser_->get().base()); } - history_type &&history() && { return std::move(history_); } - - - void prepend_history(history_type && pre_history) - { - history_.insert(history_.begin(), - std::make_move_iterator(pre_history.begin()), - std::make_move_iterator(pre_history.end())); - } private: executor_type executor_; boost::intrusive_ptr impl_; detail::lock_guard lock_; std::unique_ptr> parser_; - history_type history_; template struct async_read_op; diff --git a/src/connection_pool.cpp b/src/connection_pool.cpp index 9e1128d..0e1e276 100644 --- a/src/connection_pool.cpp +++ b/src/connection_pool.cpp @@ -93,6 +93,8 @@ void connection_pool::lookup(urls::url_view sv, system::error_code & ec) struct connection_pool::async_lookup_op : asio::coroutine { + constexpr static const char * op_name = "connection_pool::async_lookup_op"; + using executor_type = asio::any_io_executor; executor_type get_executor() {return this_->get_executor(); } @@ -116,7 +118,6 @@ void connection_pool::async_lookup_op::operator()(Self && self, system::error_code ec, typename asio::ip::tcp::resolver::results_type eps) { - if (!ec) BOOST_ASIO_CORO_REENTER(this) { if (sv.has_scheme()) @@ -134,7 +135,7 @@ void connection_pool::async_lookup_op::operator()(Self && self, else if (scheme == "http" || scheme == "https") { - BOOST_ASIO_CORO_YIELD + BOOST_REQUESTS_YIELD { auto service = sv.has_port() ? sv.port() : scheme; auto resolver = allocate_unique( @@ -236,6 +237,8 @@ auto connection_pool::get_connection(error_code & ec) -> connection struct connection_pool::async_get_connection_op : asio::coroutine { + constexpr static const char * op_name = "connection_pool::async_get_connection_op"; + using executor_type = asio::any_io_executor; executor_type get_executor() {return this_->get_executor(); } @@ -262,7 +265,6 @@ void connection_pool::async_get_connection_op::operator()( system::error_code ec) { connection conn; - if (!ec) BOOST_ASIO_CORO_REENTER (this) { @@ -303,7 +305,7 @@ void connection_pool::async_get_connection_op::operator()( nconn->use_ssl(this_->use_ssl_); nconn->set_host(this_->host_); - BOOST_ASIO_CORO_YIELD nconn->async_connect(ep, std::move(self)); + BOOST_REQUESTS_YIELD nconn->async_connect(ep, std::move(self)); if (ec == system::errc::address_not_available) { lock.lock(); @@ -325,7 +327,7 @@ void connection_pool::async_get_connection_op::operator()( } } - BOOST_ASIO_CORO_YIELD this_->cv_.async_wait(lock, std::move(self)); + BOOST_REQUESTS_YIELD this_->cv_.async_wait(lock, std::move(self)); } while(!ec); } @@ -338,6 +340,8 @@ void connection_pool::async_get_connection_op::operator()( struct connection_pool::async_ropen_op : asio::coroutine { + constexpr static const char * op_name = "connection_pool::async_ropen_op"; + using executor_type = asio::any_io_executor; executor_type get_executor() {return this_->get_executor(); } @@ -389,17 +393,16 @@ void connection_pool::async_ropen_op::operator()( boost::system::error_code ec, variant2::variant res) { - if (!ec) BOOST_ASIO_CORO_REENTER(this) { - BOOST_ASIO_CORO_YIELD this_->async_get_connection(std::move(self)); + BOOST_REQUESTS_YIELD this_->async_get_connection(std::move(self)); conn = std::move(variant2::get<1>(res)); if (!ec && !conn) BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); if (ec) break; - BOOST_ASIO_CORO_YIELD conn.async_ropen(method, path, headers, src, std::move(opt), jar, std::move(self)); + BOOST_REQUESTS_YIELD conn.async_ropen(method, path, headers, src, std::move(opt), jar, std::move(self)); return self.complete(ec, variant2::get<2>(std::move(res))); } if (is_complete()) diff --git a/src/detail/connection_impl.cpp b/src/detail/connection_impl.cpp index 3a740fb..1b6d759 100644 --- a/src/detail/connection_impl.cpp +++ b/src/detail/connection_impl.cpp @@ -87,28 +87,50 @@ auto connection_impl::ropen(beast::http::verb method, urls::pct_string_view path, http::fields & headers, source & src, - request_options opt, cookie_jar * jar) -> stream { system::error_code ec; - auto res = ropen(method, path, headers, src, std::move(opt), jar, ec); + auto res = ropen(method, path, headers, src, jar, ec); if (ec) throw_exception(system::system_error(ec)); return res; } +auto connection_impl::ropen(beast::http::verb method, + urls::url_view path, + http::fields & headers, + source & src, + cookie_jar * jar) -> stream +{ + system::error_code ec; + auto res = ropen(method, path, headers, src, jar, ec); + if (ec) + throw_exception(system::system_error(ec)); + return res; +} + +auto connection_impl::ropen(beast::http::verb method, + urls::url_view path, + http::fields & headers, + source & src, + cookie_jar * jar, + system::error_code & ec) -> stream +{ + detail::check_endpoint(path, endpoint(), host(), use_ssl_, ec); + if (ec) + return stream{get_executor(), nullptr}; + + return ropen(method, path.encoded_resource(), headers, src, jar, ec); +} auto connection_impl::ropen(beast::http::verb method, urls::pct_string_view path, http::fields & headers, source & src, - request_options opt, cookie_jar * jar, system::error_code & ec) -> stream { const auto is_secure = use_ssl_; - using lock_type = detail::lock_guard; - lock_type read_lock; if (jar) { auto cc = jar->get(host(), is_secure, path); @@ -124,163 +146,68 @@ auto connection_impl::ropen(beast::http::verb method, if (headers.count(http::field::user_agent) > 0) headers.set(http::field::user_agent, "Requests-" BOOST_BEAST_VERSION_STRING); - response_base::history_type history; - - while (!ec) + detail::lock_guard lock{write_mtx_}; + // write impl { - // write impl + if (ec) + return stream{get_executor(), nullptr}; + if (!is_open()) { - write_mtx_.lock(ec); if (ec) return stream{get_executor(), nullptr}; - lock_type wlock{write_mtx_, std::adopt_lock}; - boost::optional alock; - - if (!is_open()) - { - retry: - read_mtx_.lock(ec); - if (ec) - return stream{get_executor(), nullptr}; - - alock.emplace(read_mtx_, std::adopt_lock); - - next_layer_.next_layer().connect(endpoint_, ec); - if (use_ssl_ && !ec) - next_layer_.handshake(asio::ssl::stream_base::client, ec); - if (ec) - return stream{get_executor(), nullptr}; - } - - alock.reset(); - - if (use_ssl_) - write_request(next_layer_, method, path, headers, src); - else - write_request(next_layer_.next_layer(), method, path, headers, src); - - if (ec == asio::error::broken_pipe || ec == asio::error::connection_reset) - goto retry ; - else if (ec) - return stream{get_executor(), nullptr}; - - // release after acquire! - read_mtx_.lock(ec); + next_layer_.next_layer().connect(endpoint_, ec); + if (use_ssl_ && !ec) + next_layer_.handshake(asio::ssl::stream_base::client, ec); if (ec) return stream{get_executor(), nullptr}; - - read_lock = {read_mtx_, std::adopt_lock}; } - // write end - - if (ec) - return stream{get_executor(), nullptr}; - - stream str{get_executor(), this}; - str.parser_ = std::make_unique>(http::response_header{http::fields(headers.get_allocator())}); - str.parser_->body_limit(boost::none); - - if (method == http::verb::head || method == http::verb::connect) - str.parser_->skip(true); if (use_ssl_) - beast::http::read_header(next_layer_, buffer_, *str.parser_, ec); + write_request(next_layer_, method, path, headers, src); else - beast::http::read_header(next_layer_.next_layer(), buffer_, *str.parser_, ec); + write_request(next_layer_.next_layer(), method, path, headers, src); if (ec) - break; - - if (jar) - { - auto cookie_itr = str.headers().find(http::field::set_cookie); - if (cookie_itr != str.headers().end()) - { - auto f = requests::parse_set_cookie_field(cookie_itr->value()); - if (f) - jar->set(*f, host_); - else - { - ec = f.error(); - return str; - } - - } - } - - auto rc = str.parser_->get().base().result(); - - if ((opt.redirect == redirect_mode::none) - || ((rc != http::status::moved_permanently) - && (rc != http::status::found) - && (rc != http::status::temporary_redirect) - && (rc != http::status::permanent_redirect))) - { - // GO - str.lock_ = std::move(read_lock); - str.history_ = std::move(history); - return str; - } - response_base::buffer_type buf{headers.get_allocator()}; - if (method != http::verb::head) - str.read(buf, ec); - if (ec) - break; - - history.emplace_back(std::move( str.parser_->get().base()), std::move(buf)); + return stream{get_executor(), nullptr}; + } + lock = read_mtx_; + // write end - auto & res = history.back().base(); + if (ec) + return stream{get_executor(), nullptr}; - // read the body to put into history - auto loc_itr = res.find(http::field::location); - if (loc_itr == res.end()) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::invalid_redirect); - break ; - } + stream str{get_executor(), this}; + str.parser_ = std::make_unique>(http::response_header{http::fields(headers.get_allocator())}); + str.parser_->body_limit(boost::none); - const auto url = interpret_location(path, loc_itr->value()); - if (url.has_error()) - { - ec = url.error(); - break; - } - // we don't need to use should_redirect, bc we're on the same host. - if (url->has_authority() && - host_ == url->encoded_host() && - !same_endpoint_on_host(*url, endpoint())) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::forbidden_redirect); - break ; - } + if (method == http::verb::head || method == http::verb::connect) + str.parser_->skip(true); - if (--opt.max_redirects == 0) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::too_many_redirects); - break ; - } + if (use_ssl_) + beast::http::read_header(next_layer_, buffer_, *str.parser_, ec); + else + beast::http::read_header(next_layer_.next_layer(), buffer_, *str.parser_, ec); - path = url->encoded_resource(); - if (!url->encoded_host_and_port().empty()) - headers.set(http::field::host, url->encoded_host_and_port()); + if (ec) + return stream{get_executor(), nullptr}; - if (jar && headers.count(http::field::host) > 0u) + if (jar) + { + auto cookie_itr = str.headers().find(http::field::set_cookie); + if (cookie_itr != str.headers().end()) { - auto cc = jar->get(headers[http::field::host], is_secure, url->encoded_path()); - if (!cc.empty()) - headers.set(http::field::cookie, cc); + auto f = requests::parse_set_cookie_field(cookie_itr->value()); + if (f) + jar->set(*f, host_); else - headers.erase(http::field::cookie); + { + ec = f.error(); + return str; + } } - else - headers.erase(http::field::cookie); - - read_lock = {}; } - - stream str{get_executor(), this}; - str.history_ = std::move(history); + str.lock_ = std::move(lock); return str; } @@ -294,11 +221,11 @@ void connection_impl::set_host(core::string_view sv, system::error_code & ec) struct connection_impl::async_ropen_op : boost::asio::coroutine { + constexpr static const char * op_name = "connection_impl::async_ropen_op"; + using executor_type = asio::any_io_executor; executor_type get_executor() {return this_->get_executor(); } - using lock_type = detail::lock_guard; - boost::intrusive_ptr this_; optional str; @@ -306,46 +233,29 @@ struct connection_impl::async_ropen_op urls::pct_string_view path; http::fields & headers; source & src; - request_options opts; cookie_jar * jar{nullptr}; system::error_code ec_; + lock_guard lock_; - struct state_t - { - response_base::buffer_type buf; - - lock_type lock; - boost::optional alock; - - response_base::history_type history; - }; - - using allocator_type = asio::any_completion_handler_allocator; - std::unique_ptr> state; - - async_ropen_op(allocator_type alloc, - boost::intrusive_ptr this_, + async_ropen_op(boost::intrusive_ptr this_, beast::http::verb method, urls::pct_string_view path, http::fields & headers, source & src, - request_options opts, cookie_jar * jar) - : this_(std::move(this_)), method(method), path(path), headers(headers), src(src), opts(std::move(opts)) - , jar(jar), state(allocate_unique(alloc)) + : this_(std::move(this_)), method(method), path(path), headers(headers), src(src) + , jar(jar) { } - async_ropen_op(allocator_type alloc, - boost::intrusive_ptr this_, + async_ropen_op(boost::intrusive_ptr this_, beast::http::verb method, urls::url_view path, http::fields & headers, source & src, - request_options opt, cookie_jar * jar) : this_(this_), method(method), path(path.encoded_resource()), - headers(headers), src(src), opts(std::move(opt)), jar(jar), state(allocate_unique(alloc)) + headers(headers), src(src), jar(jar) { detail::check_endpoint(path, this_->endpoint(), this_->host(), this_->use_ssl_, ec_); } @@ -360,19 +270,17 @@ void connection_impl::async_ropen_op::operator()( Self && self, system::error_code ec, std::size_t res_) { - auto st = state.get(); - if (!ec) + BOOST_ASIO_HANDLER_LOCATION((__FILE__, __LINE__, op_name)); + BOOST_ASIO_CORO_REENTER(this) { if (ec_) { - BOOST_ASIO_CORO_YIELD asio::post(this_->get_executor(), std::move(self)); + BOOST_REQUESTS_YIELD asio::post(this_->get_executor(), std::move(self)); ec = ec_; break; } - state = boost::allocate_unique(asio::get_associated_allocator(self)); - if (jar) { @@ -389,173 +297,96 @@ void connection_impl::async_ropen_op::operator()( if (headers.count(http::field::user_agent) > 0) headers.set(http::field::user_agent, "Requests-" BOOST_BEAST_VERSION_STRING); - - while (!ec) + if (!this_->write_mtx_.try_lock()) { - BOOST_REQUESTS_AWAIT_LOCK(this_->write_mtx_, st->lock); - - if (!this_->is_open()) - { - retry: - BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, st->alock); - BOOST_ASIO_CORO_YIELD this_->next_layer_.next_layer().async_connect(this_->endpoint_, std::move(self)); - if (!ec && this_->use_ssl_) - { - BOOST_ASIO_CORO_YIELD this_->next_layer_.async_handshake(asio::ssl::stream_base::client, std::move(self)); - } - if (ec) - break; - } - - st->alock.reset(); - if (this_->use_ssl_) - { - BOOST_ASIO_CORO_YIELD async_write_request(this_->next_layer_, method, path, headers, src, std::move(self)); - } - else - { - BOOST_ASIO_CORO_YIELD async_write_request(this_->next_layer_.next_layer(), method, path, headers, src, std::move(self)); - } - - if (ec == asio::error::broken_pipe || ec == asio::error::connection_reset) - goto retry ; - else if (ec) - break; - - // release after acquire! - BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, st->lock); - // END OF write impl - - str.emplace(this_->get_executor(), this_); // , req.get_allocator().resource() - str->parser_ = std::make_unique>(http::response_header{http::fields(headers.get_allocator())}); - str->parser_->body_limit(boost::none); - - if (method == http::verb::head || method == http::verb::connect) - str->parser_->skip(true); + BOOST_REQUESTS_YIELD this_->write_mtx_.async_lock(std::move(self)); + if (ec) + return self.complete(ec, *std::move(str)); + lock_ = {this_->write_mtx_, std::adopt_lock}; + } - if (this_->use_ssl_) - { - BOOST_ASIO_CORO_YIELD beast::http::async_read_header(this_->next_layer_, this_->buffer_, *str->parser_, std::move(self)); - } - else + if (!this_->is_open()) + { + BOOST_REQUESTS_YIELD this_->next_layer_.next_layer().async_connect(this_->endpoint_, std::move(self)); + if (!ec && this_->use_ssl_) { - BOOST_ASIO_CORO_YIELD beast::http::async_read_header(this_->next_layer_.next_layer(), this_->buffer_, *str->parser_, std::move(self)); + BOOST_REQUESTS_YIELD this_->next_layer_.async_handshake(asio::ssl::stream_base::client, std::move(self)); } - if (ec) break; + } - { - if (jar) - { - auto cookie_itr = str->headers().find(http::field::set_cookie); - if (cookie_itr != str->headers().end()) - { - auto f = requests::parse_set_cookie_field(cookie_itr->value()); - if (f) - jar->set(*f, this_->host()); - else - { - ec = f.error(); - return self.complete(ec, *std::move(str)); - } - } - } + if (this_->use_ssl_) + { + BOOST_REQUESTS_YIELD async_write_request(this_->next_layer_, method, path, headers, src, std::move(self)); + } + else + { + BOOST_REQUESTS_YIELD async_write_request(this_->next_layer_.next_layer(), method, path, headers, src, std::move(self)); + } - auto rc = str->parser_->get().base().result(); - if ((opts.redirect < redirect_mode::endpoint) || - ((rc != http::status::moved_permanently) && (rc != http::status::found) && - (rc != http::status::temporary_redirect) && (rc != http::status::permanent_redirect))) - { - // GO - str->lock_ = std::move(st->lock); - str->history_ = std::move(st->history); - return self.complete(ec, *std::move(str)); - } - } + if (ec) + break; - if (method != http::verb::head) - { - BOOST_ASIO_CORO_YIELD str->async_read(st->buf, std::move(self)); - } - if (ec) - break; - state->history.emplace_back(std::move(str->parser_->get().base()), std::move(st->buf)); - st->lock = {}; - str.reset(); + str.emplace(this_->get_executor(), this_); // , req.get_allocator().resource() + str->parser_ = std::make_unique>(http::response_header{http::fields(headers.get_allocator())}); + str->parser_->body_limit(boost::none); - auto & res = state->history.back().base(); + if (method == http::verb::head || method == http::verb::connect) + str->parser_->skip(true); - // read the body to put into history - auto loc_itr = res.find(http::field::location); - if (loc_itr == res.end()) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::invalid_redirect); - break ; - } + if (this_->use_ssl_) + { + BOOST_REQUESTS_YIELD beast::http::async_read_header(this_->next_layer_, this_->buffer_, *str->parser_, std::move(self)); + } + else + { + BOOST_REQUESTS_YIELD beast::http::async_read_header(this_->next_layer_.next_layer(), this_->buffer_, *str->parser_, std::move(self)); + } - const auto url = interpret_location(path, loc_itr->value()); - if (url.has_error()) - { - ec = url.error(); - break; - } - // we don't need to use should_redirect, bc we're on the same host. - if (url->has_authority() && - this_->host_ == url->encoded_host() && - !same_endpoint_on_host(*url, this_->endpoint())) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::forbidden_redirect); - break ; - } + if (ec) + { + lock_ = {}; + return self.complete(error_code{}, stream{get_executor(), nullptr}); + } - if (--opts.max_redirects == 0) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::too_many_redirects); - break ; - } + if (!this_->read_mtx_.try_lock()) + { + BOOST_REQUESTS_YIELD this_->read_mtx_.async_lock(std::move(self)); + if (ec) + return self.complete(ec, *std::move(str)); + lock_ = {this_->read_mtx_, std::adopt_lock}; + } - path = url->encoded_resource(); - if (!url->encoded_host_and_port().empty()) - headers.set(http::field::host, url->encoded_host_and_port()); - if (jar && headers.count(http::field::host) > 0u) + if (jar) + { + auto cookie_itr = str->headers().find(http::field::set_cookie); + if (cookie_itr != str->headers().end()) { - auto cc = jar->get(headers[http::field::host], this_->use_ssl_, url->encoded_path()); - if (!cc.empty()) - headers.set(http::field::cookie, cc); + auto f = requests::parse_set_cookie_field(cookie_itr->value()); + if (f) + jar->set(*f, this_->host()); else - headers.erase(http::field::cookie); + { + ec = f.error(); + return self.complete(ec, *std::move(str)); + } } - else - headers.erase(http::field::cookie); - } - - stream str_{this_->get_executor(), nullptr}; - str_.history_ = std::move(st->history); - return self.complete(ec, std::move(str_)); - } // coro is complete if (is_complete()) { - state.reset(); - self.complete(ec, stream{this_->get_executor(), nullptr}); + lock_ = {}; + return self.complete(ec, *std::move(str)); } } void connection_impl::connect(endpoint_type ep, system::error_code & ec) { - auto wlock = detail::lock(write_mtx_, ec); - if (ec) - return; - - auto rlock = detail::lock(read_mtx_, ec); - if (ec) - return; - + detail::lock_guard lw{write_mtx_}, lr{read_mtx_}; next_layer_.next_layer().connect(endpoint_ = ep, ec); if (use_ssl_ && !ec) @@ -565,14 +396,7 @@ void connection_impl::connect(endpoint_type ep, system::error_code & ec) void connection_impl::close(system::error_code & ec) { - auto wlock = detail::lock(write_mtx_, ec); - if (ec) - return; - - auto rlock = detail::lock(read_mtx_, ec); - if (ec) - return; - + detail::lock_guard lw{write_mtx_}, lr{read_mtx_}; if (use_ssl_) next_layer_.shutdown(ec); @@ -612,81 +436,64 @@ void connection_impl::do_async_close_(asio::any_completion_handler; - std::unique_ptr> state; + constexpr static const char * op_name = "connection_impl::async_connect_op"; connection_impl * this_; endpoint_type ep; + detail::lock_guard write_lock, read_lock; + template void operator()(Self && self, system::error_code ec = {}) { - auto st = state.get(); - if (!ec) - BOOST_ASIO_CORO_REENTER(this) + BOOST_ASIO_CORO_REENTER(this) + { + BOOST_REQUESTS_AWAIT_LOCK(this_->write_mtx_, write_lock); + BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, read_lock); + BOOST_REQUESTS_YIELD this_->next_layer_.next_layer().async_connect(this_->endpoint_ = ep, std::move(self)); + if (this_->use_ssl_) { - BOOST_REQUESTS_AWAIT_LOCK(this_->write_mtx_, st->write_lock); - BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, st->read_lock); - if (this_->use_ssl_) - { - BOOST_ASIO_CORO_YIELD this_->next_layer_.async_handshake(asio::ssl::stream_base::client, std::move(self)); - } + BOOST_REQUESTS_YIELD this_->next_layer_.async_handshake(asio::ssl::stream_base::client, std::move(self)); } + } // coro is complete if (is_complete()) - { - state.reset(); self.complete(ec); - } } }; struct connection_impl::async_close_op : asio::coroutine { - using lock_type = detail::lock_guard; - struct state_t - { - lock_type read_lock, write_lock; - }; + constexpr static const char * op_name = "connection_impl::async_close_op"; using allocator_type = asio::any_completion_handler_allocator; - std::unique_ptr> state; connection_impl * this_; endpoint_type ep; + detail::lock_guard write_lock, read_lock; + template void operator()(Self && self, system::error_code ec = {}) { - auto st = state.get(); - if (!ec) - BOOST_ASIO_CORO_REENTER(this) + BOOST_ASIO_CORO_REENTER(this) + { + BOOST_REQUESTS_AWAIT_LOCK(this_->write_mtx_, write_lock); + BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, read_lock); + if (!ec && this_->use_ssl_) { - BOOST_REQUESTS_AWAIT_LOCK(this_->write_mtx_, st->write_lock); - BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, st->read_lock); - if (!ec && this_->use_ssl_) - { - BOOST_ASIO_CORO_YIELD this_->next_layer_.async_shutdown(std::move(self)); - } - if (this_->next_layer_.next_layer().is_open()) - this_->next_layer_.next_layer().close(ec); + BOOST_REQUESTS_YIELD this_->next_layer_.async_shutdown(std::move(self)); } + if (this_->next_layer_.next_layer().is_open()) + this_->next_layer_.next_layer().close(ec); + } // coro is complete if (is_complete()) - { - state.reset(); self.complete(ec); - } } }; void connection_impl::do_close_(system::error_code & ec) { - auto wlock = detail::lock(write_mtx_, ec); if (ec) return; @@ -709,34 +516,37 @@ void connection_impl::remove_from_pool_() ptr->drop_connection_(this); } -BOOST_REQUESTS_DECL void connection_impl::async_connect_impl(asio::any_completion_handler handler, connection_impl * this_, endpoint_type ep) { return asio::async_compose, void(error_code)>( - async_connect_op{{}, - allocate_unique(asio::get_associated_allocator(handler)), - this_, ep}, handler, this_->get_executor()); + async_connect_op{{}, this_, ep}, handler, this_->get_executor()); } -BOOST_REQUESTS_DECL void connection_impl::async_close_impl(asio::any_completion_handler handler, connection_impl * this_) { return asio::async_compose, void(error_code)>( - async_close_op{{}, - allocate_unique(asio::get_associated_allocator(handler)), - this_}, handler, this_->get_executor()); + async_close_op{{}, this_}, handler, this_->get_executor()); } -BOOST_REQUESTS_DECL void connection_impl::async_ropen_impl(asio::any_completion_handler handler, - connection_impl * this_, http::verb method, - urls::pct_string_view path, http::fields & headers, - source & src, request_options opt, cookie_jar * jar) + connection_impl * this_, http::verb method, + urls::pct_string_view path, http::fields & headers, + source & src, cookie_jar * jar) +{ + return asio::async_compose, void(error_code, stream)>( + async_ropen_op{this_, method, path, headers, src, jar}, + handler, this_->get_executor()); +} + +void connection_impl::async_ropen_impl_url(asio::any_completion_handler handler, + connection_impl * this_, http::verb method, + urls::url_view url, http::fields & headers, + source & src, cookie_jar * jar) { return asio::async_compose, void(error_code, stream)>( - async_ropen_op{asio::get_associated_allocator(handler), this_, method, path, headers, src, std::move(opt), jar}, + async_ropen_op{this_, method, url, headers, src, jar}, handler, this_->get_executor()); } diff --git a/src/redirect.cpp b/src/redirect.cpp index 81736fc..aca55aa 100644 --- a/src/redirect.cpp +++ b/src/redirect.cpp @@ -36,14 +36,15 @@ bool should_redirect(redirect_mode mode, urls::url_view target, const public_suffix_list & pse) { - if (mode == redirect_mode::any) - return true; - // TODO: handle encoding/decoding const auto target_domain = target.encoded_host(); const auto current_domain = current.encoded_host(); switch (mode) { + case redirect_mode::none: + return false; + case redirect_mode::any: + return true; case redirect_mode::private_domain: { // find the match of the domains @@ -94,20 +95,20 @@ bool should_redirect(redirect_mode mode, } } -bool same_endpoint_on_host(const urls::url_view current, const asio::ip::tcp::endpoint ep) +bool same_endpoint_on_host(const urls::url_view current, const asio::ip::tcp::endpoint &ep) { return get_port(current) == ep.port(); } bool same_endpoint_on_host(const urls::url_view current, - const asio::local::stream_protocol::endpoint ep) + const asio::local::stream_protocol::endpoint &ep) { - return false; // domain socket can only redirect locally + return true; // domain socket can only redirect locally } -bool same_endpoint_on_host(const urls::url_view current, const asio::generic::stream_protocol::endpoint ep) +bool same_endpoint_on_host(const urls::url_view current, const asio::generic::stream_protocol::endpoint &ep) { if (ep.protocol() == asio::local::stream_protocol()) { diff --git a/src/session.cpp b/src/session.cpp index 71b22d7..b96f911 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -23,11 +23,10 @@ auto session::ropen( source & src, system::error_code & ec) -> stream { - auto opts = options_; + /*auto opts = options_; response_base::history_type history{headers.get_allocator()}; - auto do_ropen = [&](http::fields & hd, urls::pct_string_view target, request_options opts) -> stream { @@ -35,7 +34,7 @@ auto session::ropen( if (ec) return stream{get_executor(), nullptr}; - return p->ropen(method, target, hd, src, opts, &jar_, ec); + return p->ropen(method, target, hd, src, &jar_, ec); }; const auto is_secure = (url.scheme_id() == urls::scheme::https) @@ -130,7 +129,7 @@ auto session::ropen( str = do_ropen(headers, url.encoded_target(), opts); } str.prepend_history(std::move(history)); - return str; + return str;*/ } auto session::make_request_(http::fields fields) -> requests::request_parameters { @@ -170,7 +169,7 @@ session::get_pool(urls::url_view url, error_code & ec) -> std::shared_ptr lock(mutex_); if (ec) return std::shared_ptr(); @@ -192,6 +191,8 @@ session::get_pool(urls::url_view url, error_code & ec) -> std::shared_ptrget_executor(); } @@ -199,8 +200,6 @@ struct session::async_get_pool_op : asio::coroutine urls::url_view url; const bool is_https; - detail::lock_guard lock; - async_get_pool_op(session *this_, urls::url_view url) : this_(this_), url(url), is_https((url.scheme_id() == urls::scheme::https) || (url.scheme_id() == urls::scheme::wss)) @@ -213,18 +212,23 @@ struct session::async_get_pool_op : asio::coroutine { BOOST_ASIO_CORO_REENTER(this) { - BOOST_REQUESTS_AWAIT_LOCK(this_->mutex_, lock); { + std::lock_guard lock{this_->mutex_}; auto itr = this_->pools_.find(url); if (itr != this_->pools_.end()) return self.complete(ec, std::move(itr->second)); } p = std::make_shared(this_->get_executor(), this_->sslctx_); - BOOST_ASIO_CORO_YIELD p->async_lookup(url, std::move(self)); + BOOST_REQUESTS_YIELD p->async_lookup(url, std::move(self)); if (!ec) { - this_->pools_.emplace(url, p); + std::lock_guard lock{this_->mutex_}; + auto itr = this_->pools_.find(url); + if (itr == this_->pools_.end()) + this_->pools_.emplace(url, p); + else + p = itr->second; return self.complete(ec, p); } } @@ -235,29 +239,23 @@ struct session::async_get_pool_op : asio::coroutine struct session::async_ropen_op : asio::coroutine { + constexpr static const char * op_name = "session::async_ropen_op"; + using executor_type = asio::any_io_executor; executor_type get_executor() {return this_->get_executor(); } session * this_; http::verb method; - urls::url url; struct request_options opts; - core::string_view default_mime_type; - - system::error_code ec_; bool is_secure = (url.scheme_id() == urls::scheme::https) - || (url.scheme_id() == urls::scheme::wss); - - response_base::history_type history; + || (url.scheme_id() == urls::scheme::wss); http::fields & headers; source & src; - urls::url url_cache; - async_ropen_op(session * this_, http::verb method, urls::url_view path, @@ -281,7 +279,6 @@ void session::async_ropen_op::operator()( Self && self, system::error_code ec, variant2::variant, stream> s) { - if (!ec) BOOST_ASIO_CORO_REENTER(this) { headers.set(beast::http::field::host, url.encoded_host_and_port()); @@ -290,7 +287,7 @@ void session::async_ropen_op::operator()( if (!is_secure && this_->options_.enforce_tls) { BOOST_REQUESTS_ASSIGN_EC(ec, error::insecure); - BOOST_ASIO_CORO_YIELD { + BOOST_REQUESTS_YIELD { auto exec = asio::get_associated_immediate_executor(self, this_->get_executor()); asio::dispatch(exec, asio::append(std::move(self), ec)); }; @@ -302,83 +299,14 @@ void session::async_ropen_op::operator()( headers.set(http::field::cookie, cc); } - BOOST_ASIO_CORO_YIELD this_->async_get_pool(url, std::move(self)); - BOOST_ASIO_CORO_YIELD variant2::get<1>(s)->async_ropen(method, url.encoded_resource(), + BOOST_REQUESTS_YIELD this_->async_get_pool(url, std::move(self)); + BOOST_REQUESTS_YIELD variant2::get<1>(s)->async_ropen(method, url.encoded_resource(), headers, src, opts, &this_->jar_, std::move(self)); - if (!ec || opts.max_redirects == variant2::get<2>(s).history().size()) - return self.complete(ec, std::move(variant2::get<2>(s))); - - while (ec == error::forbidden_redirect) - { - ec.clear(); - if (variant2::get<2>(s).history().empty()) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::invalid_redirect); - break; - } - opts.max_redirects -= variant2::get<2>(s).history().size(); - if (opts.max_redirects == 0) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::too_many_redirects); - break ; - } - - { - const auto & last = variant2::get<2>(s).history().back(); - auto loc_itr = last.base().find(http::field::location); - auto rc = last.base().result(); - if (((rc != http::status::moved_permanently) - && (rc != http::status::found) - && (rc != http::status::temporary_redirect) - && (rc != http::status::permanent_redirect)) - || (loc_itr == last.base().end())) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::invalid_redirect); - break; - } - const auto nurl = interpret_location(url.encoded_resource(), loc_itr->value()); - if (nurl.has_error()) - { - ec = nurl.error(); - break; - } - - if (!should_redirect(this_->options_.redirect, url, *nurl)) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::forbidden_redirect); - break ; - } - - if (nurl->has_authority()) - url = *nurl; - else - { - url_cache = url; - url_cache.set_encoded_path(nurl->encoded_path()); - url = url_cache; - } - - } - - { - auto cc = this_->jar_.get(url.encoded_host(), is_secure, url.encoded_path()); - if (!cc.empty()) - headers.set(http::field::cookie, cc); - else - headers.erase(http::field::cookie); - } - history.insert(history.end(), - std::make_move_iterator(std::move(variant2::get<2>(s)).history().begin()), - std::make_move_iterator(std::move(variant2::get<2>(s)).history().end())); - BOOST_ASIO_CORO_YIELD this_->async_get_pool(url, std::move(self)); - BOOST_ASIO_CORO_YIELD variant2::get<1>(s)->async_ropen(method, url.encoded_resource(), headers, src, opts, &this_->jar_, std::move(self)); - } - variant2::get<2>(s).prepend_history(std::move(history)); return self.complete(ec, std::move(variant2::get<2>(s))); } - if (is_complete() || ec) + if (is_complete()) self.complete(ec, stream{this_->get_executor(), nullptr}); } @@ -395,7 +323,7 @@ void session::async_ropen_impl( ); } -BOOST_REQUESTS_DECL + void session::async_get_pool_impl( asio::any_completion_handler)> handler, session * sess, urls::url_view url) diff --git a/src/stream.cpp b/src/stream.cpp index d8a8a2c..50d8f56 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -41,8 +41,6 @@ void stream::dump(system::error_code & ec) boost::system::error_code ec_; impl_->do_close_(ec_); } - /*else - impl_->do_return_();*/ } @@ -56,6 +54,8 @@ stream::~stream() struct stream::async_read_some_op : asio::coroutine { + constexpr static const char * op_name = "stream::async_read_some_op"; + using executor_type = asio::any_io_executor; executor_type get_executor() {return this_->get_executor(); } @@ -87,7 +87,6 @@ struct stream::async_read_some_op : asio::coroutine template void stream::async_read_some_op::operator()(Self && self, system::error_code ec, std::size_t res) { - if (!ec) BOOST_ASIO_CORO_REENTER(this) { if (!this_->parser_) @@ -103,7 +102,7 @@ void stream::async_read_some_op::operator()(Self && self, system::error_code ec, this_->parser_->get().body().data = buffer.data(); this_->parser_->get().body().size = buffer.size(); - BOOST_ASIO_CORO_YIELD this_->impl_->do_async_read_some_(*this_->parser_, std::move(self)); + BOOST_REQUESTS_YIELD this_->impl_->do_async_read_some_(*this_->parser_, std::move(self)); if (!this_->parser_->is_done()) { this_->parser_->get().body().more = true; @@ -116,7 +115,12 @@ void stream::async_read_some_op::operator()(Self && self, system::error_code ec, if (!this_->parser_->get().keep_alive()) { ec_ = ec ; - BOOST_ASIO_CORO_YIELD this_->impl_->do_async_close_(std::move(self)); + // we already hold the write-mutex + if (this_->impl_->uses_ssl()) + BOOST_REQUESTS_YIELD this_->impl_->next_layer_.async_shutdown(std::move(self)); + + if (this_->impl_->next_layer_.next_layer().is_open()) + this_->impl_->next_layer_.next_layer().close(ec); ec = ec_; } } @@ -140,6 +144,8 @@ void stream::async_read_some_impl( struct stream::async_dump_op : asio::coroutine { + constexpr static const char * op_name = "stream::async_dump_op"; + using executor_type = asio::any_io_executor; executor_type get_executor() {return this_->get_executor(); } @@ -161,7 +167,7 @@ void stream::async_dump_op::operator()(Self && self, system::error_code ec, std: { if (!this_->parser_ || !this_->parser_->is_done()) { - BOOST_ASIO_CORO_YIELD asio::dispatch( + BOOST_REQUESTS_YIELD asio::dispatch( asio::get_associated_immediate_executor(self, this_->get_executor()), std::move(self)); ec = ec_; @@ -173,13 +179,13 @@ void stream::async_dump_op::operator()(Self && self, system::error_code ec, std: this_->parser_->get().body().data = buffer; this_->parser_->get().body().size = BOOST_REQUESTS_CHUNK_SIZE; - BOOST_ASIO_CORO_YIELD this_->impl_->do_async_read_some_(*this_->parser_, std::move(self)); + BOOST_REQUESTS_YIELD this_->impl_->do_async_read_some_(*this_->parser_, std::move(self)); } if (ec || !this_->parser_->get().keep_alive()) { ec_ = ec ; - BOOST_ASIO_CORO_YIELD this_->impl_->do_async_close_(std::move(self)); + BOOST_REQUESTS_YIELD this_->impl_->do_async_close_(std::move(self)); ec = ec_; } } diff --git a/test/string_maker.hpp b/test/string_maker.hpp index 16cb017..115cb5c 100644 --- a/test/string_maker.hpp +++ b/test/string_maker.hpp @@ -8,7 +8,6 @@ #ifndef STRING_MAKER_HPP #define STRING_MAKER_HPP - #include "doctest.h" #include #include @@ -19,7 +18,6 @@ #include #include - namespace doctest { @@ -164,14 +162,19 @@ struct tracker_t boost::source_location loc; bool called = false; bool moved_from = false; + tracker_t( Handler && handler, const boost::source_location & loc = BOOST_CURRENT_LOCATION) - : handler(std::forward(handler)), loc(loc) {} + : handler(std::forward(handler)), loc(loc) + { + BOOST_ASIO_HANDLER_LOCATION((loc.file_name(), loc.line(), loc.function_name())); + } template void operator()(Args && ... args) { + BOOST_ASIO_HANDLER_LOCATION((loc.file_name(), loc.line(), loc.function_name())); called = true; std::move(handler)(std::forward(args)...); } @@ -185,7 +188,10 @@ struct tracker_t { if (!moved_from) { - doctest::detail::ResultBuilder rb(doctest::assertType::DT_CHECK, loc.file_name(), loc.line(), loc.function_name()); + std::string dm = boost::core::demangle(typeid(handler).name()); + doctest::detail::ResultBuilder rb(doctest::assertType::DT_CHECK, + loc.file_name(), loc.line(), + dm.c_str()); rb.setResult(doctest::detail::Result{called, "called"}); DOCTEST_ASSERT_LOG_AND_REACT(rb); } From f312b87d467dd27c21b1d5b56e84b756d263030b Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 22 Nov 2023 23:32:54 +0800 Subject: [PATCH 72/76] the great refactor. --- CMakeLists.txt | 6 +- build/Jamfile | 1 + include/boost/requests/connection.hpp | 83 +- include/boost/requests/connection_pool.hpp | 181 +- .../boost/requests/detail/check_endpoint.hpp | 97 + .../requests/detail/condition_variable.hpp | 10 +- .../boost/requests/detail/connection_impl.hpp | 132 +- .../requests/detail/impl/connection_impl.hpp | 18 +- include/boost/requests/detail/lock_guard.hpp | 65 - include/boost/requests/detail/mutex.hpp | 103 - include/boost/requests/download.hpp | 41 +- include/boost/requests/fields/location.hpp | 30 - include/boost/requests/fields/set_cookie.hpp | 2 +- include/boost/requests/grammar/token_rule.hpp | 2 +- include/boost/requests/http.hpp | 38 +- .../boost/requests/impl/connection_pool.hpp | 52 +- include/boost/requests/impl/request.hpp | 98 +- include/boost/requests/impl/session.hpp | 66 - include/boost/requests/impl/source.hpp | 2 + include/boost/requests/impl/stream.hpp | 27 +- include/boost/requests/json.hpp | 1273 +--- include/boost/requests/method.hpp | 758 +- include/boost/requests/redirect.hpp | 18 +- include/boost/requests/request.hpp | 366 +- include/boost/requests/request_parameters.hpp | 39 +- include/boost/requests/response.hpp | 3 + include/boost/requests/rfc/dates.hpp | 8 +- include/boost/requests/session.hpp | 131 +- include/boost/requests/sources/string.hpp | 2 + .../boost/requests/sources/string_view.hpp | 2 + include/boost/requests/stream.hpp | 6 +- include/boost/requests/websocket.hpp | 184 + src/connection_pool.cpp | 291 +- src/detail/connection_impl.cpp | 400 +- src/fields/location.cpp | 64 - src/json.cpp | 144 + src/request.cpp | 652 ++ src/rfc/dates.cpp | 8 +- src/rfc/link.cpp | 2 +- src/rfc/quoted_string.cpp | 2 +- src/session.cpp | 214 +- src/stream.cpp | 14 +- test/CMakeLists.txt | 45 +- test/connection.cpp | 758 -- test/connection_pool.cpp | 689 -- test/cookie.cpp | 114 - test/cookie_grammar.cpp | 220 - test/coroutine_test_case.hpp | 80 + test/detail/mutex.cpp | 293 - test/doctest.h | 6580 ----------------- test/fixtures/httpbin.cpp | 49 + test/fixtures/httpbin.hpp | 44 + test/fixtures/server.cpp | 246 + test/fixtures/server.hpp | 45 + test/httpbin/cookie.cpp | 144 + test/httpbin/json.cpp | 270 + test/httpbin/method.cpp | 419 ++ test/json.cpp | 333 - test/keep_alive.cpp | 41 - test/link.cpp | 121 - test/location.cpp | 37 - test/main_test.cpp | 6 +- test/method.cpp | 563 -- test/options.cpp | 6 - test/quoted_string.cpp | 29 - test/redirect.cpp | 106 - test/request.cpp | 12 - test/response.cpp | 19 - test/rfc.cpp | 153 - test/session.cpp | 559 -- test/string_maker.hpp | 220 - test/unit/connection.cpp | 298 + test/unit/connection_pool.cpp | 179 + test/unit/cookie_grammar.cpp | 218 + test/{ => unit}/cookie_jar.cpp | 30 +- test/{ => unit}/detail/condition_variable.cpp | 19 +- test/{ => unit}/fixed_token_rule.cpp | 25 +- test/{ => unit}/form.cpp | 19 +- test/unit/keep_alive.cpp | 40 + test/unit/link.cpp | 117 + test/unit/quoted_string.cpp | 27 + test/{ => unit}/raw_string.cpp | 15 +- test/unit/redirect.cpp | 104 + test/unit/request_stream.cpp | 281 + test/unit/rfc.cpp | 153 + test/{ => unit}/service.cpp | 20 +- test/unit/session.cpp | 52 + test/{ => unit}/source.cpp | 32 +- test/{ => unit}/token_rule.cpp | 13 +- 89 files changed, 5239 insertions(+), 14239 deletions(-) create mode 100644 include/boost/requests/detail/check_endpoint.hpp delete mode 100644 include/boost/requests/detail/lock_guard.hpp delete mode 100644 include/boost/requests/detail/mutex.hpp delete mode 100644 include/boost/requests/fields/location.hpp create mode 100644 include/boost/requests/websocket.hpp delete mode 100644 src/fields/location.cpp create mode 100644 src/json.cpp create mode 100644 src/request.cpp delete mode 100644 test/connection.cpp delete mode 100644 test/connection_pool.cpp delete mode 100644 test/cookie.cpp delete mode 100644 test/cookie_grammar.cpp create mode 100644 test/coroutine_test_case.hpp delete mode 100644 test/detail/mutex.cpp delete mode 100644 test/doctest.h create mode 100644 test/fixtures/httpbin.cpp create mode 100644 test/fixtures/httpbin.hpp create mode 100644 test/fixtures/server.cpp create mode 100644 test/fixtures/server.hpp create mode 100644 test/httpbin/cookie.cpp create mode 100644 test/httpbin/json.cpp create mode 100644 test/httpbin/method.cpp delete mode 100644 test/json.cpp delete mode 100644 test/keep_alive.cpp delete mode 100644 test/link.cpp delete mode 100644 test/location.cpp delete mode 100644 test/method.cpp delete mode 100644 test/options.cpp delete mode 100644 test/quoted_string.cpp delete mode 100644 test/redirect.cpp delete mode 100644 test/request.cpp delete mode 100644 test/response.cpp delete mode 100644 test/rfc.cpp delete mode 100644 test/session.cpp delete mode 100644 test/string_maker.hpp create mode 100644 test/unit/connection.cpp create mode 100644 test/unit/connection_pool.cpp create mode 100644 test/unit/cookie_grammar.cpp rename test/{ => unit}/cookie_jar.cpp (51%) rename test/{ => unit}/detail/condition_variable.cpp (77%) rename test/{ => unit}/fixed_token_rule.cpp (50%) rename test/{ => unit}/form.cpp (82%) create mode 100644 test/unit/keep_alive.cpp create mode 100644 test/unit/link.cpp create mode 100644 test/unit/quoted_string.cpp rename test/{ => unit}/raw_string.cpp (73%) create mode 100644 test/unit/redirect.cpp create mode 100644 test/unit/request_stream.cpp create mode 100644 test/unit/rfc.cpp rename test/{ => unit}/service.cpp (56%) create mode 100644 test/unit/session.cpp rename test/{ => unit}/source.cpp (67%) rename test/{ => unit}/token_rule.cpp (68%) diff --git a/CMakeLists.txt b/CMakeLists.txt index ae925b7..ecb2e9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,7 +45,7 @@ endif() find_package(Threads REQUIRED) if (NOT BOOST_REQUESTS_BUILD_INLINE) - find_package(Boost REQUIRED atomic container filesystem system json url) + find_package(Boost REQUIRED atomic container filesystem system json url unit_test_framework context) include_directories(include) endif() @@ -74,10 +74,8 @@ endif() add_library(boost_requests src/detail/condition_variable.cpp src/detail/connection_impl.cpp - src/detail/mutex.cpp src/fields/keep_alive.cpp src/fields/link.cpp - src/fields/location.cpp src/fields/set_cookie.cpp src/rfc/dates.cpp src/rfc/link.cpp @@ -90,9 +88,11 @@ add_library(boost_requests src/connection_pool.cpp src/cookie_jar.cpp src/error.cpp + src/json.cpp src/mime_types.cpp src/public_suffix.cpp src/redirect.cpp + src/request.cpp src/request_options.cpp src/session.cpp src/stream.cpp) diff --git a/build/Jamfile b/build/Jamfile index 5443160..ad9e3cf 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -33,6 +33,7 @@ alias requests_sources : connection_pool.cpp cookie_jar.cpp error.cpp + json.cpp mime_types.cpp public_suffix.cpp redirect.cpp diff --git a/include/boost/requests/connection.hpp b/include/boost/requests/connection.hpp index ad0eee8..679e2b8 100644 --- a/include/boost/requests/connection.hpp +++ b/include/boost/requests/connection.hpp @@ -17,7 +17,7 @@ namespace boost { namespace requests { struct stream; -struct connection : private detail::connection_owner +struct connection { /// The type of the next layer. typedef asio::ssl::stream next_layer_type; @@ -73,14 +73,14 @@ struct connection : private detail::connection_owner /// Construct a stream. template explicit connection(ExecutorOrContext && exec_or_ctx, asio::ssl::context & ctx) - : impl_(new detail::connection_impl(std::forward(exec_or_ctx), ctx)) {} + : impl_(std::make_shared(std::forward(exec_or_ctx), ctx)) {} template explicit connection(ExecutionContext &context, typename asio::constraint< asio::is_convertible::value >::type = 0) - : impl_(new detail::connection_impl(context)) {} + : impl_(std::make_shared(context)) {} explicit connection(asio::any_io_executor exec) : impl_(new detail::connection_impl(std::move(exec))) {} @@ -148,28 +148,6 @@ struct connection : private detail::connection_owner core::string_view host() const {return impl_->host();} constexpr static redirect_mode supported_redirect_mode() {return redirect_mode::endpoint;} - using request_type = request_parameters; - - template - auto ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, - request_parameters req, - system::error_code & ec) -> stream - { - return impl_->ropen(method, path, std::forward(body), std::move(req), ec); - } - - template - auto ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, - request_parameters req) -> stream - { - return impl_->ropen(method, path, std::forward(body), std::move(req)); - } - - BOOST_REQUESTS_DECL auto ropen(beast::http::verb method, urls::pct_string_view path, http::fields & headers, @@ -180,7 +158,6 @@ struct connection : private detail::connection_owner return impl_->ropen(method, path, headers, src, jar, ec); } - BOOST_REQUESTS_DECL auto ropen(beast::http::verb method, urls::pct_string_view path, http::fields & headers, @@ -190,20 +167,6 @@ struct connection : private detail::connection_owner return impl_->ropen(method, path, headers, src, jar); } - template - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, stream)) - async_ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, - request_parameters req, - CompletionToken && completion_token) - { - return impl_->async_ropen(method, path, std::forward(body), - std::move(req), std::forward(completion_token)); - } - template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, @@ -212,28 +175,48 @@ struct connection : private detail::connection_owner urls::pct_string_view path, http::fields & headers, source & src, - request_options opt, cookie_jar * jar, CompletionToken && completion_token) { - return impl_->async_ropen(method, path, headers, src, std::move(opt), jar, std::forward(completion_token)); + return impl_->async_ropen(method, path, headers, src, jar, std::forward(completion_token)); } bool uses_ssl() const {return impl_->uses_ssl();} void use_ssl(bool use_ssl = true) {impl_->use_ssl(use_ssl);} operator bool() const {return impl_ != nullptr;} + websocket upgrade( + urls::pct_string_view path, + http::fields & headers, + cookie_jar * jar, + system::error_code & ec) && + { + return impl_->upgrade(path, headers, jar, ec); + } - private: - explicit connection(boost::intrusive_ptr impl) : impl_(std::move(impl)) {} - boost::intrusive_ptr impl_; + websocket upgrade( + urls::pct_string_view path, + http::fields & headers, + cookie_jar * jar) && + { + return impl_->upgrade(path, headers, jar); + } - BOOST_REQUESTS_DECL - void return_connection_(detail::connection_impl * conn) override {} - BOOST_REQUESTS_DECL - void drop_connection_(const detail::connection_impl * conn) override {} + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, websocket)) + async_upgrade(urls::pct_string_view path, + http::fields & headers, + cookie_jar * jar, + CompletionToken && completion_token) && + { + return impl_->async_upgrade(path, headers, jar, std::forward(completion_token)); + } + private: + explicit connection(std::shared_ptr impl) : impl_(std::move(impl)) {} + + std::shared_ptr impl_; friend struct connection_pool; friend struct stream; @@ -319,6 +302,8 @@ struct connection::defaulted : connection return this->async_ropen(req, std::move(opt), jar, default_token()); } + + }; } diff --git a/include/boost/requests/connection_pool.hpp b/include/boost/requests/connection_pool.hpp index 0f29ab5..942bfdc 100644 --- a/include/boost/requests/connection_pool.hpp +++ b/include/boost/requests/connection_pool.hpp @@ -26,16 +26,9 @@ struct endpoint_hash } }; -struct connection_deleter -{ - constexpr connection_deleter() = default; - BOOST_REQUESTS_DECL - void operator()(connection_impl * ptr); -}; - } -struct connection_pool : private detail::connection_owner +struct connection_pool { /// The type of the executor associated with the object. typedef asio::any_io_executor executor_type; @@ -102,6 +95,7 @@ struct connection_pool : private detail::connection_owner /// Move constructor BOOST_REQUESTS_DECL connection_pool(connection_pool && lhs) ; + BOOST_REQUESTS_DECL ~connection_pool(); void lookup(urls::url_view av) @@ -119,106 +113,49 @@ struct connection_pool : private detail::connection_owner async_lookup(urls::url_view av, CompletionToken && completion_token ); - - std::size_t limit() const {return limit_;} + std::size_t limit() const {return limit_;} std::size_t active() const {return conns_.size();} + std::size_t free() const {return free_conns_.size();} - using request_type = request_parameters; - - BOOST_REQUESTS_DECL connection get_connection(error_code & ec); - connection get_connection() + BOOST_REQUESTS_DECL connection borrow_connection(error_code & ec); + connection borrow_connection() { boost::system::error_code ec; - auto res = get_connection(ec); + auto res = borrow_connection(ec); if (ec) - throw_exception(system::system_error(ec, "get_connection")); + throw_exception(system::system_error(ec, "borrow_connection")); return res; } - template - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, connection)) - async_get_connection(CompletionToken && completion_token); - - template - auto ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, request_parameters req, - system::error_code & ec) -> stream - { - auto conn = get_connection(ec); - if (!ec && !conn ) - BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); - if (ec) - return stream{get_executor(), nullptr}; - - BOOST_ASSERT(conn); - return conn.ropen(method, path, std::forward(body), std::move(req), ec); - } - - template - auto ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, request_parameters req) -> stream + BOOST_REQUESTS_DECL connection steal_connection(error_code & ec); + connection steal_connection() { boost::system::error_code ec; - auto res = ropen(method, path, std::forward(body), std::move(req), ec); + auto res = steal_connection(ec); if (ec) - throw_exception(system::system_error(ec, "open")); + throw_exception(system::system_error(ec, "steal_connection")); return res; } - auto ropen(beast::http::verb method, - urls::pct_string_view path, - http::fields & headers, - source & src, - cookie_jar * jar, - system::error_code & ec) -> stream - { - auto conn = get_connection(ec); - if (!ec && !conn) - BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); - if (ec) - return stream{get_executor(), nullptr}; + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, connection)) + async_borrow_connection(CompletionToken && completion_token); - BOOST_ASSERT(conn); - return conn.ropen(method, path, headers, src, jar, ec); - } - auto ropen(beast::http::verb method, - urls::pct_string_view path, - http::fields & headers, - source & src, - cookie_jar * jar) -> stream - { - boost::system::error_code ec; - auto res = ropen(method, path, headers, src, jar, ec); - if (ec) - throw_exception(system::system_error(ec, "open")); - return res; - } + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, connection)) + async_steal_connection(CompletionToken && completion_token); + bool uses_ssl() const {return use_ssl_;} - template - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, stream)) - async_ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, request_parameters req, - CompletionToken && completion_token); + BOOST_REQUESTS_DECL + void return_connection(connection conn); - template - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, stream)) - async_ropen(beast::http::verb method, - urls::pct_string_view path, - http::fields & headers, - source & src, - request_options opt, - cookie_jar * jar, - CompletionToken && completion_token); + BOOST_REQUESTS_DECL + void remove_connection(const connection &conn); + + const std::vector & endpoints() const { return endpoints_; }; - bool uses_ssl() const {return use_ssl_;} private: bool use_ssl_{true}; asio::ssl::context & context_; @@ -229,37 +166,31 @@ struct connection_pool : private detail::connection_owner std::size_t limit_; boost::unordered_multimap, + std::shared_ptr, detail::endpoint_hash> conns_; - std::vector free_conns_; + std::vector> free_conns_; struct async_lookup_op; + template struct async_get_connection_op; - struct async_ropen_op; - BOOST_REQUESTS_DECL - void return_connection_(detail::connection_impl * conn) override; - BOOST_REQUESTS_DECL - void drop_connection_(const detail::connection_impl * conn) override; friend struct connection; friend struct stream; friend struct detail::connection_impl; BOOST_REQUESTS_DECL - static void async_ropen_impl(asio::any_completion_handler handler, - connection_pool * this_, http::verb method, - urls::pct_string_view path, http::fields & headers, - source & src, request_options opt, cookie_jar * jar); + static void async_borrow_connection_impl( + asio::any_completion_handler handler, + connection_pool * this_); BOOST_REQUESTS_DECL - static void async_get_connection_impl( + static void async_steal_connection_impl( asio::any_completion_handler handler, connection_pool * this_); - BOOST_REQUESTS_DECL static void async_lookup_impl( asio::any_completion_handler handler, @@ -276,60 +207,34 @@ struct connection_pool::defaulted : connection_pool using connection = typename requests::connection::defaulted; using connection_pool::async_lookup; - using connection_pool::async_get_connection; auto async_lookup(urls::url_view av) { return connection_pool::async_lookup(av, default_token()); } - - auto - async_get_connection() - { - return async_get_connection(default_token()); - } - - template - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, stream)) - async_ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, request_parameters req, - CompletionToken && completion_token) + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, typename detail::defaulted_helper, Token>::type)) + async_borrow_connection(CompletionToken && completion_token) { - return connection_pool::async_ropen(method, path, std::forward(body), std::move(req), - detail::with_defaulted_token(std::forward(completion_token))); + return connection_pool::async_borrow_connection(detail::with_defaulted_token(std::forward(completion_token))); } template - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, stream)) - async_ropen(beast::http::verb method, - urls::pct_string_view path, - http::fields & headers, - source & src, - request_options opt, - cookie_jar * jar, - CompletionToken && completion_token ) + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, typename detail::defaulted_helper::type)) + async_steal_connection(CompletionToken && completion_token) { - return connection_pool::async_ropen(method, path, headers, src, std::move(opt), jar, - detail::with_defaulted_token(std::forward(completion_token))); + return connection_pool::async_steal_connection(detail::with_defaulted_token(std::forward(completion_token))); } - template - auto async_ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, request_parameters req) + auto async_borrow_connection() { - return async_ropen(method, path, std::forward(body), std::move(req), default_token()); + return this->async_borrow_connection(default_token()); } - template - auto async_ropen(http::request & req, - request_options opt, - cookie_jar * jar = nullptr) + auto async_steal_connection() { - return async_ropen(req, std::move(opt), jar, default_token()); + return this->async_steal_connection(default_token()); } }; diff --git a/include/boost/requests/detail/check_endpoint.hpp b/include/boost/requests/detail/check_endpoint.hpp new file mode 100644 index 0000000..3cc633c --- /dev/null +++ b/include/boost/requests/detail/check_endpoint.hpp @@ -0,0 +1,97 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_REQUESTS_CHECK_ENDPOINT_HPP +#define BOOST_REQUESTS_CHECK_ENDPOINT_HPP + +#include +#include + +#include +#include +#include +#include + +namespace boost +{ +namespace requests +{ +namespace detail +{ + +inline +bool check_endpoint( + urls::url_view path, + const asio::ip::tcp::endpoint & ep, + core::string_view host, + bool has_ssl, + system::error_code & ec) +{ + if ((path.has_port() && (get_port(path) != ep.port())) + && (path.has_authority() && (path.encoded_host() != host)) + && (path.has_scheme() && (path.host() != (has_ssl ? "https" : "http")))) + BOOST_REQUESTS_ASSIGN_EC(ec, error::wrong_host) + + return !ec; +} + +inline +bool check_endpoint( + urls::url_view path, + const asio::local::stream_protocol::endpoint & ep, + core::string_view host, + bool, + system::error_code & ec) +{ + if (path.has_port() + && (path.has_authority() && (path.host() != host)) + && (path.has_scheme() && (path.host() != "unix"))) + BOOST_REQUESTS_ASSIGN_EC(ec, error::wrong_host) + + return !ec; +} + +inline +bool check_endpoint( + urls::url_view path, + const asio::generic::stream_protocol::endpoint & ep, + core::string_view host, + bool has_ssl, + system::error_code & ec) +{ + if (ep.protocol() == asio::local::stream_protocol()) + { + asio::local::stream_protocol::endpoint cmp; + cmp.resize(ep.size()); + std::memcpy(cmp.data(), + ep.data(), + ep.size()); + return check_endpoint(path, cmp, host, has_ssl, ec); + } + else if (ep.protocol() == asio::ip::tcp::v4() + || ep.protocol() == asio::ip::tcp::v6()) + { + asio::ip::tcp::endpoint cmp; + cmp.resize(ep.size()); + std::memcpy(cmp.data(), + ep.data(), + ep.size()); + return check_endpoint(path, cmp, host, has_ssl, ec); + } + else + { + BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::no_protocol_option); + return false; + } +} + +} +} +} + + +#endif // BOOST_REQUESTS_CHECK_ENDPOINT_HPP diff --git a/include/boost/requests/detail/condition_variable.hpp b/include/boost/requests/detail/condition_variable.hpp index a7e01e7..ed46398 100644 --- a/include/boost/requests/detail/condition_variable.hpp +++ b/include/boost/requests/detail/condition_variable.hpp @@ -38,9 +38,9 @@ struct condition_variable template auto - async_wait(std::unique_lock & lock, CompletionToken && tk) + async_wait(std::mutex & mtx, CompletionToken && tk) { - return asio::async_compose(async_wait_op{this, lock}, tk, timer_); + return asio::async_compose(async_wait_op{this, mtx}, tk, timer_); } condition_variable& operator=(const condition_variable&) = delete; @@ -80,7 +80,7 @@ struct condition_variable struct async_wait_op { condition_variable * this_; - std::unique_lock & lock; + std::mutex & mtx; std::weak_ptr indicator; template @@ -88,13 +88,13 @@ struct condition_variable { indicator = this_->shutdown_indicator_; this_->timer_.async_wait(std::move(self)); - lock.unlock(); + mtx.unlock(); } template void operator()(Self && self, system::error_code ec) { - lock.lock(); + mtx.lock(); if (!indicator.expired() && self.get_cancellation_state().cancelled() == asio::cancellation_type::none) ec.clear(); self.complete(ec); diff --git a/include/boost/requests/detail/connection_impl.hpp b/include/boost/requests/detail/connection_impl.hpp index 597d5fa..528f1e0 100644 --- a/include/boost/requests/detail/connection_impl.hpp +++ b/include/boost/requests/detail/connection_impl.hpp @@ -12,12 +12,13 @@ #include #include #include -#include #include +#include #include #include #include #include +#include #include @@ -28,16 +29,8 @@ struct stream; struct connection_pool; namespace detail { -struct connection_impl; -struct connection_owner -{ - virtual void return_connection_(connection_impl * conn) = 0; - virtual void drop_connection_(const connection_impl * conn) = 0; -}; - -struct connection_deleter; -struct connection_impl +struct connection_impl : std::enable_shared_from_this { typedef asio::ssl::stream next_layer_type; typedef typename next_layer_type::executor_type executor_type; @@ -63,10 +56,10 @@ struct connection_impl connection_impl(connection_impl && lhs) = delete; connection_impl & operator=(connection_impl && lhs) = delete; template - explicit connection_impl(ExecutorOrContext && exec_or_ctx, asio::ssl::context & ctx, - connection_owner * borrowed_from = nullptr) - : next_layer_(std::forward(exec_or_ctx), ctx), use_ssl_{true}, - borrowed_from_{borrowed_from} {} + explicit connection_impl(ExecutorOrContext && exec_or_ctx, asio::ssl::context & ctx) + : next_layer_(std::forward(exec_or_ctx), ctx), use_ssl_{true} + { + } template explicit connection_impl(ExecutionContext &context, @@ -141,8 +134,6 @@ struct connection_impl core::string_view host() const {return host_;} constexpr static redirect_mode supported_redirect_mode() {return redirect_mode::endpoint;} - using request_type = request_parameters; - BOOST_REQUESTS_DECL auto ropen(beast::http::verb method, urls::pct_string_view path, @@ -158,21 +149,6 @@ struct connection_impl source & src, cookie_jar * jar) -> stream; - BOOST_REQUESTS_DECL - auto ropen(beast::http::verb method, - urls::url_view path, - http::fields & headers, - source & src, - cookie_jar * jar, - system::error_code & ec) -> stream; - - BOOST_REQUESTS_DECL - auto ropen(beast::http::verb method, - urls::url_view path, - http::fields & headers, - source & src, - cookie_jar * jar) -> stream; - template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, @@ -184,73 +160,53 @@ struct connection_impl cookie_jar * jar, CompletionToken && completion_token); - template - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, stream)) - async_ropen(beast::http::verb method, - urls::url_view path, - http::fields & headers, - source & src, - cookie_jar * jar, - CompletionToken && completion_token); - bool uses_ssl() const {return use_ssl_;} void use_ssl(bool use_ssl = true) {use_ssl_ = use_ssl;} + BOOST_REQUESTS_DECL + websocket upgrade( + urls::pct_string_view path, + http::fields & headers, + cookie_jar * jar, + system::error_code & ec); + + + BOOST_REQUESTS_DECL + websocket upgrade( + urls::pct_string_view path, + http::fields & headers, + cookie_jar * jar); + + + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + void (boost::system::error_code, websocket)) + async_upgrade(urls::pct_string_view path, + http::fields & headers, + cookie_jar * jar, + CompletionToken && completion_token); - struct connection_owner * owner() const {return borrowed_from_.load(); } private: next_layer_type next_layer_; - bool use_ssl_{true}; - detail::mutex read_mtx_{next_layer_.get_executor()}, - write_mtx_{next_layer_.get_executor()}; + bool use_ssl_{false}; std::string host_; beast::flat_buffer buffer_; endpoint_type endpoint_; - // atomic so moving the pool can be thread-safe - std::atomic borrowed_from_{nullptr}; - std::atomic borrow_count_{0u}; - struct async_close_op; struct async_connect_op; struct async_ropen_op; + struct async_upgrade_op; - BOOST_REQUESTS_DECL std::size_t do_read_some_(beast::http::basic_parser & parser); BOOST_REQUESTS_DECL std::size_t do_read_some_(beast::http::basic_parser & parser, system::error_code & ec) ; BOOST_REQUESTS_DECL void do_async_read_some_(beast::http::basic_parser & parser, asio::any_completion_handler) ; BOOST_REQUESTS_DECL void do_async_close_(asio::any_completion_handler); BOOST_REQUESTS_DECL void do_close_(system::error_code & ec); - BOOST_REQUESTS_DECL void return_to_pool_(); - BOOST_REQUESTS_DECL void remove_from_pool_(); - friend stream; friend connection_pool; - friend connection_deleter; - - // borrow usage is done through intrusive_ptrs - friend void intrusive_ptr_add_ref(connection_impl* ptr) - { - ptr->borrow_count_++; - } - - friend void intrusive_ptr_release(connection_impl* ptr) - { - if (--ptr->borrow_count_ == 0u) - { - if (ptr->borrowed_from_ != nullptr) - { - if (ptr->is_open()) - ptr->return_to_pool_(); - else - ptr->remove_from_pool_(); - } - else - delete ptr; - } - } BOOST_REQUESTS_DECL static void async_connect_impl(asio::any_completion_handler handler, @@ -266,12 +222,30 @@ struct connection_impl urls::pct_string_view path, http::fields & headers, source & src, cookie_jar * jar); + BOOST_REQUESTS_DECL - static void async_ropen_impl_url(asio::any_completion_handler handler, - connection_impl * this_, http::verb method, - urls::url_view path, http::fields & headers, - source & src, cookie_jar * jar); + static void async_upgrade_impl(asio::any_completion_handler handler, + connection_impl * this_, urls::pct_string_view path, + http::fields & headers, cookie_jar * jar); + struct handle_chunked_t_ + { + std::size_t buffer_space = 0u; + core::string_view chunked_body; + std::size_t + operator()( + std::uint64_t remain, + core::string_view body, + error_code& ec) + { + auto n = (std::min)(remain, buffer_space); + buffer_space -= n; + std::string buf{body}; + chunked_body = body.substr(0, n); + return n; + } + }; + handle_chunked_t_ handle_chunked_; }; diff --git a/include/boost/requests/detail/impl/connection_impl.hpp b/include/boost/requests/detail/impl/connection_impl.hpp index 33c2556..7ce0d36 100644 --- a/include/boost/requests/detail/impl/connection_impl.hpp +++ b/include/boost/requests/detail/impl/connection_impl.hpp @@ -88,20 +88,18 @@ connection_impl::async_ropen(beast::http::verb method, std::ref(headers), std::ref(src), jar); } + template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, stream)) -connection_impl::async_ropen(beast::http::verb method, - urls::url_view path, - http::fields & headers, - source & src, - cookie_jar * jar, - CompletionToken && completion_token) +connection_impl::async_upgrade(urls::pct_string_view path, + http::fields & headers, + cookie_jar * jar, + CompletionToken && completion_token) { - return asio::async_initiate( - &async_ropen_impl_url, completion_token, - this, method, path, - std::ref(headers), std::ref(src), jar); + return asio::async_initiate( + &async_upgrade_impl, completion_token, + this, path, std::ref(headers), jar); } } diff --git a/include/boost/requests/detail/lock_guard.hpp b/include/boost/requests/detail/lock_guard.hpp deleted file mode 100644 index 039062b..0000000 --- a/include/boost/requests/detail/lock_guard.hpp +++ /dev/null @@ -1,65 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BOOST_REQUESTS_DETAIL_LOCK_GUARD_HPP -#define BOOST_REQUESTS_DETAIL_LOCK_GUARD_HPP - -#include - -namespace boost -{ -namespace requests -{ -namespace detail -{ - -struct lock_guard -{ - lock_guard() = default; - lock_guard(const lock_guard &) = delete; - lock_guard(lock_guard &&lhs) : mtx_(lhs.mtx_) - { - lhs.mtx_ = nullptr; - } - - lock_guard & - operator=(const lock_guard &) = delete; - - lock_guard & - operator=(lock_guard &&lhs) - { - std::swap(lhs.mtx_, mtx_); - return *this; - } - - ~lock_guard() - { - if (mtx_ != nullptr) - mtx_->unlock(); - } - lock_guard(mutex & mtx, const std::adopt_lock_t &) : mtx_(&mtx) {} - lock_guard(mutex & mtx) : mtx_(&mtx) {mtx.lock();} -private: - - mutex * mtx_ = nullptr; -}; - -inline lock_guard lock(mutex & mtx, system::error_code & ec) -{ - mtx.lock(ec); - if (ec) - return lock_guard(); - else - return lock_guard(mtx, std::adopt_lock); -} - - -} -} -} - -#endif // BOOST_REQUESTS_DETAIL_LOCK_GUARD_HPP diff --git a/include/boost/requests/detail/mutex.hpp b/include/boost/requests/detail/mutex.hpp deleted file mode 100644 index 41537bb..0000000 --- a/include/boost/requests/detail/mutex.hpp +++ /dev/null @@ -1,103 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BOOST_REQUESTS_DETAIL_MUTEX_HPP -#define BOOST_REQUESTS_DETAIL_MUTEX_HPP - -#include -#include -#include -#include -#include -#include - -namespace boost -{ -namespace requests -{ -namespace detail -{ - -struct mutex -{ - using executor_type = asio::any_io_executor; - - explicit mutex(executor_type exec) : exec_(exec) {} - - template - explicit mutex(ExecutionContext & ctx, - typename std::enable_if< - std::is_convertible< - ExecutionContext&, - asio::execution_context&>::value - >::type * = nullptr) - : exec_(ctx.get_executor()) - { - } - - template - BOOST_REQUESTS_DECL auto - async_lock(CompletionToken && tk) - { - return asio::async_initiate(async_lock_impl_, tk, this); - } - - mutex& operator=(const mutex&) = delete; - mutex& operator=(mutex&& lhs) noexcept - { - std::lock_guard _(waiters_mtx_); - exec_ = std::move(lhs.exec_); - waiters_ = std::move(lhs.waiters_); - return *this; - } - - mutex(const mutex&) = delete; - mutex(mutex&& mi) noexcept - : exec_(std::move(mi.exec_)), - waiters_(std::move(mi.waiters_)) - { - } - - BOOST_REQUESTS_DECL void lock(system::error_code & ec); - void lock() - { - system::error_code ec; - lock(ec); - if (ec) - boost::throw_exception(system::system_error(ec, "lock")); - } - BOOST_REQUESTS_DECL void unlock(); - - BOOST_REQUESTS_DECL bool try_lock(); - - template - struct rebind_executor - { - /// The mutex type when rebound to the specified executor. - typedef mutex other; - }; - - executor_type - get_executor() const noexcept {return exec_;} - - BOOST_REQUESTS_DECL ~mutex(); - private: - - BOOST_REQUESTS_DECL static - void async_lock_impl_(asio::any_completion_handler handler, mutex * this_); - - asio::any_io_executor exec_; - std::mutex mutex_, waiters_mtx_; - std::list> waiters_; -}; - -} -} -} - - -#endif // BOOST_REQUESTS_MUTEX_HPP diff --git a/include/boost/requests/download.hpp b/include/boost/requests/download.hpp index c1b8fd2..67c80cd 100644 --- a/include/boost/requests/download.hpp +++ b/include/boost/requests/download.hpp @@ -18,6 +18,7 @@ #if defined(BOOST_ASIO_HAS_FILE) #include #else +#include "request.hpp" #include #endif @@ -308,27 +309,28 @@ struct download_response : response_base template inline auto download(Connection & conn, urls::url_view target, - typename Connection::request_type req, + detail::request_type req, filesystem::path download_path, system::error_code & ec) -> download_response { - auto ro = conn.ropen(http::verb::get, target, empty{}, std::move(req), ec); + auto rh = request_stream(conn, http::verb::get, target, empty{}, std::move(req), ec); + auto & ro = rh.first; if (ec) - return download_response{std::move(ro).headers(), std::move(ro).history(), {}}; + return download_response{std::move(ro).headers(), std::move(rh.second), {}}; if (filesystem::exists(download_path, ec) && filesystem::is_directory(download_path, ec) && !target.empty()) download_path /= target.segments().back(); // so we can download to a folder ec.clear(); if (!ec) write_to_file(ro, download_path, ec); - return download_response{std::move(ro).headers(), std::move(ro).history(), std::move(download_path)}; + return download_response{std::move(ro).headers(), std::move(rh.second), std::move(download_path)}; } template inline auto download(Connection & conn, urls::url_view target, - typename Connection::request_type req, + detail::request_type req, filesystem::path download_path) -> download_response { boost::system::error_code ec; @@ -371,7 +373,7 @@ struct async_download_op : asio::coroutine { Connection & conn; urls::url_view target; - typename Connection::request_type req; + detail::request_type req; filesystem::path download_path; download_response rb{}; @@ -379,7 +381,7 @@ struct async_download_op : asio::coroutine state_t(Connection * conn, urls::url_view target, - typename Connection::request_type req, + detail::request_type req, filesystem::path download_path) : conn(*conn), target(target), req(std::move(req)), download_path(std::move(download_path)) {} }; @@ -390,26 +392,29 @@ struct async_download_op : asio::coroutine async_download_op(allocator_type alloc, Connection * conn, urls::url_view target, - typename Connection::request_type req, + detail::request_type req, filesystem::path download_path) : state(allocate_unique(alloc, conn, target, req, std::move(download_path))) {} template - void operator()(Self && self, system::error_code ec = {}, optional s = none) + void operator()(Self && self, + system::error_code ec = {}, + variant2::variant s = variant2::monostate{}, + history hist = {}) { auto st = state.get(); BOOST_ASIO_CORO_REENTER(this) { - BOOST_REQUESTS_YIELD st->conn.async_ropen(http::verb::get, st->target, empty{}, - std::move(st->req), std::move(self)); + BOOST_REQUESTS_YIELD async_request_stream(st->conn, http::verb::get, st->target, empty{}, + std::move(st->req), std::move(self)); + st->rb.history = std::move(hist); if (ec) { - st->rb.history = std::move(*s).history(); - st->rb.headers = std::move(*s).headers(); + st->rb.headers = std::move(variant2::get<1>(s)).headers(); break; } - st->str_ = *std::move(s); + st->str_.emplace(std::move(variant2::get<1>(s))); if (filesystem::exists(st->download_path, ec) && filesystem::is_directory(st->download_path, ec) && !st->target.segments().empty()) st->rb.download_path = st->download_path / st->target.segments().back(); // so we can download to a folder else @@ -422,13 +427,13 @@ struct async_download_op : asio::coroutine (std::move(self)); } - st->rb.history = std::move(*st->str_).history(); st->rb.headers = std::move(*st->str_).headers(); } if (is_complete()) { + auto rr = std::move(st->rb); state.reset(); - self.complete(ec, st->rb); + self.complete(ec, std::move(rr)); } } }; @@ -436,7 +441,7 @@ struct async_download_op : asio::coroutine template void async_download_impl( asio::any_completion_handler handler, - Connection * conn, urls::url_view target, typename Connection::request_type req, filesystem::path download_path) + Connection * conn, urls::url_view target, detail::request_type req, filesystem::path download_path) { return asio::async_compose, void(boost::system::error_code, download_response)>( @@ -455,7 +460,7 @@ template req, filesystem::path download_path, CompletionToken && completion_token = typename Connection::default_token()) { diff --git a/include/boost/requests/fields/location.hpp b/include/boost/requests/fields/location.hpp deleted file mode 100644 index 06b6a9b..0000000 --- a/include/boost/requests/fields/location.hpp +++ /dev/null @@ -1,30 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BOOST_REQUESTS_FIELDS_LOCATION_HPP -#define BOOST_REQUESTS_FIELDS_LOCATION_HPP - -#include -#include -#include -#include -#include -#include - -namespace boost -{ -namespace requests -{ - -BOOST_REQUESTS_DECL system::result interpret_location( - core::string_view current_target, - core::string_view location); - -} -} - -#endif // BOOST_REQUESTS_FIELDS_LOCATION_HPP diff --git a/include/boost/requests/fields/set_cookie.hpp b/include/boost/requests/fields/set_cookie.hpp index 3072ba2..fd258cc 100644 --- a/include/boost/requests/fields/set_cookie.hpp +++ b/include/boost/requests/fields/set_cookie.hpp @@ -25,7 +25,7 @@ struct set_cookie BOOST_REQUESTS_DECL bool operator()(const core::string_view & ra) const; }; - using attributes_type = urls::grammar::range; + using attributes_type = urls::grammar::range; using extensions_type = range_detail::filtered_range; diff --git a/include/boost/requests/grammar/token_rule.hpp b/include/boost/requests/grammar/token_rule.hpp index 05f3892..8b49544 100644 --- a/include/boost/requests/grammar/token_rule.hpp +++ b/include/boost/requests/grammar/token_rule.hpp @@ -78,7 +78,7 @@ struct token_rule_2_t urls::grammar::error::mismatch); it = (urls::grammar::find_if_not)(++it, end, csr_); - return urls::string_view(it0, it - it0); + return core::string_view(it0, it - it0); } private: diff --git a/include/boost/requests/http.hpp b/include/boost/requests/http.hpp index c50fc92..a4f37a5 100644 --- a/include/boost/requests/http.hpp +++ b/include/boost/requests/http.hpp @@ -29,18 +29,46 @@ using boost::beast::http::status_class; using boost::beast::http::to_status_class; using boost::beast::http::to_string; using boost::beast::http::verb; -using fields = boost::beast::http::fields; +using boost::beast::http::fields; + +struct header +{ + http::field field = http::field::unknown; + core::string_view key; + core::string_view value; + std::string buffer; + + header() = default; + header(http::field field, core::string_view value) : field(field), value(value) {} + header(core::string_view key, core::string_view value) : key(key), value(value) {} +}; + +struct headers : fields +{ + headers(std::initializer_list
fields) + { + for (const auto & init : fields) + if (init.field != http::field::unknown) + set(init.field, init.value); + else + set(init.key, init.value); + } + headers(fields && fl) : fields(std::move(fl)) {} + using fields::fields; + using fields::operator=; + +}; using file_body = beast::http::file_body; using empty_body = beast::http::empty_body; using string_body = beast::http::string_body; using buffer_body = beast::http::buffer_body; -using request_header = beast::http::request_header ; -using response_header = beast::http::response_header; +using request_header = beast::http::request_header ; +using response_header = beast::http::response_header; -template using request = beast::http::request ; -template using response = beast::http::response; +template using request = beast::http::request ; +template using response = beast::http::response; template using request_parser = beast::http::request_parser ; template using response_parser = beast::http::response_parser; diff --git a/include/boost/requests/impl/connection_pool.hpp b/include/boost/requests/impl/connection_pool.hpp index 64cbd6d..d29c758 100644 --- a/include/boost/requests/impl/connection_pool.hpp +++ b/include/boost/requests/impl/connection_pool.hpp @@ -26,62 +26,26 @@ connection_pool::async_lookup(urls::url_view av, CompletionToken && completion_t this, av); } - - template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, connection)) -connection_pool::async_get_connection(CompletionToken && completion_token) +connection_pool::async_borrow_connection(CompletionToken && completion_token) { return asio::async_initiate( - &async_get_connection_impl, + &async_borrow_connection_impl, completion_token, this); } -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, - stream)) -connection_pool::async_ropen(beast::http::verb method, - urls::pct_string_view path, - http::fields & headers, - source & src, - request_options opt, - cookie_jar * jar, - CompletionToken && completion_token) +template +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, connection)) +connection_pool::async_steal_connection(CompletionToken && completion_token) { - - return asio::async_initiate( - &async_ropen_impl, + return asio::async_initiate( + &async_steal_connection_impl, completion_token, - this, method, path, std::ref(headers), std::ref(src), std::move(opt), jar); + this); } -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, stream)) -connection_pool::async_ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, request_parameters req, - CompletionToken && completion_token) -{ - return asio::async_initiate( - [this](auto handler, - beast::http::verb method, - urls::url_view path, - RequestBody && body, request_parameters req) - { - auto source_ptr = requests::make_source(std::forward(body)); - auto & source = *source_ptr; - auto alloc = asio::get_associated_allocator(handler, asio::recycling_allocator()); - auto header_ptr = allocate_unique(alloc, std::move(req.fields)); - auto & headers = *header_ptr; - async_ropen(method, path, headers, source, std::move(req.opts), req.jar, - asio::consign(std::move(handler), std::move(source_ptr), std::move(header_ptr))); - }, - completion_token, method, path, std::forward(body), std::move(req) - ); -} } } diff --git a/include/boost/requests/impl/request.hpp b/include/boost/requests/impl/request.hpp index 3263801..603dc1b 100644 --- a/include/boost/requests/impl/request.hpp +++ b/include/boost/requests/impl/request.hpp @@ -23,16 +23,16 @@ auto request(Connection & conn, beast::http::verb method, urls::url_view target, RequestBody && body, - typename Connection::request_type req, + detail::request_type req, system::error_code & ec) -> response { - auto s = conn.ropen(method, target, std::forward(body), std::move(req), ec); + auto p = request_stream(conn, method, target, std::forward(body), std::move(req), ec); response rb{}; if (!ec) - s.read( rb.buffer, ec); - rb.headers = std::move(s).headers(); - rb.history = std::move(s).history(); + p.first.read( rb.buffer, ec); + rb.headers = std::move(p.first).headers(); + rb.history = std::move(p.second); return rb; } @@ -55,8 +55,8 @@ struct async_request_op : asio::coroutine struct state_t { - state_t(typename Connection::request_type req) : req(std::move(req)) {} - typename Connection::request_type req; + state_t(detail::request_type req) : req(std::move(req)) {} + detail::request_type req; optional str_; response rb{}; @@ -71,7 +71,7 @@ struct async_request_op : asio::coroutine http::verb method, urls::url_view target, RequestBody_ && request_body, - typename Connection::request_type req) + detail::request_type req) : conn(*conn), method(method), target(target) , request_body(static_cast(request_body)) , state(allocate_unique(alloc, std::move(req))) {} @@ -79,12 +79,14 @@ struct async_request_op : asio::coroutine template void operator()(Self && self, system::error_code ec = {}, - variant2::variant s = 0u) + variant2::variant s = 0u, + history hist = {}) { auto st = state.get(); BOOST_ASIO_CORO_REENTER(this) { - BOOST_REQUESTS_YIELD conn.async_ropen(method, target, + BOOST_REQUESTS_YIELD async_request_stream( + conn, method, target, std::forward(request_body), std::move(st->req), std::move(self)); st->str_.emplace(std::move(variant2::get<1>(s))); @@ -93,12 +95,13 @@ struct async_request_op : asio::coroutine BOOST_REQUESTS_YIELD st->str_->async_read( st->rb.buffer, std::move(self)); } st->rb.headers = std::move(*st->str_).headers(); - st->rb.history = std::move(*st->str_).history(); + st->rb.history = std::move(hist); } if (is_complete()) { + auto rr = std::move(st->rb); state.reset(); - self.complete(ec, std::move(st->rb)); + self.complete(ec, rr); } } }; @@ -121,23 +124,26 @@ struct async_free_request_op }; template -inline void async_request_impl( - asio::any_completion_handler handler, - Connection * conn, - beast::http::verb method, - urls::url_view target, - RequestBody * body, - typename Connection::request_type req) +struct async_request_impl { - return asio::async_compose< - asio::any_completion_handler, - void(system::error_code, response)>( + void operator()(asio::any_completion_handler handler, + Connection * conn, + beast::http::verb method, + detail::target_view target, + std::remove_reference_t * body, + detail::request_type req) + { + return asio::async_compose< + asio::any_completion_handler, + void(system::error_code, response)>( async_request_op{ - asio::get_associated_allocator(handler), - conn, method, target, std::forward(*body), std::move(req) - }, - handler, conn->get_executor()); -} + asio::get_associated_allocator(handler), + conn, method, target, std::forward(*body), std::move(req) + }, + handler, conn->get_executor()); + } +}; + @@ -149,55 +155,33 @@ template target, RequestBody && body, - typename Connection::request_type req, + detail::request_type req, CompletionToken && completion_token) { return asio::async_initiate( - &detail::async_request_impl, + detail::async_request_impl{}, completion_token, &conn, method, target, &body, std::move(req)); } + template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, - response)) + BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code, response)) CompletionToken> +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) async_request(beast::http::verb method, urls::url_view path, RequestBody && body, - http::fields req, + http::headers req, CompletionToken && completion_token) { return asio::async_initiate( + void(boost::system::error_code, response)>( detail::async_free_request_op{}, completion_token, method, path, std::forward(body), std::move(req)); } -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, - response)) -async_request(beast::http::verb method, - core::string_view path, - RequestBody && body, - http::fields req, - CompletionToken && completion_token) -{ - return asio::async_initiate( - detail::async_free_request_op{}, - completion_token, method, path, std::forward(body), std::move(req)); -} - } } diff --git a/include/boost/requests/impl/session.hpp b/include/boost/requests/impl/session.hpp index f40b815..e70050e 100644 --- a/include/boost/requests/impl/session.hpp +++ b/include/boost/requests/impl/session.hpp @@ -14,8 +14,6 @@ namespace boost { namespace requests { - - template< BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code, std::shared_ptr)) CompletionToken> BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, pool_ptr)) session::async_get_pool(urls::url_view url, CompletionToken && completion_token) @@ -25,70 +23,6 @@ session::async_get_pool(urls::url_view url, CompletionToken && completion_token) &async_get_pool_impl, completion_token, this, url); } - -template -auto session::ropen( - beast::http::verb method, - urls::url_view url, - RequestBody && body, - http::fields fields, - system::error_code & ec) -> stream -{ - const auto is_secure = (url.scheme_id() == urls::scheme::https) - || (url.scheme_id() == urls::scheme::wss); - - if (!is_secure && options_.enforce_tls) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::insecure); - return stream{get_executor(), nullptr}; - } - - auto src = make_source(body); - return ropen(method, url, fields, *src, ec); -} - - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, basic_stream)) -session::async_ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, - http::fields req, - CompletionToken && completion_token) -{ - return asio::async_initiate( - [this](auto handler, - beast::http::verb method, - urls::url_view path, - RequestBody && body, http::fields req) - { - auto source_ptr = requests::make_source(std::forward(body)); - auto & source = *source_ptr; - auto alloc = asio::get_associated_allocator(handler, asio::recycling_allocator()); - auto req_ptr = allocate_unique(alloc, std::move(req)); - auto & req_ref = *req_ptr; - async_ropen(method, path, source, req_ref, - asio::consign(std::move(handler), std::move(source_ptr), std::move(req_ptr))); - }, - completion_token, method, path, std::forward(body), std::move(req) - ); -} - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, stream)) -session::async_ropen(http::verb method, - urls::url_view path, - source & src, - http::fields & headers, - CompletionToken && completion_token) -{ - return asio::async_initiate( - &async_ropen_impl, completion_token, - this, method, path, &src, &headers); -} - } } #endif // BOOST_REQUESTS_IMPL_SESSION_HPP diff --git a/include/boost/requests/impl/source.hpp b/include/boost/requests/impl/source.hpp index 72a8530..a91e155 100644 --- a/include/boost/requests/impl/source.hpp +++ b/include/boost/requests/impl/source.hpp @@ -16,6 +16,7 @@ #include #include + namespace boost { namespace requests @@ -145,6 +146,7 @@ std::size_t write_request( { http::request req(method, target, 11, src, std::move(header)); req.prepare_payload(); + auto n = beast::http::write(stream, req, ec); header = std::move(req.base()); return n; diff --git a/include/boost/requests/impl/stream.hpp b/include/boost/requests/impl/stream.hpp index f379519..925c0c4 100644 --- a/include/boost/requests/impl/stream.hpp +++ b/include/boost/requests/impl/stream.hpp @@ -42,10 +42,15 @@ std::size_t stream::read_some(const MutableBuffer & buffer, system::error_code & parser_->get().body().data = itr->data(); parser_->get().body().size = itr->size(); + if (parser_->chunked()) + { + impl_->handle_chunked_.chunked_body = {}; + impl_->handle_chunked_.buffer_space = itr->size(); + } auto res = impl_->do_read_some_(*parser_, ec); - - + if (parser_->chunked()) + res = asio::buffer_copy(*itr, asio::buffer(impl_->handle_chunked_.chunked_body, impl_->handle_chunked_.chunked_body.size())); if (!parser_->is_done()) { parser_->get().body().more = true; @@ -84,7 +89,14 @@ std::size_t stream::read(DynamicBuffer & buffer, system::error_code & ec) std::size_t res = 0u; while (!ec && !parser_->is_done()) { - auto n = read_some(buffer.prepare(parser_->content_length_remaining().value_or(BOOST_REQUESTS_CHUNK_SIZE)), ec); + auto max_size = (std::min)(parser_->content_length_remaining().value_or(BOOST_REQUESTS_CHUNK_SIZE), + buffer.max_size() - buffer.size()); + if (buffer.max_size() - buffer.size() == 0u) + { + BOOST_REQUESTS_ASSIGN_EC(ec, beast::http::error::need_buffer); + return res; + } + auto n = read_some(buffer.prepare(max_size), ec); buffer.commit(n); res += n; } @@ -139,9 +151,12 @@ struct stream::async_read_op : asio::coroutine while (!ec && !this_->parser_->is_done()) { - BOOST_REQUESTS_YIELD this_->async_read_some( - buffer.prepare(this_->parser_->content_length_remaining().value_or(BOOST_REQUESTS_CHUNK_SIZE)), - std::move(self)); + BOOST_REQUESTS_YIELD + { + auto max_size = (std::min)(this_->parser_->content_length_remaining().value_or(BOOST_REQUESTS_CHUNK_SIZE), + buffer.max_size() - buffer.size()); + this_->async_read_some(buffer.prepare(max_size), std::move(self)); + } buffer.commit(n); res += n; } diff --git a/include/boost/requests/json.hpp b/include/boost/requests/json.hpp index c89b059..f01d069 100644 --- a/include/boost/requests/json.hpp +++ b/include/boost/requests/json.hpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -34,32 +34,12 @@ namespace boost namespace requests { -inline auto as_json(const response & res, - json::storage_ptr ptr, - system::error_code & ec) -> json::value -{ - json::parser ps; - ps.write(res.string_view(), ec); - if (ec) - return nullptr; - else - return ps.release(); -} - -inline auto as_json(const response & res, json::storage_ptr ptr = {}) -> json::value -{ - boost::system::error_code ec; - auto rs = as_json(res, ptr, ec); - if (ec) - urls::detail::throw_system_error(ec); - - return rs; -} - -inline auto as_json(const response & res, system::error_code & ec) -> json::value -{ - return as_json(res, json::storage_ptr(), ec); -} +BOOST_REQUESTS_DECL +auto as_json(const response & res, json::storage_ptr ptr, system::error_code & ec) -> json::value; +BOOST_REQUESTS_DECL +auto as_json(const response & res, json::storage_ptr ptr = {}) -> json::value; +BOOST_REQUESTS_DECL +auto as_json(const response & res, system::error_code & ec) -> json::value; namespace json { @@ -68,694 +48,163 @@ using empty = beast::http::empty_body::value_type; using ::boost::json::value; using ::boost::json::storage_ptr; -template struct response : response_base { - using allocator_type = std::allocator; - using fields_type = http::fields; + response(history_type history, json::value && value, bool empty_body = false) + : response_base(std::move(history)), value(std::move(value)), empty_body(empty_body) {} + response(http::response_header header, history_type history, json::value && value, bool empty_body = false) + : response_base(std::move(header), std::move(history)), value(std::move(value)), empty_body(empty_body) {} - response(history_type history, Value && value) : response_base(std::move(history)), value(std::move(value)) {} - response(http::response_header header, history_type history, Value && value) : response_base(std::move(header), std::move(history)), value(std::move(value)) {} + response(json::value && value = {}, bool empty_body = false) : value(std::move(value)) {} + response(http::response_header header, json::value && value = {}, bool empty_body = false) + : response_base(std::move(header)), value(std::move(value)), empty_body(empty_body) {} - response(Value && value = {}) : value(std::move(value)) {} - response(http::response_header header, Value && value = {}) : response_base(std::move(header)), value(std::move(value)) {} - - using value_type = Value; + using value_type = json::value; value_type value; - + // if the body was empty and not a literal `null`. + bool empty_body; }; -template -Value read_json(Stream & str, json::storage_ptr ptr = {}) +BOOST_REQUESTS_DECL +json::value read_json(stream & str, json::storage_ptr ptr = {}); +BOOST_REQUESTS_DECL +json::value read_json(stream & str, json::storage_ptr ptr, system::error_code & ec); + +inline void set_accept_headers(beast::http::fields & hd) { - ::boost::json::stream_parser sp{ptr}; - char buffer[BOOST_REQUESTS_CHUNK_SIZE]; - while (!sp.done() && !str.done()) - { - const auto n = str.read_some(asio::buffer(buffer)); - sp.write_some(buffer, n); - } - sp.finish(); - return ::boost::json::value_to(sp.release()); + if (hd.count(http::field::accept) == 0) + hd.set(http::field::accept, "application/json"); } -template -Value read_json(Stream & str, - json::storage_ptr ptr, - system::error_code & ec) +inline void set_accept_headers(requests::request_parameters & hd) { - ::boost::json::stream_parser sp{ptr}; - char buffer[BOOST_REQUESTS_CHUNK_SIZE]; - while (!sp.done() && !ec && !str.done()) + set_accept_headers(hd.headers); +} + +template +struct bind_request +{ + template + auto operator()(Connection & conn, + urls::url_view target, + RequestBody && request_body, + detail::request_type req = {}, + json::storage_ptr ptr = {}) const -> response { - const auto n = str.read_some(asio::buffer(buffer), ec); - if (ec) - break; - sp.write_some(buffer, n, ec); + set_accept_headers(req); + auto s = request_stream(conn, Method, target, std::forward(request_body), std::move(req)); + return { std::move(s.first).headers(), std::move(s.second), read_json(s.first, ptr) }; } - if (!ec) - sp.finish(ec); - if (ec) - return Value(); - auto res = ::boost::json::try_value_to(sp.release()); - if (res.has_error()) + template + auto operator()(Connection & conn, + urls::url_view target, + RequestBody && request_body, + detail::request_type req, + json::storage_ptr ptr, + system::error_code & ec) const -> response { - ec = res.error(); - return Value(); + set_accept_headers(req); + auto s = request_stream(conn, Method, target, std::forward(request_body), std::move(req), ec); + return { std::move(s.first).headers(), std::move(s.second), read_json(s.first, ptr, ec) }; } - else - return res.value(); -} -template -optional read_optional_json(Stream & str, json::storage_ptr ptr = {}) -{ - ::boost::json::stream_parser sp{ptr}; - char buffer[BOOST_REQUESTS_CHUNK_SIZE]; - auto n = str.read_some(asio::buffer(buffer)); - if (n == 0u && str.done()) - return boost::none; + template + auto operator()(urls::url_view target, + RequestBody && request_body, + http::headers req = {}, + json::storage_ptr ptr = {}) const -> response + { + set_accept_headers(req); + auto s = request_stream(default_session(), Method, target, std::forward(request_body), std::move(req)); + return { std::move(s.first).headers(), std::move(s.second), read_json(s.first, ptr) }; + } - sp.write_some(buffer, n); - while (!sp.done() && !str.done()) + template + auto operator()(urls::url_view target, + RequestBody && request_body, + http::headers req, + json::storage_ptr ptr, + system::error_code & ec) const -> response { - n = str.read_some(asio::buffer(buffer)); - sp.write_some(buffer, n); + set_accept_headers(req); + auto s = request_stream(default_session(), Method, target, std::forward(request_body), std::move(req), ec); + return { std::move(s.first).headers(), std::move(s.second), read_json(s.first, ptr, ec) }; } - sp.finish(); - if (std::is_same::value) - return sp.release(); +}; - return ::boost::json::value_to(sp.release()); -} -template -optional read_optional_json( - Stream & str, - json::storage_ptr ptr, - system::error_code & ec) +template +struct bind_empty_request { - ::boost::json::stream_parser sp{ptr}; - char buffer[BOOST_REQUESTS_CHUNK_SIZE]; - - auto n = str.read_some(asio::buffer(buffer)); - if (n == 0u && str.done()) - return boost::none; - sp.write_some(buffer, n, ec); - - while (!sp.done() && !ec && !str.done()) + template + auto operator()(Connection & conn, + urls::url_view target, + detail::request_type req = {}, + json::storage_ptr ptr = {}) const -> response { - n = str.read_some(asio::buffer(buffer), ec); - if (ec) - break; - sp.write_some(buffer, n, ec); + set_accept_headers(req); + auto s = request_stream(conn, Method, target, empty{}, std::move(req)); + auto j = read_json(s.first, ptr); + return { std::move(s.first).headers(), std::move(s.second), std::move(j)}; } - if (!ec) - sp.finish(ec); - if (ec) - return Value(); - - if (std::is_same::value) - return sp.release(); - auto res = ::boost::json::try_value_to(sp.release()); - if (res.has_error()) + template + auto operator()(Connection & conn, + urls::url_view target, + detail::request_type req, + json::storage_ptr ptr, + system::error_code & ec) const -> response { - ec = res.error(); - return Value(); + set_accept_headers(req); + auto s = request_stream(conn, Method, target, empty{}, std::move(req), ec); + return { std::move(s.first).headers(), std::move(s.second), read_json(s.first, ptr, ec) }; } - else - return res.value(); -} -inline void set_accept_headers(http::fields & hd) -{ - if (hd.count(http::field::accept) == 0) - hd.set(http::field::accept, "application/json"); -} -inline void set_accept_headers(requests::request_parameters & hd) -{ - set_accept_headers(hd.fields); -} - -template -auto get(Connection & conn, - urls::url_view target, - typename Connection::request_type req = {}, - json::storage_ptr ptr = {}) -> response -{ - set_accept_headers(req); - // this might be a bet idea - auto s = conn.ropen(http::verb::get, target, empty{}, std::move(req)); - return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr) }; -} - - -template -auto get(Connection & conn, - urls::url_view target, - typename Connection::request_type req, - json::storage_ptr ptr, - system::error_code & ec) -> response -{ - set_accept_headers(req); - auto s = conn.ropen(http::verb::get, target, empty{}, std::move(req), ec); - - return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr, ec) }; -} - - -using requests::head; -using requests::trace; - -template -auto post(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req = {}, - json::storage_ptr ptr = {}) -> response -{ - set_accept_headers(req); - auto s = conn.ropen(http::verb::post, target, - ::boost::json::value_from(std::forward(request_body), ptr), - std::move(req)); - return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr) }; -} - -template -auto post(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req, - json::storage_ptr ptr, - system::error_code & ec) -> response -{ - set_accept_headers(req); - auto s = conn.ropen(http::verb::post, target, - ::boost::json::value_from(std::forward(request_body), ptr), - std::move(req), ec); - return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr, ec) }; -} - -template -auto patch(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req, - system::error_code & ec) -> response -{ - set_accept_headers(req); - json::storage_ptr ptr{req.get_allocator().resource()}; - auto s = conn.ropen(http::verb::patch, target, - ::boost::json::value_from(std::forward(request_body), ptr, ec), - std::move(req)); - return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr, ec) }; -} - - -template -auto patch(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req = {}, - json::storage_ptr ptr = {}) -> response -{ - set_accept_headers(req); - auto s = conn.ropen(http::verb::patch, target, - ::boost::json::value_from(std::forward(request_body), ptr), - std::move(req)); - return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr) }; -} - - -template -auto put(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req = {}, - json::storage_ptr ptr = {}) -> response> -{ - set_accept_headers(req); - auto s = conn.ropen(http::verb::put, target, - ::boost::json::value_from(std::forward(request_body), ptr), - std::move(req)); - return { std::move(s).headers(), std::move(s).history(), read_optional_json(s, ptr) }; -} - -template -auto put(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req, - json::storage_ptr ptr, - system::error_code & ec) -> response> -{ - set_accept_headers(req); - auto s = conn.ropen(http::verb::put, target, - ::boost::json::value_from(std::forward(request_body), ptr, ec), - std::move(req)); - return { std::move(s).headers(), std::move(s).history(), read_optional_json(s, ptr, ec) }; -} - - -template -auto delete_(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req = {}, - json::storage_ptr ptr = {}) -> response> -{ - set_accept_headers(req); - auto s = conn.ropen(http::verb::delete_, target, - ::boost::json::value_from(std::forward(request_body), ptr), - std::move(req)); - return { std::move(s).headers(), std::move(s).history(), read_optional_json(s, ptr) }; -} - -template -auto delete_(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req, - json::storage_ptr ptr, - system::error_code & ec) -> response> -{ - set_accept_headers(req); - auto s = conn.ropen(http::verb::delete_, target, - ::boost::json::value_from(std::forward(request_body), ptr, ec), - std::move(req)); - return { std::move(s).headers(), std::move(s).history(), read_optional_json(s, ptr, ec) }; -} - - - -template -auto delete_(Connection & conn, - urls::url_view target, - typename Connection::request_type req = {}, - json::storage_ptr ptr = {}) -> response> -{ - set_accept_headers(req); - auto s = conn.ropen(http::verb::delete_, target, - empty{}, - std::move(req)); - return { std::move(s).headers(), std::move(s).history(), read_optional_json(s, ptr) }; -} - -template -auto delete_(Connection & conn, - urls::url_view target, - typename Connection::request_type req, - json::storage_ptr ptr, - system::error_code & ec) -> response> -{ - set_accept_headers(req); - auto s = conn.ropen(http::verb::delete_, target, - empty{}, - std::move(req), ec); - return { std::move(s).headers(), std::move(s).history(), read_optional_json(s, ptr, ec) }; -} - - - -template -auto options(Connection & conn, - urls::url_view target, - typename Connection::request_type req = {}, - json::storage_ptr ptr = {}) -> response -{ - set_accept_headers(req); - auto s = conn.ropen(http::verb::options, target, empty{}, std::move(req)); - return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr) }; -} - - -template -auto options(Connection & conn, - urls::url_view target, - typename Connection::request_type req, - json::storage_ptr ptr, - system::error_code & ec) -> response -{ - set_accept_headers(req); - auto s = conn.ropen(http::verb::options, target, empty{}, std::move(req), ec); - return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr, ec) }; -} - - -template -auto get(urls::url_view target, - http::fields req = {}, - json::storage_ptr ptr = {}) -> response -{ - set_accept_headers(req); - // this might be a bet idea - auto s = default_session().ropen(http::verb::get, target, empty{}, std::move(req)); - return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr) }; -} - - -template -auto get(urls::url_view target, - http::fields req, - json::storage_ptr ptr, - system::error_code & ec) -> response -{ - set_accept_headers(req); - auto s = default_session().ropen(http::verb::get, target, empty{}, std::move(req), ec); - - return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr, ec) }; -} - - -using requests::head; -using requests::trace; - -template -auto post(urls::url_view target, - RequestBody && request_body, - http::fields req = {}, - json::storage_ptr ptr = {}) -> response -{ - set_accept_headers(req); - auto s = default_session().ropen(http::verb::post, target, - ::boost::json::value_from(std::forward(request_body), ptr), - std::move(req)); - return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr) }; -} - -template -auto post(urls::url_view target, - RequestBody && request_body, - http::fields req, - json::storage_ptr ptr, - system::error_code & ec) -> response -{ - set_accept_headers(req); - auto s = default_session().ropen(http::verb::post, target, - ::boost::json::value_from(std::forward(request_body), ptr), - std::move(req), ec); - return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr, ec) }; -} - -template -auto patch(urls::url_view target, - RequestBody && request_body, - http::fields req, - json::storage_ptr ptr, - system::error_code & ec) -> response -{ - set_accept_headers(req); - auto s = default_session().ropen(http::verb::patch, target, - ::boost::json::value_from(std::forward(request_body), ptr), - std::move(req), ec); - return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr, ec) }; -} - - -template -auto patch(urls::url_view target, - RequestBody && request_body, - http::fields req = {}, - json::storage_ptr ptr = {}) -> response -{ - set_accept_headers(req); - auto s = default_session().ropen(http::verb::patch, target, - ::boost::json::value_from(std::forward(request_body), ptr), - std::move(req)); - return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr) }; -} - - -template -auto put(urls::url_view target, - RequestBody && request_body, - http::fields req = {}, - json::storage_ptr ptr = {}) -> response> -{ - set_accept_headers(req); - auto s = default_session().ropen(http::verb::put, target, - ::boost::json::value_from(std::forward(request_body), ptr), - std::move(req)); - return { std::move(s).headers(), std::move(s).history(), read_optional_json(s, ptr) }; -} - -template -auto put(urls::url_view target, - RequestBody && request_body, - http::fields req, - json::storage_ptr ptr, - system::error_code & ec) -> response> -{ - set_accept_headers(req); - auto s = default_session().ropen(http::verb::put, target, - ::boost::json::value_from(std::forward(request_body), ptr), - std::move(req)); - return { std::move(s).headers(), std::move(s).history(), read_optional_json(s, ptr, ec) }; -} - - -template -auto delete_(urls::url_view target, - RequestBody && request_body, - http::fields req = {}, - json::storage_ptr ptr = {}) -> response> -{ - set_accept_headers(req); - auto s = default_session().ropen(http::verb::delete_, target, - ::boost::json::value_from(std::forward(request_body), ptr), - std::move(req)); - return { std::move(s).headers(), std::move(s).history(), read_optional_json(s, ptr) }; -} - -template -auto delete_(urls::url_view target, - RequestBody && request_body, - http::fields req, - json::storage_ptr ptr, - system::error_code & ec) -> response> -{ - set_accept_headers(req); - auto s = default_session().ropen(http::verb::delete_, target, - ::boost::json::value_from(std::forward(request_body), ptr), - std::move(req), ec); - return { std::move(s).headers(), std::move(s).history(), read_optional_json(s, ptr, ec) }; -} - - - -template -auto delete_(urls::url_view target, - http::fields req = {}, - json::storage_ptr ptr = {}) -> response> -{ - set_accept_headers(req); - auto s = default_session().ropen(http::verb::delete_, target, - empty{}, - std::move(req)); - return { std::move(s).headers(), std::move(s).history(), read_optional_json(s, ptr) }; -} - -template -auto delete_(urls::url_view target, - http::fields req, - json::storage_ptr ptr, - system::error_code & ec) -> response> -{ - set_accept_headers(req); - auto s = default_session().ropen(http::verb::delete_, target, - empty{}, - std::move(req), ec); - return { std::move(s).headers(), std::move(s).history(), read_optional_json(s, ptr, ec) }; -} - - - -template -auto options(urls::url_view target, - http::fields req = {}, - json::storage_ptr ptr = {}) -> response -{ - set_accept_headers(req); - auto s = default_session().ropen(http::verb::options, target, empty{}, std::move(req)); - return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr) }; -} - - -template -auto options(urls::url_view target, - http::fields req, - json::storage_ptr ptr, - system::error_code & ec) -> response -{ - set_accept_headers(req); - auto s = default_session().ropen(http::verb::options, target, empty{}, std::move(req), ec); - return { std::move(s).headers(), std::move(s).history(), read_json(s, ptr, ec) }; -} - -namespace detail -{ - -template -struct async_read_json_op : asio::coroutine -{ - constexpr static const char * op_name = "async_read_json_op"; - using executor_type = typename Stream::executor_type; - executor_type get_executor() {return str.get_executor(); } - - Stream &str; - - struct state_t + auto operator()(urls::url_view target, + http::headers req = {}, + json::storage_ptr ptr = {}) const -> response { - state_t(json::storage_ptr ptr) : sp(std::move(ptr)) {} - ::boost::json::stream_parser sp; - char buffer[BOOST_REQUESTS_CHUNK_SIZE]; - }; - - using allocator_type = asio::any_completion_handler_allocator; - std::unique_ptr> state; - - async_read_json_op(allocator_type alloc, Stream * str, json::storage_ptr ptr) - : str(*str), state{allocate_unique(alloc, std::move(ptr))} {} + set_accept_headers(req); + auto s = request_stream(default_session(), Method, target, empty{}, std::move(req)); + return { std::move(s.first).headers(), std::move(s.second), read_json(s.first, ptr) }; + } - template - void operator()(Self && self, system::error_code ec = {}, std::size_t n = 0u) + auto operator()(urls::url_view target, + http::headers req, + json::storage_ptr ptr, + system::error_code & ec) const -> response { - BOOST_ASIO_CORO_REENTER(this) - { - while (!state->sp.done() && !str.done() && !ec) - { - BOOST_REQUESTS_YIELD str.async_read_some(asio::buffer(state->buffer), std::move(self)); - if (ec) - return self.complete(ec, nullptr); - state->sp.write_some(state->buffer, n, ec); - } - if (!ec) - state->sp.finish(ec); - if (ec) - return self.complete(ec, nullptr); - - auto r = state->sp.release(); - state.reset(); - return self.complete(ec, std::move(r)); - } + set_accept_headers(req); + auto s = request_stream(default_session(), Method, target, empty{}, std::move(req), ec); + return { std::move(s.first).headers(), std::move(s.second), read_json(s.first, ptr, ec) }; } }; -template -inline void async_read_json_impl( - asio::any_completion_handler handler, - Stream * str, json::storage_ptr ptr) -{ - return asio::async_compose, - void(error_code, json::value)>( - async_read_json_op{ - asio::get_associated_allocator(handler), - str, std::move(ptr)}, - handler, - str->get_executor()); -} - - - -template -struct async_read_optional_json_op : asio::coroutine +template +struct bind_optional_request : bind_request, bind_empty_request { - constexpr static const char * op_name = "async_read_optional_json_op"; - - using executor_type = typename Stream::executor_type; - executor_type get_executor() {return str.get_executor(); } - - Stream & str; - ::boost::json::stream_parser sp; - using allocator_type = asio::any_completion_handler_allocator)>; - std::vector buffer; + using bind_request::operator(); + using bind_empty_request::operator(); +}; - async_read_optional_json_op(allocator_type alloc, Stream * str, json::storage_ptr ptr) - : str(*str), sp{ptr}, buffer(BOOST_REQUESTS_CHUNK_SIZE, std::move(alloc)){} - template - void operator()(Self && self, system::error_code ec = {}, std::size_t n = 0u) - { - BOOST_ASIO_CORO_REENTER(this) - { - BOOST_REQUESTS_YIELD str.async_read_some(asio::buffer(buffer), std::move(self)); - if (ec || (n == 0 && str.done())) - return self.complete(ec, boost::none); - sp.write_some(buffer.data(), n, ec); - if (ec) - return self.complete(ec, boost::none); - - while (!sp.done() && !str.done()) - { - BOOST_REQUESTS_YIELD str.async_read_some(asio::buffer(buffer), std::move(self)); - if (ec) - return self.complete(ec, boost::none); - sp.write_some(buffer.data(), n, ec); - } - if (!ec) - sp.finish(ec); - if (ec) - return self.complete(ec, boost::none); - return self.complete(ec, sp.release()); - } - } -}; +constexpr bind_empty_request< http::verb::get> get; +constexpr bind_request< http::verb::post> post; +constexpr bind_optional_request patch; +constexpr bind_request< http::verb::put> put; +constexpr bind_optional_request delete_; -template -inline void async_read_optional_json_impl( - asio::any_completion_handler)> handler, - Stream * str, json::storage_ptr ptr) +namespace detail { - return asio::async_compose)>, - void(error_code, optional)>( - async_read_optional_json_op{ - asio::get_associated_allocator(handler), - str, std::move(ptr)}, - handler, - str->get_executor()); -} + +BOOST_REQUESTS_DECL +void async_read_json_impl( + asio::any_completion_handler handler, + stream * str, json::storage_ptr ptr); } @@ -768,23 +217,7 @@ async_read_json(Stream & str, CompletionToken && completion_token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN( typename Stream::executor_type)) { return asio::async_initiate( - &detail::async_read_json_impl, - completion_token, - &str, std::move(ptr)); -} - - -template)) CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(typename Stream::executor_type)> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::Stream::error_code, optional)) -async_read_optional_json(Stream & str, - json::storage_ptr ptr = {}, - CompletionToken && completion_token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN( typename Stream::executor_type)) -{ - - return asio::async_initiate)>( - &detail::async_read_optional_json_impl, + &detail::async_read_json_impl, completion_token, &str, std::move(ptr)); } @@ -795,7 +228,7 @@ using requests::async_trace; namespace detail { -template +template struct async_request_json_op : asio::coroutine { constexpr static const char * op_name = "async_request_json_op"; @@ -810,20 +243,20 @@ struct async_request_json_op : asio::coroutine struct state_t { state_t(RequestBody && request_body, - typename Connection::request_type req, + requests::detail::request_type req, json::storage_ptr ptr) : request_body(std::forward(request_body)), req(std::move(req)), ptr(std::move(ptr)) {} RequestBody && request_body; - typename Connection::request_type req; + requests::detail::request_type req; json::storage_ptr ptr; optional str_; - response rb; + response rb; }; - using allocator_type = asio::any_completion_handler_allocator)>; + using allocator_type = asio::any_completion_handler_allocator; std::unique_ptr> state; @@ -843,7 +276,7 @@ struct async_request_json_op : asio::coroutine http::verb method, urls::url_view target, RequestBody && request_body, - typename Connection::request_type req, + requests::detail::request_type req, json::storage_ptr ptr) : conn(*conn), method(method), target(target), state(allocate_unique(alloc, std::forward(request_body), std::move(req), std::move(ptr))) {} @@ -851,11 +284,12 @@ struct async_request_json_op : asio::coroutine template void operator()(Self && self, system::error_code ec = {}, - variant2::variant s = variant2::monostate{}) + variant2::variant s = variant2::monostate{}, + history hist = {}) { BOOST_ASIO_CORO_REENTER(this) { - BOOST_REQUESTS_YIELD conn.async_ropen(method, target, + BOOST_REQUESTS_YIELD async_request_stream(conn, method, target, value_from(std::forward(state->request_body), std::is_same>{}), std::move(state->req), std::move(self)); @@ -863,23 +297,19 @@ struct async_request_json_op : asio::coroutine { state->str_.emplace(std::move(variant2::get<1>(s))); state->rb.headers = std::move(*state->str_).headers(); - state->rb.history = std::move(*state->str_).history(); + state->rb.history = std::move(hist); BOOST_REQUESTS_YIELD async_read_json(*state->str_, state->ptr, std::move(self)); + if (ec) break; } else { state->rb.headers = std::move(std::move(variant2::get<1>(s))).headers(); - state->rb.history = std::move(std::move(variant2::get<1>(s))).history(); + state->rb.history = std::move(hist); break; } - - auto v = ::boost::json::try_value_to(variant2::get<2>(s)); - if (v) - state->rb.value = std::move(*v); - else - ec = v.error(); + state->rb.value = variant2::get<2>(s); } if (is_complete()) @@ -887,121 +317,19 @@ struct async_request_json_op : asio::coroutine } }; - -template -struct async_request_optional_json_op : asio::coroutine -{ - constexpr static const char * op_name = "async_request_optional_json_op"; - - using executor_type = typename Connection::executor_type; - executor_type get_executor() {return conn.get_executor(); } - - Connection & conn; - http::verb method; - urls::url_view target; - struct state_t - { - state_t(RequestBody && request_body, - typename Connection::request_type req, - json::storage_ptr ptr) - : request_body(std::forward(request_body)), req(std::move(req)), ptr(std::move(ptr)) {} - - RequestBody && request_body; - typename Connection::request_type req; - json::storage_ptr ptr; - optional str_; - - response> rb{}; - }; - - using allocator_type = asio::any_completion_handler_allocator>)>; - std::unique_ptr> state; - - - - template - async_request_optional_json_op(allocator_type alloc, - Connection * conn, - http::verb method, - urls::url_view target, - RequestBody_ && request_body, - typename Connection::request_type req, - json::storage_ptr ptr) - : conn(*conn), method(method), target(target), - state(allocate_unique(alloc, std::forward(request_body), std::move(req), std::move(ptr))) {} - - template - void operator()(Self && self, - system::error_code ec = {}, - variant2::variant> s = variant2::monostate{}) - { - BOOST_ASIO_CORO_REENTER(this) - { - BOOST_REQUESTS_YIELD conn.async_ropen(method, target, - ::boost::json::value_from(std::forward(state->request_body), state->ptr), - std::move(state->req), std::move(self)); - if (!ec) - { - state->str_.emplace(std::move(variant2::get<1>(s))); - state->rb.headers = std::move(*state->str_).headers(); - state->rb.history = std::move(*state->str_).history(); - BOOST_REQUESTS_YIELD async_read_json(*state->str_, state->ptr, std::move(self)); - } - else - { - state->rb.headers = std::move(std::move(variant2::get<1>(s))).headers(); - state->rb.history = std::move(std::move(variant2::get<1>(s))).history(); - break; - } - - - if (variant2::get<2>(s)) - { - auto v = ::boost::json::try_value_to(*variant2::get<2>(s)); - if (v) - state->rb.value = std::move(*v); - else - ec = v.error(); - } - } - if (is_complete()) - self.complete(ec, std::move(state->rb)); - } -}; - -template +template inline void async_request_json_impl( - asio::any_completion_handler)> handler, - Connection * conn, - http::verb method, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req, - json::storage_ptr ptr) -{ - return asio::async_compose)>, - void(error_code, response)>( - async_request_json_op{ - asio::get_associated_allocator(handler), - conn, method, target, std::forward(request_body), std::move(req), - std::move(ptr)}, - handler, - conn->get_executor()); -} - -template -inline void async_request_optional_json_impl( - asio::any_completion_handler>)> handler, + asio::any_completion_handler handler, Connection * conn, http::verb method, urls::url_view target, RequestBody && request_body, - typename Connection::request_type req, + requests::detail::request_type req, json::storage_ptr ptr) { - return asio::async_compose>)>, - void(error_code, response>)>( - async_request_optional_json_op{ + return asio::async_compose, + void(error_code, response)>( + async_request_json_op{ asio::get_associated_allocator(handler), conn, method, target, std::forward(request_body), std::move(req), std::move(ptr)}, @@ -1009,139 +337,6 @@ inline void async_request_optional_json_impl( conn->get_executor()); } -} - -template)) CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(typename Connection::executor_type)> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) -async_get(Connection & conn, - urls::url_view target, - typename Connection::request_type req = {}, - json::storage_ptr ptr = {}, - CompletionToken && completion_token = typename Connection::default_token()) -{ - return asio::async_initiate)>( - &detail::async_request_json_impl, completion_token, - &conn, http::verb::get, target, empty{}, std::move(req), std::move(ptr)); -} - -template)) CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(typename Connection::executor_type)> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) -async_post(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req = {}, - json::storage_ptr ptr = {}, - CompletionToken && completion_token = typename Connection::default_token()) -{ - return asio::async_initiate)>( - &detail::async_request_json_impl, completion_token, - &conn, http::verb::post, target, std::forward(request_body), - std::move(req), std::move(ptr)); -} - - -template)) CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(typename Connection::executor_type)> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) -async_patch(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req = {}, - json::storage_ptr ptr = {}, - CompletionToken && completion_token = typename Connection::default_token()) -{ - return asio::async_initiate)>( - &detail::async_request_json_impl, completion_token, - &conn, http::verb::patch, target, std::forward(request_body), std::move(req), std::move(ptr)); -} - -template>)) CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(typename Connection::executor_type)> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) -async_put(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req = {}, - json::storage_ptr ptr = {}, - CompletionToken && completion_token = typename Connection::default_token()) -{ - return asio::async_initiate>)>( - &detail::async_request_optional_json_impl, completion_token, - &conn, http::verb::put, target, std::forward(request_body), std::move(req), std::move(ptr)); -} - - -template)) CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(typename Connection::executor_type)> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) -async_delete(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req = {}, - json::storage_ptr ptr = {}, - CompletionToken && completion_token = typename Connection::default_token()) -{ - return asio::async_initiate)>( - &detail::async_request_json_impl, completion_token, - &conn, http::verb::delete_, target, std::forward(request_body), std::move(req), std::move(ptr)); -} - - -template)) CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(typename Connection::executor_type)> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) -async_delete(Connection & conn, - urls::url_view target, - typename Connection::request_type req = {}, - json::storage_ptr ptr = {}, - CompletionToken && completion_token = typename Connection::default_token()) -{ - return asio::async_initiate)>( - &detail::async_request_json_impl, - completion_token, &conn, http::verb::delete_, target, empty{}, - std::move(req), std::move(ptr)); -} - - -template)) CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(typename Connection::executor_type)> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) -async_options(Connection & conn, - urls::url_view target, - typename Connection::request_type req = {}, - json::storage_ptr ptr = {}, - CompletionToken && completion_token = typename Connection::default_token()) -{ - return asio::async_initiate)>( - &detail::async_request_json_impl, completion_token, - &conn, http::verb::options, target, empty{}, std::move(req), std::move(ptr)); -} - -namespace detail -{ - -template struct async_free_request_op { template req) { auto & sess = default_session(asio::get_associated_executor(handler)); detail::async_read_json_impl( - std::forward(handler), - &sess, method, target, - std::forward(request_body), std::move(req)); + std::forward(handler), + &sess, method, target, + std::forward(request_body), std::move(req)); } }; -} -template)) CompletionToken> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) -async_get(urls::url_view target, - http::fields req, - json::storage_ptr ptr, - CompletionToken && completion_token) -{ - return asio::async_initiate( - detail::async_free_request_op{}, completion_token, - http::verb::get, target, empty{}, std::move(req), std::move(ptr)); } -template)) CompletionToken> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) -async_post(urls::url_view target, - RequestBody && request_body, - http::fields req, - json::storage_ptr ptr, - CompletionToken && completion_token) +template +struct bind_async_request { - return asio::async_initiate( - detail::async_free_request_op{}, completion_token, - http::verb::post, target, std::forward(request_body), std::move(req), std::move(ptr)); -} - + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + void (boost::system::error_code, response)) + operator()(Connection & conn, + requests::detail::target_view target, + RequestBody && request_body, + requests::detail::request_type req = {}, + json::storage_ptr ptr = {}, + CompletionToken && completion_token = typename Connection::default_token()) const + { + return asio::async_initiate( + &detail::async_request_json_impl, + completion_token, &conn, Method, target, std::forward(request_body), + std::move(req), std::move(ptr)); + } -template)) CompletionToken> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) -async_patch(urls::url_view target, - RequestBody && request_body, - http::fields req, - json::storage_ptr ptr, - CompletionToken && completion_token) -{ - return asio::async_initiate)>( - detail::async_free_request_op{}, completion_token, - http::verb::patch, target, std::forward(request_body), std::move(req), std::move(ptr)); -} + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + void (boost::system::error_code, response)) + operator()(urls::url_view target, + RequestBody && request_body, + http::headers req, + json::storage_ptr ptr, + CompletionToken && completion_token) const + { + return asio::async_initiate( + detail::async_free_request_op{}, completion_token, + Method, target, std::forward(request_body), std::move(req), std::move(ptr)); + } -template)) CompletionToken> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) -async_put(urls::url_view target, - RequestBody && request_body, - http::fields req, - json::storage_ptr ptr, - CompletionToken && completion_token) -{ - return asio::async_initiate)>( - detail::async_free_request_op{}, completion_token, - http::verb::put, target, std::forward(request_body), std::move(req), std::move(ptr)); -} +}; -template)) CompletionToken> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) -async_delete(urls::url_view target, - RequestBody && request_body, - http::fields req, - json::storage_ptr ptr, - CompletionToken && completion_token) +template +struct bind_empty_async_request { - return asio::async_initiate)>( - detail::async_free_request_op{}, completion_token, - http::verb::delete_, target, std::forward(request_body), std::move(req), std::move(ptr)); -} + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) + operator()(Connection & conn, + requests::detail::target_view target, + requests::detail::request_type req = {}, + json::storage_ptr ptr = {}, + CompletionToken && completion_token = typename Connection::default_token()) const + { + return asio::async_initiate( + &detail::async_request_json_impl, + completion_token, &conn, Method, target, empty{}, + std::move(req), std::move(ptr)); + } -template)) CompletionToken> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) -async_delete(urls::url_view target, - http::fields req, + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + void (boost::system::error_code, response)) + operator()(urls::url_view target, + http::headers req, json::storage_ptr ptr, - CompletionToken && completion_token) -{ - return asio::async_initiate)>( - detail::async_free_request_op{}, completion_token, - http::verb::delete_, target, empty{}, std::move(req), std::move(ptr)); -} + CompletionToken && completion_token) const + { + return asio::async_compose( -template)) CompletionToken> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) -async_options(urls::url_view target, - http::fields req, - json::storage_ptr ptr, - CompletionToken && completion_token) -{ - return asio::async_initiate)>( - detail::async_free_request_op{}, completion_token, - http::verb::options, target, empty{}, std::move(req), std::move(ptr)); -} + detail::async_free_request_op{}, completion_token, + Method, target, empty{}, std::move(req), std::move(ptr)); + } +}; +template +struct bind_optional_async_request : bind_async_request, bind_empty_async_request +{ + using bind_async_request::operator(); + using bind_empty_async_request::operator(); +}; +constexpr bind_empty_async_request< http::verb::get> async_get; +constexpr bind_async_request< http::verb::post> async_post; +constexpr bind_optional_async_request async_patch; +constexpr bind_async_request< http::verb::put> async_put; +constexpr bind_optional_async_request async_delete; } } diff --git a/include/boost/requests/method.hpp b/include/boost/requests/method.hpp index 26949b7..762fd92 100644 --- a/include/boost/requests/method.hpp +++ b/include/boost/requests/method.hpp @@ -17,744 +17,26 @@ namespace boost namespace requests { -using empty = beast::http::empty_body::value_type; - -template -auto get(Connection & conn, - urls::url_view target, - typename Connection::request_type req = {}) -> response -{ - response res{}; - auto s = conn.ropen(http::verb::get, target, empty{}, std::move(req)); - s.read(res.buffer); - res.headers = std::move(s).headers(); - res.history = std::move(s).history(); - return res; -} - - -template -auto get(Connection & conn, - urls::url_view target, - typename Connection::request_type req, - system::error_code & ec) -> response -{ - response res{}; - auto s = conn.ropen(http::verb::get, target, empty{}, std::move(req), ec); - if (!ec) - s.read(res.buffer, ec); - res.headers = std::move(s).headers(); - res.history = std::move(s).history(); - return res; -} - - -template -auto head(Connection & conn, - urls::url_view target, - typename Connection::request_type req = {}) -> response_base -{ - auto s = conn.ropen(http::verb::head, target, empty{}, std::move(req)); - return {std::move(s).headers(), std::move(s).history()}; -} - -template -auto head(Connection & conn, - urls::url_view target, - typename Connection::request_type req, - system::error_code & ec) -> response_base -{ - auto s = conn.ropen(http::verb::head, target, empty{}, std::move(req), ec); - return {std::move(s).headers(), std::move(s).history()}; -} - -template -auto post(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req = {}) -> response -{ - response res{}; - auto s = conn.ropen(http::verb::post, target, std::forward(request_body), std::move(req)); - s.read(res.buffer); - res.headers = std::move(s).headers(); - res.history = std::move(s).history(); - return res; -} - -template -auto post(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req, - system::error_code & ec) -> response -{ - response res{}; - auto s = conn.ropen(http::verb::post, target, std::forward(request_body), std::move(req), ec); - if (!ec) - s.read(res.buffer, ec); - res.headers = std::move(s).headers(); - res.history = std::move(s).history(); - return res; -} - - -template -auto patch(Connection & conn, - urls::url_view target, - typename Connection::request_type req = {}) -> response -{ - response res{}; - auto s = conn.ropen(http::verb::patch, target, empty{}, std::move(req)); - s.read(res.buffer); - res.headers = std::move(s).headers(); - res.history = std::move(s).history(); - return res; -} - -template -auto patch(Connection & conn, - urls::url_view target, - typename Connection::request_type req, - system::error_code & ec) -> response -{ - response res{}; - auto s = conn.ropen(http::verb::patch, target, empty{}, std::move(req), ec); - if (!ec) - s.read(res.buffer, ec); - res.headers = std::move(s).headers(); - res.history = std::move(s).history(); - return res; -} - -template -auto patch(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req = {}) -> response -{ - response res{}; - auto s = conn.ropen(http::verb::patch, target, std::forward(request_body), std::move(req)); - s.read(res.buffer); - res.headers = std::move(s).headers(); - res.history = std::move(s).history(); - return res; -} - -template -auto patch(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req, - system::error_code & ec) -> response -{ - response res{}; - auto s = conn.ropen(http::verb::patch, target, std::forward(request_body), std::move(req), ec); - if (!ec) - s.read(res.buffer, ec); - res.headers = std::move(s).headers(); - res.history = std::move(s).history(); - return res; -} - - -template -auto put(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req = {}) -> response -{ - response res{}; - auto s = conn.ropen(http::verb::put, target, std::forward(request_body), std::move(req)); - s.read(res.buffer); - res.headers = std::move(s).headers(); - res.history = std::move(s).history(); - return res; -} - -template -auto put(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req, - system::error_code & ec) -> response -{ - response res{}; - auto s = conn.ropen(http::verb::put, target, std::forward(request_body), std::move(req), ec); - if (!ec) - s.read(res.buffer, ec); - res.headers = std::move(s).headers(); - res.history = std::move(s).history(); - return res; -} - -template -auto delete_(Connection & conn, - urls::url_view target, - typename Connection::request_type req = {}) -> response -{ - response res{}; - auto s = conn.ropen(http::verb::delete_, target, empty{}, std::move(req)); - s.read(res.buffer); - res.headers = std::move(s).headers(); - res.history = std::move(s).history(); - return res; -} - -template -auto delete_(Connection & conn, - urls::url_view target, - typename Connection::request_type req, - system::error_code & ec) -> response -{ - response res{}; - auto s = conn.ropen(http::verb::delete_, target, empty{}, std::move(req), ec); - if (!ec) - s.read(res.buffer, ec); - res.headers = std::move(s).headers(); - res.history = std::move(s).history(); - return res; -} - - -template -auto delete_(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req = {}) - -> typename std::enable_if_t, - typename Connection::request_type>::value, response> -{ - response res{}; - auto s = conn.ropen(http::verb::delete_, target, std::forward(request_body), std::move(req)); - s.read(res.buffer); - res.headers = std::move(s).headers(); - res.history = std::move(s).history(); - return res; -} - -template -auto delete_(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req, - system::error_code & ec) -> response -{ - response res{}; - auto s = conn.ropen(http::verb::delete_, target, std::forward(request_body), std::move(req), ec); - if (!ec) - s.read(res.buffer, ec); - res.headers = std::move(s).headers(); - res.history = std::move(s).history(); - return res; -} - - -inline auto connect(connection & conn, - urls::url_view target, - typename connection::request_type req = {}) -> response_base -{ - auto s = conn.ropen(http::verb::connect, target, empty{}, std::move(req)); - return {std::move(s).headers(), std::move(s).history()}; -} - -inline auto connect(connection & conn, - urls::url_view target, - typename connection::request_type req, - system::error_code & ec) -> response_base -{ - auto s = conn.ropen(http::verb::connect, target, empty{}, std::move(req), ec); - return {std::move(s).headers(), std::move(s).history()}; -} - - -template -auto options(Connection & conn, - urls::url_view target, - typename Connection::request_type req = {}) -> response -{ - response res{}; - auto s = conn.ropen(http::verb::options, target, empty{}, std::move(req)); - s.read(res.buffer); - res.headers = std::move(s).headers(); - res.history = std::move(s).history(); - return s; -} - -template -auto options(Connection & conn, - urls::url_view target, - typename Connection::request_type req, - system::error_code & ec) -> response -{ - response res{}; - auto s = conn.ropen(http::verb::options, target, empty{}, std::move(req), ec); - if (!ec) - s.read(res.buffer, ec); - res.headers = std::move(s).headers(); - res.history = std::move(s).history(); - return res; -} - - -template -auto trace(Connection & conn, - urls::url_view target, - typename Connection::request_type req = {}) -> response_base -{ - auto s = conn.ropen(http::verb::trace, target, empty{}, std::move(req)); - return {std::move(s).headers(), std::move(s).history()}; -} - -template -auto trace(Connection & conn, - urls::url_view target, - typename Connection::request_type req, - system::error_code & ec) -> response_base -{ - auto s = conn.ropen(http::verb::trace, target, empty{}, std::move(req)); - return {std::move(s).headers(), std::move(s).history()}; -} - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, response)) -async_get(Connection & conn, - urls::url_view target, - typename Connection::request_type req = {}, - CompletionToken && completion_token = typename Connection::default_token()) -{ - return async_request(conn, http::verb::get, target, empty{}, std::move(req), - std::forward(completion_token)); -} - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, response)) -async_head(Connection & conn, - urls::url_view target, - typename Connection::request_type req = {}, - CompletionToken && completion_token = typename Connection::default_token()) -{ - return async_request(conn, http::verb::head, target, - empty{}, std::move(req), - std::forward(completion_token)); -} - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, response)) -async_post(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req = {}, - CompletionToken && completion_token = typename Connection::default_token()) -{ - return async_request(conn, http::verb::post, target, - std::forward(request_body), std::move(req), - std::forward(completion_token)); -} - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, response)) -async_put(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req = {}, - CompletionToken && completion_token = typename Connection::default_token()) -{ - return async_request(conn, http::verb::put, target, - std::forward(request_body), std::move(req), - std::forward(completion_token)); -} - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, response)) -async_patch(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req = {}, - CompletionToken && completion_token = typename Connection::default_token()) -{ - return async_request(conn, http::verb::patch, target, - std::forward(request_body), std::move(req), - std::forward(completion_token)); -} - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, response)) -async_delete(Connection & conn, - urls::url_view target, - RequestBody && request_body, - typename Connection::request_type req = {}, - CompletionToken && completion_token = typename Connection::default_token()) -{ - return async_request(conn, http::verb::delete_, target, - std::forward(request_body), std::move(req), - std::forward(completion_token)); -} - - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, response)) -async_delete(Connection & conn, - urls::url_view target, - typename Connection::request_type req = {}, - CompletionToken && completion_token = typename Connection::default_token()) -{ - return async_request(conn, http::verb::delete_, target, - empty{}, std::move(req), - std::forward(completion_token)); -} - - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, response)) -async_connect(connection & conn, - urls::url_view target, - typename connection::request_type req, - CompletionToken && completion_token) -{ - return async_request(conn, http::verb::connect, target, - empty{}, std::move(req), - std::forward(completion_token)); -} - - -template -auto async_connect(connection::defaulted & conn, - urls::url_view target, - typename connection::request_type req = {}) -{ - return async_request(conn, http::verb::connect, target, - empty{}, std::move(req), CompletionToken()); -} - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, response)) -async_options(Connection & conn, - urls::url_view target, - typename Connection::request_type req = {}, - CompletionToken && completion_token = typename Connection::default_token()) -{ - return async_request(conn, http::verb::options, target, - empty{}, std::move(req), - std::forward(completion_token)); -} - - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, response)) -async_trace(Connection & conn, - urls::url_view target, - typename Connection::request_type req = {}, - CompletionToken && completion_token = typename Connection::default_token()) -{ - return async_request(conn, http::verb::trace, target, - empty{}, std::move(req), - std::forward(completion_token)); -} - - -// -------------------------------------- default session stuff -------------------------------------- - -inline auto get(urls::url_view target, http::fields req = {}) -> response -{ - return request(http::verb::get, target, empty{}, std::move(req)); -} - - -inline auto get(urls::url_view target, - http::fields req, - system::error_code & ec) -> response -{ - return request(http::verb::get, target, empty{}, std::move(req), ec); -} - - -inline auto head( - urls::url_view target, - http::fields req = {}) -> response -{ - return request(http::verb::head, target, empty{}, std::move(req)); -} - -inline auto head( - urls::url_view target, - http::fields req, - system::error_code & ec) -> response -{ - return request(http::verb::head, target, empty{}, std::move(req), ec); -} - -template -auto post( - urls::url_view target, - RequestBody && request_body, - http::fields req = {}) -> response -{ - return request(http::verb::post, target, std::forward(request_body), std::move(req)); -} - -template -auto post(urls::url_view target, - RequestBody && request_body, - http::fields req, - system::error_code & ec) -> response -{ - return request(http::verb::post, target, std::forward(request_body), std::move(req), ec); -} - - -template -auto put(urls::url_view target, - RequestBody && request_body, - http::fields req = {}) -> response -{ - return request(http::verb::put, target, std::forward(request_body), std::move(req)); -} - -template -auto put(urls::url_view target, - RequestBody && request_body, - http::fields req, - system::error_code & ec) -> response -{ - return request(http::verb::put, target, std::forward(request_body), std::move(req), ec); -} - - -template -auto patch(urls::url_view target, - RequestBody && request_body, - http::fields req = {}) -> response -{ - return request(http::verb::patch, target, std::forward(request_body), std::move(req)); -} - -template -auto patch(urls::url_view target, - RequestBody && request_body, - http::fields req, - system::error_code & ec) -> response -{ - return request(http::verb::patch, target, std::forward(request_body), std::move(req), ec); -} - -template -auto delete_(urls::url_view target, - RequestBody && request_body, - http::fields req = {}) -> response -{ - return request(http::verb::delete_, target, std::forward(request_body), std::move(req)); -} - -template -auto delete_(urls::url_view target, - RequestBody && request_body, - http::fields req, - system::error_code & ec) -> response -{ - return request(http::verb::delete_, target, std::forward(request_body), std::move(req), ec); -} - - -inline auto delete_(urls::url_view target, - http::fields req = {}) -> response -{ - return request(http::verb::delete_, target, empty{}, std::move(req)); -} - -inline auto delete_(urls::url_view target, - http::fields req, - system::error_code & ec) -> response -{ - return request(http::verb::delete_, target, empty{}, std::move(req), ec); -} - - -inline auto options(urls::url_view target, - http::fields req = {}) -> response -{ - return request(http::verb::options, target, empty{}, std::move(req)); -} - -inline auto options(urls::url_view target, - http::fields req, - system::error_code & ec) -> response -{ - return request(http::verb::options, target, empty{}, std::move(req), ec); -} - - -inline auto trace(urls::url_view target, - http::fields req = {}) -> response -{ - return request(http::verb::trace, target, empty{}, std::move(req)); -} - -inline auto trace(urls::url_view target, - http::fields req, - system::error_code & ec) -> response -{ - return request(http::verb::trace, target, empty{}, std::move(req), ec); -} - -template< BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code, response)) CompletionToken> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) -async_get(urls::url_view target, - http::fields req, - CompletionToken && completion_token) -{ - return async_request(http::verb::get, target, empty{}, std::move(req), - std::forward(completion_token)); -} - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) -async_head(urls::url_view target, - http::fields req, - CompletionToken && completion_token) -{ - return async_request(http::verb::head, target, - empty{}, std::move(req), - std::forward(completion_token)); -} - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, response)) -async_post(urls::url_view target, - RequestBody && request_body, - http::fields req, - CompletionToken && completion_token) -{ - return async_request(http::verb::post, target, - std::forward(request_body), std::move(req), - std::forward(completion_token)); -} - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, response)) -async_put(urls::url_view target, - RequestBody && request_body, - http::fields req, - CompletionToken && completion_token) -{ - return async_request(http::verb::put, target, - std::forward(request_body), std::move(req), - std::forward(completion_token)); -} - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, response)) -async_patch(urls::url_view target, - RequestBody && request_body, - http::fields req, - CompletionToken && completion_token) -{ - return async_request(http::verb::patch, target, - std::forward(request_body), std::move(req), - std::forward(completion_token)); -} - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, response)) -async_delete(urls::url_view target, - RequestBody && request_body, - http::fields req, - CompletionToken && completion_token) -{ - return async_request(http::verb::delete_, target, - std::forward(request_body), std::move(req), - std::forward(completion_token)); -} - - -template< BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code, response)) CompletionToken> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, response)) -async_delete(urls::url_view target, - http::fields req, - CompletionToken && completion_token) -{ - return async_request(http::verb::delete_, target, - empty{}, std::move(req), - std::forward(completion_token)); -} - - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, response)) -async_options(urls::url_view target, - http::fields req, - CompletionToken && completion_token) -{ - return async_request(http::verb::options, target, - empty{}, std::move(req), - std::forward(completion_token)); -} - - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, response)) -async_trace(urls::url_view target, - http::fields req, - CompletionToken && completion_token) -{ - return async_request(http::verb::trace, target, - empty{}, std::move(req), - std::forward(completion_token)); -} +constexpr bind_empty_request< http::verb::get> get; +constexpr bind_empty_request< http::verb::head> head; +constexpr bind_request< http::verb::post> post; +constexpr bind_optional_request patch; +constexpr bind_request< http::verb::put> put; +constexpr bind_optional_request delete_; +constexpr bind_empty_request< http::verb::connect> connect; +constexpr bind_request< http::verb::options> options; +constexpr bind_request< http::verb::trace> trace; + + +constexpr bind_empty_async_request< http::verb::get> async_get; +constexpr bind_empty_async_request< http::verb::head> async_head; +constexpr bind_async_request< http::verb::post> async_post; +constexpr bind_optional_async_request async_patch; +constexpr bind_async_request< http::verb::put> async_put; +constexpr bind_optional_async_request async_delete; +constexpr bind_empty_async_request< http::verb::connect> async_connect; +constexpr bind_async_request< http::verb::options> async_options; +constexpr bind_async_request< http::verb::trace> async_trace; } } diff --git a/include/boost/requests/redirect.hpp b/include/boost/requests/redirect.hpp index a30e63a..2a77b52 100644 --- a/include/boost/requests/redirect.hpp +++ b/include/boost/requests/redirect.hpp @@ -5,11 +5,13 @@ #ifndef BOOST_REQUESTS_REDIRECT_HPP #define BOOST_REQUESTS_REDIRECT_HPP +#include +#include +#include + #include #include #include -#include -#include #include #include @@ -45,10 +47,10 @@ BOOST_REQUESTS_DECL bool should_redirect( BOOST_REQUESTS_DECL std::uint16_t get_port(urls::url_view domain); /// Check if the endpoint is the same as the endpoint -BOOST_REQUESTS_DECL bool same_endpoint_on_host(const urls::url_view current, const asio::ip::tcp::endpoint); +BOOST_REQUESTS_DECL bool same_endpoint_on_host(const urls::url_view current, const asio::ip::tcp::endpoint &); /// Check if the endpoint is the same as the endpoint -BOOST_REQUESTS_DECL bool same_endpoint_on_host(const urls::url_view current, const asio::local::stream_protocol::endpoint); +BOOST_REQUESTS_DECL bool same_endpoint_on_host(const urls::url_view current, const asio::local::stream_protocol::endpoint &); /// Check if the endpoint is the same as the endpoint BOOST_REQUESTS_DECL bool same_endpoint_on_host(const urls::url_view current, const asio::generic::stream_protocol::endpoint &); @@ -56,10 +58,10 @@ BOOST_REQUESTS_DECL bool same_endpoint_on_host(const urls::url_view current, con /// Check if a status is a redirect inline bool is_redirect(http::status rc) { - return ((rc != http::status::moved_permanently) - && (rc != http::status::found) - && (rc != http::status::temporary_redirect) - && (rc != http::status::permanent_redirect)); + return ((rc == http::status::moved_permanently) + || (rc == http::status::found) + || (rc == http::status::temporary_redirect) + || (rc == http::status::permanent_redirect)); } } diff --git a/include/boost/requests/request.hpp b/include/boost/requests/request.hpp index 6656ef0..d78ace5 100644 --- a/include/boost/requests/request.hpp +++ b/include/boost/requests/request.hpp @@ -12,6 +12,8 @@ #include #include +#include + namespace boost { namespace requests @@ -19,25 +21,168 @@ namespace requests namespace detail { -template -struct async_request_op; +template +using target_view = + std::conditional_t< + std::is_base_of::value, + urls::url_view, urls::pct_string_view>; + +template +using request_type = + std::conditional_t< + std::is_base_of::value, + http::headers, request_parameters>; + +BOOST_REQUESTS_DECL +std::pair +request_stream_impl( + connection & conn, + http::verb method, + urls::pct_string_view path, + source_ptr source, + request_parameters req, + system::error_code & ec); + + +BOOST_REQUESTS_DECL +std::pair +request_stream_impl( + connection_pool & pool, + http::verb method, + urls::pct_string_view path, + source_ptr source, + request_parameters req, + system::error_code & ec); + +BOOST_REQUESTS_DECL +std::pair +request_stream_impl( + session & sess, + http::verb method, + urls::url_view path, + source_ptr source, + http::headers headers, + system::error_code & ec); + + + +BOOST_REQUESTS_DECL +void async_request_stream_impl( + connection & conn, + http::verb method, + urls::pct_string_view path, + source_ptr source, + request_parameters req, + asio::any_completion_handler handler); + +BOOST_REQUESTS_DECL +void async_request_stream_impl( + connection_pool & pool, + http::verb method, + urls::pct_string_view path, + source_ptr source, + request_parameters req, + asio::any_completion_handler handler); + +BOOST_REQUESTS_DECL +void async_request_stream_impl( + session & sess, + http::verb method, + urls::url_view path, + source_ptr source, + http::headers headers, + asio::any_completion_handler handler); } + +// connection +template +auto request_stream( + Connection & conn, + http::verb method, + detail::target_view path, // pct_string_view when connection + RequestBody && body, + detail::request_type req, // headers for conn, + system::error_code & ec) -> std::pair +{ + auto src = make_source(std::forward(body)); + return detail::request_stream_impl(conn, method, path, std::move(src), std::move(req), ec); +} + + +template +auto request_stream( + Connection & conn, + http::verb method, + detail::target_view path, // pct_string_view when connection + RequestBody && body, + detail::request_type req /* headers for conn*/ ) -> std::pair +{ + boost::system::error_code ec; + auto res = request_stream(conn, method, path, std::forward(body), std::move(req), ec); + if (ec) + throw_exception(system::system_error(ec, "request_stream")); + return res; +} + + +template +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + void (boost::system::error_code, stream, history)) +async_request_stream( + http::verb method, + core::string_view path, + RequestBody && body, + http::fields req, + CompletionToken && completion_token); + +template +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (system::error_code, stream, history)) +async_request_stream( + Connection & conn, + http::verb method, + detail::target_view path, + RequestBody && body, + detail::request_type req, + CompletionToken && completion_token = typename Connection::default_token()) +{ + + return asio::async_initiate( + [](asio::any_completion_handler handler, + Connection & conn, + http::verb method, + detail::target_view path, + RequestBody && body, + detail::request_type req) + { + detail::async_request_stream_impl(conn, method, path, + make_source(std::forward(body)), + std::move(req), std::move(handler)); + }, + completion_token, + std::ref(conn), method, std::move(path), std::forward(body), std::move(req)); +} + + template auto request(Connection & conn, - beast::http::verb method, + http::verb method, urls::url_view path, RequestBody && body, - typename Connection::request_type req, + detail::request_type req, system::error_code & ec) -> response; template auto request(Connection & conn, - beast::http::verb method, + http::verb method, urls::url_view path, RequestBody && body, - typename Connection::request_type req) + detail::request_type req) -> response { boost::system::error_code ec; @@ -48,10 +193,8 @@ auto request(Connection & conn, } - - template -auto request(beast::http::verb method, +auto request(http::verb method, urls::url_view path, RequestBody && body, http::fields req, @@ -61,7 +204,7 @@ auto request(beast::http::verb method, } template -auto request(beast::http::verb method, +auto request(http::verb method, urls::url_view path, RequestBody && body, http::fields req) @@ -74,43 +217,182 @@ auto request(beast::http::verb method, return res; } - -template -auto request(beast::http::verb method, - core::string_view path, - RequestBody && body, - http::fields req) - -> response -{ - boost::system::error_code ec; - auto res = request(method, path, std::forward(body), std::move(req), ec); - if (ec) - throw_exception(system::system_error(ec, "request")); - return res; -} - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, - response)) -async_request(beast::http::verb method, - core::string_view path, - RequestBody && body, - http::fields req, - CompletionToken && completion_token); - template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (system::error_code, response)) + BOOST_ASIO_COMPLETION_TOKEN_FOR(void (error_code, response)) CompletionToken + = typename Connection::default_token> +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (error_code, response)) async_request(Connection & conn, - beast::http::verb method, - urls::url_view target, + http::verb method, + detail::target_view target, RequestBody && body, - typename Connection::request_type req, + detail::request_type req, CompletionToken && completion_token = typename Connection::default_token()); + +template +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (error_code, response)) +async_request(http::verb method, + urls::url_view target, + RequestBody && body, + http::headers req, + CompletionToken && completion_token); + +template +struct bind_request +{ + template + auto operator()(Connection & conn, + urls::url_view target, + RequestBody && request_body, + detail::request_type req = {}) const -> response + { + return request(conn, Method, target, std::forward(request_body), std::move(req)); + }; + + template + auto operator()(Connection & conn, + urls::url_view target, + RequestBody && request_body, + detail::request_type req, + system::error_code & ec) const -> response + { + return request(conn, Method, target, std::forward(request_body), std::move(req), ec); + } + + + template + auto operator()(urls::url_view target, + RequestBody && request_body, + http::fields req = {}) const -> response + { + return request(Method, target, std::forward(request_body), std::move(req)); + }; + + template + auto operator()(urls::url_view target, + RequestBody && request_body, + http::fields req, + system::error_code & ec) const -> response + { + return request(Method, target, std::forward(request_body), std::move(req), ec); + } +}; + + +template +struct bind_empty_request +{ + template + auto operator()(Connection & conn, + urls::url_view target, + detail::request_type req = {}) const -> response + { + return request(conn, Method, target, http::empty_body::value_type{}, std::move(req)); + }; + + template + auto operator()(Connection & conn, + urls::url_view target, + detail::request_type req, + system::error_code & ec) const -> response + { + return request(conn, Method, target, http::empty_body::value_type{}, std::move(req), ec); + } + + inline auto operator()(urls::url_view target, http::fields req = {}) const -> response + { + return request(Method, target, empty{}, std::move(req)); + } + inline auto operator()(urls::url_view target, http::fields req, system::error_code & ec) const -> response + { + return request(Method, target, empty{}, std::move(req), ec); + } +}; + +template +struct bind_optional_request : bind_request, bind_empty_request +{ + using bind_request::operator(); + using bind_empty_request::operator(); +}; + + +template +struct bind_async_request +{ + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + void (boost::system::error_code, response)) + operator()(Connection & conn, + urls::url_view target, + RequestBody && request_body, + detail::request_type req = {}, + CompletionToken && completion_token = typename Connection::default_token()) const + { + return async_request(conn, Method, target, + std::forward(request_body), std::move(req), + std::forward(completion_token)); + } + + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + void (boost::system::error_code, response)) + operator()(urls::url_view target, + RequestBody && request_body, + http::fields req, + CompletionToken && completion_token) const + { + return async_request(Method, target, + std::forward(request_body), std::move(req), + std::forward(completion_token)); + } + +}; + + +template +struct bind_empty_async_request +{ + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, + void (boost::system::error_code, response)) + operator()(Connection & conn, + urls::url_view target, + detail::request_type req = {}, + CompletionToken && completion_token = typename Connection::default_token()) const + { + return async_request(conn, Method, target, empty{}, std::move(req), + std::forward(completion_token)); + } + + template< BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code, response)) CompletionToken> + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, response)) + operator()(urls::url_view target, http::fields req, CompletionToken && completion_token) const + { + return async_request(Method, target, empty{}, std::move(req), + std::forward(completion_token)); + } +}; + +template +struct bind_optional_async_request : bind_async_request, bind_empty_async_request +{ + using bind_async_request::operator(); + using bind_empty_async_request::operator(); +}; + + + + } } diff --git a/include/boost/requests/request_parameters.hpp b/include/boost/requests/request_parameters.hpp index e252e50..76ce7c8 100644 --- a/include/boost/requests/request_parameters.hpp +++ b/include/boost/requests/request_parameters.hpp @@ -15,21 +15,10 @@ namespace boost { namespace requests { -struct field_entry -{ - http::field field = http::field::unknown; - core::string_view key; - core::string_view value; - std::string buffer; - - field_entry() = default; - field_entry(http::field field, core::string_view value) : field(field), value(value) {} - field_entry(core::string_view key, core::string_view value) : key(key), value(value) {} -}; -inline field_entry basic_auth(core::string_view username, - core::string_view password) +inline http::header basic_auth(core::string_view username, + core::string_view password) { auto sz = beast::detail::base64::encoded_size(username.size() + 1 + password.size()); std::string res; @@ -39,38 +28,26 @@ inline field_entry basic_auth(core::string_view username, const auto data = std::string(username) + ":" + std::string(password); beast::detail::base64::encode(&*itr, data.data(), data.size()); - field_entry fe; + http::header fe; fe.field = http::field::authorization; fe.value = fe.buffer = std::move(res); return fe; } -inline field_entry bearer(core::string_view token) +inline http::header bearer(core::string_view token) { - field_entry fe; + http::header fe; fe.field = http::field::authorization; fe.value = fe.buffer = "Bearer " + std::string(token); return fe; } -inline auto headers(std::initializer_list fields) - -> beast::http::fields -{ - beast::http::fields f; - for (const auto & init : fields) - if (init.field != http::field::unknown) - f.set(init.field, init.value); - else - f.set(init.key, init.value); - return f; -} -struct request_parameters { - //Allocator - using fields_type = beast::http::fields; - fields_type fields; +struct request_parameters +{ + http::headers headers; request_options opts{}; cookie_jar * jar = nullptr; }; diff --git a/include/boost/requests/response.hpp b/include/boost/requests/response.hpp index 21f0e33..0f176a6 100644 --- a/include/boost/requests/response.hpp +++ b/include/boost/requests/response.hpp @@ -180,6 +180,9 @@ struct response : response_base } }; +using history = response_base::history_type; + + } } diff --git a/include/boost/requests/rfc/dates.hpp b/include/boost/requests/rfc/dates.hpp index 50e22a1..c829cec 100644 --- a/include/boost/requests/rfc/dates.hpp +++ b/include/boost/requests/rfc/dates.hpp @@ -55,7 +55,7 @@ struct date_1123_t char const*& it, char const* end ) const noexcept -> - urls::error_types::result; + system::result; }; @@ -107,7 +107,7 @@ struct date_850_t char const*& it, char const* end ) const noexcept -> - urls::error_types::result; + system::result; }; @@ -153,7 +153,7 @@ struct date_asctime_t char const*& it, char const* end ) const noexcept -> - urls::error_types::result; + system::result; }; @@ -172,7 +172,7 @@ struct http_date_t char const*& it, char const* end ) const noexcept -> - urls::error_types::result; + system::result; }; diff --git a/include/boost/requests/session.hpp b/include/boost/requests/session.hpp index 1419843..a134b64 100644 --- a/include/boost/requests/session.hpp +++ b/include/boost/requests/session.hpp @@ -19,7 +19,7 @@ struct url_hash { std::size_t operator()( urls::url_view url ) const { - return boost::hash()( url.buffer() ); + return boost::hash()( url.buffer() ); } }; @@ -67,8 +67,6 @@ struct session struct request_options & options() {return options_;} const struct request_options & options() const {return options_;} - using request_type = http::fields; - // possibly make it a distinct return type. BOOST_REQUESTS_DECL std::shared_ptr get_pool(urls::url_view url, error_code & ec); @@ -83,7 +81,7 @@ struct session template)) CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, pool_ptr)) + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, std::shared_ptr)) async_get_pool(urls::url_view path, CompletionToken && completion_token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)); @@ -91,69 +89,11 @@ struct session { pools_.clear(); } - - template - auto ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, - http::fields req, - system::error_code & ec) -> stream; - - template - auto ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, - http::fields req) -> stream - { - boost::system::error_code ec; - auto res = ropen(method, path, std::move(body), std::move(req), ec); - if (ec) - throw_exception(system::system_error(ec, "ropen")); - return res; - } - - BOOST_REQUESTS_DECL auto ropen( - beast::http::verb method, - urls::url_view url, - http::fields & headers, - source & src, - system::error_code & ec) -> stream; - - auto ropen(beast::http::verb method, - urls::url_view url, - http::fields & headers, - source & src) -> stream - { - boost::system::error_code ec; - auto res = ropen(method, url, headers, src, ec); - if (ec) - throw_exception(system::system_error(ec, "ropen")); - return res; - } - - template - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, stream)) - async_ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, - http::fields req, - CompletionToken && completion_token); - - template< BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code, stream)) CompletionToken> - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, - void (boost::system::error_code, stream)) - async_ropen(http::verb method, - urls::url_view path, - source & src, - http::fields & headers, - CompletionToken && completion_token); - - + cookie_jar & jar() {return jar_;}; + const cookie_jar & jar() const {return jar_;}; private: - asio::ssl::context sslctx_{asio::ssl::context_base::tls_client}; + asio::ssl::context sslctx_{asio::ssl::context_base::tlsv12_client}; asio::any_io_executor executor_; std::mutex mutex_; @@ -163,16 +103,9 @@ struct session std::shared_ptr, detail::url_hash> pools_; - // this isn't great cookie_jar jar_{}; struct async_get_pool_op; - struct async_ropen_op; - - BOOST_REQUESTS_DECL - static void async_ropen_impl( - asio::any_completion_handler handler, - session * sess, http::verb method, urls::url_view path, source * src, http::fields * headers); BOOST_REQUESTS_DECL static void async_get_pool_impl( @@ -191,57 +124,19 @@ struct session::defaulted : session using default_token = Token; using session::session; - auto async_get_pool(urls::url_view path) - { - return session::async_get_pool(path, default_token()); - }; - - using session::async_ropen; - using session::async_get_pool; - - template - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, stream)) - async_ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, - http::fields req, - CompletionToken && completion_token) - { - return session::async_ropen(method, path, std::forward(body), std::move(req), - detail::with_defaulted_token(std::forward(completion_token))); - } - - template - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, stream)) - async_ropen(http::verb method, - urls::url_view path, - source & src, - http::fields & headers, - CompletionToken && completion_token) + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (boost::system::error_code, typename detail::defaulted_helper, Token>::type)) + async_get_pool(CompletionToken && completion_token) { - return session::async_ropen(method, path, src, headers, - detail::with_defaulted_token(std::forward(completion_token))); + return session::async_get_pool(detail::with_defaulted_token(std::forward(completion_token))); } - template - auto async_ropen(beast::http::verb method, - urls::url_view path, - RequestBody && body, - http::fields req) - { - return async_ropen(method, path, std::forward(body), std::move(req), default_token()); - } - - auto async_ropen(http::verb method, - urls::url_view path, - source & src, - http::fields & headers) + auto async_get_pool(urls::url_view path) { - return async_ropen(method, path, headers, src, default_token()); - } - + return this->async_get_pool(path, default_token()); + }; + using session::async_get_pool; }; diff --git a/include/boost/requests/sources/string.hpp b/include/boost/requests/sources/string.hpp index fa746af..acb9c25 100644 --- a/include/boost/requests/sources/string.hpp +++ b/include/boost/requests/sources/string.hpp @@ -58,6 +58,8 @@ struct basic_string_source final : source } }; +using string_source = basic_string_source, std::allocator>; + template inline source_ptr tag_invoke( make_source_tag, std::basic_string data) diff --git a/include/boost/requests/sources/string_view.hpp b/include/boost/requests/sources/string_view.hpp index 15dce85..0a3587d 100644 --- a/include/boost/requests/sources/string_view.hpp +++ b/include/boost/requests/sources/string_view.hpp @@ -60,6 +60,8 @@ struct basic_string_view_source final : source } }; +using string_view_source = basic_string_view_source; + template source_ptr tag_invoke(make_source_tag, const char (&data)[N]) { diff --git a/include/boost/requests/stream.hpp b/include/boost/requests/stream.hpp index 195ca49..3465940 100644 --- a/include/boost/requests/stream.hpp +++ b/include/boost/requests/stream.hpp @@ -9,7 +9,6 @@ #define BOOST_REQUESTS_STREAM_HPP #include -#include #include #include #include @@ -143,7 +142,7 @@ struct stream bool done() const {return !parser_ || parser_->is_done();} explicit stream(executor_type executor, std::nullptr_t ) : executor_{executor}, impl_(nullptr) {} - explicit stream(executor_type executor, boost::intrusive_ptr impl) + explicit stream(executor_type executor, std::shared_ptr impl) : executor_{executor}, impl_(std::move(impl)) {} @@ -160,8 +159,7 @@ struct stream } private: executor_type executor_; - boost::intrusive_ptr impl_; - detail::lock_guard lock_; + std::shared_ptr impl_; std::unique_ptr> parser_; diff --git a/include/boost/requests/websocket.hpp b/include/boost/requests/websocket.hpp new file mode 100644 index 0000000..d41987a --- /dev/null +++ b/include/boost/requests/websocket.hpp @@ -0,0 +1,184 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_REQUESTS_WEBSOCKET_HPP +#define BOOST_REQUESTS_WEBSOCKET_HPP + +#include +#include +#include +#include + +namespace boost { +namespace requests { +namespace detail { + +struct connection_impl + ; +template +struct optional_ssl_stream +{ + typedef asio::ssl::stream> next_layer_type; + typedef typename next_layer_type::executor_type executor_type; + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + executor_type get_executor() noexcept + { + return next_layer_.get_executor(); + } + const next_layer_type &next_layer() const noexcept + { + return next_layer_; + } + + next_layer_type &next_layer() noexcept + { + return next_layer_; + } + + using protocol_type = asio::generic::stream_protocol; + using endpoint_type = typename protocol_type::endpoint; + + template + std::size_t read_some(const MutableBufferSequence & buffers, system::error_code & ec) + { + if (use_ssl_) + return next_layer().read_some(buffers, ec); + else + return next_layer().next_layer().read_some(buffers, ec); + } + + template + std::size_t write_some(const ConstBufferSequence & buffers, system::error_code & ec) + { + if (uses_ssl()) + return next_layer().write_some(buffers, ec); + else + return next_layer().next_layer().write_some(buffers, ec); + } + + template + std::size_t read_some(const MutableBufferSequence & buffers) + { + if (use_ssl_) + return next_layer().read_some(buffers); + else + return next_layer().next_layer().read_some(buffers); + } + + template + std::size_t write_some(const ConstBufferSequence & buffers) + { + if (uses_ssl()) + return next_layer().write_some(buffers); + else + return next_layer().next_layer().write_some(buffers); + } + + template< + typename MutableBufferSequence, + typename ReadToken> + auto async_read_some(const MutableBufferSequence & buffers, ReadToken && token) + { + if (uses_ssl()) + return next_layer().async_read_some(buffers, std::forward(token)); + else + return next_layer().next_layer().async_read_some(buffers, std::forward(token)); + } + + template< + typename ConstBufferSequence, + typename WriteToken> + auto async_write_some(const ConstBufferSequence & buffers, WriteToken && token) + { + if (uses_ssl()) + return next_layer().async_write_some(buffers, std::forward(token)); + else + return next_layer().next_layer().async_write_some(buffers, std::forward(token)); + } + + /// Rebinds the socket type to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + using other = optional_ssl_stream; + }; + + bool uses_ssl() const {return use_ssl_;} + + optional_ssl_stream(optional_ssl_stream && ) =default; + + template + optional_ssl_stream(optional_ssl_stream && rhs) : next_layer_(std::move(rhs)), use_ssl_(rhs.use_ssl_) {} + +private: + next_layer_type next_layer_; + bool use_ssl_{true}; + optional_ssl_stream(Executor exec) : next_layer_(exec, detail::ssl_context_service(exec.context()).get()) {} + optional_ssl_stream(next_layer_type next_layer, bool use_ssl) + : next_layer_(std::move(next_layer)), use_ssl_(use_ssl) + { + } + + friend struct connection_impl; +}; + +template +void +teardown( + beast::role_type role, + requests::detail::optional_ssl_stream& socket, + error_code& ec) +{ + if (socket.uses_ssl()) + boost::beast::teardown(role, socket.next_layer(), ec); + else + boost::beast::websocket::teardown(role, socket.next_layer().next_layer(), ec); +} + +template< + class Executor, + class TeardownHandler> +void +async_teardown( + beast::role_type role, + requests::detail::optional_ssl_stream& socket, + TeardownHandler&& handler) +{ + + if (socket.uses_ssl()) + boost::beast::async_teardown(role, socket.next_layer(), std::forward(handler)); + else + boost::beast::websocket::async_teardown(role, socket.next_layer().next_layer(), std::forward(handler)); + +} + +} + +template +using basic_websocket = boost::beast::websocket::stream>; + +using websocket = basic_websocket<>; + +} + +namespace beast +{ +namespace websocket +{ + + + + + +} +} + +} + +#endif // BOOST_REQUESTS_WEBSOCKET_HPP diff --git a/src/connection_pool.cpp b/src/connection_pool.cpp index 0e1e276..f44c890 100644 --- a/src/connection_pool.cpp +++ b/src/connection_pool.cpp @@ -10,22 +10,9 @@ namespace boost { namespace requests { -void detail::connection_deleter::operator()(connection_impl * ptr) -{ - if (ptr->borrow_count_ == 0u) - delete ptr; - else - { - ptr->borrowed_from_ = nullptr; - if (ptr->borrow_count_ == 0u) - delete ptr; - } -} - - void connection_pool::lookup(urls::url_view sv, system::error_code & ec) { - urls::string_view scheme = "https"; + core::string_view scheme = "https"; if (sv.has_scheme()) scheme = sv.scheme(); @@ -52,7 +39,6 @@ void connection_pool::lookup(urls::url_view sv, system::error_code & ec) std::string(sv.encoded_host_address().data(), sv.encoded_host_address().size()), std::string(service.data(), service.size()), ec); - if (!ec && eps.empty()) BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found) if (ec) @@ -63,6 +49,14 @@ void connection_pool::lookup(urls::url_view sv, system::error_code & ec) return; use_ssl_ = scheme == "https"; host_ = eps->host_name(); + + if (sv.has_port() && + ( + (sv.scheme_id() == urls::scheme::http && sv.port() != "80") + || (sv.scheme_id() == urls::scheme::https && sv.port() != "443") + )) + (host_ += ":") += sv.port(); + const auto r = boost::adaptors::transform( eps, @@ -101,10 +95,12 @@ struct connection_pool::async_lookup_op : asio::coroutine connection_pool * this_; const urls::url_view sv; - urls::string_view scheme = "https"; + core::string_view scheme = "https"; std::unique_lock lock{this_->mtx_, std::defer_lock}; + std::shared_ptr resolver_; + async_lookup_op(connection_pool * this_, urls::url_view sv) : this_(this_), sv(sv) {} @@ -138,14 +134,14 @@ void connection_pool::async_lookup_op::operator()(Self && self, BOOST_REQUESTS_YIELD { auto service = sv.has_port() ? sv.port() : scheme; - auto resolver = allocate_unique( + resolver_ = std::allocate_shared( asio::get_associated_allocator(self), get_executor()); - resolver->async_resolve( + resolver_->async_resolve( std::string(sv.encoded_host_address().data(), sv.encoded_host_address().size()), - std::string(service.data(), service.size()), - asio::consign(std::move(self), std::move(resolver))); + std::string(service.data(), service.size()), std::move(self)); } + resolver_.reset(); if (!ec && eps.empty()) BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found) @@ -155,6 +151,13 @@ void connection_pool::async_lookup_op::operator()(Self && self, lock.lock(); this_->use_ssl_ = scheme == "https"; this_->host_ = eps->host_name(); + + if (sv.has_port() && + ( + (sv.scheme_id() == urls::scheme::http && sv.port() != "80") + || (sv.scheme_id() == urls::scheme::https && sv.port() != "443") + )) + (this_->host_ += ":") += sv.port(); const auto r = boost::adaptors::transform( eps, @@ -188,7 +191,7 @@ void connection_pool::async_lookup_op::operator()(Self && self, } -auto connection_pool::get_connection(error_code & ec) -> connection +auto connection_pool::borrow_connection(error_code & ec) -> connection { std::unique_lock lock{mtx_}; do @@ -202,7 +205,7 @@ auto connection_pool::get_connection(error_code & ec) -> connection } // check if we can make more connections. -> open another connection. // the race here is that we might open one too many - else if (conns_.size() <= limit_) // open another connection then -> we block the entire + else if (conns_.size() < limit_) // open another connection then -> we block the entire { if (endpoints_.empty()) { @@ -215,7 +218,8 @@ auto connection_pool::get_connection(error_code & ec) -> connection [this](const endpoint_type &a, const endpoint_type &b) { return conns_.count(a) < conns_.count(b); }); const auto ep = endpoints_.front(); - boost::intrusive_ptr nconn = conns_.emplace(ep, new detail::connection_impl(get_executor(), context_, this))->second.get(); + std::shared_ptr nconn = + conns_.emplace(ep, std::make_shared(get_executor(), context_))->second; // no one else will grab it from there, bc it's not in free_conns_ lock.unlock(); nconn->use_ssl(use_ssl_); @@ -233,8 +237,60 @@ auto connection_pool::get_connection(error_code & ec) -> connection return connection(); } +auto connection_pool::steal_connection(error_code & ec) -> connection +{ + std::unique_lock lock{mtx_}; + do + { + if (!free_conns_.empty()) + { + auto pp = std::move(free_conns_.front()); + free_conns_.erase(free_conns_.begin()); + auto itr = std::find_if( + conns_.begin(), conns_.end(), + [&](const std::pair> & p) + { + return p.second == pp; + }); + conns_.erase(itr); + return connection(pp); + } + // check if we can make more connections. -> open another connection. + // the race here is that we might open one too many + else if (conns_.size() < limit_) // open another connection then -> we block the entire + { + if (endpoints_.empty()) + { + BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); + return connection(); + } + // sort the endpoints by connections that use it + std::sort(endpoints_.begin(), endpoints_.end(), + [this](const endpoint_type &a, const endpoint_type &b) { return conns_.count(a) < conns_.count(b); }); + const auto ep = endpoints_.front(); + + std::shared_ptr nconn = std::make_shared(get_executor(), context_); + // no one else will grab it from there, bc it's not in free_conns_ + lock.unlock(); + nconn->use_ssl(use_ssl_); + nconn->set_host(host_); + nconn->connect(ep, ec); + if (ec) + return connection(); + else + return connection(std::move(nconn)); + } + cv_.wait(lock); + } + while(!ec); + + return connection(); +} + + +template struct connection_pool::async_get_connection_op : asio::coroutine { constexpr static const char * op_name = "connection_pool::async_get_connection_op"; @@ -243,15 +299,17 @@ struct connection_pool::async_get_connection_op : asio::coroutine executor_type get_executor() {return this_->get_executor(); } connection_pool * this_; + async_get_connection_op(connection_pool * this_) : this_(this_) {} using conn_t = boost::unordered_multimap, detail::endpoint_hash>; typename conn_t::iterator itr; - boost::intrusive_ptr nconn = nullptr; + std::shared_ptr nconn = nullptr; std::unique_lock lock{this_->mtx_, std::defer_lock}; endpoint_type ep; + connection conn; template void operator()( @@ -259,29 +317,39 @@ struct connection_pool::async_get_connection_op : asio::coroutine system::error_code ec = {}); }; +template template -void connection_pool::async_get_connection_op::operator()( +void connection_pool::async_get_connection_op::operator()( Self && self, system::error_code ec) { - connection conn; BOOST_ASIO_CORO_REENTER (this) { - lock.lock(); do { if (this_->endpoints_.empty()) { BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); - break; + goto immediate; } if (!this_->free_conns_.empty()) { auto pp = std::move(this_->free_conns_.front()); this_->free_conns_.erase(this_->free_conns_.begin()); - conn = connection(pp); - break; + conn = connection(pp); + BOOST_IF_CONSTEXPR (Steal) + { + auto itr = std::find_if( + this_->conns_.begin(), + this_->conns_.end(), + [&](const std::pair> & p) + { + return p.second == pp; + }); + this_->conns_.erase(itr); + } + goto immediate; } // check if we can make more connections. -> open another connection. // the race here is that we might open one too many @@ -290,7 +358,7 @@ void connection_pool::async_get_connection_op::operator()( if (this_->endpoints_.empty()) { BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); - break; + goto immediate; } // sort the endpoints by connections that use it @@ -298,7 +366,10 @@ void connection_pool::async_get_connection_op::operator()( [this](const endpoint_type &a, const endpoint_type &b) { return this_->conns_.count(a) < this_->conns_.count(b); }); ep = this_->endpoints_.front(); - nconn = this_->conns_.emplace(ep, new detail::connection_impl(get_executor(), this_->context_, this_))->second.get(); + BOOST_IF_CONSTEXPR (Steal) + nconn = std::make_shared(get_executor(), this_->context_); + else + nconn = this_->conns_.emplace(ep, std::make_shared(get_executor(), this_->context_))->second; // no one else will grab it from there, bc it's not in free_conns_ lock.unlock(); @@ -325,160 +396,100 @@ void connection_pool::async_get_connection_op::operator()( conn = connection{std::move(nconn)}; break; } - } - BOOST_REQUESTS_YIELD this_->cv_.async_wait(lock, std::move(self)); + BOOST_REQUESTS_YIELD this_->cv_.async_wait(*lock.mutex(), std::move(self)); } while(!ec); + if (false) immediate: + { + lock.unlock(); + BOOST_REQUESTS_YIELD asio::dispatch( + asio::get_associated_immediate_executor(self, this_->get_executor()), + std::move(self)); + } } - if (is_complete()) - self.complete(ec, std::move(conn)); -} - - - -struct connection_pool::async_ropen_op : asio::coroutine -{ - constexpr static const char * op_name = "connection_pool::async_ropen_op"; - - using executor_type = asio::any_io_executor; - executor_type get_executor() {return this_->get_executor(); } - - connection_pool * this_; - beast::http::verb method; - urls::pct_string_view path; - http::fields & headers; - source & src; - request_options opt; - cookie_jar * jar; - - connection conn; - - async_ropen_op(connection_pool * this_, - beast::http::verb method, - urls::pct_string_view path, - http::fields & headers, - source & src, - request_options opts, - cookie_jar * jar) - : this_(this_), method(method), path(path), headers(headers), src(src), opt(std::move(opts)), jar(jar) - { - } - - - async_ropen_op(connection_pool * this_, - beast::http::verb method, - urls::url_view path, - http::fields & headers, - source & src, - request_options opt, - cookie_jar * jar) - : this_(this_), method(method), path(path.encoded_resource()), - headers(headers), src(src), opt(std::move(opt)), jar(jar) { + if (lock.owns_lock()) + lock.unlock(); + self.complete(ec, std::move(conn)); } - template - void operator()(Self && self, - boost::system::error_code ec = {}, - variant2::variant res = variant2::monostate()); - -}; - - -template -void connection_pool::async_ropen_op::operator()( - Self && self, - boost::system::error_code ec, - variant2::variant res) -{ - BOOST_ASIO_CORO_REENTER(this) - { - BOOST_REQUESTS_YIELD this_->async_get_connection(std::move(self)); - conn = std::move(variant2::get<1>(res)); - if (!ec && !conn) - BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::not_found); - if (ec) - break; - - BOOST_REQUESTS_YIELD conn.async_ropen(method, path, headers, src, std::move(opt), jar, std::move(self)); - return self.complete(ec, variant2::get<2>(std::move(res))); - } - if (is_complete()) - self.complete(ec, stream{get_executor(), nullptr}); - } + connection_pool::connection_pool(connection_pool && lhs) : use_ssl_(lhs.use_ssl_), context_(lhs.context_), cv_(lhs.cv_.get_executor()), host_(std::move(lhs.host_)), endpoints_(std::move(lhs.endpoints_)), limit_(lhs.limit_), conns_(std::move(lhs.conns_)) { - BOOST_ASSERT(std::count_if( - conns_.begin(), conns_.end(), - [](const std::pair> & p) - { - return p.second->borrow_count_ > 0u; - }) == 0u); - - for (auto & p : conns_) - p.second->borrowed_from_ = this; - + // If this triggers, you're moving a pool with borrowed connections + BOOST_ASSERT(conns_.size() != free_conns_.size()); free_conns_ = std::move(lhs.free_conns_); } connection_pool::~connection_pool() { - for (auto & p : conns_) - p.second->borrowed_from_ = nullptr; } -void connection_pool::return_connection_(detail::connection_impl * conn) +void connection_pool::return_connection(connection conn) { - if (!conn->is_open()) - return drop_connection_(conn); - std::lock_guard lock{mtx_}; - free_conns_.push_back(std::move(conn)); + BOOST_ASSERT( + std::find_if( + conns_.begin(), + conns_.end(), + [&](const std::pair> & p) + { + return p.second == conn.impl_; + }) != conns_.end()); + free_conns_.push_back(std::move(conn.impl_)); cv_.notify_all(); } -void connection_pool::drop_connection_(const detail::connection_impl * conn) +void connection_pool::remove_connection(const connection &conn) { std::lock_guard lock{mtx_}; - auto itr = std::find_if(conns_.begin(), conns_.end(), - [&](const std::pair> & e) - { - return e.second.get() == conn; - }); + auto itr = std::find_if( + conns_.begin(), + conns_.end(), + [&](const std::pair> & p) + { + return p.second == conn.impl_; + }); + if (itr != conns_.end()) { conns_.erase(itr); cv_.notify_all(); } + } -void connection_pool::async_ropen_impl(asio::any_completion_handler handler, - connection_pool * this_, http::verb method, - urls::pct_string_view path, http::fields & headers, - source & src, request_options opt, cookie_jar * jar) + +void connection_pool::async_borrow_connection_impl( + asio::any_completion_handler handler, + connection_pool * this_) { + return asio::async_compose< - asio::any_completion_handler, void(error_code, stream)>( - async_ropen_op{this_, method, path, headers, src, std::move(opt), jar}, + asio::any_completion_handler, void(error_code, connection)>( + async_get_connection_op{this_}, handler, this_->get_executor()); } -void connection_pool::async_get_connection_impl( + +void connection_pool::async_steal_connection_impl( asio::any_completion_handler handler, connection_pool * this_) { return asio::async_compose< asio::any_completion_handler, void(error_code, connection)>( - async_get_connection_op{this_}, - handler, this_->get_executor()); + async_get_connection_op{this_}, + handler, this_->get_executor()); + } void connection_pool::async_lookup_impl( diff --git a/src/detail/connection_impl.cpp b/src/detail/connection_impl.cpp index 1b6d759..b7cdca7 100644 --- a/src/detail/connection_impl.cpp +++ b/src/detail/connection_impl.cpp @@ -18,71 +18,6 @@ namespace requests namespace detail { - -bool check_endpoint( - urls::url_view path, - const asio::ip::tcp::endpoint & ep, - core::string_view host, - bool has_ssl, - system::error_code & ec) -{ - if ((path.has_port() && (get_port(path) != ep.port())) - && (path.has_authority() && (path.encoded_host() != host)) - && (path.has_scheme() && (path.host() != (has_ssl ? "https" : "http")))) - BOOST_REQUESTS_ASSIGN_EC(ec, error::wrong_host) - - return !ec; -} - -bool check_endpoint( - urls::url_view path, - const asio::local::stream_protocol::endpoint & ep, - core::string_view host, - bool, - system::error_code & ec) -{ - if (path.has_port() - && (path.has_authority() && (path.host() != host)) - && (path.has_scheme() && (path.host() != "unix"))) - BOOST_REQUESTS_ASSIGN_EC(ec, error::wrong_host) - - return !ec; -} - -bool check_endpoint( - urls::url_view path, - const asio::generic::stream_protocol::endpoint & ep, - core::string_view host, - bool has_ssl, - system::error_code & ec) -{ - if (ep.protocol() == asio::local::stream_protocol()) - { - asio::local::stream_protocol::endpoint cmp; - cmp.resize(ep.size()); - std::memcpy(cmp.data(), - ep.data(), - ep.size()); - return check_endpoint(path, cmp, host, has_ssl, ec); - } - else if (ep.protocol() == asio::ip::tcp::v4() - || ep.protocol() == asio::ip::tcp::v6()) - { - asio::ip::tcp::endpoint cmp; - cmp.resize(ep.size()); - std::memcpy(cmp.data(), - ep.data(), - ep.size()); - return check_endpoint(path, cmp, host, has_ssl, ec); - } - else - { - BOOST_REQUESTS_ASSIGN_EC(ec, asio::error::no_protocol_option); - return false; - } -} - - auto connection_impl::ropen(beast::http::verb method, urls::pct_string_view path, http::fields & headers, @@ -96,33 +31,6 @@ auto connection_impl::ropen(beast::http::verb method, return res; } -auto connection_impl::ropen(beast::http::verb method, - urls::url_view path, - http::fields & headers, - source & src, - cookie_jar * jar) -> stream -{ - system::error_code ec; - auto res = ropen(method, path, headers, src, jar, ec); - if (ec) - throw_exception(system::system_error(ec)); - return res; -} - -auto connection_impl::ropen(beast::http::verb method, - urls::url_view path, - http::fields & headers, - source & src, - cookie_jar * jar, - system::error_code & ec) -> stream -{ - detail::check_endpoint(path, endpoint(), host(), use_ssl_, ec); - if (ec) - return stream{get_executor(), nullptr}; - - return ropen(method, path.encoded_resource(), headers, src, jar, ec); -} - auto connection_impl::ropen(beast::http::verb method, urls::pct_string_view path, http::fields & headers, @@ -143,19 +51,14 @@ auto connection_impl::ropen(beast::http::verb method, headers.erase(http::field::cookie); headers.set(http::field::host, host_); - if (headers.count(http::field::user_agent) > 0) + if (headers.count(http::field::user_agent) == 0) headers.set(http::field::user_agent, "Requests-" BOOST_BEAST_VERSION_STRING); - detail::lock_guard lock{write_mtx_}; + // write impl { - if (ec) - return stream{get_executor(), nullptr}; if (!is_open()) { - if (ec) - return stream{get_executor(), nullptr}; - next_layer_.next_layer().connect(endpoint_, ec); if (use_ssl_ && !ec) next_layer_.handshake(asio::ssl::stream_base::client, ec); @@ -171,15 +74,15 @@ auto connection_impl::ropen(beast::http::verb method, if (ec) return stream{get_executor(), nullptr}; } - lock = read_mtx_; // write end if (ec) return stream{get_executor(), nullptr}; - stream str{get_executor(), this}; + stream str{get_executor(), shared_from_this()}; str.parser_ = std::make_unique>(http::response_header{http::fields(headers.get_allocator())}); str.parser_->body_limit(boost::none); + str.parser_->on_chunk_body(handle_chunked_); if (method == http::verb::head || method == http::verb::connect) str.parser_->skip(true); @@ -207,7 +110,6 @@ auto connection_impl::ropen(beast::http::verb method, } } } - str.lock_ = std::move(lock); return str; } @@ -226,7 +128,7 @@ struct connection_impl::async_ropen_op using executor_type = asio::any_io_executor; executor_type get_executor() {return this_->get_executor(); } - boost::intrusive_ptr this_; + std::shared_ptr this_; optional str; beast::http::verb method; @@ -234,10 +136,8 @@ struct connection_impl::async_ropen_op http::fields & headers; source & src; cookie_jar * jar{nullptr}; - system::error_code ec_; - lock_guard lock_; - async_ropen_op(boost::intrusive_ptr this_, + async_ropen_op(std::shared_ptr this_, beast::http::verb method, urls::pct_string_view path, http::fields & headers, @@ -248,18 +148,6 @@ struct connection_impl::async_ropen_op { } - async_ropen_op(boost::intrusive_ptr this_, - beast::http::verb method, - urls::url_view path, - http::fields & headers, - source & src, - cookie_jar * jar) - : this_(this_), method(method), path(path.encoded_resource()), - headers(headers), src(src), jar(jar) - { - detail::check_endpoint(path, this_->endpoint(), this_->host(), this_->use_ssl_, ec_); - } - template void operator()(Self && self, system::error_code ec = {}, std::size_t res_ = 0u); }; @@ -268,19 +156,12 @@ struct connection_impl::async_ropen_op template void connection_impl::async_ropen_op::operator()( Self && self, - system::error_code ec, std::size_t res_) + system::error_code ec, std::size_t) { BOOST_ASIO_HANDLER_LOCATION((__FILE__, __LINE__, op_name)); BOOST_ASIO_CORO_REENTER(this) { - if (ec_) - { - BOOST_REQUESTS_YIELD asio::post(this_->get_executor(), std::move(self)); - ec = ec_; - break; - } - if (jar) { @@ -294,17 +175,9 @@ void connection_impl::async_ropen_op::operator()( headers.erase(http::field::cookie); headers.set(http::field::host, this_->host_); - if (headers.count(http::field::user_agent) > 0) + if (headers.count(http::field::user_agent) == 0) headers.set(http::field::user_agent, "Requests-" BOOST_BEAST_VERSION_STRING); - if (!this_->write_mtx_.try_lock()) - { - BOOST_REQUESTS_YIELD this_->write_mtx_.async_lock(std::move(self)); - if (ec) - return self.complete(ec, *std::move(str)); - lock_ = {this_->write_mtx_, std::adopt_lock}; - } - if (!this_->is_open()) { BOOST_REQUESTS_YIELD this_->next_layer_.next_layer().async_connect(this_->endpoint_, std::move(self)); @@ -316,6 +189,7 @@ void connection_impl::async_ropen_op::operator()( break; } + if (this_->use_ssl_) { BOOST_REQUESTS_YIELD async_write_request(this_->next_layer_, method, path, headers, src, std::move(self)); @@ -331,6 +205,7 @@ void connection_impl::async_ropen_op::operator()( str.emplace(this_->get_executor(), this_); // , req.get_allocator().resource() str->parser_ = std::make_unique>(http::response_header{http::fields(headers.get_allocator())}); str->parser_->body_limit(boost::none); + str->parser_->on_chunk_body(this_->handle_chunked_); if (method == http::verb::head || method == http::verb::connect) str->parser_->skip(true); @@ -345,19 +220,7 @@ void connection_impl::async_ropen_op::operator()( } if (ec) - { - lock_ = {}; return self.complete(error_code{}, stream{get_executor(), nullptr}); - } - - if (!this_->read_mtx_.try_lock()) - { - BOOST_REQUESTS_YIELD this_->read_mtx_.async_lock(std::move(self)); - if (ec) - return self.complete(ec, *std::move(str)); - lock_ = {this_->read_mtx_, std::adopt_lock}; - } - if (jar) { @@ -377,18 +240,13 @@ void connection_impl::async_ropen_op::operator()( } // coro is complete if (is_complete()) - { - lock_ = {}; return self.complete(ec, *std::move(str)); - } } void connection_impl::connect(endpoint_type ep, system::error_code & ec) { - detail::lock_guard lw{write_mtx_}, lr{read_mtx_}; next_layer_.next_layer().connect(endpoint_ = ep, ec); - if (use_ssl_ && !ec) next_layer_.handshake(asio::ssl::stream_base::client, ec); } @@ -396,7 +254,6 @@ void connection_impl::connect(endpoint_type ep, system::error_code & ec) void connection_impl::close(system::error_code & ec) { - detail::lock_guard lw{write_mtx_}, lr{read_mtx_}; if (use_ssl_) next_layer_.shutdown(ec); @@ -404,21 +261,13 @@ void connection_impl::close(system::error_code & ec) next_layer_.next_layer().close(ec); } - -std::size_t connection_impl::do_read_some_(beast::http::basic_parser & parser) -{ - if (use_ssl_) - return beast::http::read_some(next_layer_, buffer_, parser); - else - return beast::http::read_some(next_layer_.next_layer(), buffer_, parser); -} - std::size_t connection_impl::do_read_some_(beast::http::basic_parser & parser, system::error_code & ec) { if (use_ssl_) return beast::http::read_some(next_layer_, buffer_, parser, ec); else return beast::http::read_some(next_layer_.next_layer(), buffer_, parser, ec); + } void connection_impl::do_async_read_some_(beast::http::basic_parser & parser, asio::any_completion_handler tk) @@ -441,15 +290,11 @@ struct connection_impl::async_connect_op : asio::coroutine connection_impl * this_; endpoint_type ep; - detail::lock_guard write_lock, read_lock; - template void operator()(Self && self, system::error_code ec = {}) { BOOST_ASIO_CORO_REENTER(this) { - BOOST_REQUESTS_AWAIT_LOCK(this_->write_mtx_, write_lock); - BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, read_lock); BOOST_REQUESTS_YIELD this_->next_layer_.next_layer().async_connect(this_->endpoint_ = ep, std::move(self)); if (this_->use_ssl_) { @@ -470,15 +315,11 @@ struct connection_impl::async_close_op : asio::coroutine connection_impl * this_; endpoint_type ep; - detail::lock_guard write_lock, read_lock; - template void operator()(Self && self, system::error_code ec = {}) { BOOST_ASIO_CORO_REENTER(this) { - BOOST_REQUESTS_AWAIT_LOCK(this_->write_mtx_, write_lock); - BOOST_REQUESTS_AWAIT_LOCK(this_->read_mtx_, read_lock); if (!ec && this_->use_ssl_) { BOOST_REQUESTS_YIELD this_->next_layer_.async_shutdown(std::move(self)); @@ -504,18 +345,6 @@ void connection_impl::do_close_(system::error_code & ec) next_layer_.next_layer().close(ec); } -void connection_impl::return_to_pool_() -{ - if (auto ptr = borrowed_from_.load()) - ptr->return_connection_(this); -} - -void connection_impl::remove_from_pool_() -{ - if (auto ptr = borrowed_from_.load()) - ptr->drop_connection_(this); -} - void connection_impl::async_connect_impl(asio::any_completion_handler handler, connection_impl * this_, endpoint_type ep) { @@ -536,20 +365,213 @@ void connection_impl::async_ropen_impl(asio::any_completion_handler, void(error_code, stream)>( - async_ropen_op{this_, method, path, headers, src, jar}, + async_ropen_op{this_->shared_from_this(), method, path, headers, src, jar}, handler, this_->get_executor()); } -void connection_impl::async_ropen_impl_url(asio::any_completion_handler handler, - connection_impl * this_, http::verb method, - urls::url_view url, http::fields & headers, - source & src, cookie_jar * jar) +auto connection_impl::upgrade(urls::pct_string_view path, + http::fields & headers, + cookie_jar * jar) -> websocket { - return asio::async_compose, void(error_code, stream)>( - async_ropen_op{this_, method, url, headers, src, jar}, + system::error_code ec; + auto res = upgrade(path, headers, jar, ec); + if (ec) + throw_exception(system::system_error(ec)); + return res; +} + +websocket connection_impl::upgrade( + urls::pct_string_view path, + http::fields & headers, + cookie_jar * jar, + system::error_code & ec) +{ + const auto is_secure = use_ssl_; + if (jar) + { + auto cc = jar->get(host(), is_secure, path); + if (!cc.empty()) + headers.set(http::field::cookie, cc); + else + headers.erase(http::field::cookie); + } + else + headers.erase(http::field::cookie); + + headers.set(http::field::host, host_); + if (headers.count(http::field::user_agent) > 0) + headers.set(http::field::user_agent, "Requests-" BOOST_BEAST_VERSION_STRING); + + if (!is_open()) + { + next_layer_.next_layer().connect(endpoint_, ec); + if (use_ssl_ && !ec) + next_layer_.handshake(asio::ssl::stream_base::client, ec); + + if (ec) + return websocket{detail::optional_ssl_stream<>{get_executor()}}; + } + + websocket ws{detail::optional_ssl_stream<>(std::move(next_layer_), use_ssl_)}; + + ws.set_option( + beast::websocket::stream_base::decorator( + [&](beast::websocket::request_type & req) + { + for (auto & hd : headers) + if (hd.name() == http::field::unknown) + req.set(hd.name_string(), hd.value()); + else + req.set(hd.name(), hd.value()); + })); + + beast::websocket::response_type res; + ws.handshake(res, host_, core::string_view(path), ec); + + if (jar) + { + auto cookie_itr = res.find(http::field::set_cookie); + if (cookie_itr != res.end()) + { + auto f = requests::parse_set_cookie_field(cookie_itr->value()); + if (f) + jar->set(*f, host_); + else + { + ec = f.error(); + return ws; + } + } + } + return ws; +} + + +struct connection_impl::async_upgrade_op + : boost::asio::coroutine +{ + constexpr static const char * op_name = "connection_impl::async_upgrade_op"; + + using executor_type = asio::any_io_executor; + executor_type get_executor() {return this_->get_executor(); } + + std::shared_ptr this_; + + std::shared_ptr> websocket_; + + urls::pct_string_view path; + http::fields & headers; + cookie_jar * jar{nullptr}; + + async_upgrade_op(std::shared_ptr this_, + urls::pct_string_view path, + http::fields & headers, + cookie_jar * jar) + : this_(std::move(this_)), path(path), headers(headers), jar(jar) + { + } + + template + void operator()(Self && self, system::error_code ec = {}, std::size_t res_ = 0u); +}; + + +template +void connection_impl::async_upgrade_op::operator()( + Self && self, + system::error_code ec, std::size_t res_) +{ + BOOST_ASIO_HANDLER_LOCATION((__FILE__, __LINE__, op_name)); + + BOOST_ASIO_CORO_REENTER(this) + { + if (jar) + { + + auto cc = jar->get(this_->host(), this_->use_ssl_, path); + if (!cc.empty()) + headers.set(http::field::cookie, cc); + else + headers.erase(http::field::cookie); + } + else + headers.erase(http::field::cookie); + + headers.set(http::field::host, this_->host_); + if (headers.count(http::field::user_agent) > 0) + headers.set(http::field::user_agent, "Requests-" BOOST_BEAST_VERSION_STRING); + + if (!this_->is_open()) + { + BOOST_REQUESTS_YIELD this_->next_layer_.next_layer().async_connect(this_->endpoint_, std::move(self)); + + if (!ec && this_->use_ssl_) + { + BOOST_REQUESTS_YIELD this_->next_layer_.async_handshake(asio::ssl::stream_base::client, std::move(self)); + } + if (ec) + break; + } + + websocket_ = std::allocate_shared>( + asio::get_associated_allocator(self), + detail::optional_ssl_stream<>(std::move(this_->next_layer_), this_->use_ssl_), + beast::websocket::response_type{}); + websocket_->first.set_option( + beast::websocket::stream_base::decorator( + [&headers = headers](beast::websocket::request_type & req) + { + for (auto & hd : headers) + if (hd.name() == beast::http::field::unknown) + req.set(hd.name_string(), hd.value()); + else + req.set(hd.name(), hd.value()); + })); + BOOST_REQUESTS_YIELD websocket_->first.async_handshake(websocket_->second, this_->host_, core::string_view(path), std::move(self)); + if (ec) + break; + + if (jar) + { + auto cookie_itr = websocket_->second.find(http::field::set_cookie); + if (cookie_itr != websocket_->second.end()) + { + auto f = requests::parse_set_cookie_field(cookie_itr->value()); + if (f) + jar->set(*f, this_->host()); + else + { + ec = f.error(); + break; + } + } + } + } + // coro is complete + if (is_complete()) + { + if (!websocket_) + return self.complete(ec, websocket{detail::optional_ssl_stream<>{get_executor()}}); + else + { + auto tmp = std::move(websocket_->first); + websocket_ = nullptr; + return self.complete(ec, std::move(tmp)); + } + } +} + + +void connection_impl::async_upgrade_impl(asio::any_completion_handler handler, + connection_impl * this_, urls::pct_string_view path, + http::fields & headers, cookie_jar * jar) +{ + return asio::async_compose, void(error_code, websocket)>( + async_upgrade_op{this_->shared_from_this(), path, headers, jar}, handler, this_->get_executor()); } + } } } diff --git a/src/fields/location.cpp b/src/fields/location.cpp deleted file mode 100644 index e9a184b..0000000 --- a/src/fields/location.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#include - -namespace boost -{ -namespace requests -{ - -system::result interpret_location( - core::string_view current_target, - core::string_view location) -try -{ - static thread_local urls::static_url buffer; - auto nw = urls::parse_uri_reference(location); - if (nw.has_error()) - return nw.error(); - - if (nw->is_path_absolute()) - { - if (!nw->has_fragment()) - { - auto ct = urls::parse_uri_reference(current_target); - if (ct.has_error()) - return ct.error(); - - if (ct->has_fragment()) - { - buffer = *nw; - buffer.set_encoded_fragment(ct->encoded_fragment()); - return buffer; - } - } - return nw; - } - - auto ct = urls::parse_uri_reference(current_target); - if (ct.has_error()) - return ct.error(); - - buffer = *ct; - - auto segs = buffer.encoded_segments(); - for (auto ns : nw->encoded_segments()) - segs.push_back(ns); - - if (nw->has_fragment()) - buffer.set_encoded_fragment(nw->encoded_fragment()); - buffer.normalize(); - return buffer; -} -catch(system_error & se) -{ - return se.code(); -} - -} -} diff --git a/src/json.cpp b/src/json.cpp new file mode 100644 index 0000000..e4fa8c0 --- /dev/null +++ b/src/json.cpp @@ -0,0 +1,144 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +namespace boost +{ +namespace requests +{ + +auto as_json(const response & res, + json::storage_ptr ptr, + system::error_code & ec) -> json::value +{ + boost::json::parser ps; + ps.write(res.string_view(), ec); + if (ec) + return nullptr; + else + return ps.release(); +} + +auto as_json(const response & res, json::storage_ptr ptr) -> json::value +{ + boost::system::error_code ec; + auto rs = as_json(res, ptr, ec); + if (ec) + urls::detail::throw_system_error(ec); + + return rs; +} + +auto as_json(const response & res, system::error_code & ec) -> json::value +{ + return as_json(res, json::storage_ptr(), ec); +} + +namespace json +{ + + +json::value read_json(stream & str, json::storage_ptr ptr) +{ + ::boost::json::stream_parser sp{ptr}; + char buffer[BOOST_REQUESTS_CHUNK_SIZE]; + while (!sp.done() && !str.done()) + { + const auto n = str.read_some(asio::buffer(buffer)); + sp.write_some(buffer, n); + } + sp.finish(); + return sp.release(); +} + +json::value read_json(stream & str, json::storage_ptr ptr, system::error_code & ec) +{ + ::boost::json::stream_parser sp{ptr}; + char buffer[BOOST_REQUESTS_CHUNK_SIZE]; + while (!sp.done() && !ec && !str.done()) + { + const auto n = str.read_some(asio::buffer(buffer), ec); + if (ec) + break; + sp.write_some(buffer, n, ec); + } + if (!ec) + sp.finish(ec); + if (ec) + return json::value(); + + return sp.release(); +} + +namespace detail +{ + +struct async_read_json_op : asio::coroutine +{ + constexpr static const char * op_name = "async_read_json_op"; + using executor_type = typename stream::executor_type; + executor_type get_executor() {return str.get_executor(); } + + stream &str; + + struct state_t + { + state_t(json::storage_ptr ptr) : sp(std::move(ptr)) {} + ::boost::json::stream_parser sp; + char buffer[BOOST_REQUESTS_CHUNK_SIZE]; + }; + + using allocator_type = asio::any_completion_handler_allocator; + std::unique_ptr> state; + + async_read_json_op(allocator_type alloc, stream * str, json::storage_ptr ptr) + : str(*str), state{allocate_unique(alloc, std::move(ptr))} {} + + template + void operator()(Self && self, system::error_code ec = {}, std::size_t n = 0u) + { + BOOST_ASIO_CORO_REENTER(this) + { + while (!state->sp.done() && !str.done() && !ec) + { + BOOST_REQUESTS_YIELD str.async_read_some(asio::buffer(state->buffer), std::move(self)); + if (ec) + return self.complete(ec, nullptr); + state->sp.write_some(state->buffer, n, ec); + } + if (!ec) + state->sp.finish(ec); + if (ec) + return self.complete(ec, nullptr); + + auto r = state->sp.release(); + state.reset(); + return self.complete(ec, std::move(r)); + } + } +}; + +void async_read_json_impl( + asio::any_completion_handler handler, + stream * str, json::storage_ptr ptr) +{ + return asio::async_compose, + void(error_code, json::value)>( + async_read_json_op{ + asio::get_associated_allocator(handler), + str, std::move(ptr)}, + handler, + str->get_executor()); +} + +} + +} + +} +} \ No newline at end of file diff --git a/src/request.cpp b/src/request.cpp new file mode 100644 index 0000000..82b1e4f --- /dev/null +++ b/src/request.cpp @@ -0,0 +1,652 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include + +#include + +namespace boost +{ +namespace requests +{ +namespace detail +{ + + +std::pair +request_stream_impl( + connection & conn, + http::verb method, + urls::pct_string_view path, + source_ptr source, + request_parameters req, + system::error_code & ec) +{ + response_base::history_type h; + auto s = conn.ropen(method, path, req.headers, *source, req.jar, ec); + + urls::url base, new_url; + + while (is_redirect(s.headers().result()) && !ec) + { + beast::flat_buffer buffer; + s.read(buffer, ec); + auto & res = h.emplace_back(std::move(s).headers(), std::move(buffer)); + + if (ec) + break; + + auto loc_itr = res.find(http::field::location); + if (loc_itr == res.end()) + { + BOOST_REQUESTS_ASSIGN_EC(ec, error::invalid_redirect); + break ; + } + + const auto proto = conn.endpoint().protocol(); + + if ((proto == asio::ip::tcp::v4()) || proto == asio::ip::tcp::v6()) + base.set_scheme(conn.uses_ssl() ? "https" : "http"); + else if (proto == asio::local::stream_protocol()) + base.set_scheme("unix"); + else + base.set_scheme("unknown"); + base.set_path(path); + auto ref = urls::parse_uri_reference(loc_itr->value()); + if (ref.has_error()) + { + ec = ref.error(); + break; + } + const auto url = urls::resolve(base, *ref, new_url); + if (url.has_error()) + { + ec = url.error(); + break; + } + + if ((new_url.has_authority() && + (conn.host() != new_url.encoded_host() || + !same_endpoint_on_host(new_url, conn.endpoint()))) || + req.opts.redirect == requests::redirect_mode::none) + { + BOOST_REQUESTS_ASSIGN_EC(ec, error::forbidden_redirect); + break ; + } + if (--req.opts.max_redirects == 0) + { + BOOST_REQUESTS_ASSIGN_EC(ec, error::too_many_redirects); + break ; + } + + path = new_url.encoded_resource(); + source->reset(); + s = conn.ropen(method, path, req.headers, *source, req.jar, ec); + } + + return std::make_pair(std::move(s), std::move(h)); +} + + +std::pair +request_stream_impl( + connection_pool & pool, + http::verb method, + urls::pct_string_view path, + source_ptr source, + request_parameters req, + system::error_code & ec) +{ + auto conn = pool.borrow_connection(ec); + if (ec) + return std::pair{stream{pool.get_executor(), nullptr}, history{}}; + + auto res = request_stream_impl(conn, method, path, std::move(source), std::move(req), ec); + pool.return_connection(std::move(conn)); + return res; +} + +std::pair +request_stream_impl( + session & sess, + http::verb method, + urls::url_view url, + source_ptr source, + http::headers headers, + system::error_code & ec) +{ + response_base::history_type h; + auto pool = sess.get_pool(url, ec); + if (ec) + return std::pair{stream{sess.get_executor(), nullptr}, history{}}; + auto conn = pool->borrow_connection(ec); + if (ec) + return std::pair{stream{sess.get_executor(), nullptr}, history{}}; + + auto opts = sess.options(); + + const auto is_secure = (url.scheme_id() == urls::scheme::https); + if (!is_secure && opts.enforce_tls) + { + BOOST_REQUESTS_ASSIGN_EC(ec, error::insecure); + return std::pair{stream{sess.get_executor(), nullptr}, history{}}; + } + + auto s = conn.ropen(method, url.encoded_path(), headers, *source, &sess.jar(), ec); + urls::url new_url; + + while (is_redirect(s.headers().result()) && !ec) + { + beast::flat_buffer buffer; + s.read(buffer, ec); + auto & res = h.emplace_back(std::move(s).headers(), std::move(buffer)); + + if (ec) + break; + + auto loc_itr = res.find(http::field::location); + if (loc_itr == res.end()) + { + BOOST_REQUESTS_ASSIGN_EC(ec, error::invalid_redirect); + break ; + } + + auto ref = urls::parse_uri_reference(loc_itr->value()); + if (ref.has_error()) + { + ec = ref.error(); + break; + } + const auto r = urls::resolve(url, *ref, new_url); + if (r.has_error()) + { + ec = r.error(); + break; + } + + if (!should_redirect(opts.redirect, url, new_url)) + { + BOOST_REQUESTS_ASSIGN_EC(ec, error::forbidden_redirect); + break ; + } + + if (--opts.max_redirects == 0) + { + BOOST_REQUESTS_ASSIGN_EC(ec, error::too_many_redirects); + break ; + } + + // check if we need a new connection. + if (new_url.scheme() != url.scheme() + || new_url.authority() != url.authority()) + { + pool->return_connection(std::move(conn)); + pool = sess.get_pool(new_url, ec); + if (!ec) + conn = pool->borrow_connection(ec); + if (ec) + break; + } + url = new_url; + + source->reset(); + s = conn.ropen(method, url.encoded_path(), headers, *source, &sess.jar(), ec); + } + + pool->return_connection(std::move(conn)); + return std::make_pair(std::move(s), std::move(h)); +} + +struct async_request_stream_op : asio::coroutine +{ + connection & conn; + struct state_t + { + state_t(http::verb method, + urls::pct_string_view path, + source_ptr source, + request_parameters req, + asio::any_io_executor executor) + : s{std::move(executor), nullptr}, + method(method), path(path), source(std::move(source)), req(std::move(req)) {} + + stream s; + source_ptr source; + + response_base::history_type h; + urls::url base, new_url; + + http::verb method; + urls::pct_string_view path; + request_parameters req; + + beast::flat_buffer buffer; + }; + + using allocator_type = asio::any_completion_handler_allocator; + std::unique_ptr> state; + + async_request_stream_op( + connection & conn, + http::verb method, + urls::pct_string_view path, + source_ptr source, + request_parameters req, + allocator_type alloc) + : conn(conn), + state(allocate_unique(alloc, method, path, std::move(source), std::move(req), conn.get_executor())) + {} + + + + template + void operator()(Self && self, + boost::system::error_code ec = {}, + variant2::variant arg = {}) + { + reenter(this) + { + yield conn.async_ropen(state->method, state->path, state->req.headers, *state->source, state->req.jar, std::move(self)); + if (!ec) + state->s = std::move(variant2::get<2>(arg)); + while (is_redirect(state->s.headers().result())) + { + yield state->s.async_read(state->buffer, std::move(self)); + { + auto & res = state->h.emplace_back(std::move(state->s).headers(), std::move(state->buffer)); + + if (ec) + break; + + auto loc_itr = res.find(http::field::location); + if (loc_itr == res.end()) + { + BOOST_REQUESTS_ASSIGN_EC(ec, error::invalid_redirect); + break ; + } + + const auto proto = conn.endpoint().protocol(); + if ((proto == asio::ip::tcp::v4()) || proto == asio::ip::tcp::v6()) + state->base.set_scheme(conn.uses_ssl() ? "https" : "http"); + else if (proto == asio::local::stream_protocol()) + state->base.set_scheme("unix"); + else + state->base.set_scheme("unknown"); + state->base.set_path(state->path); + auto ref = urls::parse_uri_reference(loc_itr->value()); + if (ref.has_error()) + { + ec = ref.error(); + break; + } + const auto url = urls::resolve(state->base, *ref, state->new_url); + if (url.has_error()) + { + ec = url.error(); + break; + } + + if (state->new_url.has_authority() && + (conn.host() != state->new_url.encoded_host() || + !same_endpoint_on_host(state->new_url, conn.endpoint())) || + state->req.opts.redirect == requests::redirect_mode::none) + { + BOOST_REQUESTS_ASSIGN_EC(ec, error::forbidden_redirect); + break ; + } + + if (--state->req.opts.max_redirects == 0) + { + BOOST_REQUESTS_ASSIGN_EC(ec, error::too_many_redirects); + break ; + } + + state->path = state->new_url.encoded_resource(); + state->source->reset(); + } + yield conn.async_ropen(state->method, state->path, state->req.headers, *state->source, state->req.jar, std::move(self)); + state->s = std::move(variant2::get<2>(arg)); + } + } + + if (is_complete()) + { + auto s = std::move(state->s); + auto h = std::move(state->h); + state.reset(); + self.complete(ec, std::move(s), std::move(h)); + } + } +}; + + +struct async_request_stream_pool_op : asio::coroutine +{ + connection_pool & pool; + struct state_t + { + state_t(http::verb method, + urls::pct_string_view path, + source_ptr source, + request_parameters req, + asio::any_io_executor executor) + : c{std::move(executor)}, + method(method), path(path), source(std::move(source)), req(std::move(req)) {} + + connection c; + stream s{c.get_executor(), nullptr}; + source_ptr source; + + response_base::history_type h; + urls::url base, new_url; + + http::verb method; + urls::pct_string_view path; + request_parameters req; + + beast::flat_buffer buffer; + }; + + using allocator_type = asio::any_completion_handler_allocator; + std::unique_ptr> state; + + async_request_stream_pool_op( + connection_pool & pool, + http::verb method, + urls::pct_string_view path, + source_ptr source, + request_parameters req, + allocator_type alloc) + : pool(pool), + state(allocate_unique(alloc, method, path, std::move(source), std::move(req), pool.get_executor())) + {} + + + + template + void operator()(Self && self, + boost::system::error_code ec = {}, + variant2::variant arg = {}) + { + reenter(this) + { + yield pool.async_borrow_connection(std::move(self)); + if (ec) + goto pool_unavailable; + state->c = variant2::get<3>(std::move(arg)); + + yield state->c.async_ropen(state->method, state->path, state->req.headers, *state->source, state->req.jar, std::move(self)); + while (is_redirect(state->s.headers().result())) + { + yield state->s.async_read(state->buffer, std::move(self)); + { + auto & res = state->h.emplace_back(std::move(state->s).headers(), std::move(state->buffer)); + + if (ec) + break; + + auto loc_itr = res.find(http::field::location); + if (loc_itr == res.end()) + { + BOOST_REQUESTS_ASSIGN_EC(ec, error::invalid_redirect); + break ; + } + + + const auto proto = state->c.endpoint().protocol(); + if ((proto == asio::ip::tcp::v4()) || proto == asio::ip::tcp::v6()) + state->base.set_scheme(state->c.uses_ssl() ? "https" : "http"); + else if (proto == asio::local::stream_protocol()) + state->base.set_scheme("unix"); + else + state->base.set_scheme("unknown"); + state->base.set_path(state->path); + auto ref = urls::parse_uri_reference(loc_itr->value()); + if (ref.has_error()) + { + ec = ref.error(); + break; + } + const auto url = urls::resolve(state->base, *ref, state->new_url); + if (url.has_error()) + { + ec = url.error(); + break; + } + + if (state->new_url.has_authority() && + state->c.host() == state->new_url.encoded_host() && + !same_endpoint_on_host(state->new_url, state->c.endpoint())) + { + BOOST_REQUESTS_ASSIGN_EC(ec, error::forbidden_redirect); + break ; + } + + if (--state->req.opts.max_redirects == 0) + { + BOOST_REQUESTS_ASSIGN_EC(ec, error::too_many_redirects); + break ; + } + + state->path = state->new_url.encoded_resource(); + state->source->reset(); + } + yield state->c.async_ropen(state->method, state->path, state->req.headers, *state->source, state->req.jar, std::move(self)); + state->s = std::move(variant2::get<2>(arg)); + } + } + + if (is_complete()) + { + pool.return_connection(std::move(state->c)); + pool_unavailable: + auto s = std::move(state->s); + auto h = std::move(state->h); + state.reset(); + self.complete(ec, std::move(s), std::move(h)); + } + } +}; + + +struct async_request_stream_session_op : asio::coroutine +{ + session & sess; + struct state_t + { + state_t(http::verb method, + urls::url_view path, + source_ptr source, + request_options opts, + http::headers headers, + asio::any_io_executor executor) + : c{std::move(executor)}, + method(method), url(path), source(std::move(source)), opts(std::move(opts)), + headers(std::move(headers)) {} + + std::shared_ptr pool; + connection c; + stream s{c.get_executor(), nullptr}; + source_ptr source; + + response_base::history_type h; + urls::url new_url; + + http::verb method; + urls::url_view url; + request_options opts; + http::headers headers; + + beast::flat_buffer buffer; + }; + + using allocator_type = asio::any_completion_handler_allocator; + std::unique_ptr> state; + + async_request_stream_session_op( + session & sess, + http::verb method, + urls::url_view path, + source_ptr source, + http::headers headers, + allocator_type alloc) + : sess(sess), + state(allocate_unique(alloc, method, path, std::move(source), sess.options(), std::move(headers), sess.get_executor())) + {} + + + + template + void operator()(Self && self, + boost::system::error_code ec = {}, + variant2::variant> arg = {}) + { + reenter(this) + { + yield sess.async_get_pool(state->url, std::move(self)); + if (!ec) + { + state->pool = variant2::get<4>(std::move(arg)); + yield state->pool->async_borrow_connection(std::move(self)); + } + if (ec) + goto pool_unavailable; + state->c = variant2::get<3>(std::move(arg)); + yield state->c.async_ropen(state->method, state->url.encoded_resource(), + state->headers, *state->source, &sess.jar(), std::move(self)); + state->s = std::move(variant2::get<2>(arg)); + while (is_redirect(state->s.headers().result())) + { + + yield state->s.async_read(state->buffer, std::move(self)); + { + auto & res = state->h.emplace_back(std::move(state->s).headers(), std::move(state->buffer)); + + if (ec) + break; + + auto loc_itr = res.find(http::field::location); + if (loc_itr == res.end()) + { + BOOST_REQUESTS_ASSIGN_EC(ec, error::invalid_redirect); + break ; + } + + auto ref = urls::parse_uri_reference(loc_itr->value()); + if (ref.has_error()) + { + ec = ref.error(); + break; + } + const auto r = urls::resolve(state->url, *ref, state->new_url); + if (r.has_error()) + { + ec = r.error(); + break; + } + if (!should_redirect(state->opts.redirect, state->url, state->new_url)) + { + BOOST_REQUESTS_ASSIGN_EC(ec, error::forbidden_redirect); + break ; + } + if (--state->opts.max_redirects == 0) + { + BOOST_REQUESTS_ASSIGN_EC(ec, error::too_many_redirects); + break ; + } + + } + + // check if we need a new connection. + if (state->new_url.scheme() != state->url.scheme() + || state->new_url.authority() != state->url.authority()) + { + state->pool->return_connection(std::move(state->c)); + yield sess.async_get_pool(state->new_url, std::move(self)); + state->pool = variant2::get<4>(std::move(arg)); + if (!ec) + { + yield state->pool->async_borrow_connection(std::move(self)); + state->c = variant2::get<3>(std::move(arg)); + } + if (ec) + break; + } + + state->url = state->new_url; + state->source->reset(); + yield state->c.async_ropen(state->method, state->url.encoded_resource(), + state->headers, *state->source, &sess.jar(), std::move(self)); + state->s = std::move(variant2::get<2>(arg)); + + } + } + + if (is_complete()) + { + state->pool->return_connection(std::move(state->c)); + pool_unavailable: + auto s = std::move(state->s); + auto h = std::move(state->h); + state.reset(); + self.complete(ec, std::move(s), std::move(h)); + } + } +}; + +void async_request_stream_impl( + connection & conn, + http::verb method, + urls::pct_string_view path, + source_ptr source, + request_parameters req, + asio::any_completion_handler handler) +{ + return asio::async_compose, + void(error_code, stream, history)>( + async_request_stream_op{ + conn, method, path, std::move(source), std::move(req), asio::get_associated_allocator(handler)}, + handler, + conn.get_executor()); +} + +void async_request_stream_impl( + connection_pool & pool, + http::verb method, + urls::pct_string_view path, + source_ptr source, + request_parameters req, + asio::any_completion_handler handler) +{ + return asio::async_compose, + void(error_code, stream, history)>( + async_request_stream_pool_op{ + pool, method, path, std::move(source), std::move(req), asio::get_associated_allocator(handler)}, + handler, + pool.get_executor()); +} + +void async_request_stream_impl( + session & sess, + http::verb method, + urls::url_view path, + source_ptr source, + http::headers headers, + asio::any_completion_handler handler) +{ + return asio::async_compose, + void(error_code, stream, history)>( + async_request_stream_session_op{ + sess, method, path, std::move(source), std::move(headers), asio::get_associated_allocator(handler)}, + handler, + sess.get_executor()); +} + +} +} +} \ No newline at end of file diff --git a/src/rfc/dates.cpp b/src/rfc/dates.cpp index cceccf3..64f1204 100644 --- a/src/rfc/dates.cpp +++ b/src/rfc/dates.cpp @@ -151,7 +151,7 @@ date_1123_t::parse( char const*& it, char const* end ) const noexcept -> - urls::error_types::result + system::result { namespace ug = boost::urls::grammar; using namespace detail; @@ -194,7 +194,7 @@ date_850_t::parse( char const*& it, char const* end ) const noexcept -> - urls::error_types::result + system::result { namespace ug = boost::urls::grammar; using namespace detail; @@ -231,7 +231,7 @@ date_asctime_t::parse( char const*& it, char const* end ) const noexcept -> - urls::error_types::result + system::result { namespace ug = boost::urls::grammar; using namespace detail; @@ -284,7 +284,7 @@ http_date_t::parse( char const*& it, char const* end ) const noexcept -> - urls::error_types::result + system::result { namespace ug = boost::urls::grammar; auto res = ug::parse(it, end, ug::variant_rule(date_1123, date_850, date_asctime )); diff --git a/src/rfc/link.cpp b/src/rfc/link.cpp index a5ba286..e9a68c8 100644 --- a/src/rfc/link.cpp +++ b/src/rfc/link.cpp @@ -79,7 +79,7 @@ link_value_t::parse( char const*& it, char const* end ) const noexcept -> - urls::error_types::result + system::result { namespace ug = boost::urls::grammar; using namespace detail; diff --git a/src/rfc/quoted_string.cpp b/src/rfc/quoted_string.cpp index 74360e7..744d685 100644 --- a/src/rfc/quoted_string.cpp +++ b/src/rfc/quoted_string.cpp @@ -18,7 +18,7 @@ quoted_string_t::parse( char const*& it, char const* end ) const noexcept -> - urls::error_types::result + system::result { namespace ug = boost::urls::grammar; diff --git a/src/session.cpp b/src/session.cpp index b96f911..dc939a8 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -15,123 +15,6 @@ namespace boost { namespace requests { - -auto session::ropen( - beast::http::verb method, - urls::url_view url, - http::fields & headers, - source & src, - system::error_code & ec) -> stream -{ - /*auto opts = options_; - - response_base::history_type history{headers.get_allocator()}; - - auto do_ropen = - [&](http::fields & hd, urls::pct_string_view target, request_options opts) -> stream - { - auto p = get_pool(url, ec); - if (ec) - return stream{get_executor(), nullptr}; - - return p->ropen(method, target, hd, src, &jar_, ec); - }; - - const auto is_secure = (url.scheme_id() == urls::scheme::https) - || (url.scheme_id() == urls::scheme::wss); - - auto host = url.encoded_host(); - - if (!is_secure && opts.enforce_tls) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::insecure); - return stream{get_executor(), nullptr}; - } - - { - auto cc = jar_.get(host, is_secure, url.encoded_path()); - if (!cc.empty()) - headers.set(http::field::cookie, cc); - } - - - auto str = do_ropen(headers, url.encoded_target(), opts); - - if (!ec) // all good - return str; - - if (opts.max_redirects == str.history().size()) - return str; - - urls::url url_cache; - while (ec == error::forbidden_redirect) - { - ec.clear(); - if (str.history().empty()) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::invalid_redirect); - break; - } - opts.max_redirects -= str.history().size(); - const auto & last = str.history().back(); - auto rc = last.base().result(); - - auto loc_itr = last.base().find(http::field::location); - - if (opts.max_redirects == 0) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::too_many_redirects); - break ; - } - - if (((rc != http::status::moved_permanently) - && (rc != http::status::found) - && (rc != http::status::temporary_redirect) - && (rc != http::status::permanent_redirect)) - || (loc_itr == last.base().end())) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::invalid_redirect); - break; - } - - const auto nurl = interpret_location(url.encoded_resource(), loc_itr->value()); - if (nurl.has_error()) - { - ec = nurl.error(); - break; - } - - if (!should_redirect(options_.redirect, url, *nurl)) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::forbidden_redirect); - break ; - } - - if (nurl->has_authority()) - url = *nurl; - else - { - url_cache = url; - url_cache.set_encoded_path(nurl->encoded_path()); - url = url_cache; - } - - - { - auto cc = jar_.get(host, is_secure, url.encoded_path()); - if (!cc.empty()) - headers.set(http::field::cookie, cc); - } - history.insert(history.end(), - std::make_move_iterator(std::move(str).history().begin()), - std::make_move_iterator(std::move(str).history().end())); - - str = do_ropen(headers, url.encoded_target(), opts); - } - str.prepend_history(std::move(history)); - return str;*/ -} - auto session::make_request_(http::fields fields) -> requests::request_parameters { return requests::request_parameters{ std::move(fields), @@ -145,7 +28,8 @@ urls::url session::normalize_(urls::url_view in) urls::url res; if (!in.has_scheme() || in.scheme() == "https") { - res.set_scheme("https"); + if (!in.has_scheme()) + res.set_scheme("https"); res.set_encoded_host(in.encoded_host()); if (in.has_port()) res.set_port(in.port()); @@ -198,11 +82,9 @@ struct session::async_get_pool_op : asio::coroutine session *this_; urls::url_view url; - const bool is_https; async_get_pool_op(session *this_, urls::url_view url) - : this_(this_), url(url), - is_https((url.scheme_id() == urls::scheme::https) || (url.scheme_id() == urls::scheme::wss)) + : this_(this_), url(url) {} std::shared_ptr p; @@ -229,101 +111,13 @@ struct session::async_get_pool_op : asio::coroutine this_->pools_.emplace(url, p); else p = itr->second; - return self.complete(ec, p); } + return self.complete(ec, p); } } }; - -struct session::async_ropen_op : asio::coroutine -{ - constexpr static const char * op_name = "session::async_ropen_op"; - - using executor_type = asio::any_io_executor; - executor_type get_executor() {return this_->get_executor(); } - - session * this_; - - http::verb method; - urls::url url; - struct request_options opts; - - bool is_secure = (url.scheme_id() == urls::scheme::https) - || (url.scheme_id() == urls::scheme::wss); - - http::fields & headers; - source & src; - - async_ropen_op(session * this_, - http::verb method, - urls::url_view path, - source & src, - http::fields & headers) - : this_(this_), method(method), url(path), opts(this_->options_), headers(headers), src(src) - { - } - - using completion_signature_type = void(system::error_code, stream); - using step_signature_type = void(system::error_code, variant2::variant, stream>); - - template - void operator()(Self && self, system::error_code ec = {}, - variant2::variant, stream> s = variant2::monostate{}); -}; - - -template -void session::async_ropen_op::operator()( - Self && self, system::error_code ec, - variant2::variant, stream> s) -{ - BOOST_ASIO_CORO_REENTER(this) - { - headers.set(beast::http::field::host, url.encoded_host_and_port()); - headers.set(beast::http::field::user_agent, "Requests-" BOOST_BEAST_VERSION_STRING); - - if (!is_secure && this_->options_.enforce_tls) - { - BOOST_REQUESTS_ASSIGN_EC(ec, error::insecure); - BOOST_REQUESTS_YIELD { - auto exec = asio::get_associated_immediate_executor(self, this_->get_executor()); - asio::dispatch(exec, asio::append(std::move(self), ec)); - }; - } - - { - auto cc = this_->jar_.get(url.encoded_host(), is_secure, url.encoded_path()); - if (!cc.empty()) - headers.set(http::field::cookie, cc); - } - - BOOST_REQUESTS_YIELD this_->async_get_pool(url, std::move(self)); - BOOST_REQUESTS_YIELD variant2::get<1>(s)->async_ropen(method, url.encoded_resource(), - headers, src, opts, &this_->jar_, std::move(self)); - - return self.complete(ec, std::move(variant2::get<2>(s))); - - } - if (is_complete()) - self.complete(ec, stream{this_->get_executor(), nullptr}); -} - -BOOST_REQUESTS_DECL -void session::async_ropen_impl( - asio::any_completion_handler handler, - session * sess, http::verb method, urls::url_view path, source * src, http::fields * headers) -{ - asio::async_compose< - asio::any_completion_handler, - void (boost::system::error_code, stream)>( - async_ropen_op{ - sess, method, path, *src, *headers}, handler, sess->get_executor() - ); -} - - void session::async_get_pool_impl( asio::any_completion_handler)> handler, session * sess, urls::url_view url) diff --git a/src/stream.cpp b/src/stream.cpp index 50d8f56..7fc13d6 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -24,6 +24,9 @@ void stream::dump(system::error_code & ec) { parser_->get().body().data = data; parser_->get().body().size = sizeof(data); + if (parser_->chunked()) + impl_->handle_chunked_.buffer_space = sizeof(data); + impl_->do_read_some_(*parser_, ec); if (!ec && !parser_->is_done()) @@ -101,8 +104,17 @@ void stream::async_read_some_op::operator()(Self && self, system::error_code ec, this_->parser_->get().body().data = buffer.data(); this_->parser_->get().body().size = buffer.size(); + if (this_->parser_->chunked()) + { + this_->impl_->handle_chunked_.chunked_body = {}; + this_->impl_->handle_chunked_.buffer_space = buffer.size(); + } BOOST_REQUESTS_YIELD this_->impl_->do_async_read_some_(*this_->parser_, std::move(self)); + if (this_->parser_->chunked()) + res = asio::buffer_copy(buffer, + asio::buffer(this_->impl_->handle_chunked_.chunked_body, + this_->impl_->handle_chunked_.chunked_body.size())); if (!this_->parser_->is_done()) { this_->parser_->get().body().more = true; @@ -178,7 +190,7 @@ void stream::async_dump_op::operator()(Self && self, system::error_code ec, std: { this_->parser_->get().body().data = buffer; this_->parser_->get().body().size = BOOST_REQUESTS_CHUNK_SIZE; - + this_->impl_->handle_chunked_.buffer_space = BOOST_REQUESTS_CHUNK_SIZE; BOOST_REQUESTS_YIELD this_->impl_->do_async_read_some_(*this_->parser_, std::move(self)); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 82375f9..3868731 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,11 +1,44 @@ +enable_testing() -file(GLOB_RECURSE ALL_TEST_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +add_executable(boost_requests_unit_tests + main_test.cpp + fixtures/server.cpp + unit/detail/condition_variable.cpp + unit/connection.cpp + unit/connection_pool.cpp + unit/cookie_grammar.cpp + unit/cookie_jar.cpp + unit/fixed_token_rule.cpp + unit/form.cpp + unit/keep_alive.cpp + unit/link.cpp + unit/quoted_string.cpp + unit/raw_string.cpp + unit/redirect.cpp + unit/request_stream.cpp + unit/rfc.cpp + unit/service.cpp + unit/session.cpp + unit/source.cpp + unit/token_rule.cpp +) -add_executable(boost_requests_tests ${ALL_TEST_FILES}) -target_link_libraries(boost_requests_tests PUBLIC Boost::requests Boost::filesystem rt) +target_link_libraries(boost_requests_unit_tests PUBLIC + Boost::requests Boost::filesystem Boost::context Boost::unit_test_framework rt) -target_compile_definitions(boost_requests_tests PUBLIC - BOOST_REQUESTS_SEPARATE_COMPILATION=1 - BOOST_FILESYSTEM_NO_DEPRECATED=1) +add_test(NAME boost_requests_unit_tests COMMAND boost_requests_unit_tests) +add_executable(boost_requests_httpbin_tests + main_test.cpp + fixtures/httpbin.cpp + httpbin/cookie.cpp + httpbin/json.cpp + httpbin/method.cpp +) + +target_link_libraries(boost_requests_httpbin_tests PUBLIC + Boost::requests Boost::filesystem Boost::context Boost::unit_test_framework rt) + +add_test(NAME boost_requests_httpbin_tests COMMAND boost_requests_httpbin_tests) + diff --git a/test/connection.cpp b/test/connection.cpp deleted file mode 100644 index 7d20118..0000000 --- a/test/connection.cpp +++ /dev/null @@ -1,758 +0,0 @@ -// Copyright (c) 2021 Klemens D. Morgenstern -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "doctest.h" -#include "string_maker.hpp" - -#include - -namespace requests = boost::requests; -namespace filesystem = requests::filesystem; -namespace asio = boost::asio; -namespace json = boost::json; -namespace urls = boost::urls; -using boost::system::error_code ; - -#if defined(BOOST_REQUESTS_USE_STD_FS) -using fs_error_code = std::error_code; -#else -using fs_error_code = boost::system::error_code; -#endif - -inline std::string httpbin() -{ - std::string url = "httpbin.org"; - if (auto p = ::getenv("BOOST_REQUEST_HTTPBIN")) - url = p; - return url; -} - - - - -TEST_SUITE_BEGIN("connection"); - -TEST_CASE("sync-http") -{ - urls::url u = urls::parse_uri("http://" + httpbin()).value(); - auto url = u.encoded_host_and_port(); - asio::io_context ctx; - asio::ssl::context sslctx{asio::ssl::context_base::tls_client}; - - sslctx.set_verify_mode(asio::ssl::verify_peer); - sslctx.set_default_verify_paths(); - - requests::connection hc(ctx.get_executor()); - hc.set_host(url); - hc.use_ssl(false); - - asio::ip::tcp::endpoint ep; - if (url == "localhost") - ep = {asio::ip::make_address("127.0.0.1"), u.port_number()}; - else - { - asio::ip::tcp::resolver rslvr{ctx}; - ep = *rslvr.resolve(u.host_name(), u.has_port() ? u.port() : u.scheme()).begin(); - } - hc.connect(ep); - - // headers - { - auto hdr = request(hc, requests::http::verb::get, urls::url_view("/headers"), - requests::empty{}, - {requests::headers({{"Test-Header", "it works"}}), {false}}); - - auto hd = as_json(hdr).at("headers"); - CHECK_HTTP_RESULT(hdr.headers); - CHECK(hd.at("Host") == json::value(url)); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - // stream - { - auto str = hc.ropen(requests::http::verb::get, urls::url_view("/get"), - requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}); - - CHECK_HTTP_RESULT(str.headers()); - json::stream_parser sp; - - char buf[32]; - - error_code ec; - while (!str.done() && !ec) - { - auto sz = str.read_some(asio::buffer(buf), ec); - CHECK(ec == error_code{}); - sp.write_some(buf, sz, ec); - CHECK(ec == error_code{}); - } - - auto hd = sp.release().at("headers"); - CHECK(hd.at("Host") == json::value(url)); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - - // stream-all - { - auto str = hc.ropen(requests::http::verb::get, urls::url_view("/get"), - requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}); - - CHECK_HTTP_RESULT(str.headers()); - std::string buf; - auto bb = asio::dynamic_buffer(buf); - - error_code ec; - CHECK(str.read(bb, ec) > 0u); - - CHECK(ec == error_code{}); - - auto val = json::parse(buf); - - auto hd = val.at("headers"); - CHECK(hd.at("Host") == json::value(url)); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - - // stream-dump - { - auto str = hc.ropen(requests::http::verb::get, urls::url_view("/get"), - requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}); - CHECK_HTTP_RESULT(str.headers()); - str.dump(); - } - - // get - { - auto hdr = get(hc, urls::url_view("/get"), {requests::headers({{"Test-Header", "it works"}}), {false}}); - - auto hd = as_json(hdr).at("headers"); - CHECK_HTTP_RESULT(hdr.headers); - - CHECK(hd.at("Host") == json::value(url)); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - // get-redirect - { - auto hdr = get(hc, urls::url_view("/redirect-to?url=%2Fget"), - {requests::headers({{"Test-Header", "it works"}}), {false}}); - - CHECK_HTTP_RESULT(hdr.headers); - CHECK(hdr.history.size() == 1u); - CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); - - auto hd = as_json(hdr).at("headers"); - CHECK(hd.at("Host") == json::value(url)); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - // too-many-redirects - { - error_code ec; - auto res = get(hc, urls::url_view("/redirect/10"), {{}, {false, requests::redirect_mode::private_domain, 5}}, ec); - CHECK_HTTP_RESULT(res.headers); - CHECK(res.history.size() == 5); - CHECK(res.headers.begin() == res.headers.end()); - CHECK(ec == requests::error::too_many_redirects); - } - - // download - { - error_code ec; - const auto tmp = filesystem::temp_directory_path(ec); - check_ec(ec); - const auto target = tmp / "requests-test.png"; - if (filesystem::exists(target)) - filesystem::remove(target); - - CHECK(!filesystem::exists(target)); - CHECK_MESSAGE( filesystem::exists(tmp), tmp); - auto res = download(hc, urls::url_view("/image"), {{}, {false}}, target); - CHECK_HTTP_RESULT(res.headers); - - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec_; - filesystem::remove(target, ec_); - } - - - // download-redirect - { - const auto target = filesystem::temp_directory_path() / "requests-test.png"; - if (filesystem::exists(target)) - filesystem::remove(target); - - CHECK(!filesystem::exists(target)); - auto res = download(hc, urls::url_view("/redirect-to?url=%2Fimage"), {{}, {false}}, target); - CHECK_HTTP_RESULT(res.headers); - CHECK(res.history.size() == 1u); - CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); - - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec; - filesystem::remove(target, ec); - } - - - // download-too-many-redirects - { - error_code ec; - const auto target = filesystem::temp_directory_path() / "requests-test.html"; - if (filesystem::exists(target)) - filesystem::remove(target); - auto res = download(hc, urls::url_view("/redirect/10"), {{}, {false, requests::redirect_mode::private_domain, 3}}, target, ec); - - CHECK_HTTP_RESULT(res.headers); - CHECK(res.history.size() == 3); - CHECK(res.headers.begin() == res.headers.end()); - - CHECK(ec == requests::error::too_many_redirects); - CHECK(!filesystem::exists(target)); - } - - - // delete - { - auto hdr = delete_(hc, urls::url_view("/delete"), json::value{{"test-key", "test-value"}}, {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - } - - // patch-json - { - json::value msg {{"test-key", "test-value"}}; - auto hdr = patch(hc, urls::url_view("/patch"), msg, {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - CHECK(js.at("json") == msg); - } - - // patch-form - { - auto hdr = patch(hc, urls::url_view("/patch"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } - - // put-json - { - json::value msg {{"test-key", "test-value"}}; - auto hdr = put(hc, urls::url_view("/put"), msg, {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - CHECK(js.at("json") == msg); - } - - // put-form - { - auto hdr = put(hc, urls::url_view("/put"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } - - // post-json - { - json::value msg {{"test-key", "test-value"}}; - auto hdr = post(hc, urls::url_view("/post"), msg, {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - CHECK(js.at("json") == msg); - } - - // post-form - { - auto hdr = post(hc, urls::url_view("/post"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } - - // post-multipart-form - { - boost::system::error_code ec; - auto hdr = post(hc, urls::url_view("/post"), - requests::multi_part_form{{"foo", "data 1"}, {"bar", "data 2"}, {"foobar", "data 3"}}, - {{}, {false}}, ec); - check_ec(ec); - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK_MESSAGE(hdr.headers.result() == requests::http::status::ok, hdr.headers); - CHECK_MESSAGE(js.at("headers").at("Content-Type").as_string().starts_with("multipart/form-data"), - js.at("headers").at("Content-Type")); - CHECK(js.at("form") == json::value{{"foo", "data 1"}, {"bar", "data 2"}, {"foobar" , "data 3"}}); - } -} - -TEST_CASE("sync-https") -{ - asio::io_context ctx; - - asio::ssl::context sslctx{asio::ssl::context_base::tlsv13_client}; - sslctx.set_default_verify_paths(); - - requests::connection hc (ctx.get_executor(), sslctx); - - const auto host = "google.com"; - hc.set_host(host); - hc.use_ssl(true); - asio::ip::tcp::resolver rslvr{ctx}; - asio::ip::tcp::endpoint ep = *rslvr.resolve(host, "https").begin(); - error_code ec; - hc.connect(ep, ec); - CHECK_FALSE(ec); - - // header - { - auto hdr = request(hc, requests::http::verb::head, urls::url_view("/"), requests::empty{}, {{}, {false}}, ec); - CHECK_FALSE(ec); - CHECK_HTTP_RESULT(hdr.headers); - } - - // stream - { - auto str = hc.ropen(requests::http::verb::get, urls::url_view("/"), - requests::empty{}, {{}, {false}}); - - CHECK_HTTP_RESULT(str.headers()); - json::stream_parser sp; - - char buf[32]; - while (!str.done()) - auto sz = str.read_some(asio::buffer(buf)); - } - - - // stream-all - { - auto str = hc.ropen(requests::http::verb::get, urls::url_view("/"), - requests::empty{}, {{}, {false}}); - - CHECK_HTTP_RESULT(str.headers()); - std::string buf; - auto bb = asio::dynamic_buffer(buf); - CHECK(str.read(bb) > 0u); - } - - - // stream-dump - { - auto str = hc.ropen(requests::http::verb::get, urls::url_view("/"), - requests::empty{}, {{}, {false}}); - CHECK_HTTP_RESULT(str.headers()); - str.dump(); - } - - // get - { - auto hdr = get(hc, urls::url_view("/"), {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - } -} - - -void run_tests(error_code ec, - requests::connection & conn, - urls::url_view url) -{ - namespace http = requests::http; - namespace filesystem = requests::filesystem; - - check_ec(ec); - - // headers - async_request( - conn, - http::verb::get, urls::url_view("/headers"), - requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - check_ec(ec); - auto hd = as_json(hdr).at("headers"); - CHECK_HTTP_RESULT(hdr.headers); - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - })); - - // headers - async_get(conn, - urls::url_view("/get"), - {requests::headers({{"Test-Header", "it works"}}), {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - check_ec(ec); - auto hd = as_json(hdr).at("headers"); - CHECK_HTTP_RESULT(hdr.headers); - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - - })); - - // get-redirect - async_get(conn, urls::url_view("/redirect-to?url=%2Fget"), - {requests::headers({{"Test-Header", "it works"}}), {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - { - CHECK(hdr.history.size() == 1u); - CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); - CHECK_HTTP_RESULT(hdr.headers); - - auto hd = as_json(hdr).at("headers"); - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - })); - - // too-many-redirects - async_get(conn, urls::url_view("/redirect/10"), - {{}, {false, requests::redirect_mode::private_domain, 5}}, - tracker( - [url](error_code ec, requests::response res) - { - CHECK_HTTP_RESULT(res.headers); - - CHECK(res.history.size() == 5); - CHECK(res.headers.begin() == res.headers.end()); - CHECK(ec == requests::error::too_many_redirects); - - })); - - { // download - auto pt = filesystem::temp_directory_path(); - const auto target = pt / "requests-test.png"; - if (filesystem::exists(target)) - filesystem::remove(target); - - CHECK(!filesystem::exists(target)); - async_download(conn, urls::url_view("/image"), {{}, {false}}, target, - tracker( - [url, target](error_code ec, requests::response_base res) - { - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK_HTTP_RESULT(res.headers); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec_; - filesystem::remove(target, ec_); - })); - } - - { - const auto target = filesystem::temp_directory_path() / "requests-test-2.png"; - if (filesystem::exists(target)) - filesystem::remove(target); - - CHECK(!filesystem::exists(target)); - // download-redirect - async_download(conn, urls::url_view("/redirect-to?url=%2Fimage"), {{}, {false}}, target, - tracker( - [url, target](error_code ec, requests::response_base res) - { - CHECK(res.history.size() == 1u); - CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); - CHECK_HTTP_RESULT(res.headers); - - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec_; - filesystem::remove(target, ec_); - })); - - } - - // delete - async_delete(conn, urls::url_view("/delete"), json::value{{"test-key", "test-value"}}, {{}, {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - })); - - - // patch-json - async_patch(conn, urls::url_view("/patch"), json::value{{"test-key", "test-value"}}, {{}, {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - })); - - // patch-form - async_patch(conn, urls::url_view("/patch"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {{}, {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - - })); - - // put-json - async_put(conn, urls::url_view("/put"), json::value{{"test-key", "test-value"}}, {{}, {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - })); - - // put-form - async_put(conn, urls::url_view("/put"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {{}, {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - - })); - - // post-json - async_post(conn, urls::url_view("/post"), json::value{{"test-key", "test-value"}}, {{}, {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - })); - - - // post-form - async_post(conn, urls::url_view("/post"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {{}, {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - })); -} - - -TEST_CASE("async-http") -{ - urls::url url = urls::parse_uri("http://" + httpbin()).value(); - asio::io_context ctx; - asio::ssl::context sslctx{asio::ssl::context_base::tls_client}; - sslctx.set_verify_mode(asio::ssl::verify_peer); - sslctx.set_default_verify_paths(); - requests::connection conn(ctx, sslctx); - - auto do_the_thing = asio::deferred( - [&](error_code ec, asio::ip::tcp::resolver::results_type res) - { - check_ec(ec); - asio::ip::tcp::endpoint ep; - if (!ec) - ep = res.begin()->endpoint(); - else - REQUIRE(!res.size()); - - return asio::deferred.when(!ec) - .then(conn.async_connect(ep, asio::deferred)) - .otherwise(asio::post(ctx, asio::append(asio::deferred, ec))); - }); - - conn = requests::connection(ctx); - asio::ip::tcp::resolver rslvr{ctx}; - url.set_scheme("http"); - conn.use_ssl(false); - CHECK(!conn.uses_ssl()); - conn.set_host(url.encoded_host()); - - if (url.encoded_host() == "localhost") - conn.async_connect( - asio::ip::tcp::endpoint{asio::ip::make_address("127.0.0.1"), url.port_number()}, - asio::append(&run_tests, std::ref(conn), urls::url_view(url))); - else - rslvr.async_resolve(std::string(url.encoded_host().data(), url.encoded_host().size()), - "80", do_the_thing) - (asio::append(&run_tests, std::ref(conn), urls::url_view(url))); - ctx.run(); - -} - -void async_https_impl(requests::connection & hc) -{ - - // header - async_request(hc, requests::http::verb::head, urls::url_view("/"), requests::empty{}, {{}, {false}}, - tracker([](error_code ec, requests::response hdr) - { - CHECK_FALSE(ec); - CHECK_HTTP_RESULT(hdr.headers); - })); - - // stream - hc.async_ropen(requests::http::verb::get, urls::url_view("/"), - requests::empty{}, {{}, {false}}, - tracker([](error_code ec, requests::stream str) - { - CHECK_FALSE(ec); - CHECK_HTTP_RESULT(str.headers()); - - struct ht - { - requests::stream str; - std::unique_ptr buf = std::make_unique(32); - void operator()(error_code ec, std::size_t n) - { - CHECK_FALSE(ec); - if (!ec && !str.done()) - str.async_read_some(asio::buffer(buf.get(), 32), tracker(std::move(*this))); - } - }; - ht h{std::move(str)}; - h.str.async_read_some(asio::buffer(h.buf.get(), 32), tracker(std::move(h))); - })); - - - // stream-all - hc.async_ropen(requests::http::verb::get, urls::url_view("/"), - requests::empty{}, {{}, {false}}, - tracker( - [](error_code ec, requests::stream str) - { - CHECK_FALSE(ec); - CHECK_HTTP_RESULT(str.headers()); - thread_local static std::string buf; - auto bb = asio::dynamic_buffer(buf); - str.async_read( - bb, - tracker( - [](error_code ec, std::size_t n) - { - CHECK(n > 0u); - CHECK_FALSE(ec); - })); - })); - - - // stream-dump - hc.async_ropen(requests::http::verb::get, urls::url_view("/"), - requests::empty{}, {{}, {false}}, - tracker( - [](error_code ec, requests::stream str) - { - CHECK_FALSE(ec); - CHECK_HTTP_RESULT(str.headers()); - str.async_dump(tracker([](error_code ec) {CHECK_FALSE(ec);})); - })); - - // get - async_get(hc, urls::url_view("/"), {{}, {false}}, - tracker( - [](error_code ec, requests::response hdr) - { - CHECK_FALSE(ec); - CHECK_HTTP_RESULT(hdr.headers); - })); -} - -TEST_CASE("async-https") -{ - asio::io_context ctx; - - asio::ssl::context sslctx{asio::ssl::context_base::tls_client}; - - sslctx.set_default_verify_paths(); - - requests::connection hc (ctx.get_executor(), sslctx); - - const auto host = "google.com"; - hc.set_host(host); - hc.use_ssl(true); - asio::ip::tcp::resolver rslvr{ctx}; - asio::ip::tcp::endpoint ep = *rslvr.resolve(host, "https").begin(); - error_code ec; - hc.async_connect( - ep, - tracker([&](error_code ec) - { - CHECK_FALSE(ec); - if (!ec) - async_https_impl(hc); - })); - - ctx.run(); -} -TEST_SUITE_END(); \ No newline at end of file diff --git a/test/connection_pool.cpp b/test/connection_pool.cpp deleted file mode 100644 index 2606625..0000000 --- a/test/connection_pool.cpp +++ /dev/null @@ -1,689 +0,0 @@ -// Copyright (c) 2021 Klemens D. Morgenstern -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "doctest.h" -#include "string_maker.hpp" - - -namespace requests = boost::requests; -namespace filesystem = requests::filesystem; -namespace asio = boost::asio; -namespace json = boost::json; -namespace urls = boost::urls; - -using boost::system::error_code; -#if defined(BOOST_REQUESTS_USE_STD_FS) -using fs_error_code = std::error_code; -#else -using fs_error_code = boost::system::error_code; -#endif - - -inline std::string httpbin() -{ - std::string url = "httpbin.org"; - if (auto p = ::getenv("BOOST_REQUEST_HTTPBIN")) - url = p; - return url; -} - - -TEST_SUITE_BEGIN("connection-pool"); - -TEST_CASE("sync-http") -{ - auto url = urls::url("http://" + httpbin()); - - asio::io_context ctx; - - asio::ssl::context sslctx{asio::ssl::context_base::tls_client}; - - sslctx.set_verify_mode(asio::ssl::verify_peer); - sslctx.set_default_verify_paths(); - - requests::connection_pool hc(ctx.get_executor(), sslctx); - hc.lookup(url); - CHECK(!hc.uses_ssl()); - - - // headers - { - auto hdr = request(hc, requests::http::verb::get, urls::url_view("/headers"), - requests::empty{}, - {requests::headers({{"Test-Header", "it works"}}), {false}}); - - auto hd = as_json(hdr).at("headers"); - CHECK_HTTP_RESULT(hdr.headers); - CHECK(hd.at("Host") == json::value(url.encoded_host())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - // stream - { - auto str = hc.ropen(requests::http::verb::get, urls::url_view("/get"), - requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}); - - CHECK_HTTP_RESULT(str.headers()); - json::stream_parser sp; - - char buf[32]; - - error_code ec; - while (!str.done() && !ec) - { - auto sz = str.read_some(asio::buffer(buf), ec); - CHECK(ec == error_code{}); - sp.write_some(buf, sz, ec); - CHECK(ec == error_code{}); - } - - auto hd = sp.release().at("headers"); - CHECK(hd.at("Host") == json::value(url.encoded_host())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - - // stream-all - { - auto str = hc.ropen(requests::http::verb::get, urls::url_view("/get"), - requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}); - - CHECK_HTTP_RESULT(str.headers()); - std::string buf; - auto bb = asio::dynamic_buffer(buf); - - error_code ec; - CHECK(str.read(bb, ec) > 0u); - - CHECK(ec == error_code{}); - - auto val = json::parse(buf); - - auto hd = val.at("headers"); - CHECK(hd.at("Host") == json::value(url.encoded_host())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - - // stream-dump - { - auto str = hc.ropen(requests::http::verb::get, urls::url_view("/get"), - requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}); - CHECK_HTTP_RESULT(str.headers()); - str.dump(); - } - - // get - { - auto hdr = get(hc, urls::url_view("/get"), {requests::headers({{"Test-Header", "it works"}}), {false}}); - - auto hd = as_json(hdr).at("headers"); - CHECK_HTTP_RESULT(hdr.headers); - - CHECK(hd.at("Host") == json::value(url.encoded_host())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - // get-redirect - { - auto hdr = get(hc, urls::url_view("/redirect-to?url=%2Fget"), - {requests::headers({{"Test-Header", "it works"}}), {false}}); - - CHECK_HTTP_RESULT(hdr.headers); - CHECK(hdr.history.size() == 1u); - CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); - - auto hd = as_json(hdr).at("headers"); - - CHECK(hd.at("Host") == json::value(url.encoded_host())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - // too-many-redirects - { - error_code ec; - auto res = get(hc, urls::url_view("/redirect/10"), {{}, {false, requests::redirect_mode::private_domain, 5}}, ec); - CHECK_HTTP_RESULT(res.headers); - CHECK(res.history.size() == 5); - CHECK(res.headers.begin() == res.headers.end()); - CHECK(ec == requests::error::too_many_redirects); - } - - // download - { - error_code ec; - const auto tmp = filesystem::temp_directory_path(ec); - check_ec(ec); - const auto target = tmp / "requests-test.png"; - if (filesystem::exists(target)) - filesystem::remove(target); - - CHECK(!filesystem::exists(target)); - CHECK_MESSAGE( filesystem::exists(tmp), tmp); - auto res = download(hc, urls::url_view("/image"), {{}, {false}}, target); - CHECK_HTTP_RESULT(res.headers); - - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec_; - filesystem::remove(target, ec_); - } - - - // download-redirect - { - const auto target = filesystem::temp_directory_path() / "requests-test.png"; - if (filesystem::exists(target)) - filesystem::remove(target); - - CHECK(!filesystem::exists(target)); - auto res = download(hc, urls::url_view("/redirect-to?url=%2Fimage"), {{}, {false}}, target); - CHECK_HTTP_RESULT(res.headers); - CHECK(res.history.size() == 1u); - CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); - - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec; - filesystem::remove(target, ec); - } - - - // download-too-many-redirects - { - error_code ec; - const auto target = filesystem::temp_directory_path() / "requests-test.html"; - if (filesystem::exists(target)) - filesystem::remove(target); - auto res = download(hc, urls::url_view("/redirect/10"), {{}, {false, requests::redirect_mode::private_domain, 3}}, target, ec); - - CHECK_HTTP_RESULT(res.headers); - CHECK(res.history.size() == 3); - CHECK(res.headers.begin() == res.headers.end()); - - CHECK(ec == requests::error::too_many_redirects); - CHECK(!filesystem::exists(target)); - } - - - // delete - { - auto hdr = delete_(hc, urls::url_view("/delete"), json::value{{"test-key", "test-value"}}, {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - } - - // patch-json - { - json::value msg {{"test-key", "test-value"}}; - auto hdr = patch(hc, urls::url_view("/patch"), msg, {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - CHECK(js.at("json") == msg); - } - - // patch-form - { - auto hdr = patch(hc, urls::url_view("/patch"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } - - // put-json - { - json::value msg {{"test-key", "test-value"}}; - auto hdr = put(hc, urls::url_view("/put"), msg, {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - CHECK(js.at("json") == msg); - } - - // put-form - { - auto hdr = put(hc, urls::url_view("/put"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } - - // post-json - { - json::value msg {{"test-key", "test-value"}}; - auto hdr = post(hc, urls::url_view("/post"), msg, {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - CHECK(js.at("json") == msg); - } - - // post-form - { - auto hdr = post(hc, urls::url_view("/post"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } -} - -TEST_CASE("sync-https") { - asio::io_context ctx; - - asio::ssl::context sslctx{asio::ssl::context_base::tlsv13_client}; - sslctx.set_default_verify_paths(); - - requests::connection_pool hc(ctx.get_executor(), sslctx); - hc.lookup("https://google.com"); - CHECK(hc.uses_ssl()); - // header - { - auto hdr = request(hc, requests::http::verb::head, urls::url_view("/"), requests::empty{}, {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - } - - // stream - { - auto str = hc.ropen(requests::http::verb::get, urls::url_view("/"), requests::empty{}, {{}, {false}}); - - CHECK_HTTP_RESULT(str.headers()); - json::stream_parser sp; - - char buf[32]; - while (!str.done()) - auto sz = str.read_some(asio::buffer(buf)); - } - - // stream-all - { - auto str = hc.ropen(requests::http::verb::get, urls::url_view("/"), requests::empty{}, {{}, {false}}); - - CHECK_HTTP_RESULT(str.headers()); - std::string buf; - auto bb = asio::dynamic_buffer(buf); - CHECK(str.read(bb) > 0u); - } - - // stream-dump - { - auto str = hc.ropen(requests::http::verb::get, urls::url_view("/"), requests::empty{}, {{}, {false}}); - CHECK_HTTP_RESULT(str.headers()); - str.dump(); - } - - // get - { - auto hdr = get(hc, urls::url_view("/"), {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - } -} - -void run_tests(error_code ec, - requests::connection_pool & conn, - urls::url_view url) -{ - namespace http = requests::http; - namespace filesystem = requests::filesystem; - - check_ec(ec); - - // headers - async_request( - conn, - http::verb::get, urls::url_view("/headers"), - requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - check_ec(ec); - auto hd = as_json(hdr).at("headers"); - CHECK_HTTP_RESULT(hdr.headers); - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - })); - - // headers - async_get(conn, - urls::url_view("/get"), - {requests::headers({{"Test-Header", "it works"}}), {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - check_ec(ec); - auto hd = as_json(hdr).at("headers"); - CHECK_HTTP_RESULT(hdr.headers); - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - - })); - - // get-redirect - async_get(conn, urls::url_view("/redirect-to?url=%2Fget"), - {requests::headers({{"Test-Header", "it works"}}), {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - { - CHECK(hdr.history.size() == 1u); - CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); - CHECK_HTTP_RESULT(hdr.headers); - - auto hd = as_json(hdr).at("headers"); - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - })); - - // too-many-redirects - async_get(conn, urls::url_view("/redirect/10"), - {{}, {false, requests::redirect_mode::private_domain, 5}}, - tracker( - [url](error_code ec, requests::response res) - { - CHECK_HTTP_RESULT(res.headers); - - CHECK(res.history.size() == 5); - CHECK(res.headers.begin() == res.headers.end()); - CHECK(ec == requests::error::too_many_redirects); - - })); - - { // download - auto pt = filesystem::temp_directory_path(); - const auto target = pt / "requests-test.png"; - if (filesystem::exists(target)) - filesystem::remove(target); - - CHECK(!filesystem::exists(target)); - async_download(conn, urls::url_view("/image"), {{}, {false}}, target, - tracker( - [url, target](error_code ec, requests::response_base res) - { - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - CHECK_HTTP_RESULT(res.headers); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec_; - filesystem::remove(target, ec_); - })); - } - - { - const auto target = filesystem::temp_directory_path() / "requests-test-2.png"; - if (filesystem::exists(target)) - filesystem::remove(target); - - CHECK(!filesystem::exists(target)); - // download-redirect - async_download(conn, urls::url_view("/redirect-to?url=%2Fimage"), {{}, {false}}, target, - tracker( - [url, target](error_code ec, requests::response_base res) - { - CHECK(res.history.size() == 1u); - CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); - CHECK_HTTP_RESULT(res.headers); - - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec_; - filesystem::remove(target, ec_); - })); - - } - - // delete - async_delete(conn, urls::url_view("/delete"), json::value{{"test-key", "test-value"}}, {{}, {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - })); - - - // patch-json - async_patch(conn, urls::url_view("/patch"), json::value{{"test-key", "test-value"}}, {{}, {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - })); - - // patch-form - async_patch(conn, urls::url_view("/patch"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {{}, {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - - })); - - // put-json - async_put(conn, urls::url_view("/put"), json::value{{"test-key", "test-value"}}, {{}, {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - })); - - // put-form - async_put(conn, urls::url_view("/put"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {{}, {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - - })); - - // post-json - async_post(conn, urls::url_view("/post"), json::value{{"test-key", "test-value"}}, {{}, {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - })); - - - // post-form - async_post(conn, urls::url_view("/post"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {{}, {false}}, - tracker( - [url](error_code ec, requests::response hdr) - { - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - })); -} - - -TEST_CASE("async-http") -{ - auto url = urls::url("http://" + httpbin()); - - asio::io_context ctx; - asio::ssl::context sslctx{asio::ssl::context_base::tls_client}; - sslctx.set_default_verify_paths(); - requests::connection_pool conn(ctx, sslctx); - - url.set_scheme("http"); - conn.async_lookup(url, asio::append(&run_tests, std::ref(conn), urls::url_view(url))); - ctx.run(); - CHECK(!conn.uses_ssl()); - CHECK(conn.limit() >= conn.active()); -} - - -void async_https_impl(requests::connection_pool & hc) -{ - - // header - async_request(hc, requests::http::verb::head, urls::url_view("/"), requests::empty{}, {{}, {false}}, - tracker([](error_code ec, requests::response hdr) - { - CHECK_FALSE(ec); - CHECK_HTTP_RESULT(hdr.headers); - })); - - // stream - hc.async_ropen(requests::http::verb::get, urls::url_view("/"), - requests::empty{}, {{}, {false}}, - tracker([](error_code ec, requests::stream str) - { - CHECK_FALSE(ec); - CHECK_HTTP_RESULT(str.headers()); - - struct ht - { - requests::stream str; - std::unique_ptr buf = std::make_unique(32); - void operator()(error_code ec, std::size_t n) - { - CHECK_FALSE(ec); - if (!ec && !str.done()) - str.async_read_some(asio::buffer(buf.get(), 32), tracker(std::move(*this))); - } - }; - ht h{std::move(str)}; - h.str.async_read_some(asio::buffer(h.buf.get(), 32), tracker(std::move(h))); - })); - - - // stream-all - hc.async_ropen(requests::http::verb::get, urls::url_view("/"), - requests::empty{}, {{}, {false}}, - tracker( - [](error_code ec, requests::stream str) - { - CHECK_FALSE(ec); - CHECK_HTTP_RESULT(str.headers()); - thread_local static std::string buf; - auto bb = asio::dynamic_buffer(buf); - str.async_read( - bb, - tracker( - [](error_code ec, std::size_t n) - { - CHECK(n > 0u); - CHECK_FALSE(ec); - })); - })); - - - // stream-dump - hc.async_ropen(requests::http::verb::get, urls::url_view("/"), - requests::empty{}, {{}, {false}}, - tracker( - [](error_code ec, requests::stream str) - { - CHECK_FALSE(ec); - CHECK_HTTP_RESULT(str.headers()); - str.async_dump(tracker([](error_code ec) {CHECK_FALSE(ec);})); - })); - - // get - async_get(hc, urls::url_view("/"), {{}, {false}}, - tracker( - [](error_code ec, requests::response hdr) - { - CHECK_FALSE(ec); - CHECK_HTTP_RESULT(hdr.headers); - })); -} - -TEST_CASE("async-https") -{ - asio::io_context ctx; - - asio::ssl::context sslctx{asio::ssl::context_base::tls_client}; - - sslctx.set_default_verify_paths(); - - requests::connection_pool hc (ctx.get_executor(), sslctx); - - error_code ec; - hc.async_lookup( - "https://google.com", - tracker([&](error_code ec) - { - CHECK_FALSE(ec); - if (!ec) - async_https_impl(hc); - })); - - ctx.run(); -} - - -TEST_SUITE_END(); \ No newline at end of file diff --git a/test/cookie.cpp b/test/cookie.cpp deleted file mode 100644 index 10285bd..0000000 --- a/test/cookie.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) 2021 Klemens D. Morgenstern -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "doctest.h" -#include "string_maker.hpp" - -#include - -using namespace boost; - -inline std::string httpbin() -{ - std::string url = "httpbin.org"; - if (auto p = ::getenv("BOOST_REQUEST_HTTPBIN")) - url = p; - return url; -} - -TEST_SUITE_BEGIN("cookie"); - - -void http_request_cookie_connection(bool https) -{ - auto url = httpbin(); - - asio::io_context ctx; - - asio::ssl::context sslctx{asio::ssl::context_base::tls_client}; - - sslctx.set_verify_mode(asio::ssl::verify_peer); - sslctx.set_default_verify_paths(); - auto hc = https ? requests::connection(ctx.get_executor(), sslctx) : requests::connection(ctx.get_executor()); - hc.set_host(url); - hc.use_ssl(https); - - asio::ip::tcp::resolver rslvr{ctx}; - asio::ip::tcp::endpoint ep = *rslvr.resolve(url, https ? "https" : "http").begin(); - - hc.connect(ep); - - requests::cookie_jar jar; - auto res = requests::json::get(hc, urls::url_view{"/cookies"}, {{}, /*.opts=*/{false}, /*.jar=*/&jar}); - - CHECK(res.value.at("cookies").as_object().empty()); - CHECK(jar.content.empty()); - - res = requests::json::get(hc, urls::url_view{"/cookies/set?cookie-1=foo"}, {{}, /*.opts=*/{false}, /*.jar=*/&jar}); - - CHECK(res.value.at("cookies") == json::object{{"cookie-1", "foo"}}); - REQUIRE(!jar.content.empty()); - auto citr = jar.content.begin(); - CHECK(citr->value == "foo"); - CHECK(citr->name == "cookie-1"); - CHECK(citr->secure_only_flag == false); - CHECK(citr->path == "/"); - - res = requests::json::get(hc, urls::url_view{"/cookies/set/cookie-2/bar"}, {{}, /*.opts=*/{false}, /*.jar=*/&jar}); - - CHECK(res.value.at("cookies") == json::object{{"cookie-1", "foo"}, {"cookie-2", "bar"}}); - REQUIRE(jar.content.size() == 2u); - citr = jar.content.begin(); - if (citr->name == "cookie-1") - { - CHECK(citr->value == "foo"); - CHECK(citr->secure_only_flag == false); - CHECK(citr->path == "/"); - citr ++ ; - CHECK(citr->value == "bar"); - CHECK(citr->secure_only_flag == false); - CHECK(citr->path == "/"); - - } - else - { - CHECK(citr->value == "bar"); - CHECK(citr->secure_only_flag == false); - CHECK(citr->path == "/"); - citr ++ ; - CHECK(citr->value == "foo"); - CHECK(citr->secure_only_flag == false); - CHECK(citr->path == "/"); - } - res = requests::json::get(hc, urls::url_view{"/cookies/delete?cookie-1"}, {{}, /*.opts=*/{false}, /*.jar=*/&jar}); - - CHECK(!jar.content.empty()); - REQUIRE(jar.content.size() == 1u); - citr = jar.content.begin(); - CHECK(citr->value == "bar"); - CHECK(citr->name == "cookie-2"); - CHECK(citr->secure_only_flag == false); - CHECK(citr->path == "/"); - - res = requests::json::get(hc, urls::url_view{"/cookies/delete?cookie-2"}, {{}, /*.opts=*/{false}, /*.jar=*/&jar}); - CHECK(jar.content.empty()); -} - -TEST_CASE("sync-connection-cookie-request") -{ - SUBCASE("http") { http_request_cookie_connection(false);} - SUBCASE("https") { http_request_cookie_connection(true);} -} - -TEST_SUITE_END(); \ No newline at end of file diff --git a/test/cookie_grammar.cpp b/test/cookie_grammar.cpp deleted file mode 100644 index bbae47c..0000000 --- a/test/cookie_grammar.cpp +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright (c) 2022 Klemens D. Morgenstern -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "string_maker.hpp" - -using namespace boost; - -#include -#include "doctest.h" -#include "string_maker.hpp" - -TEST_SUITE_BEGIN("cookie-grammar"); - -TEST_CASE("non-zero-digit") -{ - CHECK(urls::grammar::parse("1234", urls::grammar::token_rule(requests::grammar::non_zero_digit))); -} - -TEST_CASE("any-char-except") -{ - CHECK(urls::grammar::parse("1234", urls::grammar::token_rule(requests::grammar::any_char_except_ctl_semicolon))); - - auto res = urls::grammar::parse("1234;", urls::grammar::token_rule(requests::grammar::any_char_except_ctl_semicolon)); - CHECK(res == urls::grammar::error::leftover); -} - -TEST_CASE("path-av") -{ - constexpr auto rule = urls::grammar::tuple_rule( - urls::grammar::squelch(urls::grammar::literal_rule("Path=")), - urls::grammar::token_rule(requests::grammar::any_char_except_ctl_semicolon) - ); - - - CHECK(!urls::grammar::parse("1234", rule)); - - auto res = urls::grammar::parse("Path=foobar;", rule); - CHECK(res == urls::grammar::error::leftover); - - res = urls::grammar::parse("Path=foobar", rule); - CHECK(res.value() == "foobar"); -} - - -TEST_CASE("sane-cookie-date") -{ - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(784111777)) == - urls::grammar::parse("Sun, 06 Nov 1994 08:49:37 GMT", requests::rfc::sane_cookie_date)); - CHECK(urls::grammar::error::mismatch == urls::grammar::parse("Mon, 06 Nov 1994 08:49:37 GMT", requests::rfc::sane_cookie_date)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1696335075)) == - urls::grammar::parse("Tue, 03 Oct 2023 12:11:15 GMT", requests::rfc::sane_cookie_date)); - - CHECK(urls::grammar::error::mismatch == - urls::grammar::parse("Sun, 03 Oct 2023 12:11:15 GMT", requests::rfc::sane_cookie_date)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1623233894)) == - urls::grammar::parse("Wed, 09 Jun 2021 10:18:14 GMT", requests::rfc::sane_cookie_date)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == - urls::grammar::parse("Thu, 01 Jan 1970 00:00:01 GMT", requests::rfc::sane_cookie_date)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == - urls::grammar::parse("Thu, 01 Jan 1970 00:00:01 GMT", requests::rfc::sane_cookie_date)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(0)) == - urls::grammar::parse("Thu, 01-Jan-1970 00:00:00 GMT", requests::rfc::sane_cookie_date)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::hours(1)) == - urls::grammar::parse("Thu, 01-Jan-1970 01:00:00 GMT", requests::rfc::sane_cookie_date)); - -} - - -TEST_CASE("fixed-token") -{ - constexpr auto rule = - requests::grammar::fixed_token_rule<2>(urls::grammar::digit_chars); - - CHECK("12" == urls::grammar::parse("12", rule)); - CHECK(urls::grammar::error::leftover == urls::grammar::parse("123", rule )); - CHECK(urls::grammar::error::need_more == urls::grammar::parse("1", rule )); -} - -TEST_CASE("domain") -{ - constexpr auto & rule = requests::grammar::domain; - - CHECK(!urls::grammar::parse("12", rule)); - CHECK("b12" == urls::grammar::parse("b12", rule)); - CHECK("foo.bar" == urls::grammar::parse("foo.bar", rule)); - CHECK(!urls::grammar::parse("foo.bar;", rule)); - CHECK("foo.bar-23-2" == urls::grammar::parse("foo.bar-23-2", rule)); -} - -TEST_CASE("set-cookie") -{ - auto v1 = requests::parse_set_cookie_field("theme=light"); - CHECK(v1); - CHECK(v1->name == "theme"); - CHECK(v1->value == "light"); - CHECK(v1->extensions().empty()); - CHECK(v1->expires == std::chrono::system_clock::time_point::max()); - CHECK(v1->max_age == std::chrono::seconds::max()); - CHECK(v1->domain == ""); - CHECK(v1->path == ""); - CHECK(!v1->secure); - CHECK(!v1->http_only); - - auto v2 = requests::parse_set_cookie_field("sessionToken=abc123; Expires=Wed, 09 Jun 2021 10:18:14 GMT"); - CHECK(v2); - CHECK(v2->name == "sessionToken"); - CHECK(v2->value == "abc123"); - CHECK(v2->extensions().empty()); - CHECK(v2->expires == std::chrono::system_clock::time_point(std::chrono::seconds(1623233894))); - CHECK(v2->max_age == std::chrono::seconds::max()); - CHECK(v2->domain == ""); - CHECK(v2->path == ""); - CHECK(!v2->secure); - CHECK(!v2->http_only); - - - auto v4 = requests::parse_set_cookie_field("LSID=DQAAAKEaem_vYg; Path=/accounts; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly"); - CHECK(v4); - CHECK(v4->name == "LSID"); - CHECK(v4->value == "DQAAAKEaem_vYg"); - CHECK(v4->extensions().empty()); - CHECK(v4->expires == std::chrono::system_clock::time_point(std::chrono::seconds(1610576581))); - CHECK(v4->max_age == std::chrono::seconds::max()); - CHECK(v4->domain == ""); - CHECK(v4->path == "/accounts"); - CHECK(v4->secure); - CHECK(v4->http_only); - - auto v5 = requests::parse_set_cookie_field("HSID=AYQEVnDKrdst; Domain=.foo.com; Path=/; Expires=Wed, 13 Jan 2021 22:23:01 GMT; HttpOnly"); - CHECK(v5); - CHECK(v5->name == "HSID"); - CHECK(v5->value == "AYQEVnDKrdst"); - CHECK(v5->extensions().empty()); - CHECK(v5->expires == std::chrono::system_clock::time_point(std::chrono::seconds(1610576581))); - CHECK(v5->max_age == std::chrono::seconds::max()); - CHECK(v5->domain == "foo.com"); - CHECK(v5->path == "/"); - CHECK(!v5->secure); - CHECK(v5->http_only); - - auto v6 = requests::parse_set_cookie_field("SSID=Ap4PGTEq; Domain=foo.com; Path=/; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly"); - CHECK(v6); - CHECK(v6->name == "SSID"); - CHECK(v6->value == "Ap4PGTEq"); - CHECK(v6->extensions().empty()); - CHECK(v6->expires == std::chrono::system_clock::time_point(std::chrono::seconds(1610576581))); - CHECK(v6->max_age == std::chrono::seconds::max()); - CHECK(v6->domain == "foo.com"); - CHECK(v6->path == "/"); - CHECK(v6->secure); - CHECK(v6->http_only); - - auto v7 = requests::parse_set_cookie_field("lu=Rg3vHJZnehYLjVg7qi3bZjzg; Expires=Tue, 15 Jan 2013 21:47:38 GMT; Path=/; Domain=.example.com; HttpOnly"); - CHECK(v7); - CHECK(v7->name == "lu"); - CHECK(v7->value == "Rg3vHJZnehYLjVg7qi3bZjzg"); - CHECK(v7->extensions().empty()); - CHECK(v7->expires == std::chrono::system_clock::time_point(std::chrono::seconds(1358286458))); - CHECK(v7->max_age == std::chrono::seconds::max()); - CHECK(v7->domain == "example.com"); - CHECK(v7->path == "/"); - CHECK(!v7->secure); - CHECK(v7->http_only); - - - auto v8 = requests::parse_set_cookie_field("made_write_conn=1295214458; Path=/; Domain=.example.com"); - CHECK(v8); - CHECK(v8->name == "made_write_conn"); - CHECK(v8->value == "1295214458"); - CHECK(v8->extensions().empty()); - CHECK(v8->expires == std::chrono::system_clock::time_point::max()); - CHECK(v8->max_age == std::chrono::seconds::max()); - CHECK(v8->domain == "example.com"); - CHECK(v8->path == "/"); - CHECK(!v8->secure); - CHECK(!v8->http_only); - - - auto v9 = requests::parse_set_cookie_field("reg_fb_gate=deleted; Expires=Thu, 01 Jan 1970 00:00:01 GMT; Path=/; Domain=.example.thingy; HttpOnly"); - CHECK(v9); - CHECK(v9->name == "reg_fb_gate"); - CHECK(v9->value == "deleted"); - CHECK(v9->extensions().empty()); - CHECK(v9->expires == std::chrono::system_clock::time_point(std::chrono::seconds(1))); - CHECK(v9->max_age == std::chrono::seconds::max()); - CHECK(v9->domain == "example.thingy"); - CHECK(v9->path == "/"); - CHECK(!v9->secure); - CHECK(v9->http_only); - - std::array cks = {*v1, *v2, *v4, *v5, *v6, *v7, *v8, *v9}; - - CHECK(requests::detail::make_cookie_field(cks) - == "theme=light; sessionToken=abc123; LSID=DQAAAKEaem_vYg; HSID=AYQEVnDKrdst; SSID=Ap4PGTEq; lu=Rg3vHJZnehYLjVg7qi3bZjzg; made_write_conn=1295214458; reg_fb_gate=deleted" - ); -} - - -TEST_SUITE_END(); diff --git a/test/coroutine_test_case.hpp b/test/coroutine_test_case.hpp new file mode 100644 index 0000000..4197c9a --- /dev/null +++ b/test/coroutine_test_case.hpp @@ -0,0 +1,80 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_REQUESTS_ASYNC_TEST_CASE_HPP +#define BOOST_REQUESTS_ASYNC_TEST_CASE_HPP + +#include +#include + +#define BOOST_FIXTURE_COROUTINE_TEST_CASE_WITH_DECOR( test_name, F, decorators ) \ +struct test_name : public F { void test_method(boost::asio::yield_context yield ); }; \ + \ +static void BOOST_AUTO_TC_INVOKER( test_name )() \ +{ \ + BOOST_TEST_CHECKPOINT('"' << #test_name << "\" fixture ctor"); \ + test_name t; \ + BOOST_TEST_CHECKPOINT('"' << #test_name << "\" fixture setup"); \ + boost::unit_test::setup_conditional(t); \ + BOOST_TEST_CHECKPOINT('"' << #test_name << "\" test entry"); \ + boost::asio::io_context ctx{1}; \ + boost::asio::spawn(ctx, \ + [&t](boost::asio::yield_context yield) \ + { \ + t.test_method(std::move(yield)); \ + }, \ + [](std::exception_ptr ep) \ + { \ + if (ep) \ + std::rethrow_exception(ep); \ + }); \ + \ + ctx.run(); \ + BOOST_TEST_CHECKPOINT('"' << #test_name << "\" fixture teardown"); \ + boost::unit_test::teardown_conditional(t); \ + BOOST_TEST_CHECKPOINT('"' << #test_name << "\" fixture dtor"); \ +} \ + \ +struct BOOST_AUTO_TC_UNIQUE_ID( test_name ) {}; \ + \ +BOOST_AUTO_TU_REGISTRAR( test_name )( \ + boost::unit_test::make_test_case( \ + &BOOST_AUTO_TC_INVOKER( test_name ), \ + #test_name, __FILE__, __LINE__ ), \ + decorators ); \ + \ +void test_name::test_method(boost::asio::yield_context yield) /**/ + +#define BOOST_FIXTURE_COROUTINE_TEST_CASE_NO_DECOR( test_name, F ) \ +BOOST_FIXTURE_COROUTINE_TEST_CASE_WITH_DECOR( test_name, F, \ + boost::unit_test::decorator::collector_t::instance() ) \ +/**/ + +#define BOOST_FIXTURE_COROUTINE_TEST_CASE( ... ) \ + BOOST_TEST_INVOKE_IF_N_ARGS( 2, \ + BOOST_FIXTURE_COROUTINE_TEST_CASE_NO_DECOR, \ + BOOST_FIXTURE_COROUTINE_TEST_CASE_WITH_DECOR, \ + __VA_ARGS__) \ + +#define BOOST_COROUTINE_TEST_CASE_NO_DECOR( test_name ) \ + BOOST_FIXTURE_COROUTINE_TEST_CASE_NO_DECOR( test_name, \ + BOOST_AUTO_TEST_CASE_FIXTURE ) \ +/**/ + +#define BOOST_COROUTINE_TEST_CASE_WITH_DECOR( test_name, decorators ) \ + BOOST_FIXTURE_COROUTINE_TEST_CASE_WITH_DECOR( test_name, \ + BOOST_COROUTINE_TEST_CASE_FIXTURE, decorators ) \ +/**/ + +#define BOOST_COROUTINE_TEST_CASE( ... ) \ + BOOST_TEST_INVOKE_IF_N_ARGS( 1, \ + BOOST_COROUTINE_TEST_CASE_NO_DECOR, \ + BOOST_COROUTINE_TEST_CASE_WITH_DECOR, \ + __VA_ARGS__) \ + + +#endif // BOOST_REQUESTS_ASYNC_TEST_CASE_HPP diff --git a/test/detail/mutex.cpp b/test/detail/mutex.cpp deleted file mode 100644 index 50d8108..0000000 --- a/test/detail/mutex.cpp +++ /dev/null @@ -1,293 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#include -#include - -#include -#include -#include - -#include - -#include "../string_maker.hpp" - -namespace asio = boost::asio; -namespace requests = boost::requests; -namespace container = boost::container; - -using boost::system::error_code; - -using requests::detail::mutex; -using requests::detail::lock_guard; - -inline void run_impl(asio::io_context & ctx) -{ - ctx.run(); -} - -inline void run_impl(asio::thread_pool & ctx) -{ - ctx.join(); -} - -struct basic_main_impl -{ - mutex mtx; - std::vector< int > seq; - - basic_main_impl(asio::any_io_executor exec) : mtx{exec} {} - -}; - -struct basic_main -{ - struct step_impl : asio::coroutine - { - std::vector< int > &v; - mutex &mtx; - int i; - - using executor_type = asio::any_io_executor; - executor_type get_executor() {return mtx.get_executor();} - - step_impl(std::vector< int > &v, - mutex &mtx, - int i) : v(v), mtx(mtx), i(i) {} - - - std::unique_ptr tim; - - using completion_signature_type = void(error_code); - using step_signature_type = void(error_code, lock_guard); - - template - void operator()(Self && self, error_code ec = {}, lock_guard l = {}) - { - BOOST_ASIO_CORO_REENTER(this) - { - BOOST_ASIO_CORO_YIELD mtx.async_lock(std::move(self)); - v.push_back(i); - tim = std::make_unique(mtx.get_executor(), std::chrono::milliseconds(10)); - v.push_back(i + 1); - BOOST_ASIO_CORO_YIELD tim->async_wait(std::move(self)); - self.complete(ec); - } - } - }; - - basic_main(asio::any_io_executor exec) : impl_(std::make_unique(exec)) {} - - std::unique_ptr impl_; - - static auto f(std::vector< int > &v, mutex &mtx, int i) - { - return asio::async_compose( - step_impl(v, mtx, i), asio::deferred); - } - - - void operator()(error_code = {}) - { - asio::experimental::make_parallel_group( - f(impl_->seq, impl_->mtx, 0), - f(impl_->seq, impl_->mtx, 3), - f(impl_->seq, impl_->mtx, 6), - f(impl_->seq, impl_->mtx, 9)) - .async_wait(asio::experimental::wait_for_all(), std::move(*this)); - } - - void operator()(std::array order, - error_code ec1, error_code ec2, - error_code ec3, error_code ec4) - { - CHECK(!ec1); - CHECK(!ec2); - CHECK(!ec3); - CHECK(!ec4); - CHECK(impl_->seq.size() == 8); - CHECK((impl_->seq[0] + 1) == impl_->seq[1]); - CHECK((impl_->seq[2] + 1) == impl_->seq[3]); - CHECK((impl_->seq[4] + 1) == impl_->seq[5]); - CHECK((impl_->seq[6] + 1) == impl_->seq[7]); - } - -}; - -TEST_SUITE_BEGIN("mutex"); - -TEST_CASE_TEMPLATE("random", Context, asio::io_context, asio::thread_pool) -{ - asio::thread_pool ctx; - asio::post(ctx, basic_main{ctx.get_executor()}); - run_impl(ctx); -} - - - -TEST_CASE("rebind_mutex") -{ - asio::io_context ctx; - auto res = asio::deferred.as_default_on(mutex{ctx.get_executor()}); -} - -TEST_CASE("sync_lock_mt") -{ - asio::thread_pool ctx; - mutex mtx{ctx}; - - mtx.lock(); - asio::steady_timer tim{ctx, std::chrono::milliseconds(10)}; - tim.async_wait([&](auto){mtx.unlock();}); - mtx.lock(); - - mtx.unlock(); - mtx.lock(); - - ctx.join(); -} - - - -TEST_CASE("sync_lock_mt-io") -{ - asio::io_context ctx; - mutex mtx{ctx}; - - mtx.lock(); - asio::post(ctx, [&]{mtx.unlock();}); - std::thread thr{ - [&]{ - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - ctx.run(); - - }}; - - mtx.lock(); - thr.join(); -} - -TEST_CASE_TEMPLATE("multi_lock", Context, asio::io_context, asio::thread_pool) -{ - Context ctx; - mutex mtx{ctx}; - - - run_impl(ctx); -} - -TEST_CASE("cancel_twice") -{ - asio::io_context ctx; - - std::vector ecs; - - { - mutex mtx{ctx}; - mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); - mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); - mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); - mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); - mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); - mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); - mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); - - ctx.run_for(std::chrono::milliseconds(10)); - - mtx.unlock(); - ctx.restart(); - ctx.run_for(std::chrono::milliseconds(10)); - - mtx.unlock(); - ctx.restart(); - ctx.run_for(std::chrono::milliseconds(10)); - - } - ctx.restart(); - ctx.run_for(std::chrono::milliseconds(10)); - - - CHECK(ecs.size() == 7u); - CHECK(!ecs.at(0)); - CHECK(!ecs.at(1)); - CHECK(!ecs.at(2)); - - CHECK(4u == std::count(ecs.begin(), ecs.end(), asio::error::operation_aborted)); -} - - - -TEST_CASE("cancel_lock") -{ - asio::io_context ctx; - - std::vector ecs; - - { - mutex mtx{ctx}; - mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); - mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); - mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); - mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); - mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); - mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); - mtx.async_lock([&](error_code ec){ecs.push_back(ec);}); - ctx.run_for(std::chrono::milliseconds(10)); - - mtx.unlock(); - mutex mt2{std::move(mtx)}; - ctx.restart(); - ctx.run_for(std::chrono::milliseconds(10)); - - mt2.unlock(); - mtx.unlock(); // should do nothing - } - ctx.restart(); - ctx.run_for(std::chrono::milliseconds(10)); - - - CHECK(ecs.size() == 7u); - CHECK(!ecs.at(0)); - CHECK(!ecs.at(1)); - CHECK(!ecs.at(2)); - - CHECK(4u == std::count(ecs.begin(), ecs.end(), asio::error::operation_aborted)); -} - - -TEST_CASE("cancel_one") -{ - asio::io_context ctx; - asio::cancellation_signal sig; - std::vector ecs; - - { - - - mutex mtx{ctx}; - mtx.lock(); - mtx.async_lock(asio::bind_cancellation_slot(sig.slot(), [&](error_code ec){ecs.push_back(ec);})); - mtx.async_lock(asio::bind_cancellation_slot(sig.slot(), [&](error_code ec){ecs.push_back(ec);})); - ctx.run_for(std::chrono::milliseconds(10)); - CHECK(ecs.empty()); - - sig.emit(asio::cancellation_type::all); - ctx.restart(); - ctx.run_for(std::chrono::milliseconds(10)); - - REQUIRE(ecs.size() == 1u); - CHECK(ecs.front() == asio::error::operation_aborted); - } - - ctx.restart(); - ctx.run_for(std::chrono::milliseconds(10)); - CHECK(ecs.size() == 2u); -} - - - -TEST_SUITE_END(); \ No newline at end of file diff --git a/test/doctest.h b/test/doctest.h deleted file mode 100644 index 42eb039..0000000 --- a/test/doctest.h +++ /dev/null @@ -1,6580 +0,0 @@ -// ====================================================================== lgtm [cpp/missing-header-guard] -// == DO NOT MODIFY THIS FILE BY HAND - IT IS AUTO GENERATED BY CMAKE! == -// ====================================================================== -// -// doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD -// -// Copyright (c) 2016-2021 Viktor Kirilov -// -// Distributed under the MIT Software License -// See accompanying file LICENSE.txt or copy at -// https://opensource.org/licenses/MIT -// -// The documentation can be found at the library's page: -// https://github.com/onqtam/doctest/blob/master/doc/markdown/readme.md -// -// ================================================================================================= -// ================================================================================================= -// ================================================================================================= -// -// The library is heavily influenced by Catch - https://github.com/catchorg/Catch2 -// which uses the Boost Software License - Version 1.0 -// see here - https://github.com/catchorg/Catch2/blob/master/LICENSE.txt -// -// The concept of subcases (sections in Catch) and expression decomposition are from there. -// Some parts of the code are taken directly: -// - stringification - the detection of "ostream& operator<<(ostream&, const T&)" and StringMaker<> -// - the Approx() helper class for floating point comparison -// - colors in the console -// - breaking into a debugger -// - signal / SEH handling -// - timer -// - XmlWriter class - thanks to Phil Nash for allowing the direct reuse (AKA copy/paste) -// -// The expression decomposing templates are taken from lest - https://github.com/martinmoene/lest -// which uses the Boost Software License - Version 1.0 -// see here - https://github.com/martinmoene/lest/blob/master/LICENSE.txt -// -// ================================================================================================= -// ================================================================================================= -// ================================================================================================= - -#ifndef DOCTEST_LIBRARY_INCLUDED -#define DOCTEST_LIBRARY_INCLUDED - -// ================================================================================================= -// == VERSION ====================================================================================== -// ================================================================================================= - -#define DOCTEST_VERSION_MAJOR 2 -#define DOCTEST_VERSION_MINOR 4 -#define DOCTEST_VERSION_PATCH 6 -#define DOCTEST_VERSION_STR "2.4.6" - -#define DOCTEST_VERSION \ - (DOCTEST_VERSION_MAJOR * 10000 + DOCTEST_VERSION_MINOR * 100 + DOCTEST_VERSION_PATCH) - -// ================================================================================================= -// == COMPILER VERSION ============================================================================= -// ================================================================================================= - -// ideas for the version stuff are taken from here: https://github.com/cxxstuff/cxx_detect - -#define DOCTEST_COMPILER(MAJOR, MINOR, PATCH) ((MAJOR)*10000000 + (MINOR)*100000 + (PATCH)) - -// GCC/Clang and GCC/MSVC are mutually exclusive, but Clang/MSVC are not because of clang-cl... -#if defined(_MSC_VER) && defined(_MSC_FULL_VER) -#if _MSC_VER == _MSC_FULL_VER / 10000 -#define DOCTEST_MSVC DOCTEST_COMPILER(_MSC_VER / 100, _MSC_VER % 100, _MSC_FULL_VER % 10000) -#else // MSVC -#define DOCTEST_MSVC \ - DOCTEST_COMPILER(_MSC_VER / 100, (_MSC_FULL_VER / 100000) % 100, _MSC_FULL_VER % 100000) -#endif // MSVC -#endif // MSVC -#if defined(__clang__) && defined(__clang_minor__) -#define DOCTEST_CLANG DOCTEST_COMPILER(__clang_major__, __clang_minor__, __clang_patchlevel__) -#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && \ - !defined(__INTEL_COMPILER) -#define DOCTEST_GCC DOCTEST_COMPILER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) -#endif // GCC - -#ifndef DOCTEST_MSVC -#define DOCTEST_MSVC 0 -#endif // DOCTEST_MSVC -#ifndef DOCTEST_CLANG -#define DOCTEST_CLANG 0 -#endif // DOCTEST_CLANG -#ifndef DOCTEST_GCC -#define DOCTEST_GCC 0 -#endif // DOCTEST_GCC - -// ================================================================================================= -// == COMPILER WARNINGS HELPERS ==================================================================== -// ================================================================================================= - -#if DOCTEST_CLANG -#define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x) -#define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH _Pragma("clang diagnostic push") -#define DOCTEST_CLANG_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(clang diagnostic ignored w) -#define DOCTEST_CLANG_SUPPRESS_WARNING_POP _Pragma("clang diagnostic pop") -#define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) \ - DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING(w) -#else // DOCTEST_CLANG -#define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH -#define DOCTEST_CLANG_SUPPRESS_WARNING(w) -#define DOCTEST_CLANG_SUPPRESS_WARNING_POP -#define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) -#endif // DOCTEST_CLANG - -#if DOCTEST_GCC -#define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x) -#define DOCTEST_GCC_SUPPRESS_WARNING_PUSH _Pragma("GCC diagnostic push") -#define DOCTEST_GCC_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(GCC diagnostic ignored w) -#define DOCTEST_GCC_SUPPRESS_WARNING_POP _Pragma("GCC diagnostic pop") -#define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w) \ - DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING(w) -#else // DOCTEST_GCC -#define DOCTEST_GCC_SUPPRESS_WARNING_PUSH -#define DOCTEST_GCC_SUPPRESS_WARNING(w) -#define DOCTEST_GCC_SUPPRESS_WARNING_POP -#define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w) -#endif // DOCTEST_GCC - -#if DOCTEST_MSVC -#define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH __pragma(warning(push)) -#define DOCTEST_MSVC_SUPPRESS_WARNING(w) __pragma(warning(disable : w)) -#define DOCTEST_MSVC_SUPPRESS_WARNING_POP __pragma(warning(pop)) -#define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) \ - DOCTEST_MSVC_SUPPRESS_WARNING_PUSH DOCTEST_MSVC_SUPPRESS_WARNING(w) -#else // DOCTEST_MSVC -#define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH -#define DOCTEST_MSVC_SUPPRESS_WARNING(w) -#define DOCTEST_MSVC_SUPPRESS_WARNING_POP -#define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) -#endif // DOCTEST_MSVC - -// ================================================================================================= -// == COMPILER WARNINGS ============================================================================ -// ================================================================================================= - -DOCTEST_CLANG_SUPPRESS_WARNING_PUSH -DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wnon-virtual-dtor") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wdeprecated") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") - -DOCTEST_GCC_SUPPRESS_WARNING_PUSH -DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") -DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") -DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") -DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") -DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") -DOCTEST_GCC_SUPPRESS_WARNING("-Wctor-dtor-privacy") -DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") -DOCTEST_GCC_SUPPRESS_WARNING("-Wnon-virtual-dtor") -DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs") -DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") -DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") -DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-promo") - -DOCTEST_MSVC_SUPPRESS_WARNING_PUSH -DOCTEST_MSVC_SUPPRESS_WARNING(4616) // invalid compiler warning -DOCTEST_MSVC_SUPPRESS_WARNING(4619) // invalid compiler warning -DOCTEST_MSVC_SUPPRESS_WARNING(4996) // The compiler encountered a deprecated declaration -DOCTEST_MSVC_SUPPRESS_WARNING(4706) // assignment within conditional expression -DOCTEST_MSVC_SUPPRESS_WARNING(4512) // 'class' : assignment operator could not be generated -DOCTEST_MSVC_SUPPRESS_WARNING(4127) // conditional expression is constant -DOCTEST_MSVC_SUPPRESS_WARNING(4820) // padding -DOCTEST_MSVC_SUPPRESS_WARNING(4625) // copy constructor was implicitly defined as deleted -DOCTEST_MSVC_SUPPRESS_WARNING(4626) // assignment operator was implicitly defined as deleted -DOCTEST_MSVC_SUPPRESS_WARNING(5027) // move assignment operator was implicitly defined as deleted -DOCTEST_MSVC_SUPPRESS_WARNING(5026) // move constructor was implicitly defined as deleted -DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly defined as deleted -DOCTEST_MSVC_SUPPRESS_WARNING(4640) // construction of local static object is not thread-safe -// static analysis -DOCTEST_MSVC_SUPPRESS_WARNING(26439) // This kind of function may not throw. Declare it 'noexcept' -DOCTEST_MSVC_SUPPRESS_WARNING(26495) // Always initialize a member variable -DOCTEST_MSVC_SUPPRESS_WARNING(26451) // Arithmetic overflow ... -DOCTEST_MSVC_SUPPRESS_WARNING(26444) // Avoid unnamed objects with custom construction and dtr... -DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum' - -// 4548 - expression before comma has no effect; expected expression with side - effect -// 4265 - class has virtual functions, but destructor is not virtual -// 4986 - exception specification does not match previous declaration -// 4350 - behavior change: 'member1' called instead of 'member2' -// 4668 - 'x' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' -// 4365 - conversion from 'int' to 'unsigned long', signed/unsigned mismatch -// 4774 - format string expected in argument 'x' is not a string literal -// 4820 - padding in structs - -// only 4 should be disabled globally: -// - 4514 # unreferenced inline function has been removed -// - 4571 # SEH related -// - 4710 # function not inlined -// - 4711 # function 'x' selected for automatic inline expansion - -#define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN \ - DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \ - DOCTEST_MSVC_SUPPRESS_WARNING(4548) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4265) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4986) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4350) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4668) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4365) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4774) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4820) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4625) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4626) \ - DOCTEST_MSVC_SUPPRESS_WARNING(5027) \ - DOCTEST_MSVC_SUPPRESS_WARNING(5026) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4623) \ - DOCTEST_MSVC_SUPPRESS_WARNING(5039) \ - DOCTEST_MSVC_SUPPRESS_WARNING(5045) \ - DOCTEST_MSVC_SUPPRESS_WARNING(5105) - -#define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END DOCTEST_MSVC_SUPPRESS_WARNING_POP - -// ================================================================================================= -// == FEATURE DETECTION ============================================================================ -// ================================================================================================= - -// general compiler feature support table: https://en.cppreference.com/w/cpp/compiler_support -// MSVC C++11 feature support table: https://msdn.microsoft.com/en-us/library/hh567368.aspx -// GCC C++11 feature support table: https://gcc.gnu.org/projects/cxx-status.html -// MSVC version table: -// https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering -// MSVC++ 14.2 (16) _MSC_VER == 1920 (Visual Studio 2019) -// MSVC++ 14.1 (15) _MSC_VER == 1910 (Visual Studio 2017) -// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) -// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) -// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) -// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) -// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) -// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) - -#if DOCTEST_MSVC && !defined(DOCTEST_CONFIG_WINDOWS_SEH) -#define DOCTEST_CONFIG_WINDOWS_SEH -#endif // MSVC -#if defined(DOCTEST_CONFIG_NO_WINDOWS_SEH) && defined(DOCTEST_CONFIG_WINDOWS_SEH) -#undef DOCTEST_CONFIG_WINDOWS_SEH -#endif // DOCTEST_CONFIG_NO_WINDOWS_SEH - -#if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && \ - !defined(__EMSCRIPTEN__) -#define DOCTEST_CONFIG_POSIX_SIGNALS -#endif // _WIN32 -#if defined(DOCTEST_CONFIG_NO_POSIX_SIGNALS) && defined(DOCTEST_CONFIG_POSIX_SIGNALS) -#undef DOCTEST_CONFIG_POSIX_SIGNALS -#endif // DOCTEST_CONFIG_NO_POSIX_SIGNALS - -#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS -#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND) -#define DOCTEST_CONFIG_NO_EXCEPTIONS -#endif // no exceptions -#endif // DOCTEST_CONFIG_NO_EXCEPTIONS - -#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS -#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS -#define DOCTEST_CONFIG_NO_EXCEPTIONS -#endif // DOCTEST_CONFIG_NO_EXCEPTIONS -#endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS - -#if defined(DOCTEST_CONFIG_NO_EXCEPTIONS) && !defined(DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS) -#define DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS -#endif // DOCTEST_CONFIG_NO_EXCEPTIONS && !DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS - -#if defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) && !defined(DOCTEST_CONFIG_IMPLEMENT) -#define DOCTEST_CONFIG_IMPLEMENT -#endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN - -#if defined(_WIN32) || defined(__CYGWIN__) -#if DOCTEST_MSVC -#define DOCTEST_SYMBOL_EXPORT __declspec(dllexport) -#define DOCTEST_SYMBOL_IMPORT __declspec(dllimport) -#else // MSVC -#define DOCTEST_SYMBOL_EXPORT __attribute__((dllexport)) -#define DOCTEST_SYMBOL_IMPORT __attribute__((dllimport)) -#endif // MSVC -#else // _WIN32 -#define DOCTEST_SYMBOL_EXPORT __attribute__((visibility("default"))) -#define DOCTEST_SYMBOL_IMPORT -#endif // _WIN32 - -#ifdef DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL -#ifdef DOCTEST_CONFIG_IMPLEMENT -#define DOCTEST_INTERFACE DOCTEST_SYMBOL_EXPORT -#else // DOCTEST_CONFIG_IMPLEMENT -#define DOCTEST_INTERFACE DOCTEST_SYMBOL_IMPORT -#endif // DOCTEST_CONFIG_IMPLEMENT -#else // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL -#define DOCTEST_INTERFACE -#endif // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL - -#define DOCTEST_EMPTY - -#if DOCTEST_MSVC -#define DOCTEST_NOINLINE __declspec(noinline) -#define DOCTEST_UNUSED -#define DOCTEST_ALIGNMENT(x) -#elif DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 5, 0) -#define DOCTEST_NOINLINE -#define DOCTEST_UNUSED -#define DOCTEST_ALIGNMENT(x) -#else -#define DOCTEST_NOINLINE __attribute__((noinline)) -#define DOCTEST_UNUSED __attribute__((unused)) -#define DOCTEST_ALIGNMENT(x) __attribute__((aligned(x))) -#endif - -#ifndef DOCTEST_NORETURN -#define DOCTEST_NORETURN [[noreturn]] -#endif // DOCTEST_NORETURN - -#ifndef DOCTEST_NOEXCEPT -#define DOCTEST_NOEXCEPT noexcept -#endif // DOCTEST_NOEXCEPT - -// ================================================================================================= -// == FEATURE DETECTION END ======================================================================== -// ================================================================================================= - -// internal macros for string concatenation and anonymous variable name generation -#define DOCTEST_CAT_IMPL(s1, s2) s1##s2 -#define DOCTEST_CAT(s1, s2) DOCTEST_CAT_IMPL(s1, s2) -#ifdef __COUNTER__ // not standard and may be missing for some compilers -#define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __COUNTER__) -#else // __COUNTER__ -#define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __LINE__) -#endif // __COUNTER__ - -#define DOCTEST_TOSTR(x) #x - -#ifndef DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE -#define DOCTEST_REF_WRAP(x) x& -#else // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE -#define DOCTEST_REF_WRAP(x) x -#endif // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE - -// not using __APPLE__ because... this is how Catch does it -#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED -#define DOCTEST_PLATFORM_MAC -#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) -#define DOCTEST_PLATFORM_IPHONE -#elif defined(_WIN32) -#define DOCTEST_PLATFORM_WINDOWS -#else // DOCTEST_PLATFORM -#define DOCTEST_PLATFORM_LINUX -#endif // DOCTEST_PLATFORM - -#define DOCTEST_GLOBAL_NO_WARNINGS(var) \ - DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") \ - DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-variable") \ - static const int var DOCTEST_UNUSED // NOLINT(fuchsia-statically-constructed-objects,cert-err58-cpp) -#define DOCTEST_GLOBAL_NO_WARNINGS_END() DOCTEST_CLANG_SUPPRESS_WARNING_POP - -#ifndef DOCTEST_BREAK_INTO_DEBUGGER -// should probably take a look at https://github.com/scottt/debugbreak -#ifdef DOCTEST_PLATFORM_LINUX -#if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) -// Break at the location of the failing check if possible -#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT (hicpp-no-assembler) -#else -#include -#define DOCTEST_BREAK_INTO_DEBUGGER() raise(SIGTRAP) -#endif -#elif defined(DOCTEST_PLATFORM_MAC) -#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__i386) -#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT (hicpp-no-assembler) -#else -#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("brk #0"); // NOLINT (hicpp-no-assembler) -#endif -#elif DOCTEST_MSVC -#define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak() -#elif defined(__MINGW32__) -DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wredundant-decls") -extern "C" __declspec(dllimport) void __stdcall DebugBreak(); -DOCTEST_GCC_SUPPRESS_WARNING_POP -#define DOCTEST_BREAK_INTO_DEBUGGER() ::DebugBreak() -#else // linux -#define DOCTEST_BREAK_INTO_DEBUGGER() (static_cast(0)) -#endif // linux -#endif // DOCTEST_BREAK_INTO_DEBUGGER - -// this is kept here for backwards compatibility since the config option was changed -#ifdef DOCTEST_CONFIG_USE_IOSFWD -#define DOCTEST_CONFIG_USE_STD_HEADERS -#endif // DOCTEST_CONFIG_USE_IOSFWD - -#ifdef DOCTEST_CONFIG_USE_STD_HEADERS -#ifndef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS -#define DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS -#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS -#include -#include -#include -#else // DOCTEST_CONFIG_USE_STD_HEADERS - -#if DOCTEST_CLANG -// to detect if libc++ is being used with clang (the _LIBCPP_VERSION identifier) -#include -#endif // clang - -#ifdef _LIBCPP_VERSION -#define DOCTEST_STD_NAMESPACE_BEGIN _LIBCPP_BEGIN_NAMESPACE_STD -#define DOCTEST_STD_NAMESPACE_END _LIBCPP_END_NAMESPACE_STD -#else // _LIBCPP_VERSION -#define DOCTEST_STD_NAMESPACE_BEGIN namespace std { -#define DOCTEST_STD_NAMESPACE_END } -#endif // _LIBCPP_VERSION - -// Forward declaring 'X' in namespace std is not permitted by the C++ Standard. -DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4643) - -DOCTEST_STD_NAMESPACE_BEGIN // NOLINT (cert-dcl58-cpp) -typedef decltype(nullptr) nullptr_t; -template -struct char_traits; -template <> -struct char_traits; -template -class basic_ostream; -typedef basic_ostream> ostream; -template -class tuple; -#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) -// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183 -template -class allocator; -template -class basic_string; -using string = basic_string, allocator>; -#endif // VS 2019 -DOCTEST_STD_NAMESPACE_END - -DOCTEST_MSVC_SUPPRESS_WARNING_POP - -#endif // DOCTEST_CONFIG_USE_STD_HEADERS - -#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS -#include -#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS - -namespace doctest { - -DOCTEST_INTERFACE extern bool is_running_in_test; - -// A 24 byte string class (can be as small as 17 for x64 and 13 for x86) that can hold strings with length -// of up to 23 chars on the stack before going on the heap - the last byte of the buffer is used for: -// - "is small" bit - the highest bit - if "0" then it is small - otherwise its "1" (128) -// - if small - capacity left before going on the heap - using the lowest 5 bits -// - if small - 2 bits are left unused - the second and third highest ones -// - if small - acts as a null terminator if strlen() is 23 (24 including the null terminator) -// and the "is small" bit remains "0" ("as well as the capacity left") so its OK -// Idea taken from this lecture about the string implementation of facebook/folly - fbstring -// https://www.youtube.com/watch?v=kPR8h4-qZdk -// TODO: -// - optimizations - like not deleting memory unnecessarily in operator= and etc. -// - resize/reserve/clear -// - substr -// - replace -// - back/front -// - iterator stuff -// - find & friends -// - push_back/pop_back -// - assign/insert/erase -// - relational operators as free functions - taking const char* as one of the params -class DOCTEST_INTERFACE String -{ - static const unsigned len = 24; //!OCLINT avoid private static members - static const unsigned last = len - 1; //!OCLINT avoid private static members - - struct view // len should be more than sizeof(view) - because of the final byte for flags - { - char* ptr; - unsigned size; - unsigned capacity; - }; - - union - { - char buf[len]; - view data; - }; - - bool isOnStack() const { return (buf[last] & 128) == 0; } - void setOnHeap(); - void setLast(unsigned in = last); - - void copy(const String& other); - -public: - String(); - ~String(); - - // cppcheck-suppress noExplicitConstructor - String(const char* in); - String(const char* in, unsigned in_size); - - String(const String& other); - String& operator=(const String& other); - - String& operator+=(const String& other); - String operator+(const String& other) const; - - String(String&& other); - String& operator=(String&& other); - - char operator[](unsigned i) const; - char& operator[](unsigned i); - - // the only functions I'm willing to leave in the interface - available for inlining - const char* c_str() const { return const_cast(this)->c_str(); } // NOLINT - char* c_str() { - if(isOnStack()) - return reinterpret_cast(buf); - return data.ptr; - } - - unsigned size() const; - unsigned capacity() const; - - int compare(const char* other, bool no_case = false) const; - int compare(const String& other, bool no_case = false) const; -}; - -DOCTEST_INTERFACE bool operator==(const String& lhs, const String& rhs); -DOCTEST_INTERFACE bool operator!=(const String& lhs, const String& rhs); -DOCTEST_INTERFACE bool operator<(const String& lhs, const String& rhs); -DOCTEST_INTERFACE bool operator>(const String& lhs, const String& rhs); -DOCTEST_INTERFACE bool operator<=(const String& lhs, const String& rhs); -DOCTEST_INTERFACE bool operator>=(const String& lhs, const String& rhs); - -DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in); - -namespace Color { - enum Enum - { - None = 0, - White, - Red, - Green, - Blue, - Cyan, - Yellow, - Grey, - - Bright = 0x10, - - BrightRed = Bright | Red, - BrightGreen = Bright | Green, - LightGrey = Bright | Grey, - BrightWhite = Bright | White - }; - - DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, Color::Enum code); -} // namespace Color - -namespace assertType { - enum Enum - { - // macro traits - - is_warn = 1, - is_check = 2 * is_warn, - is_require = 2 * is_check, - - is_normal = 2 * is_require, - is_throws = 2 * is_normal, - is_throws_as = 2 * is_throws, - is_throws_with = 2 * is_throws_as, - is_nothrow = 2 * is_throws_with, - - is_false = 2 * is_nothrow, - is_unary = 2 * is_false, // not checked anywhere - used just to distinguish the types - - is_eq = 2 * is_unary, - is_ne = 2 * is_eq, - - is_lt = 2 * is_ne, - is_gt = 2 * is_lt, - - is_ge = 2 * is_gt, - is_le = 2 * is_ge, - - // macro types - - DT_WARN = is_normal | is_warn, - DT_CHECK = is_normal | is_check, - DT_REQUIRE = is_normal | is_require, - - DT_WARN_FALSE = is_normal | is_false | is_warn, - DT_CHECK_FALSE = is_normal | is_false | is_check, - DT_REQUIRE_FALSE = is_normal | is_false | is_require, - - DT_WARN_THROWS = is_throws | is_warn, - DT_CHECK_THROWS = is_throws | is_check, - DT_REQUIRE_THROWS = is_throws | is_require, - - DT_WARN_THROWS_AS = is_throws_as | is_warn, - DT_CHECK_THROWS_AS = is_throws_as | is_check, - DT_REQUIRE_THROWS_AS = is_throws_as | is_require, - - DT_WARN_THROWS_WITH = is_throws_with | is_warn, - DT_CHECK_THROWS_WITH = is_throws_with | is_check, - DT_REQUIRE_THROWS_WITH = is_throws_with | is_require, - - DT_WARN_THROWS_WITH_AS = is_throws_with | is_throws_as | is_warn, - DT_CHECK_THROWS_WITH_AS = is_throws_with | is_throws_as | is_check, - DT_REQUIRE_THROWS_WITH_AS = is_throws_with | is_throws_as | is_require, - - DT_WARN_NOTHROW = is_nothrow | is_warn, - DT_CHECK_NOTHROW = is_nothrow | is_check, - DT_REQUIRE_NOTHROW = is_nothrow | is_require, - - DT_WARN_EQ = is_normal | is_eq | is_warn, - DT_CHECK_EQ = is_normal | is_eq | is_check, - DT_REQUIRE_EQ = is_normal | is_eq | is_require, - - DT_WARN_NE = is_normal | is_ne | is_warn, - DT_CHECK_NE = is_normal | is_ne | is_check, - DT_REQUIRE_NE = is_normal | is_ne | is_require, - - DT_WARN_GT = is_normal | is_gt | is_warn, - DT_CHECK_GT = is_normal | is_gt | is_check, - DT_REQUIRE_GT = is_normal | is_gt | is_require, - - DT_WARN_LT = is_normal | is_lt | is_warn, - DT_CHECK_LT = is_normal | is_lt | is_check, - DT_REQUIRE_LT = is_normal | is_lt | is_require, - - DT_WARN_GE = is_normal | is_ge | is_warn, - DT_CHECK_GE = is_normal | is_ge | is_check, - DT_REQUIRE_GE = is_normal | is_ge | is_require, - - DT_WARN_LE = is_normal | is_le | is_warn, - DT_CHECK_LE = is_normal | is_le | is_check, - DT_REQUIRE_LE = is_normal | is_le | is_require, - - DT_WARN_UNARY = is_normal | is_unary | is_warn, - DT_CHECK_UNARY = is_normal | is_unary | is_check, - DT_REQUIRE_UNARY = is_normal | is_unary | is_require, - - DT_WARN_UNARY_FALSE = is_normal | is_false | is_unary | is_warn, - DT_CHECK_UNARY_FALSE = is_normal | is_false | is_unary | is_check, - DT_REQUIRE_UNARY_FALSE = is_normal | is_false | is_unary | is_require, - }; -} // namespace assertType - -DOCTEST_INTERFACE const char* assertString(assertType::Enum at); -DOCTEST_INTERFACE const char* failureString(assertType::Enum at); -DOCTEST_INTERFACE const char* skipPathFromFilename(const char* file); - -struct DOCTEST_INTERFACE TestCaseData -{ - String m_file; // the file in which the test was registered (using String - see #350) - unsigned m_line; // the line where the test was registered - const char* m_name; // name of the test case - const char* m_test_suite; // the test suite in which the test was added - const char* m_description; - bool m_skip; - bool m_no_breaks; - bool m_no_output; - bool m_may_fail; - bool m_should_fail; - int m_expected_failures; - double m_timeout; -}; - -struct DOCTEST_INTERFACE AssertData -{ - // common - for all asserts - const TestCaseData* m_test_case; - assertType::Enum m_at; - const char* m_file; - int m_line; - const char* m_expr; - bool m_failed; - - // exception-related - for all asserts - bool m_threw; - String m_exception; - - // for normal asserts - String m_decomp; - - // for specific exception-related asserts - bool m_threw_as; - const char* m_exception_type; - const char* m_exception_string; -}; - -struct DOCTEST_INTERFACE MessageData -{ - String m_string; - const char* m_file; - int m_line; - assertType::Enum m_severity; -}; - -struct DOCTEST_INTERFACE SubcaseSignature -{ - String m_name; - const char* m_file; - int m_line; - - bool operator<(const SubcaseSignature& other) const; -}; - -struct DOCTEST_INTERFACE IContextScope -{ - IContextScope(); - virtual ~IContextScope(); - virtual void stringify(std::ostream*) const = 0; -}; - -namespace detail { - struct DOCTEST_INTERFACE TestCase; -} // namespace detail - -struct ContextOptions //!OCLINT too many fields -{ - std::ostream* cout; // stdout stream - std::cout by default - std::ostream* cerr; // stderr stream - std::cerr by default - String binary_name; // the test binary name - - const detail::TestCase* currentTest = nullptr; - - // == parameters from the command line - String out; // output filename - String order_by; // how tests should be ordered - unsigned rand_seed; // the seed for rand ordering - - unsigned first; // the first (matching) test to be executed - unsigned last; // the last (matching) test to be executed - - int abort_after; // stop tests after this many failed assertions - int subcase_filter_levels; // apply the subcase filters for the first N levels - - bool success; // include successful assertions in output - bool case_sensitive; // if filtering should be case sensitive - bool exit; // if the program should be exited after the tests are ran/whatever - bool duration; // print the time duration of each test case - bool no_throw; // to skip exceptions-related assertion macros - bool no_exitcode; // if the framework should return 0 as the exitcode - bool no_run; // to not run the tests at all (can be done with an "*" exclude) - bool no_version; // to not print the version of the framework - bool no_colors; // if output to the console should be colorized - bool force_colors; // forces the use of colors even when a tty cannot be detected - bool no_breaks; // to not break into the debugger - bool no_skip; // don't skip test cases which are marked to be skipped - bool gnu_file_line; // if line numbers should be surrounded with :x: and not (x): - bool no_path_in_filenames; // if the path to files should be removed from the output - bool no_line_numbers; // if source code line numbers should be omitted from the output - bool no_debug_output; // no output in the debug console when a debugger is attached - bool no_skipped_summary; // don't print "skipped" in the summary !!! UNDOCUMENTED !!! - bool no_time_in_output; // omit any time/timestamps from output !!! UNDOCUMENTED !!! - - bool help; // to print the help - bool version; // to print the version - bool count; // if only the count of matching tests is to be retrieved - bool list_test_cases; // to list all tests matching the filters - bool list_test_suites; // to list all suites matching the filters - bool list_reporters; // lists all registered reporters -}; - -namespace detail { - template - struct enable_if - {}; - - template - struct enable_if - { typedef TYPE type; }; - - // clang-format off - template struct remove_reference { typedef T type; }; - template struct remove_reference { typedef T type; }; - template struct remove_reference { typedef T type; }; - - template U declval(int); - - template T declval(long); - - template auto declval() DOCTEST_NOEXCEPT -> decltype(declval(0)) ; - - template struct is_lvalue_reference { const static bool value=false; }; - template struct is_lvalue_reference { const static bool value=true; }; - - template - inline T&& forward(typename remove_reference::type& t) DOCTEST_NOEXCEPT - { - return static_cast(t); - } - - template - inline T&& forward(typename remove_reference::type&& t) DOCTEST_NOEXCEPT - { - static_assert(!is_lvalue_reference::value, - "Can not forward an rvalue as an lvalue."); - return static_cast(t); - } - - template struct remove_const { typedef T type; }; - template struct remove_const { typedef T type; }; -#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS - template struct is_enum : public std::is_enum {}; - template struct underlying_type : public std::underlying_type {}; -#else - // Use compiler intrinsics - template struct is_enum { constexpr static bool value = __is_enum(T); }; - template struct underlying_type { typedef __underlying_type(T) type; }; -#endif - // clang-format on - - template - struct deferred_false - // cppcheck-suppress unusedStructMember - { static const bool value = false; }; - - namespace has_insertion_operator_impl { - std::ostream &os(); - template - DOCTEST_REF_WRAP(T) val(); - - template - struct check { - static constexpr bool value = false; - }; - - template - struct check(), void())> { - static constexpr bool value = true; - }; - } // namespace has_insertion_operator_impl - - template - using has_insertion_operator = has_insertion_operator_impl::check; - - DOCTEST_INTERFACE void my_memcpy(void* dest, const void* src, unsigned num); - - DOCTEST_INTERFACE std::ostream* getTlsOss(); // returns a thread-local ostringstream - DOCTEST_INTERFACE String getTlsOssResult(); - - template - struct StringMakerBase - { - template - static String convert(const DOCTEST_REF_WRAP(T)) { - return "{?}"; - } - }; - - template <> - struct StringMakerBase - { - template - static String convert(const DOCTEST_REF_WRAP(T) in) { - *getTlsOss() << in; - return getTlsOssResult(); - } - }; - - DOCTEST_INTERFACE String rawMemoryToString(const void* object, unsigned size); - - template - String rawMemoryToString(const DOCTEST_REF_WRAP(T) object) { - return rawMemoryToString(&object, sizeof(object)); - } - - template - const char* type_to_string() { - return "<>"; - } -} // namespace detail - -template -struct StringMaker : public detail::StringMakerBase::value> -{}; - -template -struct StringMaker -{ - template - static String convert(U* p) { - if(p) - return detail::rawMemoryToString(p); - return "NULL"; - } -}; - -template -struct StringMaker -{ - static String convert(R C::*p) { - if(p) - return detail::rawMemoryToString(p); - return "NULL"; - } -}; - -template ::value, bool>::type = true> -String toString(const DOCTEST_REF_WRAP(T) value) { - return StringMaker::convert(value); -} - -#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING -DOCTEST_INTERFACE String toString(char* in); -DOCTEST_INTERFACE String toString(const char* in); -#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING -DOCTEST_INTERFACE String toString(bool in); -DOCTEST_INTERFACE String toString(float in); -DOCTEST_INTERFACE String toString(double in); -DOCTEST_INTERFACE String toString(double long in); - -DOCTEST_INTERFACE String toString(char in); -DOCTEST_INTERFACE String toString(char signed in); -DOCTEST_INTERFACE String toString(char unsigned in); -DOCTEST_INTERFACE String toString(int short in); -DOCTEST_INTERFACE String toString(int short unsigned in); -DOCTEST_INTERFACE String toString(int in); -DOCTEST_INTERFACE String toString(int unsigned in); -DOCTEST_INTERFACE String toString(int long in); -DOCTEST_INTERFACE String toString(int long unsigned in); -DOCTEST_INTERFACE String toString(int long long in); -DOCTEST_INTERFACE String toString(int long long unsigned in); -DOCTEST_INTERFACE String toString(std::nullptr_t in); - -template ::value, bool>::type = true> -String toString(const DOCTEST_REF_WRAP(T) value) { - typedef typename detail::underlying_type::type UT; - return toString(static_cast(value)); -} - -#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) -// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183 -DOCTEST_INTERFACE String toString(const std::string& in); -#endif // VS 2019 - -class DOCTEST_INTERFACE Approx -{ -public: - explicit Approx(double value); - - Approx operator()(double value) const; - -#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS - template - explicit Approx(const T& value, - typename detail::enable_if::value>::type* = - static_cast(nullptr)) { - *this = Approx(static_cast(value)); - } -#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS - - Approx& epsilon(double newEpsilon); - -#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS - template - typename detail::enable_if::value, Approx&>::type epsilon( - const T& newEpsilon) { - m_epsilon = static_cast(newEpsilon); - return *this; - } -#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS - - Approx& scale(double newScale); - -#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS - template - typename detail::enable_if::value, Approx&>::type scale( - const T& newScale) { - m_scale = static_cast(newScale); - return *this; - } -#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS - - // clang-format off - DOCTEST_INTERFACE friend bool operator==(double lhs, const Approx & rhs); - DOCTEST_INTERFACE friend bool operator==(const Approx & lhs, double rhs); - DOCTEST_INTERFACE friend bool operator!=(double lhs, const Approx & rhs); - DOCTEST_INTERFACE friend bool operator!=(const Approx & lhs, double rhs); - DOCTEST_INTERFACE friend bool operator<=(double lhs, const Approx & rhs); - DOCTEST_INTERFACE friend bool operator<=(const Approx & lhs, double rhs); - DOCTEST_INTERFACE friend bool operator>=(double lhs, const Approx & rhs); - DOCTEST_INTERFACE friend bool operator>=(const Approx & lhs, double rhs); - DOCTEST_INTERFACE friend bool operator< (double lhs, const Approx & rhs); - DOCTEST_INTERFACE friend bool operator< (const Approx & lhs, double rhs); - DOCTEST_INTERFACE friend bool operator> (double lhs, const Approx & rhs); - DOCTEST_INTERFACE friend bool operator> (const Approx & lhs, double rhs); - - DOCTEST_INTERFACE friend String toString(const Approx& in); - -#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS -#define DOCTEST_APPROX_PREFIX \ - template friend typename detail::enable_if::value, bool>::type - - DOCTEST_APPROX_PREFIX operator==(const T& lhs, const Approx& rhs) { return operator==(double(lhs), rhs); } - DOCTEST_APPROX_PREFIX operator==(const Approx& lhs, const T& rhs) { return operator==(rhs, lhs); } - DOCTEST_APPROX_PREFIX operator!=(const T& lhs, const Approx& rhs) { return !operator==(lhs, rhs); } - DOCTEST_APPROX_PREFIX operator!=(const Approx& lhs, const T& rhs) { return !operator==(rhs, lhs); } - DOCTEST_APPROX_PREFIX operator<=(const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value || lhs == rhs; } - DOCTEST_APPROX_PREFIX operator<=(const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) || lhs == rhs; } - DOCTEST_APPROX_PREFIX operator>=(const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value || lhs == rhs; } - DOCTEST_APPROX_PREFIX operator>=(const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) || lhs == rhs; } - DOCTEST_APPROX_PREFIX operator< (const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value && lhs != rhs; } - DOCTEST_APPROX_PREFIX operator< (const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) && lhs != rhs; } - DOCTEST_APPROX_PREFIX operator> (const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value && lhs != rhs; } - DOCTEST_APPROX_PREFIX operator> (const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) && lhs != rhs; } -#undef DOCTEST_APPROX_PREFIX -#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS - - // clang-format on - -private: - double m_epsilon; - double m_scale; - double m_value; -}; - -DOCTEST_INTERFACE String toString(const Approx& in); - -DOCTEST_INTERFACE const ContextOptions* getContextOptions(); - -#if !defined(DOCTEST_CONFIG_DISABLE) - -namespace detail { - // clang-format off -#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - template struct decay_array { typedef T type; }; - template struct decay_array { typedef T* type; }; - template struct decay_array { typedef T* type; }; - - template struct not_char_pointer { enum { value = 1 }; }; - template<> struct not_char_pointer { enum { value = 0 }; }; - template<> struct not_char_pointer { enum { value = 0 }; }; - - template struct can_use_op : public not_char_pointer::type> {}; -#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - // clang-format on - - struct DOCTEST_INTERFACE TestFailureException - { - }; - - DOCTEST_INTERFACE bool checkIfShouldThrow(assertType::Enum at); - -#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS - DOCTEST_NORETURN -#endif // DOCTEST_CONFIG_NO_EXCEPTIONS - DOCTEST_INTERFACE void throwException(); - - struct DOCTEST_INTERFACE Subcase - { - SubcaseSignature m_signature; - bool m_entered = false; - - Subcase(const String& name, const char* file, int line); - ~Subcase(); - - operator bool() const; - }; - - template - String stringifyBinaryExpr(const DOCTEST_REF_WRAP(L) lhs, const char* op, - const DOCTEST_REF_WRAP(R) rhs) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) - return toString(lhs) + op + toString(rhs); - } - -#if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0) -DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison") -#endif - -// This will check if there is any way it could find a operator like member or friend and uses it. -// If not it doesn't find the operator or if the operator at global scope is defined after -// this template, the template won't be instantiated due to SFINAE. Once the template is not -// instantiated it can look for global operator using normal conversions. -#define SFINAE_OP(ret,op) decltype(doctest::detail::declval() op doctest::detail::declval(),static_cast(0)) - -#define DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(op, op_str, op_macro) \ - template \ - DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(R&& rhs) { \ - bool res = op_macro(doctest::detail::forward(lhs), doctest::detail::forward(rhs)); \ - if(m_at & assertType::is_false) \ - res = !res; \ - if(!res || doctest::getContextOptions()->success) \ - return Result(res, stringifyBinaryExpr(lhs, op_str, rhs)); \ - return Result(res); \ - } - - // more checks could be added - like in Catch: - // https://github.com/catchorg/Catch2/pull/1480/files - // https://github.com/catchorg/Catch2/pull/1481/files -#define DOCTEST_FORBIT_EXPRESSION(rt, op) \ - template \ - rt& operator op(const R&) { \ - static_assert(deferred_false::value, \ - "Expression Too Complex Please Rewrite As Binary Comparison!"); \ - return *this; \ - } - - struct DOCTEST_INTERFACE Result - { - bool m_passed; - String m_decomp; - - Result(bool passed, const String& decomposition = String()); - - // forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence - DOCTEST_FORBIT_EXPRESSION(Result, &) - DOCTEST_FORBIT_EXPRESSION(Result, ^) - DOCTEST_FORBIT_EXPRESSION(Result, |) - DOCTEST_FORBIT_EXPRESSION(Result, &&) - DOCTEST_FORBIT_EXPRESSION(Result, ||) - DOCTEST_FORBIT_EXPRESSION(Result, ==) - DOCTEST_FORBIT_EXPRESSION(Result, !=) - DOCTEST_FORBIT_EXPRESSION(Result, <) - DOCTEST_FORBIT_EXPRESSION(Result, >) - DOCTEST_FORBIT_EXPRESSION(Result, <=) - DOCTEST_FORBIT_EXPRESSION(Result, >=) - DOCTEST_FORBIT_EXPRESSION(Result, =) - DOCTEST_FORBIT_EXPRESSION(Result, +=) - DOCTEST_FORBIT_EXPRESSION(Result, -=) - DOCTEST_FORBIT_EXPRESSION(Result, *=) - DOCTEST_FORBIT_EXPRESSION(Result, /=) - DOCTEST_FORBIT_EXPRESSION(Result, %=) - DOCTEST_FORBIT_EXPRESSION(Result, <<=) - DOCTEST_FORBIT_EXPRESSION(Result, >>=) - DOCTEST_FORBIT_EXPRESSION(Result, &=) - DOCTEST_FORBIT_EXPRESSION(Result, ^=) - DOCTEST_FORBIT_EXPRESSION(Result, |=) - }; - -#ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION - - DOCTEST_CLANG_SUPPRESS_WARNING_PUSH - DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion") - DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-compare") - //DOCTEST_CLANG_SUPPRESS_WARNING("-Wdouble-promotion") - //DOCTEST_CLANG_SUPPRESS_WARNING("-Wconversion") - //DOCTEST_CLANG_SUPPRESS_WARNING("-Wfloat-equal") - - DOCTEST_GCC_SUPPRESS_WARNING_PUSH - DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion") - DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-compare") - //DOCTEST_GCC_SUPPRESS_WARNING("-Wdouble-promotion") - //DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion") - //DOCTEST_GCC_SUPPRESS_WARNING("-Wfloat-equal") - - DOCTEST_MSVC_SUPPRESS_WARNING_PUSH - // https://stackoverflow.com/questions/39479163 what's the difference between 4018 and 4389 - DOCTEST_MSVC_SUPPRESS_WARNING(4388) // signed/unsigned mismatch - DOCTEST_MSVC_SUPPRESS_WARNING(4389) // 'operator' : signed/unsigned mismatch - DOCTEST_MSVC_SUPPRESS_WARNING(4018) // 'expression' : signed/unsigned mismatch - //DOCTEST_MSVC_SUPPRESS_WARNING(4805) // 'operation' : unsafe mix of type 'type' and type 'type' in operation - -#endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION - - // clang-format off -#ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING -#define DOCTEST_COMPARISON_RETURN_TYPE bool -#else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING -#define DOCTEST_COMPARISON_RETURN_TYPE typename enable_if::value || can_use_op::value, bool>::type - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) - inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); } - inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); } - inline bool lt(const char* lhs, const char* rhs) { return String(lhs) < String(rhs); } - inline bool gt(const char* lhs, const char* rhs) { return String(lhs) > String(rhs); } - inline bool le(const char* lhs, const char* rhs) { return String(lhs) <= String(rhs); } - inline bool ge(const char* lhs, const char* rhs) { return String(lhs) >= String(rhs); } -#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - // clang-format on - -#define DOCTEST_RELATIONAL_OP(name, op) \ - template \ - DOCTEST_COMPARISON_RETURN_TYPE name(const DOCTEST_REF_WRAP(L) lhs, \ - const DOCTEST_REF_WRAP(R) rhs) { \ - return lhs op rhs; \ - } - - DOCTEST_RELATIONAL_OP(eq, ==) - DOCTEST_RELATIONAL_OP(ne, !=) - DOCTEST_RELATIONAL_OP(lt, <) - DOCTEST_RELATIONAL_OP(gt, >) - DOCTEST_RELATIONAL_OP(le, <=) - DOCTEST_RELATIONAL_OP(ge, >=) - -#ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING -#define DOCTEST_CMP_EQ(l, r) l == r -#define DOCTEST_CMP_NE(l, r) l != r -#define DOCTEST_CMP_GT(l, r) l > r -#define DOCTEST_CMP_LT(l, r) l < r -#define DOCTEST_CMP_GE(l, r) l >= r -#define DOCTEST_CMP_LE(l, r) l <= r -#else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING -#define DOCTEST_CMP_EQ(l, r) eq(l, r) -#define DOCTEST_CMP_NE(l, r) ne(l, r) -#define DOCTEST_CMP_GT(l, r) gt(l, r) -#define DOCTEST_CMP_LT(l, r) lt(l, r) -#define DOCTEST_CMP_GE(l, r) ge(l, r) -#define DOCTEST_CMP_LE(l, r) le(l, r) -#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - - template - // cppcheck-suppress copyCtorAndEqOperator - struct Expression_lhs - { - L lhs; - assertType::Enum m_at; - - explicit Expression_lhs(L&& in, assertType::Enum at) - : lhs(doctest::detail::forward(in)) - , m_at(at) {} - - DOCTEST_NOINLINE operator Result() { -// this is needed only foc MSVC 2015: -// https://ci.appveyor.com/project/onqtam/doctest/builds/38181202 -DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4800) // 'int': forcing value to bool - bool res = static_cast(lhs); -DOCTEST_MSVC_SUPPRESS_WARNING_POP - if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional - res = !res; - - if(!res || getContextOptions()->success) - return Result(res, toString(lhs)); - return Result(res); - } - - /* This is required for user-defined conversions from Expression_lhs to L */ - //operator L() const { return lhs; } - operator L() const { return lhs; } - - // clang-format off - DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(==, " == ", DOCTEST_CMP_EQ) //!OCLINT bitwise operator in conditional - DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(!=, " != ", DOCTEST_CMP_NE) //!OCLINT bitwise operator in conditional - DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>, " > ", DOCTEST_CMP_GT) //!OCLINT bitwise operator in conditional - DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<, " < ", DOCTEST_CMP_LT) //!OCLINT bitwise operator in conditional - DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>=, " >= ", DOCTEST_CMP_GE) //!OCLINT bitwise operator in conditional - DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<=, " <= ", DOCTEST_CMP_LE) //!OCLINT bitwise operator in conditional - // clang-format on - - // forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence - DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &) - DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ^) - DOCTEST_FORBIT_EXPRESSION(Expression_lhs, |) - DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &&) - DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ||) - DOCTEST_FORBIT_EXPRESSION(Expression_lhs, =) - DOCTEST_FORBIT_EXPRESSION(Expression_lhs, +=) - DOCTEST_FORBIT_EXPRESSION(Expression_lhs, -=) - DOCTEST_FORBIT_EXPRESSION(Expression_lhs, *=) - DOCTEST_FORBIT_EXPRESSION(Expression_lhs, /=) - DOCTEST_FORBIT_EXPRESSION(Expression_lhs, %=) - DOCTEST_FORBIT_EXPRESSION(Expression_lhs, <<=) - DOCTEST_FORBIT_EXPRESSION(Expression_lhs, >>=) - DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &=) - DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ^=) - DOCTEST_FORBIT_EXPRESSION(Expression_lhs, |=) - // these 2 are unfortunate because they should be allowed - they have higher precedence over the comparisons, but the - // ExpressionDecomposer class uses the left shift operator to capture the left operand of the binary expression... - DOCTEST_FORBIT_EXPRESSION(Expression_lhs, <<) - DOCTEST_FORBIT_EXPRESSION(Expression_lhs, >>) - }; - -#ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION - - DOCTEST_CLANG_SUPPRESS_WARNING_POP - DOCTEST_MSVC_SUPPRESS_WARNING_POP - DOCTEST_GCC_SUPPRESS_WARNING_POP - -#endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION - -#if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0) -DOCTEST_CLANG_SUPPRESS_WARNING_POP -#endif - - struct DOCTEST_INTERFACE ExpressionDecomposer - { - assertType::Enum m_at; - - ExpressionDecomposer(assertType::Enum at); - - // The right operator for capturing expressions is "<=" instead of "<<" (based on the operator precedence table) - // but then there will be warnings from GCC about "-Wparentheses" and since "_Pragma()" is problematic this will stay for now... - // https://github.com/catchorg/Catch2/issues/870 - // https://github.com/catchorg/Catch2/issues/565 - template - Expression_lhs operator<<(L &&operand) { - return Expression_lhs(doctest::detail::forward(operand), m_at); - } - }; - - struct DOCTEST_INTERFACE TestSuite - { - const char* m_test_suite; - const char* m_description; - bool m_skip; - bool m_no_breaks; - bool m_no_output; - bool m_may_fail; - bool m_should_fail; - int m_expected_failures; - double m_timeout; - - TestSuite& operator*(const char* in); - - template - TestSuite& operator*(const T& in) { - in.fill(*this); - return *this; - } - }; - - typedef void (*funcType)(); - - struct DOCTEST_INTERFACE TestCase : public TestCaseData - { - funcType m_test; // a function pointer to the test case - - const char* m_type; // for templated test cases - gets appended to the real name - int m_template_id; // an ID used to distinguish between the different versions of a templated test case - String m_full_name; // contains the name (only for templated test cases!) + the template type - - TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, - const char* type = "", int template_id = -1); - - TestCase(const TestCase& other); - - DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function - TestCase& operator=(const TestCase& other); - DOCTEST_MSVC_SUPPRESS_WARNING_POP - - TestCase& operator*(const char* in); - - template - TestCase& operator*(const T& in) { - in.fill(*this); - return *this; - } - - bool operator<(const TestCase& other) const; - }; - - // forward declarations of functions used by the macros - DOCTEST_INTERFACE int regTest(const TestCase& tc); - DOCTEST_INTERFACE int setTestSuite(const TestSuite& ts); - DOCTEST_INTERFACE bool isDebuggerActive(); - - template - int instantiationHelper(const T&) { return 0; } - - namespace binaryAssertComparison { - enum Enum - { - eq = 0, - ne, - gt, - lt, - ge, - le - }; - } // namespace binaryAssertComparison - - // clang-format off - template struct RelationalComparator { bool operator()(const DOCTEST_REF_WRAP(L), const DOCTEST_REF_WRAP(R) ) const { return false; } }; - -#define DOCTEST_BINARY_RELATIONAL_OP(n, op) \ - template struct RelationalComparator { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return op(lhs, rhs); } }; - // clang-format on - - DOCTEST_BINARY_RELATIONAL_OP(0, doctest::detail::eq) - DOCTEST_BINARY_RELATIONAL_OP(1, doctest::detail::ne) - DOCTEST_BINARY_RELATIONAL_OP(2, doctest::detail::gt) - DOCTEST_BINARY_RELATIONAL_OP(3, doctest::detail::lt) - DOCTEST_BINARY_RELATIONAL_OP(4, doctest::detail::ge) - DOCTEST_BINARY_RELATIONAL_OP(5, doctest::detail::le) - - struct DOCTEST_INTERFACE ResultBuilder : public AssertData - { - ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, - const char* exception_type = "", const char* exception_string = ""); - - void setResult(const Result& res); - - template - DOCTEST_NOINLINE void binary_assert(const DOCTEST_REF_WRAP(L) lhs, - const DOCTEST_REF_WRAP(R) rhs) { - m_failed = !RelationalComparator()(lhs, rhs); - if(m_failed || getContextOptions()->success) - m_decomp = stringifyBinaryExpr(lhs, ", ", rhs); - } - - template - DOCTEST_NOINLINE void unary_assert(const DOCTEST_REF_WRAP(L) val) { - m_failed = !val; - - if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional - m_failed = !m_failed; - - if(m_failed || getContextOptions()->success) - m_decomp = toString(val); - } - - void translateException(); - - bool log(); - void react() const; - }; - - namespace assertAction { - enum Enum - { - nothing = 0, - dbgbreak = 1, - shouldthrow = 2 - }; - } // namespace assertAction - - DOCTEST_INTERFACE void failed_out_of_a_testing_context(const AssertData& ad); - - DOCTEST_INTERFACE void decomp_assert(assertType::Enum at, const char* file, int line, - const char* expr, Result result); - -#define DOCTEST_ASSERT_OUT_OF_TESTS(decomp) \ - do { \ - if(!is_running_in_test) { \ - if(failed) { \ - ResultBuilder rb(at, file, line, expr); \ - rb.m_failed = failed; \ - rb.m_decomp = decomp; \ - failed_out_of_a_testing_context(rb); \ - if(isDebuggerActive() && !getContextOptions()->no_breaks) \ - DOCTEST_BREAK_INTO_DEBUGGER(); \ - if(checkIfShouldThrow(at)) \ - throwException(); \ - } \ - return; \ - } \ - } while(false) - -#define DOCTEST_ASSERT_IN_TESTS(decomp) \ - ResultBuilder rb(at, file, line, expr); \ - rb.m_failed = failed; \ - if(rb.m_failed || getContextOptions()->success) \ - rb.m_decomp = decomp; \ - if(rb.log()) \ - DOCTEST_BREAK_INTO_DEBUGGER(); \ - if(rb.m_failed && checkIfShouldThrow(at)) \ - throwException() - - template - DOCTEST_NOINLINE void binary_assert(assertType::Enum at, const char* file, int line, - const char* expr, const DOCTEST_REF_WRAP(L) lhs, - const DOCTEST_REF_WRAP(R) rhs) { - bool failed = !RelationalComparator()(lhs, rhs); - - // ################################################################################### - // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT - // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED - // ################################################################################### - DOCTEST_ASSERT_OUT_OF_TESTS(stringifyBinaryExpr(lhs, ", ", rhs)); - DOCTEST_ASSERT_IN_TESTS(stringifyBinaryExpr(lhs, ", ", rhs)); - } - - template - DOCTEST_NOINLINE void unary_assert(assertType::Enum at, const char* file, int line, - const char* expr, const DOCTEST_REF_WRAP(L) val) { - bool failed = !val; - - if(at & assertType::is_false) //!OCLINT bitwise operator in conditional - failed = !failed; - - // ################################################################################### - // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT - // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED - // ################################################################################### - DOCTEST_ASSERT_OUT_OF_TESTS(toString(val)); - DOCTEST_ASSERT_IN_TESTS(toString(val)); - } - - struct DOCTEST_INTERFACE IExceptionTranslator - { - IExceptionTranslator(); - virtual ~IExceptionTranslator(); - virtual bool translate(String&) const = 0; - }; - - template - class ExceptionTranslator : public IExceptionTranslator //!OCLINT destructor of virtual class - { - public: - explicit ExceptionTranslator(String (*translateFunction)(T)) - : m_translateFunction(translateFunction) {} - - bool translate(String& res) const override { -#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS - try { - throw; // lgtm [cpp/rethrow-no-exception] - // cppcheck-suppress catchExceptionByValue - } catch(T ex) { // NOLINT - res = m_translateFunction(ex); //!OCLINT parameter reassignment - return true; - } catch(...) {} //!OCLINT - empty catch statement -#endif // DOCTEST_CONFIG_NO_EXCEPTIONS - static_cast(res); // to silence -Wunused-parameter - return false; - } - - private: - String (*m_translateFunction)(T); - }; - - DOCTEST_INTERFACE void registerExceptionTranslatorImpl(const IExceptionTranslator* et); - - template - struct StringStreamBase - { - template - static void convert(std::ostream* s, const T& in) { - *s << toString(in); - } - - // always treat char* as a string in this context - no matter - // if DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING is defined - static void convert(std::ostream* s, const char* in) { *s << String(in); } - }; - - template <> - struct StringStreamBase - { - template - static void convert(std::ostream* s, const T& in) { - *s << in; - } - }; - - template - struct StringStream : public StringStreamBase::value> - {}; - - template - void toStream(std::ostream* s, const T& value) { - StringStream::convert(s, value); - } - -#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - DOCTEST_INTERFACE void toStream(std::ostream* s, char* in); - DOCTEST_INTERFACE void toStream(std::ostream* s, const char* in); -#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - DOCTEST_INTERFACE void toStream(std::ostream* s, bool in); - DOCTEST_INTERFACE void toStream(std::ostream* s, float in); - DOCTEST_INTERFACE void toStream(std::ostream* s, double in); - DOCTEST_INTERFACE void toStream(std::ostream* s, double long in); - - DOCTEST_INTERFACE void toStream(std::ostream* s, char in); - DOCTEST_INTERFACE void toStream(std::ostream* s, char signed in); - DOCTEST_INTERFACE void toStream(std::ostream* s, char unsigned in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int short in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int short unsigned in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int unsigned in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int long in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int long unsigned in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int long long in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int long long unsigned in); - - // ContextScope base class used to allow implementing methods of ContextScope - // that don't depend on the template parameter in doctest.cpp. - class DOCTEST_INTERFACE ContextScopeBase : public IContextScope { - protected: - ContextScopeBase(); - - void destroy(); - }; - - template class ContextScope : public ContextScopeBase - { - const L lambda_; - - public: - explicit ContextScope(const L &lambda) : lambda_(lambda) {} - - ContextScope(ContextScope &&other) : lambda_(other.lambda_) {} - - void stringify(std::ostream* s) const override { lambda_(s); } - - ~ContextScope() override { destroy(); } - }; - - struct DOCTEST_INTERFACE MessageBuilder : public MessageData - { - std::ostream* m_stream; - - MessageBuilder(const char* file, int line, assertType::Enum severity); - MessageBuilder() = delete; - ~MessageBuilder(); - - // the preferred way of chaining parameters for stringification - template - MessageBuilder& operator,(const T& in) { - toStream(m_stream, in); - return *this; - } - - // kept here just for backwards-compatibility - the comma operator should be preferred now - template - MessageBuilder& operator<<(const T& in) { return this->operator,(in); } - - // the `,` operator has the lowest operator precedence - if `<<` is used by the user then - // the `,` operator will be called last which is not what we want and thus the `*` operator - // is used first (has higher operator precedence compared to `<<`) so that we guarantee that - // an operator of the MessageBuilder class is called first before the rest of the parameters - template - MessageBuilder& operator*(const T& in) { return this->operator,(in); } - - bool log(); - void react(); - }; - - template - ContextScope MakeContextScope(const L &lambda) { - return ContextScope(lambda); - } -} // namespace detail - -#define DOCTEST_DEFINE_DECORATOR(name, type, def) \ - struct name \ - { \ - type data; \ - name(type in = def) \ - : data(in) {} \ - void fill(detail::TestCase& state) const { state.DOCTEST_CAT(m_, name) = data; } \ - void fill(detail::TestSuite& state) const { state.DOCTEST_CAT(m_, name) = data; } \ - } - -DOCTEST_DEFINE_DECORATOR(test_suite, const char*, ""); -DOCTEST_DEFINE_DECORATOR(description, const char*, ""); -DOCTEST_DEFINE_DECORATOR(skip, bool, true); -DOCTEST_DEFINE_DECORATOR(no_breaks, bool, true); -DOCTEST_DEFINE_DECORATOR(no_output, bool, true); -DOCTEST_DEFINE_DECORATOR(timeout, double, 0); -DOCTEST_DEFINE_DECORATOR(may_fail, bool, true); -DOCTEST_DEFINE_DECORATOR(should_fail, bool, true); -DOCTEST_DEFINE_DECORATOR(expected_failures, int, 0); - -template -int registerExceptionTranslator(String (*translateFunction)(T)) { - DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") - static detail::ExceptionTranslator exceptionTranslator(translateFunction); - DOCTEST_CLANG_SUPPRESS_WARNING_POP - detail::registerExceptionTranslatorImpl(&exceptionTranslator); - return 0; -} - -} // namespace doctest - -// in a separate namespace outside of doctest because the DOCTEST_TEST_SUITE macro -// introduces an anonymous namespace in which getCurrentTestSuite gets overridden -namespace doctest_detail_test_suite_ns { -DOCTEST_INTERFACE doctest::detail::TestSuite& getCurrentTestSuite(); -} // namespace doctest_detail_test_suite_ns - -namespace doctest { -#else // DOCTEST_CONFIG_DISABLE -template -int registerExceptionTranslator(String (*)(T)) { - return 0; -} -#endif // DOCTEST_CONFIG_DISABLE - -namespace detail { - typedef void (*assert_handler)(const AssertData&); - struct ContextState; -} // namespace detail - -class DOCTEST_INTERFACE Context -{ - detail::ContextState* p; - - void parseArgs(int argc, const char* const* argv, bool withDefaults = false); - -public: - explicit Context(int argc = 0, const char* const* argv = nullptr); - - ~Context(); - - void applyCommandLine(int argc, const char* const* argv); - - void addFilter(const char* filter, const char* value); - void clearFilters(); - void setOption(const char* option, int value); - void setOption(const char* option, const char* value); - - bool shouldExit(); - - void setAsDefaultForAssertsOutOfTestCases(); - - void setAssertHandler(detail::assert_handler ah); - - int run(); -}; - -namespace TestCaseFailureReason { - enum Enum - { - None = 0, - AssertFailure = 1, // an assertion has failed in the test case - Exception = 2, // test case threw an exception - Crash = 4, // a crash... - TooManyFailedAsserts = 8, // the abort-after option - Timeout = 16, // see the timeout decorator - ShouldHaveFailedButDidnt = 32, // see the should_fail decorator - ShouldHaveFailedAndDid = 64, // see the should_fail decorator - DidntFailExactlyNumTimes = 128, // see the expected_failures decorator - FailedExactlyNumTimes = 256, // see the expected_failures decorator - CouldHaveFailedAndDid = 512 // see the may_fail decorator - }; -} // namespace TestCaseFailureReason - -struct DOCTEST_INTERFACE CurrentTestCaseStats -{ - int numAssertsCurrentTest; - int numAssertsFailedCurrentTest; - double seconds; - int failure_flags; // use TestCaseFailureReason::Enum -}; - -struct DOCTEST_INTERFACE TestCaseException -{ - String error_string; - bool is_crash; -}; - -struct DOCTEST_INTERFACE TestRunStats -{ - unsigned numTestCases; - unsigned numTestCasesPassingFilters; - unsigned numTestSuitesPassingFilters; - unsigned numTestCasesFailed; - int numAsserts; - int numAssertsFailed; -}; - -struct QueryData -{ - const TestRunStats* run_stats = nullptr; - const TestCaseData** data = nullptr; - unsigned num_data = 0; -}; - -struct DOCTEST_INTERFACE IReporter -{ - // The constructor has to accept "const ContextOptions&" as a single argument - // which has most of the options for the run + a pointer to the stdout stream - // Reporter(const ContextOptions& in) - - // called when a query should be reported (listing test cases, printing the version, etc.) - virtual void report_query(const QueryData&) = 0; - - // called when the whole test run starts - virtual void test_run_start() = 0; - // called when the whole test run ends (caching a pointer to the input doesn't make sense here) - virtual void test_run_end(const TestRunStats&) = 0; - - // called when a test case is started (safe to cache a pointer to the input) - virtual void test_case_start(const TestCaseData&) = 0; - // called when a test case is reentered because of unfinished subcases (safe to cache a pointer to the input) - virtual void test_case_reenter(const TestCaseData&) = 0; - // called when a test case has ended - virtual void test_case_end(const CurrentTestCaseStats&) = 0; - - // called when an exception is thrown from the test case (or it crashes) - virtual void test_case_exception(const TestCaseException&) = 0; - - // called whenever a subcase is entered (don't cache pointers to the input) - virtual void subcase_start(const SubcaseSignature&) = 0; - // called whenever a subcase is exited (don't cache pointers to the input) - virtual void subcase_end() = 0; - - // called for each assert (don't cache pointers to the input) - virtual void log_assert(const AssertData&) = 0; - // called for each message (don't cache pointers to the input) - virtual void log_message(const MessageData&) = 0; - - // called when a test case is skipped either because it doesn't pass the filters, has a skip decorator - // or isn't in the execution range (between first and last) (safe to cache a pointer to the input) - virtual void test_case_skipped(const TestCaseData&) = 0; - - // doctest will not be managing the lifetimes of reporters given to it but this would still be nice to have - virtual ~IReporter(); - - // can obtain all currently active contexts and stringify them if one wishes to do so - static int get_num_active_contexts(); - static const IContextScope* const* get_active_contexts(); - - // can iterate through contexts which have been stringified automatically in their destructors when an exception has been thrown - static int get_num_stringified_contexts(); - static const String* get_stringified_contexts(); -}; - -namespace detail { - typedef IReporter* (*reporterCreatorFunc)(const ContextOptions&); - - DOCTEST_INTERFACE void registerReporterImpl(const char* name, int prio, reporterCreatorFunc c, bool isReporter); - - template - IReporter* reporterCreator(const ContextOptions& o) { - return new Reporter(o); - } -} // namespace detail - -template -int registerReporter(const char* name, int priority, bool isReporter) { - detail::registerReporterImpl(name, priority, detail::reporterCreator, isReporter); - return 0; -} -} // namespace doctest - -// if registering is not disabled -#if !defined(DOCTEST_CONFIG_DISABLE) - -// common code in asserts - for convenience -#define DOCTEST_ASSERT_LOG_AND_REACT(b) \ - if(b.log()) \ - DOCTEST_BREAK_INTO_DEBUGGER(); \ - b.react() - -#ifdef DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS -#define DOCTEST_WRAP_IN_TRY(x) x; -#else // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS -#define DOCTEST_WRAP_IN_TRY(x) \ - try { \ - x; \ - } catch(...) { _DOCTEST_RB.translateException(); } -#endif // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS - -#ifdef DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS -#define DOCTEST_CAST_TO_VOID(...) \ - DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wuseless-cast") \ - static_cast(__VA_ARGS__); \ - DOCTEST_GCC_SUPPRESS_WARNING_POP -#else // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS -#define DOCTEST_CAST_TO_VOID(...) __VA_ARGS__; -#endif // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS - -// registers the test by initializing a dummy var with a function -#define DOCTEST_REGISTER_FUNCTION(global_prefix, f, decorators) \ - global_prefix DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \ - doctest::detail::regTest( \ - doctest::detail::TestCase( \ - f, __FILE__, __LINE__, \ - doctest_detail_test_suite_ns::getCurrentTestSuite()) * \ - decorators); \ - DOCTEST_GLOBAL_NO_WARNINGS_END() - -#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, decorators) \ - namespace { \ - struct der : public base \ - { \ - void f(); \ - }; \ - static void func() { \ - der v; \ - v.f(); \ - } \ - DOCTEST_REGISTER_FUNCTION(DOCTEST_EMPTY, func, decorators) \ - } \ - inline DOCTEST_NOINLINE void der::f() - -#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, decorators) \ - static void f(); \ - DOCTEST_REGISTER_FUNCTION(DOCTEST_EMPTY, f, decorators) \ - static void f() - -#define DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(f, proxy, decorators) \ - static doctest::detail::funcType proxy() { return f; } \ - DOCTEST_REGISTER_FUNCTION(inline const, proxy(), decorators) \ - static void f() - -// for registering tests -#define DOCTEST_TEST_CASE(decorators) \ - DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), decorators) - -// for registering tests in classes - requires C++17 for inline variables! -#if __cplusplus >= 201703L || (DOCTEST_MSVC >= DOCTEST_COMPILER(19, 12, 0) && _MSVC_LANG >= 201703L) -#define DOCTEST_TEST_CASE_CLASS(decorators) \ - DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), \ - DOCTEST_ANONYMOUS(_DOCTEST_ANON_PROXY_), \ - decorators) -#else // DOCTEST_TEST_CASE_CLASS -#define DOCTEST_TEST_CASE_CLASS(...) \ - TEST_CASES_CAN_BE_REGISTERED_IN_CLASSES_ONLY_IN_CPP17_MODE_OR_WITH_VS_2017_OR_NEWER -#endif // DOCTEST_TEST_CASE_CLASS - -// for registering tests with a fixture -#define DOCTEST_TEST_CASE_FIXTURE(c, decorators) \ - DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), c, \ - DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), decorators) - -// for converting types to strings without the header and demangling -#define DOCTEST_TYPE_TO_STRING_IMPL(...) \ - template <> \ - inline const char* type_to_string<__VA_ARGS__>() { \ - return "<" #__VA_ARGS__ ">"; \ - } -#define DOCTEST_TYPE_TO_STRING(...) \ - namespace doctest { namespace detail { \ - DOCTEST_TYPE_TO_STRING_IMPL(__VA_ARGS__) \ - } \ - } \ - typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) - -#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, iter, func) \ - template \ - static void func(); \ - namespace { \ - template \ - struct iter; \ - template \ - struct iter> \ - { \ - iter(const char* file, unsigned line, int index) { \ - doctest::detail::regTest(doctest::detail::TestCase(func, file, line, \ - doctest_detail_test_suite_ns::getCurrentTestSuite(), \ - doctest::detail::type_to_string(), \ - int(line) * 1000 + index) \ - * dec); \ - iter>(file, line, index + 1); \ - } \ - }; \ - template <> \ - struct iter> \ - { \ - iter(const char*, unsigned, int) {} \ - }; \ - } \ - template \ - static void func() - -#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(dec, T, id) \ - DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(id, ITERATOR), \ - DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)) - -#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, anon, ...) \ - DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY)) = \ - doctest::detail::instantiationHelper(DOCTEST_CAT(id, ITERATOR)<__VA_ARGS__>(__FILE__, __LINE__, 0));\ - DOCTEST_GLOBAL_NO_WARNINGS_END() - -#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) \ - DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), std::tuple<__VA_ARGS__>) \ - typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) - -#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) \ - DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), __VA_ARGS__) \ - typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) - -#define DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, anon, ...) \ - DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(anon, ITERATOR), anon); \ - DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(anon, anon, std::tuple<__VA_ARGS__>) \ - template \ - static void anon() - -#define DOCTEST_TEST_CASE_TEMPLATE(dec, T, ...) \ - DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), __VA_ARGS__) - -// for subcases -#define DOCTEST_SUBCASE(name) \ - if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUBCASE_) DOCTEST_UNUSED = \ - doctest::detail::Subcase(name, __FILE__, __LINE__)) - -// for grouping tests in test suites by using code blocks -#define DOCTEST_TEST_SUITE_IMPL(decorators, ns_name) \ - namespace ns_name { namespace doctest_detail_test_suite_ns { \ - static DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() { \ - DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4640) \ - DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") \ - DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmissing-field-initializers") \ - static doctest::detail::TestSuite data{}; \ - static bool inited = false; \ - DOCTEST_MSVC_SUPPRESS_WARNING_POP \ - DOCTEST_CLANG_SUPPRESS_WARNING_POP \ - DOCTEST_GCC_SUPPRESS_WARNING_POP \ - if(!inited) { \ - data* decorators; \ - inited = true; \ - } \ - return data; \ - } \ - } \ - } \ - namespace ns_name - -#define DOCTEST_TEST_SUITE(decorators) \ - DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUITE_)) - -// for starting a testsuite block -#define DOCTEST_TEST_SUITE_BEGIN(decorators) \ - DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \ - doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators); \ - DOCTEST_GLOBAL_NO_WARNINGS_END() \ - typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) - -// for ending a testsuite block -#define DOCTEST_TEST_SUITE_END \ - DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \ - doctest::detail::setTestSuite(doctest::detail::TestSuite() * ""); \ - DOCTEST_GLOBAL_NO_WARNINGS_END() \ - typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) - -// for registering exception translators -#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(translatorName, signature) \ - inline doctest::String translatorName(signature); \ - DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)) = \ - doctest::registerExceptionTranslator(translatorName); \ - DOCTEST_GLOBAL_NO_WARNINGS_END() \ - doctest::String translatorName(signature) - -#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \ - DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_), \ - signature) - -// for registering reporters -#define DOCTEST_REGISTER_REPORTER(name, priority, reporter) \ - DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_REPORTER_)) = \ - doctest::registerReporter(name, priority, true); \ - DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) - -// for registering listeners -#define DOCTEST_REGISTER_LISTENER(name, priority, reporter) \ - DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_REPORTER_)) = \ - doctest::registerReporter(name, priority, false); \ - DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) - -// for logging -#define DOCTEST_INFO(...) \ - DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), \ - __VA_ARGS__) - -#define DOCTEST_INFO_IMPL(mb_name, s_name, ...) \ - auto DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope( \ - [&](std::ostream* s_name) { \ - doctest::detail::MessageBuilder mb_name(__FILE__, __LINE__, doctest::assertType::is_warn); \ - mb_name.m_stream = s_name; \ - mb_name * __VA_ARGS__; \ - }) - -#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := ", x) - -#define DOCTEST_ADD_AT_IMPL(type, file, line, mb, ...) \ - do { \ - doctest::detail::MessageBuilder mb(file, line, doctest::assertType::type); \ - mb * __VA_ARGS__; \ - DOCTEST_ASSERT_LOG_AND_REACT(mb); \ - } while(false) - -// clang-format off -#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), __VA_ARGS__) -#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), __VA_ARGS__) -#define DOCTEST_ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), __VA_ARGS__) -// clang-format on - -#define DOCTEST_MESSAGE(...) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, __VA_ARGS__) -#define DOCTEST_FAIL_CHECK(...) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, __VA_ARGS__) -#define DOCTEST_FAIL(...) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, __VA_ARGS__) - -#define DOCTEST_TO_LVALUE(...) __VA_ARGS__ // Not removed to keep backwards compatibility. - -#ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS - -#define DOCTEST_ASSERT_IMPLEMENT_2(assert_type, ...) \ - DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \ - doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ - __LINE__, #__VA_ARGS__); \ - DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.setResult( \ - doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \ - << __VA_ARGS__)) \ - DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB) \ - DOCTEST_CLANG_SUPPRESS_WARNING_POP - -#define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \ - do { \ - DOCTEST_ASSERT_IMPLEMENT_2(assert_type, __VA_ARGS__); \ - } while(false) - -#else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS - -// necessary for _MESSAGE -#define DOCTEST_ASSERT_IMPLEMENT_2 DOCTEST_ASSERT_IMPLEMENT_1 - -#define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \ - DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \ - doctest::detail::decomp_assert( \ - doctest::assertType::assert_type, __FILE__, __LINE__, #__VA_ARGS__, \ - doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \ - << __VA_ARGS__) DOCTEST_CLANG_SUPPRESS_WARNING_POP - -#endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS - -#define DOCTEST_WARN(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_WARN, __VA_ARGS__) -#define DOCTEST_CHECK(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_CHECK, __VA_ARGS__) -#define DOCTEST_REQUIRE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE, __VA_ARGS__) -#define DOCTEST_WARN_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_WARN_FALSE, __VA_ARGS__) -#define DOCTEST_CHECK_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_CHECK_FALSE, __VA_ARGS__) -#define DOCTEST_REQUIRE_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE_FALSE, __VA_ARGS__) - -// clang-format off -#define DOCTEST_WARN_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); } while(false) -#define DOCTEST_CHECK_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); } while(false) -#define DOCTEST_REQUIRE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); } while(false) -#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); } while(false) -#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); } while(false) -#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); } while(false) -// clang-format on - -#define DOCTEST_ASSERT_THROWS_AS(expr, assert_type, message, ...) \ - do { \ - if(!doctest::getContextOptions()->no_throw) { \ - doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ - __LINE__, #expr, #__VA_ARGS__, message); \ - try { \ - DOCTEST_CAST_TO_VOID(expr) \ - } catch(const typename doctest::detail::remove_const< \ - typename doctest::detail::remove_reference<__VA_ARGS__>::type>::type&) { \ - _DOCTEST_RB.translateException(); \ - _DOCTEST_RB.m_threw_as = true; \ - } catch(...) { _DOCTEST_RB.translateException(); } \ - DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ - } \ - } while(false) - -#define DOCTEST_ASSERT_THROWS_WITH(expr, expr_str, assert_type, ...) \ - do { \ - if(!doctest::getContextOptions()->no_throw) { \ - doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ - __LINE__, expr_str, "", __VA_ARGS__); \ - try { \ - DOCTEST_CAST_TO_VOID(expr) \ - } catch(...) { _DOCTEST_RB.translateException(); } \ - DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ - } \ - } while(false) - -#define DOCTEST_ASSERT_NOTHROW(assert_type, ...) \ - do { \ - doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ - __LINE__, #__VA_ARGS__); \ - try { \ - DOCTEST_CAST_TO_VOID(__VA_ARGS__) \ - } catch(...) { _DOCTEST_RB.translateException(); } \ - DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ - } while(false) - -// clang-format off -#define DOCTEST_WARN_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_WARN_THROWS, "") -#define DOCTEST_CHECK_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_CHECK_THROWS, "") -#define DOCTEST_REQUIRE_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_REQUIRE_THROWS, "") - -#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_AS, "", __VA_ARGS__) -#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_AS, "", __VA_ARGS__) -#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_AS, "", __VA_ARGS__) - -#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_WARN_THROWS_WITH, __VA_ARGS__) -#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_CHECK_THROWS_WITH, __VA_ARGS__) -#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_REQUIRE_THROWS_WITH, __VA_ARGS__) - -#define DOCTEST_WARN_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_WITH_AS, message, __VA_ARGS__) -#define DOCTEST_CHECK_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_WITH_AS, message, __VA_ARGS__) -#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_WITH_AS, message, __VA_ARGS__) - -#define DOCTEST_WARN_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_WARN_NOTHROW, __VA_ARGS__) -#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_CHECK_NOTHROW, __VA_ARGS__) -#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_REQUIRE_NOTHROW, __VA_ARGS__) - -#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS(expr); } while(false) -#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS(expr); } while(false) -#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS(expr); } while(false) -#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_AS(expr, ex); } while(false) -#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_AS(expr, ex); } while(false) -#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } while(false) -#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH(expr, with); } while(false) -#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH(expr, with); } while(false) -#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH(expr, with); } while(false) -#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex); } while(false) -#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex); } while(false) -#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex); } while(false) -#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_NOTHROW(expr); } while(false) -#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_NOTHROW(expr); } while(false) -#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_NOTHROW(expr); } while(false) -// clang-format on - -#ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS - -#define DOCTEST_BINARY_ASSERT(assert_type, comp, ...) \ - do { \ - doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ - __LINE__, #__VA_ARGS__); \ - DOCTEST_WRAP_IN_TRY( \ - _DOCTEST_RB.binary_assert( \ - __VA_ARGS__)) \ - DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ - } while(false) - -#define DOCTEST_UNARY_ASSERT(assert_type, ...) \ - do { \ - doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ - __LINE__, #__VA_ARGS__); \ - DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.unary_assert(__VA_ARGS__)) \ - DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ - } while(false) - -#else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS - -#define DOCTEST_BINARY_ASSERT(assert_type, comparison, ...) \ - doctest::detail::binary_assert( \ - doctest::assertType::assert_type, __FILE__, __LINE__, #__VA_ARGS__, __VA_ARGS__) - -#define DOCTEST_UNARY_ASSERT(assert_type, ...) \ - doctest::detail::unary_assert(doctest::assertType::assert_type, __FILE__, __LINE__, \ - #__VA_ARGS__, __VA_ARGS__) - -#endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS - -#define DOCTEST_WARN_EQ(...) DOCTEST_BINARY_ASSERT(DT_WARN_EQ, eq, __VA_ARGS__) -#define DOCTEST_CHECK_EQ(...) DOCTEST_BINARY_ASSERT(DT_CHECK_EQ, eq, __VA_ARGS__) -#define DOCTEST_REQUIRE_EQ(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_EQ, eq, __VA_ARGS__) -#define DOCTEST_WARN_NE(...) DOCTEST_BINARY_ASSERT(DT_WARN_NE, ne, __VA_ARGS__) -#define DOCTEST_CHECK_NE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_NE, ne, __VA_ARGS__) -#define DOCTEST_REQUIRE_NE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_NE, ne, __VA_ARGS__) -#define DOCTEST_WARN_GT(...) DOCTEST_BINARY_ASSERT(DT_WARN_GT, gt, __VA_ARGS__) -#define DOCTEST_CHECK_GT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GT, gt, __VA_ARGS__) -#define DOCTEST_REQUIRE_GT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GT, gt, __VA_ARGS__) -#define DOCTEST_WARN_LT(...) DOCTEST_BINARY_ASSERT(DT_WARN_LT, lt, __VA_ARGS__) -#define DOCTEST_CHECK_LT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LT, lt, __VA_ARGS__) -#define DOCTEST_REQUIRE_LT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LT, lt, __VA_ARGS__) -#define DOCTEST_WARN_GE(...) DOCTEST_BINARY_ASSERT(DT_WARN_GE, ge, __VA_ARGS__) -#define DOCTEST_CHECK_GE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GE, ge, __VA_ARGS__) -#define DOCTEST_REQUIRE_GE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GE, ge, __VA_ARGS__) -#define DOCTEST_WARN_LE(...) DOCTEST_BINARY_ASSERT(DT_WARN_LE, le, __VA_ARGS__) -#define DOCTEST_CHECK_LE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LE, le, __VA_ARGS__) -#define DOCTEST_REQUIRE_LE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LE, le, __VA_ARGS__) - -#define DOCTEST_WARN_UNARY(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY, __VA_ARGS__) -#define DOCTEST_CHECK_UNARY(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY, __VA_ARGS__) -#define DOCTEST_REQUIRE_UNARY(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY, __VA_ARGS__) -#define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY_FALSE, __VA_ARGS__) -#define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, __VA_ARGS__) -#define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, __VA_ARGS__) - -#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS - -#undef DOCTEST_WARN_THROWS -#undef DOCTEST_CHECK_THROWS -#undef DOCTEST_REQUIRE_THROWS -#undef DOCTEST_WARN_THROWS_AS -#undef DOCTEST_CHECK_THROWS_AS -#undef DOCTEST_REQUIRE_THROWS_AS -#undef DOCTEST_WARN_THROWS_WITH -#undef DOCTEST_CHECK_THROWS_WITH -#undef DOCTEST_REQUIRE_THROWS_WITH -#undef DOCTEST_WARN_THROWS_WITH_AS -#undef DOCTEST_CHECK_THROWS_WITH_AS -#undef DOCTEST_REQUIRE_THROWS_WITH_AS -#undef DOCTEST_WARN_NOTHROW -#undef DOCTEST_CHECK_NOTHROW -#undef DOCTEST_REQUIRE_NOTHROW - -#undef DOCTEST_WARN_THROWS_MESSAGE -#undef DOCTEST_CHECK_THROWS_MESSAGE -#undef DOCTEST_REQUIRE_THROWS_MESSAGE -#undef DOCTEST_WARN_THROWS_AS_MESSAGE -#undef DOCTEST_CHECK_THROWS_AS_MESSAGE -#undef DOCTEST_REQUIRE_THROWS_AS_MESSAGE -#undef DOCTEST_WARN_THROWS_WITH_MESSAGE -#undef DOCTEST_CHECK_THROWS_WITH_MESSAGE -#undef DOCTEST_REQUIRE_THROWS_WITH_MESSAGE -#undef DOCTEST_WARN_THROWS_WITH_AS_MESSAGE -#undef DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE -#undef DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE -#undef DOCTEST_WARN_NOTHROW_MESSAGE -#undef DOCTEST_CHECK_NOTHROW_MESSAGE -#undef DOCTEST_REQUIRE_NOTHROW_MESSAGE - -#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS - -#define DOCTEST_WARN_THROWS(...) (static_cast(0)) -#define DOCTEST_CHECK_THROWS(...) (static_cast(0)) -#define DOCTEST_REQUIRE_THROWS(...) (static_cast(0)) -#define DOCTEST_WARN_THROWS_AS(expr, ...) (static_cast(0)) -#define DOCTEST_CHECK_THROWS_AS(expr, ...) (static_cast(0)) -#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) (static_cast(0)) -#define DOCTEST_WARN_THROWS_WITH(expr, ...) (static_cast(0)) -#define DOCTEST_CHECK_THROWS_WITH(expr, ...) (static_cast(0)) -#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) (static_cast(0)) -#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) (static_cast(0)) -#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) (static_cast(0)) -#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) (static_cast(0)) -#define DOCTEST_WARN_NOTHROW(...) (static_cast(0)) -#define DOCTEST_CHECK_NOTHROW(...) (static_cast(0)) -#define DOCTEST_REQUIRE_NOTHROW(...) (static_cast(0)) - -#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) (static_cast(0)) -#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) (static_cast(0)) -#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) (static_cast(0)) -#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast(0)) -#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast(0)) -#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast(0)) -#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast(0)) -#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast(0)) -#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast(0)) -#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast(0)) -#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast(0)) -#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast(0)) -#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) (static_cast(0)) -#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) (static_cast(0)) -#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) (static_cast(0)) - -#else // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS - -#undef DOCTEST_REQUIRE -#undef DOCTEST_REQUIRE_FALSE -#undef DOCTEST_REQUIRE_MESSAGE -#undef DOCTEST_REQUIRE_FALSE_MESSAGE -#undef DOCTEST_REQUIRE_EQ -#undef DOCTEST_REQUIRE_NE -#undef DOCTEST_REQUIRE_GT -#undef DOCTEST_REQUIRE_LT -#undef DOCTEST_REQUIRE_GE -#undef DOCTEST_REQUIRE_LE -#undef DOCTEST_REQUIRE_UNARY -#undef DOCTEST_REQUIRE_UNARY_FALSE - -#endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS - -#endif // DOCTEST_CONFIG_NO_EXCEPTIONS - -// ================================================================================================= -// == WHAT FOLLOWS IS VERSIONS OF THE MACROS THAT DO NOT DO ANY REGISTERING! == -// == THIS CAN BE ENABLED BY DEFINING DOCTEST_CONFIG_DISABLE GLOBALLY! == -// ================================================================================================= -#else // DOCTEST_CONFIG_DISABLE - -#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, name) \ - namespace { \ - template \ - struct der : public base \ - { void f(); }; \ - } \ - template \ - inline void der::f() - -#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, name) \ - template \ - static inline void f() - -// for registering tests -#define DOCTEST_TEST_CASE(name) \ - DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name) - -// for registering tests in classes -#define DOCTEST_TEST_CASE_CLASS(name) \ - DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name) - -// for registering tests with a fixture -#define DOCTEST_TEST_CASE_FIXTURE(x, name) \ - DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), x, \ - DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name) - -// for converting types to strings without the header and demangling -#define DOCTEST_TYPE_TO_STRING(...) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) -#define DOCTEST_TYPE_TO_STRING_IMPL(...) - -// for typed tests -#define DOCTEST_TEST_CASE_TEMPLATE(name, type, ...) \ - template \ - inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)() - -#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, type, id) \ - template \ - inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)() - -#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) \ - typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) - -#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) \ - typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) - -// for subcases -#define DOCTEST_SUBCASE(name) - -// for a testsuite block -#define DOCTEST_TEST_SUITE(name) namespace - -// for starting a testsuite block -#define DOCTEST_TEST_SUITE_BEGIN(name) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) - -// for ending a testsuite block -#define DOCTEST_TEST_SUITE_END typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) - -#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \ - template \ - static inline doctest::String DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)(signature) - -#define DOCTEST_REGISTER_REPORTER(name, priority, reporter) -#define DOCTEST_REGISTER_LISTENER(name, priority, reporter) - -#define DOCTEST_INFO(...) (static_cast(0)) -#define DOCTEST_CAPTURE(x) (static_cast(0)) -#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) (static_cast(0)) -#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) (static_cast(0)) -#define DOCTEST_ADD_FAIL_AT(file, line, ...) (static_cast(0)) -#define DOCTEST_MESSAGE(...) (static_cast(0)) -#define DOCTEST_FAIL_CHECK(...) (static_cast(0)) -#define DOCTEST_FAIL(...) (static_cast(0)) - -#define DOCTEST_WARN(...) (static_cast(0)) -#define DOCTEST_CHECK(...) (static_cast(0)) -#define DOCTEST_REQUIRE(...) (static_cast(0)) -#define DOCTEST_WARN_FALSE(...) (static_cast(0)) -#define DOCTEST_CHECK_FALSE(...) (static_cast(0)) -#define DOCTEST_REQUIRE_FALSE(...) (static_cast(0)) - -#define DOCTEST_WARN_MESSAGE(cond, ...) (static_cast(0)) -#define DOCTEST_CHECK_MESSAGE(cond, ...) (static_cast(0)) -#define DOCTEST_REQUIRE_MESSAGE(cond, ...) (static_cast(0)) -#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) (static_cast(0)) -#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) (static_cast(0)) -#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) (static_cast(0)) - -#define DOCTEST_WARN_THROWS(...) (static_cast(0)) -#define DOCTEST_CHECK_THROWS(...) (static_cast(0)) -#define DOCTEST_REQUIRE_THROWS(...) (static_cast(0)) -#define DOCTEST_WARN_THROWS_AS(expr, ...) (static_cast(0)) -#define DOCTEST_CHECK_THROWS_AS(expr, ...) (static_cast(0)) -#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) (static_cast(0)) -#define DOCTEST_WARN_THROWS_WITH(expr, ...) (static_cast(0)) -#define DOCTEST_CHECK_THROWS_WITH(expr, ...) (static_cast(0)) -#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) (static_cast(0)) -#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) (static_cast(0)) -#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) (static_cast(0)) -#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) (static_cast(0)) -#define DOCTEST_WARN_NOTHROW(...) (static_cast(0)) -#define DOCTEST_CHECK_NOTHROW(...) (static_cast(0)) -#define DOCTEST_REQUIRE_NOTHROW(...) (static_cast(0)) - -#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) (static_cast(0)) -#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) (static_cast(0)) -#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) (static_cast(0)) -#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast(0)) -#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast(0)) -#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast(0)) -#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast(0)) -#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast(0)) -#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast(0)) -#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast(0)) -#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast(0)) -#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast(0)) -#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) (static_cast(0)) -#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) (static_cast(0)) -#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) (static_cast(0)) - -#define DOCTEST_WARN_EQ(...) (static_cast(0)) -#define DOCTEST_CHECK_EQ(...) (static_cast(0)) -#define DOCTEST_REQUIRE_EQ(...) (static_cast(0)) -#define DOCTEST_WARN_NE(...) (static_cast(0)) -#define DOCTEST_CHECK_NE(...) (static_cast(0)) -#define DOCTEST_REQUIRE_NE(...) (static_cast(0)) -#define DOCTEST_WARN_GT(...) (static_cast(0)) -#define DOCTEST_CHECK_GT(...) (static_cast(0)) -#define DOCTEST_REQUIRE_GT(...) (static_cast(0)) -#define DOCTEST_WARN_LT(...) (static_cast(0)) -#define DOCTEST_CHECK_LT(...) (static_cast(0)) -#define DOCTEST_REQUIRE_LT(...) (static_cast(0)) -#define DOCTEST_WARN_GE(...) (static_cast(0)) -#define DOCTEST_CHECK_GE(...) (static_cast(0)) -#define DOCTEST_REQUIRE_GE(...) (static_cast(0)) -#define DOCTEST_WARN_LE(...) (static_cast(0)) -#define DOCTEST_CHECK_LE(...) (static_cast(0)) -#define DOCTEST_REQUIRE_LE(...) (static_cast(0)) - -#define DOCTEST_WARN_UNARY(...) (static_cast(0)) -#define DOCTEST_CHECK_UNARY(...) (static_cast(0)) -#define DOCTEST_REQUIRE_UNARY(...) (static_cast(0)) -#define DOCTEST_WARN_UNARY_FALSE(...) (static_cast(0)) -#define DOCTEST_CHECK_UNARY_FALSE(...) (static_cast(0)) -#define DOCTEST_REQUIRE_UNARY_FALSE(...) (static_cast(0)) - -#endif // DOCTEST_CONFIG_DISABLE - -// clang-format off -// KEPT FOR BACKWARDS COMPATIBILITY - FORWARDING TO THE RIGHT MACROS -#define DOCTEST_FAST_WARN_EQ DOCTEST_WARN_EQ -#define DOCTEST_FAST_CHECK_EQ DOCTEST_CHECK_EQ -#define DOCTEST_FAST_REQUIRE_EQ DOCTEST_REQUIRE_EQ -#define DOCTEST_FAST_WARN_NE DOCTEST_WARN_NE -#define DOCTEST_FAST_CHECK_NE DOCTEST_CHECK_NE -#define DOCTEST_FAST_REQUIRE_NE DOCTEST_REQUIRE_NE -#define DOCTEST_FAST_WARN_GT DOCTEST_WARN_GT -#define DOCTEST_FAST_CHECK_GT DOCTEST_CHECK_GT -#define DOCTEST_FAST_REQUIRE_GT DOCTEST_REQUIRE_GT -#define DOCTEST_FAST_WARN_LT DOCTEST_WARN_LT -#define DOCTEST_FAST_CHECK_LT DOCTEST_CHECK_LT -#define DOCTEST_FAST_REQUIRE_LT DOCTEST_REQUIRE_LT -#define DOCTEST_FAST_WARN_GE DOCTEST_WARN_GE -#define DOCTEST_FAST_CHECK_GE DOCTEST_CHECK_GE -#define DOCTEST_FAST_REQUIRE_GE DOCTEST_REQUIRE_GE -#define DOCTEST_FAST_WARN_LE DOCTEST_WARN_LE -#define DOCTEST_FAST_CHECK_LE DOCTEST_CHECK_LE -#define DOCTEST_FAST_REQUIRE_LE DOCTEST_REQUIRE_LE - -#define DOCTEST_FAST_WARN_UNARY DOCTEST_WARN_UNARY -#define DOCTEST_FAST_CHECK_UNARY DOCTEST_CHECK_UNARY -#define DOCTEST_FAST_REQUIRE_UNARY DOCTEST_REQUIRE_UNARY -#define DOCTEST_FAST_WARN_UNARY_FALSE DOCTEST_WARN_UNARY_FALSE -#define DOCTEST_FAST_CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE -#define DOCTEST_FAST_REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE - -#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id,__VA_ARGS__) -// clang-format on - -// BDD style macros -// clang-format off -#define DOCTEST_SCENARIO(name) DOCTEST_TEST_CASE(" Scenario: " name) -#define DOCTEST_SCENARIO_CLASS(name) DOCTEST_TEST_CASE_CLASS(" Scenario: " name) -#define DOCTEST_SCENARIO_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(" Scenario: " name, T, __VA_ARGS__) -#define DOCTEST_SCENARIO_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(" Scenario: " name, T, id) - -#define DOCTEST_GIVEN(name) DOCTEST_SUBCASE(" Given: " name) -#define DOCTEST_WHEN(name) DOCTEST_SUBCASE(" When: " name) -#define DOCTEST_AND_WHEN(name) DOCTEST_SUBCASE("And when: " name) -#define DOCTEST_THEN(name) DOCTEST_SUBCASE(" Then: " name) -#define DOCTEST_AND_THEN(name) DOCTEST_SUBCASE(" And: " name) -// clang-format on - -// == SHORT VERSIONS OF THE MACROS -#if !defined(DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES) - -#define TEST_CASE(name) DOCTEST_TEST_CASE(name) -#define TEST_CASE_CLASS(name) DOCTEST_TEST_CASE_CLASS(name) -#define TEST_CASE_FIXTURE(x, name) DOCTEST_TEST_CASE_FIXTURE(x, name) -#define TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING(__VA_ARGS__) -#define TEST_CASE_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(name, T, __VA_ARGS__) -#define TEST_CASE_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, T, id) -#define TEST_CASE_TEMPLATE_INVOKE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, __VA_ARGS__) -#define TEST_CASE_TEMPLATE_APPLY(id, ...) DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, __VA_ARGS__) -#define SUBCASE(name) DOCTEST_SUBCASE(name) -#define TEST_SUITE(decorators) DOCTEST_TEST_SUITE(decorators) -#define TEST_SUITE_BEGIN(name) DOCTEST_TEST_SUITE_BEGIN(name) -#define TEST_SUITE_END DOCTEST_TEST_SUITE_END -#define REGISTER_EXCEPTION_TRANSLATOR(signature) DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) -#define REGISTER_REPORTER(name, priority, reporter) DOCTEST_REGISTER_REPORTER(name, priority, reporter) -#define REGISTER_LISTENER(name, priority, reporter) DOCTEST_REGISTER_LISTENER(name, priority, reporter) -#define INFO(...) DOCTEST_INFO(__VA_ARGS__) -#define CAPTURE(x) DOCTEST_CAPTURE(x) -#define ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_MESSAGE_AT(file, line, __VA_ARGS__) -#define ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_FAIL_CHECK_AT(file, line, __VA_ARGS__) -#define ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_FAIL_AT(file, line, __VA_ARGS__) -#define MESSAGE(...) DOCTEST_MESSAGE(__VA_ARGS__) -#define FAIL_CHECK(...) DOCTEST_FAIL_CHECK(__VA_ARGS__) -#define FAIL(...) DOCTEST_FAIL(__VA_ARGS__) -#define TO_LVALUE(...) DOCTEST_TO_LVALUE(__VA_ARGS__) - -#define WARN(...) DOCTEST_WARN(__VA_ARGS__) -#define WARN_FALSE(...) DOCTEST_WARN_FALSE(__VA_ARGS__) -#define WARN_THROWS(...) DOCTEST_WARN_THROWS(__VA_ARGS__) -#define WARN_THROWS_AS(expr, ...) DOCTEST_WARN_THROWS_AS(expr, __VA_ARGS__) -#define WARN_THROWS_WITH(expr, ...) DOCTEST_WARN_THROWS_WITH(expr, __VA_ARGS__) -#define WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_WARN_THROWS_WITH_AS(expr, with, __VA_ARGS__) -#define WARN_NOTHROW(...) DOCTEST_WARN_NOTHROW(__VA_ARGS__) -#define CHECK(...) DOCTEST_CHECK(__VA_ARGS__) -#define CHECK_FALSE(...) DOCTEST_CHECK_FALSE(__VA_ARGS__) -#define CHECK_THROWS(...) DOCTEST_CHECK_THROWS(__VA_ARGS__) -#define CHECK_THROWS_AS(expr, ...) DOCTEST_CHECK_THROWS_AS(expr, __VA_ARGS__) -#define CHECK_THROWS_WITH(expr, ...) DOCTEST_CHECK_THROWS_WITH(expr, __VA_ARGS__) -#define CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_CHECK_THROWS_WITH_AS(expr, with, __VA_ARGS__) -#define CHECK_NOTHROW(...) DOCTEST_CHECK_NOTHROW(__VA_ARGS__) -#define REQUIRE(...) DOCTEST_REQUIRE(__VA_ARGS__) -#define REQUIRE_FALSE(...) DOCTEST_REQUIRE_FALSE(__VA_ARGS__) -#define REQUIRE_THROWS(...) DOCTEST_REQUIRE_THROWS(__VA_ARGS__) -#define REQUIRE_THROWS_AS(expr, ...) DOCTEST_REQUIRE_THROWS_AS(expr, __VA_ARGS__) -#define REQUIRE_THROWS_WITH(expr, ...) DOCTEST_REQUIRE_THROWS_WITH(expr, __VA_ARGS__) -#define REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, __VA_ARGS__) -#define REQUIRE_NOTHROW(...) DOCTEST_REQUIRE_NOTHROW(__VA_ARGS__) - -#define WARN_MESSAGE(cond, ...) DOCTEST_WARN_MESSAGE(cond, __VA_ARGS__) -#define WARN_FALSE_MESSAGE(cond, ...) DOCTEST_WARN_FALSE_MESSAGE(cond, __VA_ARGS__) -#define WARN_THROWS_MESSAGE(expr, ...) DOCTEST_WARN_THROWS_MESSAGE(expr, __VA_ARGS__) -#define WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__) -#define WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__) -#define WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__) -#define WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_WARN_NOTHROW_MESSAGE(expr, __VA_ARGS__) -#define CHECK_MESSAGE(cond, ...) DOCTEST_CHECK_MESSAGE(cond, __VA_ARGS__) -#define CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_CHECK_FALSE_MESSAGE(cond, __VA_ARGS__) -#define CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_CHECK_THROWS_MESSAGE(expr, __VA_ARGS__) -#define CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__) -#define CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__) -#define CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__) -#define CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_CHECK_NOTHROW_MESSAGE(expr, __VA_ARGS__) -#define REQUIRE_MESSAGE(cond, ...) DOCTEST_REQUIRE_MESSAGE(cond, __VA_ARGS__) -#define REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_REQUIRE_FALSE_MESSAGE(cond, __VA_ARGS__) -#define REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_REQUIRE_THROWS_MESSAGE(expr, __VA_ARGS__) -#define REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__) -#define REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__) -#define REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__) -#define REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, __VA_ARGS__) - -#define SCENARIO(name) DOCTEST_SCENARIO(name) -#define SCENARIO_CLASS(name) DOCTEST_SCENARIO_CLASS(name) -#define SCENARIO_TEMPLATE(name, T, ...) DOCTEST_SCENARIO_TEMPLATE(name, T, __VA_ARGS__) -#define SCENARIO_TEMPLATE_DEFINE(name, T, id) DOCTEST_SCENARIO_TEMPLATE_DEFINE(name, T, id) -#define GIVEN(name) DOCTEST_GIVEN(name) -#define WHEN(name) DOCTEST_WHEN(name) -#define AND_WHEN(name) DOCTEST_AND_WHEN(name) -#define THEN(name) DOCTEST_THEN(name) -#define AND_THEN(name) DOCTEST_AND_THEN(name) - -#define WARN_EQ(...) DOCTEST_WARN_EQ(__VA_ARGS__) -#define CHECK_EQ(...) DOCTEST_CHECK_EQ(__VA_ARGS__) -#define REQUIRE_EQ(...) DOCTEST_REQUIRE_EQ(__VA_ARGS__) -#define WARN_NE(...) DOCTEST_WARN_NE(__VA_ARGS__) -#define CHECK_NE(...) DOCTEST_CHECK_NE(__VA_ARGS__) -#define REQUIRE_NE(...) DOCTEST_REQUIRE_NE(__VA_ARGS__) -#define WARN_GT(...) DOCTEST_WARN_GT(__VA_ARGS__) -#define CHECK_GT(...) DOCTEST_CHECK_GT(__VA_ARGS__) -#define REQUIRE_GT(...) DOCTEST_REQUIRE_GT(__VA_ARGS__) -#define WARN_LT(...) DOCTEST_WARN_LT(__VA_ARGS__) -#define CHECK_LT(...) DOCTEST_CHECK_LT(__VA_ARGS__) -#define REQUIRE_LT(...) DOCTEST_REQUIRE_LT(__VA_ARGS__) -#define WARN_GE(...) DOCTEST_WARN_GE(__VA_ARGS__) -#define CHECK_GE(...) DOCTEST_CHECK_GE(__VA_ARGS__) -#define REQUIRE_GE(...) DOCTEST_REQUIRE_GE(__VA_ARGS__) -#define WARN_LE(...) DOCTEST_WARN_LE(__VA_ARGS__) -#define CHECK_LE(...) DOCTEST_CHECK_LE(__VA_ARGS__) -#define REQUIRE_LE(...) DOCTEST_REQUIRE_LE(__VA_ARGS__) -#define WARN_UNARY(...) DOCTEST_WARN_UNARY(__VA_ARGS__) -#define CHECK_UNARY(...) DOCTEST_CHECK_UNARY(__VA_ARGS__) -#define REQUIRE_UNARY(...) DOCTEST_REQUIRE_UNARY(__VA_ARGS__) -#define WARN_UNARY_FALSE(...) DOCTEST_WARN_UNARY_FALSE(__VA_ARGS__) -#define CHECK_UNARY_FALSE(...) DOCTEST_CHECK_UNARY_FALSE(__VA_ARGS__) -#define REQUIRE_UNARY_FALSE(...) DOCTEST_REQUIRE_UNARY_FALSE(__VA_ARGS__) - -// KEPT FOR BACKWARDS COMPATIBILITY -#define FAST_WARN_EQ(...) DOCTEST_FAST_WARN_EQ(__VA_ARGS__) -#define FAST_CHECK_EQ(...) DOCTEST_FAST_CHECK_EQ(__VA_ARGS__) -#define FAST_REQUIRE_EQ(...) DOCTEST_FAST_REQUIRE_EQ(__VA_ARGS__) -#define FAST_WARN_NE(...) DOCTEST_FAST_WARN_NE(__VA_ARGS__) -#define FAST_CHECK_NE(...) DOCTEST_FAST_CHECK_NE(__VA_ARGS__) -#define FAST_REQUIRE_NE(...) DOCTEST_FAST_REQUIRE_NE(__VA_ARGS__) -#define FAST_WARN_GT(...) DOCTEST_FAST_WARN_GT(__VA_ARGS__) -#define FAST_CHECK_GT(...) DOCTEST_FAST_CHECK_GT(__VA_ARGS__) -#define FAST_REQUIRE_GT(...) DOCTEST_FAST_REQUIRE_GT(__VA_ARGS__) -#define FAST_WARN_LT(...) DOCTEST_FAST_WARN_LT(__VA_ARGS__) -#define FAST_CHECK_LT(...) DOCTEST_FAST_CHECK_LT(__VA_ARGS__) -#define FAST_REQUIRE_LT(...) DOCTEST_FAST_REQUIRE_LT(__VA_ARGS__) -#define FAST_WARN_GE(...) DOCTEST_FAST_WARN_GE(__VA_ARGS__) -#define FAST_CHECK_GE(...) DOCTEST_FAST_CHECK_GE(__VA_ARGS__) -#define FAST_REQUIRE_GE(...) DOCTEST_FAST_REQUIRE_GE(__VA_ARGS__) -#define FAST_WARN_LE(...) DOCTEST_FAST_WARN_LE(__VA_ARGS__) -#define FAST_CHECK_LE(...) DOCTEST_FAST_CHECK_LE(__VA_ARGS__) -#define FAST_REQUIRE_LE(...) DOCTEST_FAST_REQUIRE_LE(__VA_ARGS__) - -#define FAST_WARN_UNARY(...) DOCTEST_FAST_WARN_UNARY(__VA_ARGS__) -#define FAST_CHECK_UNARY(...) DOCTEST_FAST_CHECK_UNARY(__VA_ARGS__) -#define FAST_REQUIRE_UNARY(...) DOCTEST_FAST_REQUIRE_UNARY(__VA_ARGS__) -#define FAST_WARN_UNARY_FALSE(...) DOCTEST_FAST_WARN_UNARY_FALSE(__VA_ARGS__) -#define FAST_CHECK_UNARY_FALSE(...) DOCTEST_FAST_CHECK_UNARY_FALSE(__VA_ARGS__) -#define FAST_REQUIRE_UNARY_FALSE(...) DOCTEST_FAST_REQUIRE_UNARY_FALSE(__VA_ARGS__) - -#define TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, __VA_ARGS__) - -#endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES - -#if !defined(DOCTEST_CONFIG_DISABLE) - -// this is here to clear the 'current test suite' for the current translation unit - at the top -DOCTEST_TEST_SUITE_END(); - -// add stringification for primitive/fundamental types -namespace doctest { namespace detail { - DOCTEST_TYPE_TO_STRING_IMPL(bool) - DOCTEST_TYPE_TO_STRING_IMPL(float) - DOCTEST_TYPE_TO_STRING_IMPL(double) - DOCTEST_TYPE_TO_STRING_IMPL(long double) - DOCTEST_TYPE_TO_STRING_IMPL(char) - DOCTEST_TYPE_TO_STRING_IMPL(signed char) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned char) -#if !DOCTEST_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) - DOCTEST_TYPE_TO_STRING_IMPL(wchar_t) -#endif // not MSVC or wchar_t support enabled - DOCTEST_TYPE_TO_STRING_IMPL(short int) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned short int) - DOCTEST_TYPE_TO_STRING_IMPL(int) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned int) - DOCTEST_TYPE_TO_STRING_IMPL(long int) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned long int) - DOCTEST_TYPE_TO_STRING_IMPL(long long int) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned long long int) -}} // namespace doctest::detail - -#endif // DOCTEST_CONFIG_DISABLE - -DOCTEST_CLANG_SUPPRESS_WARNING_POP -DOCTEST_MSVC_SUPPRESS_WARNING_POP -DOCTEST_GCC_SUPPRESS_WARNING_POP - -#endif // DOCTEST_LIBRARY_INCLUDED - -#ifndef DOCTEST_SINGLE_HEADER -#define DOCTEST_SINGLE_HEADER -#endif // DOCTEST_SINGLE_HEADER - -#if defined(DOCTEST_CONFIG_IMPLEMENT) || !defined(DOCTEST_SINGLE_HEADER) - -#ifndef DOCTEST_SINGLE_HEADER -#include "doctest_fwd.h" -#endif // DOCTEST_SINGLE_HEADER - -DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-macros") - -#ifndef DOCTEST_LIBRARY_IMPLEMENTATION -#define DOCTEST_LIBRARY_IMPLEMENTATION - -DOCTEST_CLANG_SUPPRESS_WARNING_POP - -DOCTEST_CLANG_SUPPRESS_WARNING_PUSH -DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wglobal-constructors") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wshorten-64-to-32") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-variable-declarations") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch-enum") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wcovered-switch-default") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-noreturn") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wdisabled-macro-expansion") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-braces") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-field-initializers") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-member-function") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wnonportable-system-include-path") - -DOCTEST_GCC_SUPPRESS_WARNING_PUSH -DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") -DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") -DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion") -DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") -DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion") -DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") -DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") -DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-field-initializers") -DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-braces") -DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") -DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch") -DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-enum") -DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-default") -DOCTEST_GCC_SUPPRESS_WARNING("-Wunsafe-loop-optimizations") -DOCTEST_GCC_SUPPRESS_WARNING("-Wold-style-cast") -DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs") -DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") -DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-function") -DOCTEST_GCC_SUPPRESS_WARNING("-Wmultiple-inheritance") -DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") -DOCTEST_GCC_SUPPRESS_WARNING("-Wsuggest-attribute") - -DOCTEST_MSVC_SUPPRESS_WARNING_PUSH -DOCTEST_MSVC_SUPPRESS_WARNING(4616) // invalid compiler warning -DOCTEST_MSVC_SUPPRESS_WARNING(4619) // invalid compiler warning -DOCTEST_MSVC_SUPPRESS_WARNING(4996) // The compiler encountered a deprecated declaration -DOCTEST_MSVC_SUPPRESS_WARNING(4267) // 'var' : conversion from 'x' to 'y', possible loss of data -DOCTEST_MSVC_SUPPRESS_WARNING(4706) // assignment within conditional expression -DOCTEST_MSVC_SUPPRESS_WARNING(4512) // 'class' : assignment operator could not be generated -DOCTEST_MSVC_SUPPRESS_WARNING(4127) // conditional expression is constant -DOCTEST_MSVC_SUPPRESS_WARNING(4530) // C++ exception handler used, but unwind semantics not enabled -DOCTEST_MSVC_SUPPRESS_WARNING(4577) // 'noexcept' used with no exception handling mode specified -DOCTEST_MSVC_SUPPRESS_WARNING(4774) // format string expected in argument is not a string literal -DOCTEST_MSVC_SUPPRESS_WARNING(4365) // conversion from 'int' to 'unsigned', signed/unsigned mismatch -DOCTEST_MSVC_SUPPRESS_WARNING(4820) // padding in structs -DOCTEST_MSVC_SUPPRESS_WARNING(4640) // construction of local static object is not thread-safe -DOCTEST_MSVC_SUPPRESS_WARNING(5039) // pointer to potentially throwing function passed to extern C -DOCTEST_MSVC_SUPPRESS_WARNING(5045) // Spectre mitigation stuff -DOCTEST_MSVC_SUPPRESS_WARNING(4626) // assignment operator was implicitly defined as deleted -DOCTEST_MSVC_SUPPRESS_WARNING(5027) // move assignment operator was implicitly defined as deleted -DOCTEST_MSVC_SUPPRESS_WARNING(5026) // move constructor was implicitly defined as deleted -DOCTEST_MSVC_SUPPRESS_WARNING(4625) // copy constructor was implicitly defined as deleted -DOCTEST_MSVC_SUPPRESS_WARNING(4800) // forcing value to bool 'true' or 'false' (performance warning) -// static analysis -DOCTEST_MSVC_SUPPRESS_WARNING(26439) // This kind of function may not throw. Declare it 'noexcept' -DOCTEST_MSVC_SUPPRESS_WARNING(26495) // Always initialize a member variable -DOCTEST_MSVC_SUPPRESS_WARNING(26451) // Arithmetic overflow ... -DOCTEST_MSVC_SUPPRESS_WARNING(26444) // Avoid unnamed objects with custom construction and dtor... -DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum' - -DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN - -// required includes - will go only in one translation unit! -#include -#include -#include -// borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/onqtam/doctest/pull/37 -#ifdef __BORLANDC__ -#include -#endif // __BORLANDC__ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef DOCTEST_PLATFORM_MAC -#include -#include -#include -#endif // DOCTEST_PLATFORM_MAC - -#ifdef DOCTEST_PLATFORM_WINDOWS - -// defines for a leaner windows.h -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif // WIN32_LEAN_AND_MEAN -#ifndef NOMINMAX -#define NOMINMAX -#endif // NOMINMAX - -// not sure what AfxWin.h is for - here I do what Catch does -#ifdef __AFXDLL -#include -#else -#include -#endif -#include - -#else // DOCTEST_PLATFORM_WINDOWS - -#include -#include - -#endif // DOCTEST_PLATFORM_WINDOWS - -// this is a fix for https://github.com/onqtam/doctest/issues/348 -// https://mail.gnome.org/archives/xml/2012-January/msg00000.html -#if !defined(HAVE_UNISTD_H) && !defined(STDOUT_FILENO) -#define STDOUT_FILENO fileno(stdout) -#endif // HAVE_UNISTD_H - -DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END - -// counts the number of elements in a C array -#define DOCTEST_COUNTOF(x) (sizeof(x) / sizeof(x[0])) - -#ifdef DOCTEST_CONFIG_DISABLE -#define DOCTEST_BRANCH_ON_DISABLED(if_disabled, if_not_disabled) if_disabled -#else // DOCTEST_CONFIG_DISABLE -#define DOCTEST_BRANCH_ON_DISABLED(if_disabled, if_not_disabled) if_not_disabled -#endif // DOCTEST_CONFIG_DISABLE - -#ifndef DOCTEST_CONFIG_OPTIONS_PREFIX -#define DOCTEST_CONFIG_OPTIONS_PREFIX "dt-" -#endif - -#ifndef DOCTEST_THREAD_LOCAL -#define DOCTEST_THREAD_LOCAL thread_local -#endif - -#ifndef DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES -#define DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES 32 -#endif - -#ifndef DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE -#define DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE 64 -#endif - -#ifdef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS -#define DOCTEST_OPTIONS_PREFIX_DISPLAY DOCTEST_CONFIG_OPTIONS_PREFIX -#else -#define DOCTEST_OPTIONS_PREFIX_DISPLAY "" -#endif - -#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) -#define DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS -#endif - -namespace doctest { - -bool is_running_in_test = false; - -namespace { - using namespace detail; - // case insensitive strcmp - int stricmp(const char* a, const char* b) { - for(;; a++, b++) { - const int d = tolower(*a) - tolower(*b); - if(d != 0 || !*a) - return d; - } - } - - template - String fpToString(T value, int precision) { - std::ostringstream oss; - oss << std::setprecision(precision) << std::fixed << value; - std::string d = oss.str(); - size_t i = d.find_last_not_of('0'); - if(i != std::string::npos && i != d.size() - 1) { - if(d[i] == '.') - i++; - d = d.substr(0, i + 1); - } - return d.c_str(); - } - - struct Endianness - { - enum Arch - { - Big, - Little - }; - - static Arch which() { - int x = 1; - // casting any data pointer to char* is allowed - auto ptr = reinterpret_cast(&x); - if(*ptr) - return Little; - return Big; - } - }; -} // namespace - -namespace detail { - void my_memcpy(void* dest, const void* src, unsigned num) { memcpy(dest, src, num); } - - String rawMemoryToString(const void* object, unsigned size) { - // Reverse order for little endian architectures - int i = 0, end = static_cast(size), inc = 1; - if(Endianness::which() == Endianness::Little) { - i = end - 1; - end = inc = -1; - } - - unsigned const char* bytes = static_cast(object); - std::ostringstream oss; - oss << "0x" << std::setfill('0') << std::hex; - for(; i != end; i += inc) - oss << std::setw(2) << static_cast(bytes[i]); - return oss.str().c_str(); - } - - DOCTEST_THREAD_LOCAL std::ostringstream g_oss; // NOLINT(cert-err58-cpp) - - std::ostream* getTlsOss() { - g_oss.clear(); // there shouldn't be anything worth clearing in the flags - g_oss.str(""); // the slow way of resetting a string stream - //g_oss.seekp(0); // optimal reset - as seen here: https://stackoverflow.com/a/624291/3162383 - return &g_oss; - } - - String getTlsOssResult() { - //g_oss << std::ends; // needed - as shown here: https://stackoverflow.com/a/624291/3162383 - return g_oss.str().c_str(); - } - -#ifndef DOCTEST_CONFIG_DISABLE - -namespace timer_large_integer -{ - -#if defined(DOCTEST_PLATFORM_WINDOWS) - typedef ULONGLONG type; -#else // DOCTEST_PLATFORM_WINDOWS - using namespace std; - typedef uint64_t type; -#endif // DOCTEST_PLATFORM_WINDOWS -} - -typedef timer_large_integer::type ticks_t; - -#ifdef DOCTEST_CONFIG_GETCURRENTTICKS - ticks_t getCurrentTicks() { return DOCTEST_CONFIG_GETCURRENTTICKS(); } -#elif defined(DOCTEST_PLATFORM_WINDOWS) - ticks_t getCurrentTicks() { - static LARGE_INTEGER hz = {0}, hzo = {0}; - if(!hz.QuadPart) { - QueryPerformanceFrequency(&hz); - QueryPerformanceCounter(&hzo); - } - LARGE_INTEGER t; - QueryPerformanceCounter(&t); - return ((t.QuadPart - hzo.QuadPart) * LONGLONG(1000000)) / hz.QuadPart; - } -#else // DOCTEST_PLATFORM_WINDOWS - ticks_t getCurrentTicks() { - timeval t; - gettimeofday(&t, nullptr); - return static_cast(t.tv_sec) * 1000000 + static_cast(t.tv_usec); - } -#endif // DOCTEST_PLATFORM_WINDOWS - - struct Timer - { - void start() { m_ticks = getCurrentTicks(); } - unsigned int getElapsedMicroseconds() const { - return static_cast(getCurrentTicks() - m_ticks); - } - //unsigned int getElapsedMilliseconds() const { - // return static_cast(getElapsedMicroseconds() / 1000); - //} - double getElapsedSeconds() const { return static_cast(getCurrentTicks() - m_ticks) / 1000000.0; } - - private: - ticks_t m_ticks = 0; - }; - -#ifdef DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS - template - using AtomicOrMultiLaneAtomic = std::atomic; -#else // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS - // Provides a multilane implementation of an atomic variable that supports add, sub, load, - // store. Instead of using a single atomic variable, this splits up into multiple ones, - // each sitting on a separate cache line. The goal is to provide a speedup when most - // operations are modifying. It achieves this with two properties: - // - // * Multiple atomics are used, so chance of congestion from the same atomic is reduced. - // * Each atomic sits on a separate cache line, so false sharing is reduced. - // - // The disadvantage is that there is a small overhead due to the use of TLS, and load/store - // is slower because all atomics have to be accessed. - template - class MultiLaneAtomic - { - struct CacheLineAlignedAtomic - { - std::atomic atomic{}; - char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(std::atomic)]; - }; - CacheLineAlignedAtomic m_atomics[DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES]; - - static_assert(sizeof(CacheLineAlignedAtomic) == DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE, - "guarantee one atomic takes exactly one cache line"); - - public: - T operator++() DOCTEST_NOEXCEPT { return fetch_add(1) + 1; } - - T operator++(int) DOCTEST_NOEXCEPT { return fetch_add(1); } - - T fetch_add(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT { - return myAtomic().fetch_add(arg, order); - } - - T fetch_sub(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT { - return myAtomic().fetch_sub(arg, order); - } - - operator T() const DOCTEST_NOEXCEPT { return load(); } - - T load(std::memory_order order = std::memory_order_seq_cst) const DOCTEST_NOEXCEPT { - auto result = T(); - for(auto const& c : m_atomics) { - result += c.atomic.load(order); - } - return result; - } - - T operator=(T desired) DOCTEST_NOEXCEPT { - store(desired); - return desired; - } - - void store(T desired, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT { - // first value becomes desired", all others become 0. - for(auto& c : m_atomics) { - c.atomic.store(desired, order); - desired = {}; - } - } - - private: - // Each thread has a different atomic that it operates on. If more than NumLanes threads - // use this, some will use the same atomic. So performance will degrate a bit, but still - // everything will work. - // - // The logic here is a bit tricky. The call should be as fast as possible, so that there - // is minimal to no overhead in determining the correct atomic for the current thread. - // - // 1. A global static counter laneCounter counts continuously up. - // 2. Each successive thread will use modulo operation of that counter so it gets an atomic - // assigned in a round-robin fashion. - // 3. This tlsLaneIdx is stored in the thread local data, so it is directly available with - // little overhead. - std::atomic& myAtomic() DOCTEST_NOEXCEPT { - static std::atomic laneCounter; - DOCTEST_THREAD_LOCAL size_t tlsLaneIdx = - laneCounter++ % DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES; - - return m_atomics[tlsLaneIdx].atomic; - } - }; - - template - using AtomicOrMultiLaneAtomic = MultiLaneAtomic; -#endif // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS - - // this holds both parameters from the command line and runtime data for tests - struct ContextState : ContextOptions, TestRunStats, CurrentTestCaseStats - { - AtomicOrMultiLaneAtomic numAssertsCurrentTest_atomic; - AtomicOrMultiLaneAtomic numAssertsFailedCurrentTest_atomic; - - std::vector> filters = decltype(filters)(9); // 9 different filters - - std::vector reporters_currently_used; - - assert_handler ah = nullptr; - - Timer timer; - - std::vector stringifiedContexts; // logging from INFO() due to an exception - - // stuff for subcases - std::vector subcasesStack; - std::set subcasesPassed; - int subcasesCurrentMaxLevel; - bool should_reenter; - std::atomic shouldLogCurrentException; - - void resetRunData() { - numTestCases = 0; - numTestCasesPassingFilters = 0; - numTestSuitesPassingFilters = 0; - numTestCasesFailed = 0; - numAsserts = 0; - numAssertsFailed = 0; - numAssertsCurrentTest = 0; - numAssertsFailedCurrentTest = 0; - } - - void finalizeTestCaseData() { - seconds = timer.getElapsedSeconds(); - - // update the non-atomic counters - numAsserts += numAssertsCurrentTest_atomic; - numAssertsFailed += numAssertsFailedCurrentTest_atomic; - numAssertsCurrentTest = numAssertsCurrentTest_atomic; - numAssertsFailedCurrentTest = numAssertsFailedCurrentTest_atomic; - - if(numAssertsFailedCurrentTest) - failure_flags |= TestCaseFailureReason::AssertFailure; - - if(Approx(currentTest->m_timeout).epsilon(DBL_EPSILON) != 0 && - Approx(seconds).epsilon(DBL_EPSILON) > currentTest->m_timeout) - failure_flags |= TestCaseFailureReason::Timeout; - - if(currentTest->m_should_fail) { - if(failure_flags) { - failure_flags |= TestCaseFailureReason::ShouldHaveFailedAndDid; - } else { - failure_flags |= TestCaseFailureReason::ShouldHaveFailedButDidnt; - } - } else if(failure_flags && currentTest->m_may_fail) { - failure_flags |= TestCaseFailureReason::CouldHaveFailedAndDid; - } else if(currentTest->m_expected_failures > 0) { - if(numAssertsFailedCurrentTest == currentTest->m_expected_failures) { - failure_flags |= TestCaseFailureReason::FailedExactlyNumTimes; - } else { - failure_flags |= TestCaseFailureReason::DidntFailExactlyNumTimes; - } - } - - bool ok_to_fail = (TestCaseFailureReason::ShouldHaveFailedAndDid & failure_flags) || - (TestCaseFailureReason::CouldHaveFailedAndDid & failure_flags) || - (TestCaseFailureReason::FailedExactlyNumTimes & failure_flags); - - // if any subcase has failed - the whole test case has failed - if(failure_flags && !ok_to_fail) - numTestCasesFailed++; - } - }; - - ContextState* g_cs = nullptr; - - // used to avoid locks for the debug output - // TODO: figure out if this is indeed necessary/correct - seems like either there still - // could be a race or that there wouldn't be a race even if using the context directly - DOCTEST_THREAD_LOCAL bool g_no_colors; - -#endif // DOCTEST_CONFIG_DISABLE -} // namespace detail - -void String::setOnHeap() { *reinterpret_cast(&buf[last]) = 128; } -void String::setLast(unsigned in) { buf[last] = char(in); } - -void String::copy(const String& other) { - using namespace std; - if(other.isOnStack()) { - memcpy(buf, other.buf, len); - } else { - setOnHeap(); - data.size = other.data.size; - data.capacity = data.size + 1; - data.ptr = new char[data.capacity]; - memcpy(data.ptr, other.data.ptr, data.size + 1); - } -} - -String::String() { - buf[0] = '\0'; - setLast(); -} - -String::~String() { - if(!isOnStack()) - delete[] data.ptr; - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) -} - -String::String(const char* in) - : String(in, strlen(in)) {} - -String::String(const char* in, unsigned in_size) { - using namespace std; - if(in_size <= last) { - memcpy(buf, in, in_size); - buf[in_size] = '\0'; - setLast(last - in_size); - } else { - setOnHeap(); - data.size = in_size; - data.capacity = data.size + 1; - data.ptr = new char[data.capacity]; - memcpy(data.ptr, in, in_size); - data.ptr[in_size] = '\0'; - } -} - -String::String(const String& other) { copy(other); } - -String& String::operator=(const String& other) { - if(this != &other) { - if(!isOnStack()) - delete[] data.ptr; - - copy(other); - } - - return *this; -} - -String& String::operator+=(const String& other) { - const unsigned my_old_size = size(); - const unsigned other_size = other.size(); - const unsigned total_size = my_old_size + other_size; - using namespace std; - if(isOnStack()) { - if(total_size < len) { - // append to the current stack space - memcpy(buf + my_old_size, other.c_str(), other_size + 1); - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) - setLast(last - total_size); - } else { - // alloc new chunk - char* temp = new char[total_size + 1]; - // copy current data to new location before writing in the union - memcpy(temp, buf, my_old_size); // skip the +1 ('\0') for speed - // update data in union - setOnHeap(); - data.size = total_size; - data.capacity = data.size + 1; - data.ptr = temp; - // transfer the rest of the data - memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); - } - } else { - if(data.capacity > total_size) { - // append to the current heap block - data.size = total_size; - memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); - } else { - // resize - data.capacity *= 2; - if(data.capacity <= total_size) - data.capacity = total_size + 1; - // alloc new chunk - char* temp = new char[data.capacity]; - // copy current data to new location before releasing it - memcpy(temp, data.ptr, my_old_size); // skip the +1 ('\0') for speed - // release old chunk - delete[] data.ptr; - // update the rest of the union members - data.size = total_size; - data.ptr = temp; - // transfer the rest of the data - memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); - } - } - - return *this; -} - -// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) -String String::operator+(const String& other) const { return String(*this) += other; } - -String::String(String&& other) { - using namespace std; - memcpy(buf, other.buf, len); - other.buf[0] = '\0'; - other.setLast(); -} - -String& String::operator=(String&& other) { - using namespace std; - if(this != &other) { - if(!isOnStack()) - delete[] data.ptr; - memcpy(buf, other.buf, len); - other.buf[0] = '\0'; - other.setLast(); - } - return *this; -} - -char String::operator[](unsigned i) const { - return const_cast(this)->operator[](i); // NOLINT -} - -char& String::operator[](unsigned i) { - if(isOnStack()) - return reinterpret_cast(buf)[i]; - return data.ptr[i]; -} - -DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmaybe-uninitialized") -unsigned String::size() const { - if(isOnStack()) - return last - (unsigned(buf[last]) & 31); // using "last" would work only if "len" is 32 - return data.size; -} -DOCTEST_GCC_SUPPRESS_WARNING_POP - -unsigned String::capacity() const { - if(isOnStack()) - return len; - return data.capacity; -} - -int String::compare(const char* other, bool no_case) const { - if(no_case) - return doctest::stricmp(c_str(), other); - return std::strcmp(c_str(), other); -} - -int String::compare(const String& other, bool no_case) const { - return compare(other.c_str(), no_case); -} - -// clang-format off -bool operator==(const String& lhs, const String& rhs) { return lhs.compare(rhs) == 0; } -bool operator!=(const String& lhs, const String& rhs) { return lhs.compare(rhs) != 0; } -bool operator< (const String& lhs, const String& rhs) { return lhs.compare(rhs) < 0; } -bool operator> (const String& lhs, const String& rhs) { return lhs.compare(rhs) > 0; } -bool operator<=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) < 0 : true; } -bool operator>=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) > 0 : true; } -// clang-format on - -std::ostream& operator<<(std::ostream& s, const String& in) { return s << in.c_str(); } - -namespace { - void color_to_stream(std::ostream&, Color::Enum) DOCTEST_BRANCH_ON_DISABLED({}, ;) -} // namespace - -namespace Color { - std::ostream& operator<<(std::ostream& s, Color::Enum code) { - color_to_stream(s, code); - return s; - } -} // namespace Color - -// clang-format off -const char* assertString(assertType::Enum at) { - DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4062) // enum 'x' in switch of enum 'y' is not handled - switch(at) { //!OCLINT missing default in switch statements - case assertType::DT_WARN : return "WARN"; - case assertType::DT_CHECK : return "CHECK"; - case assertType::DT_REQUIRE : return "REQUIRE"; - - case assertType::DT_WARN_FALSE : return "WARN_FALSE"; - case assertType::DT_CHECK_FALSE : return "CHECK_FALSE"; - case assertType::DT_REQUIRE_FALSE : return "REQUIRE_FALSE"; - - case assertType::DT_WARN_THROWS : return "WARN_THROWS"; - case assertType::DT_CHECK_THROWS : return "CHECK_THROWS"; - case assertType::DT_REQUIRE_THROWS : return "REQUIRE_THROWS"; - - case assertType::DT_WARN_THROWS_AS : return "WARN_THROWS_AS"; - case assertType::DT_CHECK_THROWS_AS : return "CHECK_THROWS_AS"; - case assertType::DT_REQUIRE_THROWS_AS : return "REQUIRE_THROWS_AS"; - - case assertType::DT_WARN_THROWS_WITH : return "WARN_THROWS_WITH"; - case assertType::DT_CHECK_THROWS_WITH : return "CHECK_THROWS_WITH"; - case assertType::DT_REQUIRE_THROWS_WITH : return "REQUIRE_THROWS_WITH"; - - case assertType::DT_WARN_THROWS_WITH_AS : return "WARN_THROWS_WITH_AS"; - case assertType::DT_CHECK_THROWS_WITH_AS : return "CHECK_THROWS_WITH_AS"; - case assertType::DT_REQUIRE_THROWS_WITH_AS : return "REQUIRE_THROWS_WITH_AS"; - - case assertType::DT_WARN_NOTHROW : return "WARN_NOTHROW"; - case assertType::DT_CHECK_NOTHROW : return "CHECK_NOTHROW"; - case assertType::DT_REQUIRE_NOTHROW : return "REQUIRE_NOTHROW"; - - case assertType::DT_WARN_EQ : return "WARN_EQ"; - case assertType::DT_CHECK_EQ : return "CHECK_EQ"; - case assertType::DT_REQUIRE_EQ : return "REQUIRE_EQ"; - case assertType::DT_WARN_NE : return "WARN_NE"; - case assertType::DT_CHECK_NE : return "CHECK_NE"; - case assertType::DT_REQUIRE_NE : return "REQUIRE_NE"; - case assertType::DT_WARN_GT : return "WARN_GT"; - case assertType::DT_CHECK_GT : return "CHECK_GT"; - case assertType::DT_REQUIRE_GT : return "REQUIRE_GT"; - case assertType::DT_WARN_LT : return "WARN_LT"; - case assertType::DT_CHECK_LT : return "CHECK_LT"; - case assertType::DT_REQUIRE_LT : return "REQUIRE_LT"; - case assertType::DT_WARN_GE : return "WARN_GE"; - case assertType::DT_CHECK_GE : return "CHECK_GE"; - case assertType::DT_REQUIRE_GE : return "REQUIRE_GE"; - case assertType::DT_WARN_LE : return "WARN_LE"; - case assertType::DT_CHECK_LE : return "CHECK_LE"; - case assertType::DT_REQUIRE_LE : return "REQUIRE_LE"; - - case assertType::DT_WARN_UNARY : return "WARN_UNARY"; - case assertType::DT_CHECK_UNARY : return "CHECK_UNARY"; - case assertType::DT_REQUIRE_UNARY : return "REQUIRE_UNARY"; - case assertType::DT_WARN_UNARY_FALSE : return "WARN_UNARY_FALSE"; - case assertType::DT_CHECK_UNARY_FALSE : return "CHECK_UNARY_FALSE"; - case assertType::DT_REQUIRE_UNARY_FALSE : return "REQUIRE_UNARY_FALSE"; - } - DOCTEST_MSVC_SUPPRESS_WARNING_POP - return ""; -} -// clang-format on - -const char* failureString(assertType::Enum at) { - if(at & assertType::is_warn) //!OCLINT bitwise operator in conditional - return "WARNING"; - if(at & assertType::is_check) //!OCLINT bitwise operator in conditional - return "ERROR"; - if(at & assertType::is_require) //!OCLINT bitwise operator in conditional - return "FATAL ERROR"; - return ""; -} - -DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference") -DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference") -// depending on the current options this will remove the path of filenames -const char* skipPathFromFilename(const char* file) { -#ifndef DOCTEST_CONFIG_DISABLE - if(getContextOptions()->no_path_in_filenames) { - auto back = std::strrchr(file, '\\'); - auto forward = std::strrchr(file, '/'); - if(back || forward) { - if(back > forward) - forward = back; - return forward + 1; - } - } -#endif // DOCTEST_CONFIG_DISABLE - return file; -} -DOCTEST_CLANG_SUPPRESS_WARNING_POP -DOCTEST_GCC_SUPPRESS_WARNING_POP - -bool SubcaseSignature::operator<(const SubcaseSignature& other) const { - if(m_line != other.m_line) - return m_line < other.m_line; - if(std::strcmp(m_file, other.m_file) != 0) - return std::strcmp(m_file, other.m_file) < 0; - return m_name.compare(other.m_name) < 0; -} - -IContextScope::IContextScope() = default; -IContextScope::~IContextScope() = default; - -#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING -String toString(char* in) { return toString(static_cast(in)); } -// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) -String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; } -#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING -String toString(bool in) { return in ? "true" : "false"; } -String toString(float in) { return fpToString(in, 5) + "f"; } -String toString(double in) { return fpToString(in, 10); } -String toString(double long in) { return fpToString(in, 15); } - -#define DOCTEST_TO_STRING_OVERLOAD(type, fmt) \ - String toString(type in) { \ - char buf[64]; \ - std::sprintf(buf, fmt, in); \ - return buf; \ - } - -DOCTEST_TO_STRING_OVERLOAD(char, "%d") -DOCTEST_TO_STRING_OVERLOAD(char signed, "%d") -DOCTEST_TO_STRING_OVERLOAD(char unsigned, "%u") -DOCTEST_TO_STRING_OVERLOAD(int short, "%d") -DOCTEST_TO_STRING_OVERLOAD(int short unsigned, "%u") -DOCTEST_TO_STRING_OVERLOAD(int, "%d") -DOCTEST_TO_STRING_OVERLOAD(unsigned, "%u") -DOCTEST_TO_STRING_OVERLOAD(int long, "%ld") -DOCTEST_TO_STRING_OVERLOAD(int long unsigned, "%lu") -DOCTEST_TO_STRING_OVERLOAD(int long long, "%lld") -DOCTEST_TO_STRING_OVERLOAD(int long long unsigned, "%llu") - -String toString(std::nullptr_t) { return "NULL"; } - -#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) -// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183 -String toString(const std::string& in) { return in.c_str(); } -#endif // VS 2019 - -Approx::Approx(double value) - : m_epsilon(static_cast(std::numeric_limits::epsilon()) * 100) - , m_scale(1.0) - , m_value(value) {} - -Approx Approx::operator()(double value) const { - Approx approx(value); - approx.epsilon(m_epsilon); - approx.scale(m_scale); - return approx; -} - -Approx& Approx::epsilon(double newEpsilon) { - m_epsilon = newEpsilon; - return *this; -} -Approx& Approx::scale(double newScale) { - m_scale = newScale; - return *this; -} - -bool operator==(double lhs, const Approx& rhs) { - // Thanks to Richard Harris for his help refining this formula - return std::fabs(lhs - rhs.m_value) < - rhs.m_epsilon * (rhs.m_scale + std::max(std::fabs(lhs), std::fabs(rhs.m_value))); -} -bool operator==(const Approx& lhs, double rhs) { return operator==(rhs, lhs); } -bool operator!=(double lhs, const Approx& rhs) { return !operator==(lhs, rhs); } -bool operator!=(const Approx& lhs, double rhs) { return !operator==(rhs, lhs); } -bool operator<=(double lhs, const Approx& rhs) { return lhs < rhs.m_value || lhs == rhs; } -bool operator<=(const Approx& lhs, double rhs) { return lhs.m_value < rhs || lhs == rhs; } -bool operator>=(double lhs, const Approx& rhs) { return lhs > rhs.m_value || lhs == rhs; } -bool operator>=(const Approx& lhs, double rhs) { return lhs.m_value > rhs || lhs == rhs; } -bool operator<(double lhs, const Approx& rhs) { return lhs < rhs.m_value && lhs != rhs; } -bool operator<(const Approx& lhs, double rhs) { return lhs.m_value < rhs && lhs != rhs; } -bool operator>(double lhs, const Approx& rhs) { return lhs > rhs.m_value && lhs != rhs; } -bool operator>(const Approx& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; } - -String toString(const Approx& in) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) - return String("Approx( ") + doctest::toString(in.m_value) + " )"; -} -const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(nullptr, g_cs); } - -} // namespace doctest - -#ifdef DOCTEST_CONFIG_DISABLE -namespace doctest { -Context::Context(int, const char* const*) {} -Context::~Context() = default; -void Context::applyCommandLine(int, const char* const*) {} -void Context::addFilter(const char*, const char*) {} -void Context::clearFilters() {} -void Context::setOption(const char*, int) {} -void Context::setOption(const char*, const char*) {} -bool Context::shouldExit() { return false; } -void Context::setAsDefaultForAssertsOutOfTestCases() {} -void Context::setAssertHandler(detail::assert_handler) {} -int Context::run() { return 0; } - -IReporter::~IReporter() = default; - -int IReporter::get_num_active_contexts() { return 0; } -const IContextScope* const* IReporter::get_active_contexts() { return nullptr; } -int IReporter::get_num_stringified_contexts() { return 0; } -const String* IReporter::get_stringified_contexts() { return nullptr; } - -int registerReporter(const char*, int, IReporter*) { return 0; } - -} // namespace doctest -#else // DOCTEST_CONFIG_DISABLE - -#if !defined(DOCTEST_CONFIG_COLORS_NONE) -#if !defined(DOCTEST_CONFIG_COLORS_WINDOWS) && !defined(DOCTEST_CONFIG_COLORS_ANSI) -#ifdef DOCTEST_PLATFORM_WINDOWS -#define DOCTEST_CONFIG_COLORS_WINDOWS -#else // linux -#define DOCTEST_CONFIG_COLORS_ANSI -#endif // platform -#endif // DOCTEST_CONFIG_COLORS_WINDOWS && DOCTEST_CONFIG_COLORS_ANSI -#endif // DOCTEST_CONFIG_COLORS_NONE - -namespace doctest_detail_test_suite_ns { -// holds the current test suite -doctest::detail::TestSuite& getCurrentTestSuite() { - static doctest::detail::TestSuite data{}; - return data; -} -} // namespace doctest_detail_test_suite_ns - -namespace doctest { -namespace { - // the int (priority) is part of the key for automatic sorting - sadly one can register a - // reporter with a duplicate name and a different priority but hopefully that won't happen often :| - typedef std::map, reporterCreatorFunc> reporterMap; - - reporterMap& getReporters() { - static reporterMap data; - return data; - } - reporterMap& getListeners() { - static reporterMap data; - return data; - } -} // namespace -namespace detail { -#define DOCTEST_ITERATE_THROUGH_REPORTERS(function, ...) \ - for(auto& curr_rep : g_cs->reporters_currently_used) \ - curr_rep->function(__VA_ARGS__) - - bool checkIfShouldThrow(assertType::Enum at) { - if(at & assertType::is_require) //!OCLINT bitwise operator in conditional - return true; - - if((at & assertType::is_check) //!OCLINT bitwise operator in conditional - && getContextOptions()->abort_after > 0 && - (g_cs->numAssertsFailed + g_cs->numAssertsFailedCurrentTest_atomic) >= - getContextOptions()->abort_after) - return true; - - return false; - } - -#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS - DOCTEST_NORETURN void throwException() { - g_cs->shouldLogCurrentException = false; - throw TestFailureException(); - } // NOLINT(cert-err60-cpp) -#else // DOCTEST_CONFIG_NO_EXCEPTIONS - void throwException() {} -#endif // DOCTEST_CONFIG_NO_EXCEPTIONS -} // namespace detail - -namespace { - using namespace detail; - // matching of a string against a wildcard mask (case sensitivity configurable) taken from - // https://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing - int wildcmp(const char* str, const char* wild, bool caseSensitive) { - const char* cp = str; - const char* mp = wild; - - while((*str) && (*wild != '*')) { - if((caseSensitive ? (*wild != *str) : (tolower(*wild) != tolower(*str))) && - (*wild != '?')) { - return 0; - } - wild++; - str++; - } - - while(*str) { - if(*wild == '*') { - if(!*++wild) { - return 1; - } - mp = wild; - cp = str + 1; - } else if((caseSensitive ? (*wild == *str) : (tolower(*wild) == tolower(*str))) || - (*wild == '?')) { - wild++; - str++; - } else { - wild = mp; //!OCLINT parameter reassignment - str = cp++; //!OCLINT parameter reassignment - } - } - - while(*wild == '*') { - wild++; - } - return !*wild; - } - - //// C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html - //unsigned hashStr(unsigned const char* str) { - // unsigned long hash = 5381; - // char c; - // while((c = *str++)) - // hash = ((hash << 5) + hash) + c; // hash * 33 + c - // return hash; - //} - - // checks if the name matches any of the filters (and can be configured what to do when empty) - bool matchesAny(const char* name, const std::vector& filters, bool matchEmpty, - bool caseSensitive) { - if(filters.empty() && matchEmpty) - return true; - for(auto& curr : filters) - if(wildcmp(name, curr.c_str(), caseSensitive)) - return true; - return false; - } -} // namespace -namespace detail { - - Subcase::Subcase(const String& name, const char* file, int line) - : m_signature({name, file, line}) { - auto* s = g_cs; - - // check subcase filters - if(s->subcasesStack.size() < size_t(s->subcase_filter_levels)) { - if(!matchesAny(m_signature.m_name.c_str(), s->filters[6], true, s->case_sensitive)) - return; - if(matchesAny(m_signature.m_name.c_str(), s->filters[7], false, s->case_sensitive)) - return; - } - - // if a Subcase on the same level has already been entered - if(s->subcasesStack.size() < size_t(s->subcasesCurrentMaxLevel)) { - s->should_reenter = true; - return; - } - - // push the current signature to the stack so we can check if the - // current stack + the current new subcase have been traversed - s->subcasesStack.push_back(m_signature); - if(s->subcasesPassed.count(s->subcasesStack) != 0) { - // pop - revert to previous stack since we've already passed this - s->subcasesStack.pop_back(); - return; - } - - s->subcasesCurrentMaxLevel = s->subcasesStack.size(); - m_entered = true; - - DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature); - } - - DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 - DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") - DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") - - Subcase::~Subcase() { - if(m_entered) { - // only mark the subcase stack as passed if no subcases have been skipped - if(g_cs->should_reenter == false) - g_cs->subcasesPassed.insert(g_cs->subcasesStack); - g_cs->subcasesStack.pop_back(); - -#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) - if(std::uncaught_exceptions() > 0 -#else - if(std::uncaught_exception() -#endif - && g_cs->shouldLogCurrentException) { - DOCTEST_ITERATE_THROUGH_REPORTERS( - test_case_exception, {"exception thrown in subcase - will translate later " - "when the whole test case has been exited (cannot " - "translate while there is an active exception)", - false}); - g_cs->shouldLogCurrentException = false; - } - DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY); - } - } - - DOCTEST_CLANG_SUPPRESS_WARNING_POP - DOCTEST_GCC_SUPPRESS_WARNING_POP - DOCTEST_MSVC_SUPPRESS_WARNING_POP - - Subcase::operator bool() const { return m_entered; } - - Result::Result(bool passed, const String& decomposition) - : m_passed(passed) - , m_decomp(decomposition) {} - - ExpressionDecomposer::ExpressionDecomposer(assertType::Enum at) - : m_at(at) {} - - TestSuite& TestSuite::operator*(const char* in) { - m_test_suite = in; - // clear state - m_description = nullptr; - m_skip = false; - m_no_breaks = false; - m_no_output = false; - m_may_fail = false; - m_should_fail = false; - m_expected_failures = 0; - m_timeout = 0; - return *this; - } - - TestCase::TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, - const char* type, int template_id) { - m_file = file; - m_line = line; - m_name = nullptr; // will be later overridden in operator* - m_test_suite = test_suite.m_test_suite; - m_description = test_suite.m_description; - m_skip = test_suite.m_skip; - m_no_breaks = test_suite.m_no_breaks; - m_no_output = test_suite.m_no_output; - m_may_fail = test_suite.m_may_fail; - m_should_fail = test_suite.m_should_fail; - m_expected_failures = test_suite.m_expected_failures; - m_timeout = test_suite.m_timeout; - - m_test = test; - m_type = type; - m_template_id = template_id; - } - - TestCase::TestCase(const TestCase& other) - : TestCaseData() { - *this = other; - } - - DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function - DOCTEST_MSVC_SUPPRESS_WARNING(26437) // Do not slice - TestCase& TestCase::operator=(const TestCase& other) { - static_cast(*this) = static_cast(other); - - m_test = other.m_test; - m_type = other.m_type; - m_template_id = other.m_template_id; - m_full_name = other.m_full_name; - - if(m_template_id != -1) - m_name = m_full_name.c_str(); - return *this; - } - DOCTEST_MSVC_SUPPRESS_WARNING_POP - - TestCase& TestCase::operator*(const char* in) { - m_name = in; - // make a new name with an appended type for templated test case - if(m_template_id != -1) { - m_full_name = String(m_name) + m_type; - // redirect the name to point to the newly constructed full name - m_name = m_full_name.c_str(); - } - return *this; - } - - bool TestCase::operator<(const TestCase& other) const { - // this will be used only to differentiate between test cases - not relevant for sorting - if(m_line != other.m_line) - return m_line < other.m_line; - const int name_cmp = strcmp(m_name, other.m_name); - if(name_cmp != 0) - return name_cmp < 0; - const int file_cmp = m_file.compare(other.m_file); - if(file_cmp != 0) - return file_cmp < 0; - return m_template_id < other.m_template_id; - } - - // all the registered tests - std::set& getRegisteredTests() { - static std::set data; - return data; - } -} // namespace detail -namespace { - using namespace detail; - // for sorting tests by file/line - bool fileOrderComparator(const TestCase* lhs, const TestCase* rhs) { - // this is needed because MSVC gives different case for drive letters - // for __FILE__ when evaluated in a header and a source file - const int res = lhs->m_file.compare(rhs->m_file, bool(DOCTEST_MSVC)); - if(res != 0) - return res < 0; - if(lhs->m_line != rhs->m_line) - return lhs->m_line < rhs->m_line; - return lhs->m_template_id < rhs->m_template_id; - } - - // for sorting tests by suite/file/line - bool suiteOrderComparator(const TestCase* lhs, const TestCase* rhs) { - const int res = std::strcmp(lhs->m_test_suite, rhs->m_test_suite); - if(res != 0) - return res < 0; - return fileOrderComparator(lhs, rhs); - } - - // for sorting tests by name/suite/file/line - bool nameOrderComparator(const TestCase* lhs, const TestCase* rhs) { - const int res = std::strcmp(lhs->m_name, rhs->m_name); - if(res != 0) - return res < 0; - return suiteOrderComparator(lhs, rhs); - } - -#ifdef DOCTEST_CONFIG_COLORS_WINDOWS - HANDLE g_stdoutHandle; - WORD g_origFgAttrs; - WORD g_origBgAttrs; - bool g_attrsInitted = false; - - int colors_init() { - if(!g_attrsInitted) { - g_stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); - g_attrsInitted = true; - CONSOLE_SCREEN_BUFFER_INFO csbiInfo; - GetConsoleScreenBufferInfo(g_stdoutHandle, &csbiInfo); - g_origFgAttrs = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED | - BACKGROUND_BLUE | BACKGROUND_INTENSITY); - g_origBgAttrs = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED | - FOREGROUND_BLUE | FOREGROUND_INTENSITY); - } - return 0; - } - - int dumy_init_console_colors = colors_init(); -#endif // DOCTEST_CONFIG_COLORS_WINDOWS - - DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") - void color_to_stream(std::ostream& s, Color::Enum code) { - static_cast(s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS - static_cast(code); // for DOCTEST_CONFIG_COLORS_NONE -#ifdef DOCTEST_CONFIG_COLORS_ANSI - if(g_no_colors || - (isatty(STDOUT_FILENO) == false && getContextOptions()->force_colors == false)) - return; - - auto col = ""; - // clang-format off - switch(code) { //!OCLINT missing break in switch statement / unnecessary default statement in covered switch statement - case Color::Red: col = "[0;31m"; break; - case Color::Green: col = "[0;32m"; break; - case Color::Blue: col = "[0;34m"; break; - case Color::Cyan: col = "[0;36m"; break; - case Color::Yellow: col = "[0;33m"; break; - case Color::Grey: col = "[1;30m"; break; - case Color::LightGrey: col = "[0;37m"; break; - case Color::BrightRed: col = "[1;31m"; break; - case Color::BrightGreen: col = "[1;32m"; break; - case Color::BrightWhite: col = "[1;37m"; break; - case Color::Bright: // invalid - case Color::None: - case Color::White: - default: col = "[0m"; - } - // clang-format on - s << "\033" << col; -#endif // DOCTEST_CONFIG_COLORS_ANSI - -#ifdef DOCTEST_CONFIG_COLORS_WINDOWS - if(g_no_colors || - (isatty(fileno(stdout)) == false && getContextOptions()->force_colors == false)) - return; - -#define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(g_stdoutHandle, x | g_origBgAttrs) - - // clang-format off - switch (code) { - case Color::White: DOCTEST_SET_ATTR(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break; - case Color::Red: DOCTEST_SET_ATTR(FOREGROUND_RED); break; - case Color::Green: DOCTEST_SET_ATTR(FOREGROUND_GREEN); break; - case Color::Blue: DOCTEST_SET_ATTR(FOREGROUND_BLUE); break; - case Color::Cyan: DOCTEST_SET_ATTR(FOREGROUND_BLUE | FOREGROUND_GREEN); break; - case Color::Yellow: DOCTEST_SET_ATTR(FOREGROUND_RED | FOREGROUND_GREEN); break; - case Color::Grey: DOCTEST_SET_ATTR(0); break; - case Color::LightGrey: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY); break; - case Color::BrightRed: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_RED); break; - case Color::BrightGreen: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN); break; - case Color::BrightWhite: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break; - case Color::None: - case Color::Bright: // invalid - default: DOCTEST_SET_ATTR(g_origFgAttrs); - } - // clang-format on -#endif // DOCTEST_CONFIG_COLORS_WINDOWS - } - DOCTEST_CLANG_SUPPRESS_WARNING_POP - - std::vector& getExceptionTranslators() { - static std::vector data; - return data; - } - - String translateActiveException() { -#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS - String res; - auto& translators = getExceptionTranslators(); - for(auto& curr : translators) - if(curr->translate(res)) - return res; - // clang-format off - DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wcatch-value") - try { - throw; - } catch(std::exception& ex) { - return ex.what(); - } catch(std::string& msg) { - return msg.c_str(); - } catch(const char* msg) { - return msg; - } catch(...) { - return "unknown exception"; - } - DOCTEST_GCC_SUPPRESS_WARNING_POP -// clang-format on -#else // DOCTEST_CONFIG_NO_EXCEPTIONS - return ""; -#endif // DOCTEST_CONFIG_NO_EXCEPTIONS - } -} // namespace - -namespace detail { - // used by the macros for registering tests - int regTest(const TestCase& tc) { - getRegisteredTests().insert(tc); - return 0; - } - - // sets the current test suite - int setTestSuite(const TestSuite& ts) { - doctest_detail_test_suite_ns::getCurrentTestSuite() = ts; - return 0; - } - -#ifdef DOCTEST_IS_DEBUGGER_ACTIVE - bool isDebuggerActive() { return DOCTEST_IS_DEBUGGER_ACTIVE(); } -#else // DOCTEST_IS_DEBUGGER_ACTIVE -#ifdef DOCTEST_PLATFORM_LINUX - class ErrnoGuard { - public: - ErrnoGuard() : m_oldErrno(errno) {} - ~ErrnoGuard() { errno = m_oldErrno; } - private: - int m_oldErrno; - }; - // See the comments in Catch2 for the reasoning behind this implementation: - // https://github.com/catchorg/Catch2/blob/v2.13.1/include/internal/catch_debugger.cpp#L79-L102 - bool isDebuggerActive() { - ErrnoGuard guard; - std::ifstream in("/proc/self/status"); - for(std::string line; std::getline(in, line);) { - static const int PREFIX_LEN = 11; - if(line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0) { - return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; - } - } - return false; - } -#elif defined(DOCTEST_PLATFORM_MAC) - // The following function is taken directly from the following technical note: - // https://developer.apple.com/library/archive/qa/qa1361/_index.html - // Returns true if the current process is being debugged (either - // running under the debugger or has a debugger attached post facto). - bool isDebuggerActive() { - int mib[4]; - kinfo_proc info; - size_t size; - // Initialize the flags so that, if sysctl fails for some bizarre - // reason, we get a predictable result. - info.kp_proc.p_flag = 0; - // Initialize mib, which tells sysctl the info we want, in this case - // we're looking for information about a specific process ID. - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = getpid(); - // Call sysctl. - size = sizeof(info); - if(sysctl(mib, DOCTEST_COUNTOF(mib), &info, &size, 0, 0) != 0) { - std::cerr << "\nCall to sysctl failed - unable to determine if debugger is active **\n"; - return false; - } - // We're being debugged if the P_TRACED flag is set. - return ((info.kp_proc.p_flag & P_TRACED) != 0); - } -#elif DOCTEST_MSVC || defined(__MINGW32__) || defined(__MINGW64__) - bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; } -#else - bool isDebuggerActive() { return false; } -#endif // Platform -#endif // DOCTEST_IS_DEBUGGER_ACTIVE - - void registerExceptionTranslatorImpl(const IExceptionTranslator* et) { - if(std::find(getExceptionTranslators().begin(), getExceptionTranslators().end(), et) == - getExceptionTranslators().end()) - getExceptionTranslators().push_back(et); - } - -#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - void toStream(std::ostream* s, char* in) { *s << in; } - void toStream(std::ostream* s, const char* in) { *s << in; } -#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - void toStream(std::ostream* s, bool in) { *s << std::boolalpha << in << std::noboolalpha; } - void toStream(std::ostream* s, float in) { *s << in; } - void toStream(std::ostream* s, double in) { *s << in; } - void toStream(std::ostream* s, double long in) { *s << in; } - - void toStream(std::ostream* s, char in) { *s << in; } - void toStream(std::ostream* s, char signed in) { *s << in; } - void toStream(std::ostream* s, char unsigned in) { *s << in; } - void toStream(std::ostream* s, int short in) { *s << in; } - void toStream(std::ostream* s, int short unsigned in) { *s << in; } - void toStream(std::ostream* s, int in) { *s << in; } - void toStream(std::ostream* s, int unsigned in) { *s << in; } - void toStream(std::ostream* s, int long in) { *s << in; } - void toStream(std::ostream* s, int long unsigned in) { *s << in; } - void toStream(std::ostream* s, int long long in) { *s << in; } - void toStream(std::ostream* s, int long long unsigned in) { *s << in; } - - DOCTEST_THREAD_LOCAL std::vector g_infoContexts; // for logging with INFO() - - ContextScopeBase::ContextScopeBase() { - g_infoContexts.push_back(this); - } - - DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 - DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") - DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") - - // destroy cannot be inlined into the destructor because that would mean calling stringify after - // ContextScope has been destroyed (base class destructors run after derived class destructors). - // Instead, ContextScope calls this method directly from its destructor. - void ContextScopeBase::destroy() { -#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) - if(std::uncaught_exceptions() > 0) { -#else - if(std::uncaught_exception()) { -#endif - std::ostringstream s; - this->stringify(&s); - g_cs->stringifiedContexts.push_back(s.str().c_str()); - } - g_infoContexts.pop_back(); - } - - DOCTEST_CLANG_SUPPRESS_WARNING_POP - DOCTEST_GCC_SUPPRESS_WARNING_POP - DOCTEST_MSVC_SUPPRESS_WARNING_POP -} // namespace detail -namespace { - using namespace detail; - -#if !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && !defined(DOCTEST_CONFIG_WINDOWS_SEH) - struct FatalConditionHandler - { - static void reset() {} - static void allocateAltStackMem() {} - static void freeAltStackMem() {} - }; -#else // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH - - void reportFatal(const std::string&); - -#ifdef DOCTEST_PLATFORM_WINDOWS - - struct SignalDefs - { - DWORD id; - const char* name; - }; - // There is no 1-1 mapping between signals and windows exceptions. - // Windows can easily distinguish between SO and SigSegV, - // but SigInt, SigTerm, etc are handled differently. - SignalDefs signalDefs[] = { - {static_cast(EXCEPTION_ILLEGAL_INSTRUCTION), - "SIGILL - Illegal instruction signal"}, - {static_cast(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow"}, - {static_cast(EXCEPTION_ACCESS_VIOLATION), - "SIGSEGV - Segmentation violation signal"}, - {static_cast(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error"}, - }; - - struct FatalConditionHandler - { - static LONG CALLBACK handleException(PEXCEPTION_POINTERS ExceptionInfo) { - // Multiple threads may enter this filter/handler at once. We want the error message to be printed on the - // console just once no matter how many threads have crashed. - static std::mutex mutex; - static bool execute = true; - { - std::lock_guard lock(mutex); - if(execute) { - bool reported = false; - for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { - if(ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { - reportFatal(signalDefs[i].name); - reported = true; - break; - } - } - if(reported == false) - reportFatal("Unhandled SEH exception caught"); - if(isDebuggerActive() && !g_cs->no_breaks) - DOCTEST_BREAK_INTO_DEBUGGER(); - } - execute = false; - } - std::exit(EXIT_FAILURE); - } - - static void allocateAltStackMem() {} - static void freeAltStackMem() {} - - FatalConditionHandler() { - isSet = true; - // 32k seems enough for doctest to handle stack overflow, - // but the value was found experimentally, so there is no strong guarantee - guaranteeSize = 32 * 1024; - // Register an unhandled exception filter - previousTop = SetUnhandledExceptionFilter(handleException); - // Pass in guarantee size to be filled - SetThreadStackGuarantee(&guaranteeSize); - - // On Windows uncaught exceptions from another thread, exceptions from - // destructors, or calls to std::terminate are not a SEH exception - - // The terminal handler gets called when: - // - std::terminate is called FROM THE TEST RUNNER THREAD - // - an exception is thrown from a destructor FROM THE TEST RUNNER THREAD - original_terminate_handler = std::get_terminate(); - std::set_terminate([]() DOCTEST_NOEXCEPT { - reportFatal("Terminate handler called"); - if(isDebuggerActive() && !g_cs->no_breaks) - DOCTEST_BREAK_INTO_DEBUGGER(); - std::exit(EXIT_FAILURE); // explicitly exit - otherwise the SIGABRT handler may be called as well - }); - - // SIGABRT is raised when: - // - std::terminate is called FROM A DIFFERENT THREAD - // - an exception is thrown from a destructor FROM A DIFFERENT THREAD - // - an uncaught exception is thrown FROM A DIFFERENT THREAD - prev_sigabrt_handler = std::signal(SIGABRT, [](int signal) DOCTEST_NOEXCEPT { - if(signal == SIGABRT) { - reportFatal("SIGABRT - Abort (abnormal termination) signal"); - if(isDebuggerActive() && !g_cs->no_breaks) - DOCTEST_BREAK_INTO_DEBUGGER(); - std::exit(EXIT_FAILURE); - } - }); - - // The following settings are taken from google test, and more - // specifically from UnitTest::Run() inside of gtest.cc - - // the user does not want to see pop-up dialogs about crashes - prev_error_mode_1 = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | - SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); - // This forces the abort message to go to stderr in all circumstances. - prev_error_mode_2 = _set_error_mode(_OUT_TO_STDERR); - // In the debug version, Visual Studio pops up a separate dialog - // offering a choice to debug the aborted program - we want to disable that. - prev_abort_behavior = _set_abort_behavior(0x0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); - // In debug mode, the Windows CRT can crash with an assertion over invalid - // input (e.g. passing an invalid file descriptor). The default handling - // for these assertions is to pop up a dialog and wait for user input. - // Instead ask the CRT to dump such assertions to stderr non-interactively. - prev_report_mode = _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); - prev_report_file = _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); - } - - static void reset() { - if(isSet) { - // Unregister handler and restore the old guarantee - SetUnhandledExceptionFilter(previousTop); - SetThreadStackGuarantee(&guaranteeSize); - std::set_terminate(original_terminate_handler); - std::signal(SIGABRT, prev_sigabrt_handler); - SetErrorMode(prev_error_mode_1); - _set_error_mode(prev_error_mode_2); - _set_abort_behavior(prev_abort_behavior, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); - static_cast(_CrtSetReportMode(_CRT_ASSERT, prev_report_mode)); - static_cast(_CrtSetReportFile(_CRT_ASSERT, prev_report_file)); - isSet = false; - } - } - - ~FatalConditionHandler() { reset(); } - - private: - static UINT prev_error_mode_1; - static int prev_error_mode_2; - static unsigned int prev_abort_behavior; - static int prev_report_mode; - static _HFILE prev_report_file; - static void (*prev_sigabrt_handler)(int); - static std::terminate_handler original_terminate_handler; - static bool isSet; - static ULONG guaranteeSize; - static LPTOP_LEVEL_EXCEPTION_FILTER previousTop; - }; - - UINT FatalConditionHandler::prev_error_mode_1; - int FatalConditionHandler::prev_error_mode_2; - unsigned int FatalConditionHandler::prev_abort_behavior; - int FatalConditionHandler::prev_report_mode; - _HFILE FatalConditionHandler::prev_report_file; - void (*FatalConditionHandler::prev_sigabrt_handler)(int); - std::terminate_handler FatalConditionHandler::original_terminate_handler; - bool FatalConditionHandler::isSet = false; - ULONG FatalConditionHandler::guaranteeSize = 0; - LPTOP_LEVEL_EXCEPTION_FILTER FatalConditionHandler::previousTop = nullptr; - -#else // DOCTEST_PLATFORM_WINDOWS - - struct SignalDefs - { - int id; - const char* name; - }; - SignalDefs signalDefs[] = {{SIGINT, "SIGINT - Terminal interrupt signal"}, - {SIGILL, "SIGILL - Illegal instruction signal"}, - {SIGFPE, "SIGFPE - Floating point error signal"}, - {SIGSEGV, "SIGSEGV - Segmentation violation signal"}, - {SIGTERM, "SIGTERM - Termination request signal"}, - {SIGABRT, "SIGABRT - Abort (abnormal termination) signal"}}; - - struct FatalConditionHandler - { - static bool isSet; - static struct sigaction oldSigActions[DOCTEST_COUNTOF(signalDefs)]; - static stack_t oldSigStack; - static size_t altStackSize; - static char* altStackMem; - - static void handleSignal(int sig) { - const char* name = ""; - for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { - SignalDefs& def = signalDefs[i]; - if(sig == def.id) { - name = def.name; - break; - } - } - reset(); - reportFatal(name); - raise(sig); - } - - static void allocateAltStackMem() { - altStackMem = new char[altStackSize]; - } - - static void freeAltStackMem() { - delete[] altStackMem; - } - - FatalConditionHandler() { - isSet = true; - stack_t sigStack; - sigStack.ss_sp = altStackMem; - sigStack.ss_size = altStackSize; - sigStack.ss_flags = 0; - sigaltstack(&sigStack, &oldSigStack); - struct sigaction sa = {}; - sa.sa_handler = handleSignal; // NOLINT - sa.sa_flags = SA_ONSTACK; - for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { - sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); - } - } - - ~FatalConditionHandler() { reset(); } - static void reset() { - if(isSet) { - // Set signals back to previous values -- hopefully nobody overwrote them in the meantime - for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { - sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); - } - // Return the old stack - sigaltstack(&oldSigStack, nullptr); - isSet = false; - } - } - }; - - bool FatalConditionHandler::isSet = false; - struct sigaction FatalConditionHandler::oldSigActions[DOCTEST_COUNTOF(signalDefs)] = {}; - stack_t FatalConditionHandler::oldSigStack = {}; - size_t FatalConditionHandler::altStackSize = 4 * SIGSTKSZ; - char* FatalConditionHandler::altStackMem = nullptr; - -#endif // DOCTEST_PLATFORM_WINDOWS -#endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH - -} // namespace - -namespace { - using namespace detail; - -#ifdef DOCTEST_PLATFORM_WINDOWS -#define DOCTEST_OUTPUT_DEBUG_STRING(text) ::OutputDebugStringA(text) -#else - // TODO: integration with XCode and other IDEs -#define DOCTEST_OUTPUT_DEBUG_STRING(text) // NOLINT(clang-diagnostic-unused-macros) -#endif // Platform - - void addAssert(assertType::Enum at) { - if((at & assertType::is_warn) == 0) //!OCLINT bitwise operator in conditional - g_cs->numAssertsCurrentTest_atomic++; - } - - void addFailedAssert(assertType::Enum at) { - if((at & assertType::is_warn) == 0) //!OCLINT bitwise operator in conditional - g_cs->numAssertsFailedCurrentTest_atomic++; - } - -#if defined(DOCTEST_CONFIG_POSIX_SIGNALS) || defined(DOCTEST_CONFIG_WINDOWS_SEH) - void reportFatal(const std::string& message) { - g_cs->failure_flags |= TestCaseFailureReason::Crash; - - DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, {message.c_str(), true}); - - while(g_cs->subcasesStack.size()) { - g_cs->subcasesStack.pop_back(); - DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY); - } - - g_cs->finalizeTestCaseData(); - - DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_end, *g_cs); - - DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_end, *g_cs); - } -#endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH -} // namespace -namespace detail { - - ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, - const char* exception_type, const char* exception_string) { - m_test_case = g_cs->currentTest; - m_at = at; - m_file = file; - m_line = line; - m_expr = expr; - m_failed = true; - m_threw = false; - m_threw_as = false; - m_exception_type = exception_type; - m_exception_string = exception_string; -#if DOCTEST_MSVC - if(m_expr[0] == ' ') // this happens when variadic macros are disabled under MSVC - ++m_expr; -#endif // MSVC - } - - void ResultBuilder::setResult(const Result& res) { - m_decomp = res.m_decomp; - m_failed = !res.m_passed; - } - - void ResultBuilder::translateException() { - m_threw = true; - m_exception = translateActiveException(); - } - - bool ResultBuilder::log() { - if(m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional - m_failed = !m_threw; - } else if((m_at & assertType::is_throws_as) && (m_at & assertType::is_throws_with)) { //!OCLINT - m_failed = !m_threw_as || (m_exception != m_exception_string); - } else if(m_at & assertType::is_throws_as) { //!OCLINT bitwise operator in conditional - m_failed = !m_threw_as; - } else if(m_at & assertType::is_throws_with) { //!OCLINT bitwise operator in conditional - m_failed = m_exception != m_exception_string; - } else if(m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional - m_failed = m_threw; - } - - if(m_exception.size()) - m_exception = String("\"") + m_exception + "\""; - - if(is_running_in_test) { - addAssert(m_at); - DOCTEST_ITERATE_THROUGH_REPORTERS(log_assert, *this); - - if(m_failed) - addFailedAssert(m_at); - } else if(m_failed) { - failed_out_of_a_testing_context(*this); - } - - return m_failed && isDebuggerActive() && !getContextOptions()->no_breaks && - (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger - } - - void ResultBuilder::react() const { - if(m_failed && checkIfShouldThrow(m_at)) - throwException(); - } - - void failed_out_of_a_testing_context(const AssertData& ad) { - if(g_cs->ah) - g_cs->ah(ad); - else - std::abort(); - } - - void decomp_assert(assertType::Enum at, const char* file, int line, const char* expr, - Result result) { - bool failed = !result.m_passed; - - // ################################################################################### - // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT - // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED - // ################################################################################### - DOCTEST_ASSERT_OUT_OF_TESTS(result.m_decomp); - DOCTEST_ASSERT_IN_TESTS(result.m_decomp); - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) - } - - MessageBuilder::MessageBuilder(const char* file, int line, assertType::Enum severity) { - m_stream = getTlsOss(); - m_file = file; - m_line = line; - m_severity = severity; - } - - IExceptionTranslator::IExceptionTranslator() = default; - IExceptionTranslator::~IExceptionTranslator() = default; - - bool MessageBuilder::log() { - m_string = getTlsOssResult(); - DOCTEST_ITERATE_THROUGH_REPORTERS(log_message, *this); - - const bool isWarn = m_severity & assertType::is_warn; - - // warn is just a message in this context so we don't treat it as an assert - if(!isWarn) { - addAssert(m_severity); - addFailedAssert(m_severity); - } - - return isDebuggerActive() && !getContextOptions()->no_breaks && !isWarn && - (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger - } - - void MessageBuilder::react() { - if(m_severity & assertType::is_require) //!OCLINT bitwise operator in conditional - throwException(); - } - - MessageBuilder::~MessageBuilder() = default; -} // namespace detail -namespace { - using namespace detail; - - template - DOCTEST_NORETURN void throw_exception(Ex const& e) { -#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS - throw e; -#else // DOCTEST_CONFIG_NO_EXCEPTIONS - std::cerr << "doctest will terminate because it needed to throw an exception.\n" - << "The message was: " << e.what() << '\n'; - std::terminate(); -#endif // DOCTEST_CONFIG_NO_EXCEPTIONS - } - -#ifndef DOCTEST_INTERNAL_ERROR -#define DOCTEST_INTERNAL_ERROR(msg) \ - throw_exception(std::logic_error( \ - __FILE__ ":" DOCTEST_TOSTR(__LINE__) ": Internal doctest error: " msg)) -#endif // DOCTEST_INTERNAL_ERROR - - // clang-format off - -// ================================================================================================= -// The following code has been taken verbatim from Catch2/include/internal/catch_xmlwriter.h/cpp -// This is done so cherry-picking bug fixes is trivial - even the style/formatting is untouched. -// ================================================================================================= - - class XmlEncode { - public: - enum ForWhat { ForTextNodes, ForAttributes }; - - XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); - - void encodeTo( std::ostream& os ) const; - - friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); - - private: - std::string m_str; - ForWhat m_forWhat; - }; - - class XmlWriter { - public: - - class ScopedElement { - public: - ScopedElement( XmlWriter* writer ); - - ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT; - ScopedElement& operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT; - - ~ScopedElement(); - - ScopedElement& writeText( std::string const& text, bool indent = true ); - - template - ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { - m_writer->writeAttribute( name, attribute ); - return *this; - } - - private: - mutable XmlWriter* m_writer = nullptr; - }; - - XmlWriter( std::ostream& os = std::cout ); - ~XmlWriter(); - - XmlWriter( XmlWriter const& ) = delete; - XmlWriter& operator=( XmlWriter const& ) = delete; - - XmlWriter& startElement( std::string const& name ); - - ScopedElement scopedElement( std::string const& name ); - - XmlWriter& endElement(); - - XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); - - XmlWriter& writeAttribute( std::string const& name, const char* attribute ); - - XmlWriter& writeAttribute( std::string const& name, bool attribute ); - - template - XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { - std::stringstream rss; - rss << attribute; - return writeAttribute( name, rss.str() ); - } - - XmlWriter& writeText( std::string const& text, bool indent = true ); - - //XmlWriter& writeComment( std::string const& text ); - - //void writeStylesheetRef( std::string const& url ); - - //XmlWriter& writeBlankLine(); - - void ensureTagClosed(); - - private: - - void writeDeclaration(); - - void newlineIfNecessary(); - - bool m_tagIsOpen = false; - bool m_needsNewline = false; - std::vector m_tags; - std::string m_indent; - std::ostream& m_os; - }; - -// ================================================================================================= -// The following code has been taken verbatim from Catch2/include/internal/catch_xmlwriter.h/cpp -// This is done so cherry-picking bug fixes is trivial - even the style/formatting is untouched. -// ================================================================================================= - -using uchar = unsigned char; - -namespace { - - size_t trailingBytes(unsigned char c) { - if ((c & 0xE0) == 0xC0) { - return 2; - } - if ((c & 0xF0) == 0xE0) { - return 3; - } - if ((c & 0xF8) == 0xF0) { - return 4; - } - DOCTEST_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); - } - - uint32_t headerValue(unsigned char c) { - if ((c & 0xE0) == 0xC0) { - return c & 0x1F; - } - if ((c & 0xF0) == 0xE0) { - return c & 0x0F; - } - if ((c & 0xF8) == 0xF0) { - return c & 0x07; - } - DOCTEST_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); - } - - void hexEscapeChar(std::ostream& os, unsigned char c) { - std::ios_base::fmtflags f(os.flags()); - os << "\\x" - << std::uppercase << std::hex << std::setfill('0') << std::setw(2) - << static_cast(c); - os.flags(f); - } - -} // anonymous namespace - - XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) - : m_str( str ), - m_forWhat( forWhat ) - {} - - void XmlEncode::encodeTo( std::ostream& os ) const { - // Apostrophe escaping not necessary if we always use " to write attributes - // (see: https://www.w3.org/TR/xml/#syntax) - - for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { - uchar c = m_str[idx]; - switch (c) { - case '<': os << "<"; break; - case '&': os << "&"; break; - - case '>': - // See: https://www.w3.org/TR/xml/#syntax - if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') - os << ">"; - else - os << c; - break; - - case '\"': - if (m_forWhat == ForAttributes) - os << """; - else - os << c; - break; - - default: - // Check for control characters and invalid utf-8 - - // Escape control characters in standard ascii - // see https://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 - if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { - hexEscapeChar(os, c); - break; - } - - // Plain ASCII: Write it to stream - if (c < 0x7F) { - os << c; - break; - } - - // UTF-8 territory - // Check if the encoding is valid and if it is not, hex escape bytes. - // Important: We do not check the exact decoded values for validity, only the encoding format - // First check that this bytes is a valid lead byte: - // This means that it is not encoded as 1111 1XXX - // Or as 10XX XXXX - if (c < 0xC0 || - c >= 0xF8) { - hexEscapeChar(os, c); - break; - } - - auto encBytes = trailingBytes(c); - // Are there enough bytes left to avoid accessing out-of-bounds memory? - if (idx + encBytes - 1 >= m_str.size()) { - hexEscapeChar(os, c); - break; - } - // The header is valid, check data - // The next encBytes bytes must together be a valid utf-8 - // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) - bool valid = true; - uint32_t value = headerValue(c); - for (std::size_t n = 1; n < encBytes; ++n) { - uchar nc = m_str[idx + n]; - valid &= ((nc & 0xC0) == 0x80); - value = (value << 6) | (nc & 0x3F); - } - - if ( - // Wrong bit pattern of following bytes - (!valid) || - // Overlong encodings - (value < 0x80) || - ( value < 0x800 && encBytes > 2) || // removed "0x80 <= value &&" because redundant - (0x800 < value && value < 0x10000 && encBytes > 3) || - // Encoded value out of range - (value >= 0x110000) - ) { - hexEscapeChar(os, c); - break; - } - - // If we got here, this is in fact a valid(ish) utf-8 sequence - for (std::size_t n = 0; n < encBytes; ++n) { - os << m_str[idx + n]; - } - idx += encBytes - 1; - break; - } - } - } - - std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { - xmlEncode.encodeTo( os ); - return os; - } - - XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) - : m_writer( writer ) - {} - - XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT - : m_writer( other.m_writer ){ - other.m_writer = nullptr; - } - XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT { - if ( m_writer ) { - m_writer->endElement(); - } - m_writer = other.m_writer; - other.m_writer = nullptr; - return *this; - } - - - XmlWriter::ScopedElement::~ScopedElement() { - if( m_writer ) - m_writer->endElement(); - } - - XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { - m_writer->writeText( text, indent ); - return *this; - } - - XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) - { - writeDeclaration(); - } - - XmlWriter::~XmlWriter() { - while( !m_tags.empty() ) - endElement(); - } - - XmlWriter& XmlWriter::startElement( std::string const& name ) { - ensureTagClosed(); - newlineIfNecessary(); - m_os << m_indent << '<' << name; - m_tags.push_back( name ); - m_indent += " "; - m_tagIsOpen = true; - return *this; - } - - XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { - ScopedElement scoped( this ); - startElement( name ); - return scoped; - } - - XmlWriter& XmlWriter::endElement() { - newlineIfNecessary(); - m_indent = m_indent.substr( 0, m_indent.size()-2 ); - if( m_tagIsOpen ) { - m_os << "/>"; - m_tagIsOpen = false; - } - else { - m_os << m_indent << ""; - } - m_os << std::endl; - m_tags.pop_back(); - return *this; - } - - XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) { - if( !name.empty() && !attribute.empty() ) - m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; - return *this; - } - - XmlWriter& XmlWriter::writeAttribute( std::string const& name, const char* attribute ) { - if( !name.empty() && attribute && attribute[0] != '\0' ) - m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; - return *this; - } - - XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) { - m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; - return *this; - } - - XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { - if( !text.empty() ){ - bool tagWasOpen = m_tagIsOpen; - ensureTagClosed(); - if( tagWasOpen && indent ) - m_os << m_indent; - m_os << XmlEncode( text ); - m_needsNewline = true; - } - return *this; - } - - //XmlWriter& XmlWriter::writeComment( std::string const& text ) { - // ensureTagClosed(); - // m_os << m_indent << ""; - // m_needsNewline = true; - // return *this; - //} - - //void XmlWriter::writeStylesheetRef( std::string const& url ) { - // m_os << "\n"; - //} - - //XmlWriter& XmlWriter::writeBlankLine() { - // ensureTagClosed(); - // m_os << '\n'; - // return *this; - //} - - void XmlWriter::ensureTagClosed() { - if( m_tagIsOpen ) { - m_os << ">" << std::endl; - m_tagIsOpen = false; - } - } - - void XmlWriter::writeDeclaration() { - m_os << "\n"; - } - - void XmlWriter::newlineIfNecessary() { - if( m_needsNewline ) { - m_os << std::endl; - m_needsNewline = false; - } - } - -// ================================================================================================= -// End of copy-pasted code from Catch -// ================================================================================================= - - // clang-format on - - struct XmlReporter : public IReporter - { - XmlWriter xml; - std::mutex mutex; - - // caching pointers/references to objects of these types - safe to do - const ContextOptions& opt; - const TestCaseData* tc = nullptr; - - XmlReporter(const ContextOptions& co) - : xml(*co.cout) - , opt(co) {} - - void log_contexts() { - int num_contexts = get_num_active_contexts(); - if(num_contexts) { - auto contexts = get_active_contexts(); - std::stringstream ss; - for(int i = 0; i < num_contexts; ++i) { - contexts[i]->stringify(&ss); - xml.scopedElement("Info").writeText(ss.str()); - ss.str(""); - } - } - } - - unsigned line(unsigned l) const { return opt.no_line_numbers ? 0 : l; } - - void test_case_start_impl(const TestCaseData& in) { - bool open_ts_tag = false; - if(tc != nullptr) { // we have already opened a test suite - if(std::strcmp(tc->m_test_suite, in.m_test_suite) != 0) { - xml.endElement(); - open_ts_tag = true; - } - } - else { - open_ts_tag = true; // first test case ==> first test suite - } - - if(open_ts_tag) { - xml.startElement("TestSuite"); - xml.writeAttribute("name", in.m_test_suite); - } - - tc = ∈ - xml.startElement("TestCase") - .writeAttribute("name", in.m_name) - .writeAttribute("filename", skipPathFromFilename(in.m_file.c_str())) - .writeAttribute("line", line(in.m_line)) - .writeAttribute("description", in.m_description); - - if(Approx(in.m_timeout) != 0) - xml.writeAttribute("timeout", in.m_timeout); - if(in.m_may_fail) - xml.writeAttribute("may_fail", true); - if(in.m_should_fail) - xml.writeAttribute("should_fail", true); - } - - // ========================================================================================= - // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE - // ========================================================================================= - - void report_query(const QueryData& in) override { - test_run_start(); - if(opt.list_reporters) { - for(auto& curr : getListeners()) - xml.scopedElement("Listener") - .writeAttribute("priority", curr.first.first) - .writeAttribute("name", curr.first.second); - for(auto& curr : getReporters()) - xml.scopedElement("Reporter") - .writeAttribute("priority", curr.first.first) - .writeAttribute("name", curr.first.second); - } else if(opt.count || opt.list_test_cases) { - for(unsigned i = 0; i < in.num_data; ++i) { - xml.scopedElement("TestCase").writeAttribute("name", in.data[i]->m_name) - .writeAttribute("testsuite", in.data[i]->m_test_suite) - .writeAttribute("filename", skipPathFromFilename(in.data[i]->m_file.c_str())) - .writeAttribute("line", line(in.data[i]->m_line)); - } - xml.scopedElement("OverallResultsTestCases") - .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters); - } else if(opt.list_test_suites) { - for(unsigned i = 0; i < in.num_data; ++i) - xml.scopedElement("TestSuite").writeAttribute("name", in.data[i]->m_test_suite); - xml.scopedElement("OverallResultsTestCases") - .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters); - xml.scopedElement("OverallResultsTestSuites") - .writeAttribute("unskipped", in.run_stats->numTestSuitesPassingFilters); - } - xml.endElement(); - } - - void test_run_start() override { - // remove .exe extension - mainly to have the same output on UNIX and Windows - std::string binary_name = skipPathFromFilename(opt.binary_name.c_str()); -#ifdef DOCTEST_PLATFORM_WINDOWS - if(binary_name.rfind(".exe") != std::string::npos) - binary_name = binary_name.substr(0, binary_name.length() - 4); -#endif // DOCTEST_PLATFORM_WINDOWS - - xml.startElement("doctest").writeAttribute("binary", binary_name); - if(opt.no_version == false) - xml.writeAttribute("version", DOCTEST_VERSION_STR); - - // only the consequential ones (TODO: filters) - xml.scopedElement("Options") - .writeAttribute("order_by", opt.order_by.c_str()) - .writeAttribute("rand_seed", opt.rand_seed) - .writeAttribute("first", opt.first) - .writeAttribute("last", opt.last) - .writeAttribute("abort_after", opt.abort_after) - .writeAttribute("subcase_filter_levels", opt.subcase_filter_levels) - .writeAttribute("case_sensitive", opt.case_sensitive) - .writeAttribute("no_throw", opt.no_throw) - .writeAttribute("no_skip", opt.no_skip); - } - - void test_run_end(const TestRunStats& p) override { - if(tc) // the TestSuite tag - only if there has been at least 1 test case - xml.endElement(); - - xml.scopedElement("OverallResultsAsserts") - .writeAttribute("successes", p.numAsserts - p.numAssertsFailed) - .writeAttribute("failures", p.numAssertsFailed); - - xml.startElement("OverallResultsTestCases") - .writeAttribute("successes", - p.numTestCasesPassingFilters - p.numTestCasesFailed) - .writeAttribute("failures", p.numTestCasesFailed); - if(opt.no_skipped_summary == false) - xml.writeAttribute("skipped", p.numTestCases - p.numTestCasesPassingFilters); - xml.endElement(); - - xml.endElement(); - } - - void test_case_start(const TestCaseData& in) override { - test_case_start_impl(in); - xml.ensureTagClosed(); - } - - void test_case_reenter(const TestCaseData&) override {} - - void test_case_end(const CurrentTestCaseStats& st) override { - xml.startElement("OverallResultsAsserts") - .writeAttribute("successes", - st.numAssertsCurrentTest - st.numAssertsFailedCurrentTest) - .writeAttribute("failures", st.numAssertsFailedCurrentTest); - if(opt.duration) - xml.writeAttribute("duration", st.seconds); - if(tc->m_expected_failures) - xml.writeAttribute("expected_failures", tc->m_expected_failures); - xml.endElement(); - - xml.endElement(); - } - - void test_case_exception(const TestCaseException& e) override { - std::lock_guard lock(mutex); - - xml.scopedElement("Exception") - .writeAttribute("crash", e.is_crash) - .writeText(e.error_string.c_str()); - } - - void subcase_start(const SubcaseSignature& in) override { - std::lock_guard lock(mutex); - - xml.startElement("SubCase") - .writeAttribute("name", in.m_name) - .writeAttribute("filename", skipPathFromFilename(in.m_file)) - .writeAttribute("line", line(in.m_line)); - xml.ensureTagClosed(); - } - - void subcase_end() override { xml.endElement(); } - - void log_assert(const AssertData& rb) override { - if(!rb.m_failed && !opt.success) - return; - - std::lock_guard lock(mutex); - - xml.startElement("Expression") - .writeAttribute("success", !rb.m_failed) - .writeAttribute("type", assertString(rb.m_at)) - .writeAttribute("filename", skipPathFromFilename(rb.m_file)) - .writeAttribute("line", line(rb.m_line)); - - xml.scopedElement("Original").writeText(rb.m_expr); - - if(rb.m_threw) - xml.scopedElement("Exception").writeText(rb.m_exception.c_str()); - - if(rb.m_at & assertType::is_throws_as) - xml.scopedElement("ExpectedException").writeText(rb.m_exception_type); - if(rb.m_at & assertType::is_throws_with) - xml.scopedElement("ExpectedExceptionString").writeText(rb.m_exception_string); - if((rb.m_at & assertType::is_normal) && !rb.m_threw) - xml.scopedElement("Expanded").writeText(rb.m_decomp.c_str()); - - log_contexts(); - - xml.endElement(); - } - - void log_message(const MessageData& mb) override { - std::lock_guard lock(mutex); - - xml.startElement("Message") - .writeAttribute("type", failureString(mb.m_severity)) - .writeAttribute("filename", skipPathFromFilename(mb.m_file)) - .writeAttribute("line", line(mb.m_line)); - - xml.scopedElement("Text").writeText(mb.m_string.c_str()); - - log_contexts(); - - xml.endElement(); - } - - void test_case_skipped(const TestCaseData& in) override { - if(opt.no_skipped_summary == false) { - test_case_start_impl(in); - xml.writeAttribute("skipped", "true"); - xml.endElement(); - } - } - }; - - DOCTEST_REGISTER_REPORTER("xml", 0, XmlReporter); - - void fulltext_log_assert_to_stream(std::ostream& s, const AssertData& rb) { - if((rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) == - 0) //!OCLINT bitwise operator in conditional - s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << " ) " - << Color::None; - - if(rb.m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional - s << (rb.m_threw ? "threw as expected!" : "did NOT throw at all!") << "\n"; - } else if((rb.m_at & assertType::is_throws_as) && - (rb.m_at & assertType::is_throws_with)) { //!OCLINT - s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" - << rb.m_exception_string << "\", " << rb.m_exception_type << " ) " << Color::None; - if(rb.m_threw) { - if(!rb.m_failed) { - s << "threw as expected!\n"; - } else { - s << "threw a DIFFERENT exception! (contents: " << rb.m_exception << ")\n"; - } - } else { - s << "did NOT throw at all!\n"; - } - } else if(rb.m_at & - assertType::is_throws_as) { //!OCLINT bitwise operator in conditional - s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", " - << rb.m_exception_type << " ) " << Color::None - << (rb.m_threw ? (rb.m_threw_as ? "threw as expected!" : - "threw a DIFFERENT exception: ") : - "did NOT throw at all!") - << Color::Cyan << rb.m_exception << "\n"; - } else if(rb.m_at & - assertType::is_throws_with) { //!OCLINT bitwise operator in conditional - s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" - << rb.m_exception_string << "\" ) " << Color::None - << (rb.m_threw ? (!rb.m_failed ? "threw as expected!" : - "threw a DIFFERENT exception: ") : - "did NOT throw at all!") - << Color::Cyan << rb.m_exception << "\n"; - } else if(rb.m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional - s << (rb.m_threw ? "THREW exception: " : "didn't throw!") << Color::Cyan - << rb.m_exception << "\n"; - } else { - s << (rb.m_threw ? "THREW exception: " : - (!rb.m_failed ? "is correct!\n" : "is NOT correct!\n")); - if(rb.m_threw) - s << rb.m_exception << "\n"; - else - s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomp << " )\n"; - } - } - - // TODO: - // - log_message() - // - respond to queries - // - honor remaining options - // - more attributes in tags - struct JUnitReporter : public IReporter - { - XmlWriter xml; - std::mutex mutex; - Timer timer; - std::vector deepestSubcaseStackNames; - - struct JUnitTestCaseData - { - static std::string getCurrentTimestamp() { - // Beware, this is not reentrant because of backward compatibility issues - // Also, UTC only, again because of backward compatibility (%z is C++11) - time_t rawtime; - std::time(&rawtime); - auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); - - std::tm timeInfo; -#ifdef DOCTEST_PLATFORM_WINDOWS - gmtime_s(&timeInfo, &rawtime); -#else // DOCTEST_PLATFORM_WINDOWS - gmtime_r(&rawtime, &timeInfo); -#endif // DOCTEST_PLATFORM_WINDOWS - - char timeStamp[timeStampSize]; - const char* const fmt = "%Y-%m-%dT%H:%M:%SZ"; - - std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); - return std::string(timeStamp); - } - - struct JUnitTestMessage - { - JUnitTestMessage(const std::string& _message, const std::string& _type, const std::string& _details) - : message(_message), type(_type), details(_details) {} - - JUnitTestMessage(const std::string& _message, const std::string& _details) - : message(_message), type(), details(_details) {} - - std::string message, type, details; - }; - - struct JUnitTestCase - { - JUnitTestCase(const std::string& _classname, const std::string& _name) - : classname(_classname), name(_name), time(0), failures() {} - - std::string classname, name; - double time; - std::vector failures, errors; - }; - - void add(const std::string& classname, const std::string& name) { - testcases.emplace_back(classname, name); - } - - void appendSubcaseNamesToLastTestcase(std::vector nameStack) { - for(auto& curr: nameStack) - if(curr.size()) - testcases.back().name += std::string("/") + curr.c_str(); - } - - void addTime(double time) { - if(time < 1e-4) - time = 0; - testcases.back().time = time; - totalSeconds += time; - } - - void addFailure(const std::string& message, const std::string& type, const std::string& details) { - testcases.back().failures.emplace_back(message, type, details); - ++totalFailures; - } - - void addError(const std::string& message, const std::string& details) { - testcases.back().errors.emplace_back(message, details); - ++totalErrors; - } - - std::vector testcases; - double totalSeconds = 0; - int totalErrors = 0, totalFailures = 0; - }; - - JUnitTestCaseData testCaseData; - - // caching pointers/references to objects of these types - safe to do - const ContextOptions& opt; - const TestCaseData* tc = nullptr; - - JUnitReporter(const ContextOptions& co) - : xml(*co.cout) - , opt(co) {} - - unsigned line(unsigned l) const { return opt.no_line_numbers ? 0 : l; } - - // ========================================================================================= - // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE - // ========================================================================================= - - void report_query(const QueryData&) override {} - - void test_run_start() override {} - - void test_run_end(const TestRunStats& p) override { - // remove .exe extension - mainly to have the same output on UNIX and Windows - std::string binary_name = skipPathFromFilename(opt.binary_name.c_str()); -#ifdef DOCTEST_PLATFORM_WINDOWS - if(binary_name.rfind(".exe") != std::string::npos) - binary_name = binary_name.substr(0, binary_name.length() - 4); -#endif // DOCTEST_PLATFORM_WINDOWS - xml.startElement("testsuites"); - xml.startElement("testsuite").writeAttribute("name", binary_name) - .writeAttribute("errors", testCaseData.totalErrors) - .writeAttribute("failures", testCaseData.totalFailures) - .writeAttribute("tests", p.numAsserts); - if(opt.no_time_in_output == false) { - xml.writeAttribute("time", testCaseData.totalSeconds); - xml.writeAttribute("timestamp", JUnitTestCaseData::getCurrentTimestamp()); - } - if(opt.no_version == false) - xml.writeAttribute("doctest_version", DOCTEST_VERSION_STR); - - for(const auto& testCase : testCaseData.testcases) { - xml.startElement("testcase") - .writeAttribute("classname", testCase.classname) - .writeAttribute("name", testCase.name); - if(opt.no_time_in_output == false) - xml.writeAttribute("time", testCase.time); - // This is not ideal, but it should be enough to mimic gtest's junit output. - xml.writeAttribute("status", "run"); - - for(const auto& failure : testCase.failures) { - xml.scopedElement("failure") - .writeAttribute("message", failure.message) - .writeAttribute("type", failure.type) - .writeText(failure.details, false); - } - - for(const auto& error : testCase.errors) { - xml.scopedElement("error") - .writeAttribute("message", error.message) - .writeText(error.details); - } - - xml.endElement(); - } - xml.endElement(); - xml.endElement(); - } - - void test_case_start(const TestCaseData& in) override { - testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name); - timer.start(); - } - - void test_case_reenter(const TestCaseData& in) override { - testCaseData.addTime(timer.getElapsedSeconds()); - testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames); - deepestSubcaseStackNames.clear(); - - timer.start(); - testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name); - } - - void test_case_end(const CurrentTestCaseStats&) override { - testCaseData.addTime(timer.getElapsedSeconds()); - testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames); - deepestSubcaseStackNames.clear(); - } - - void test_case_exception(const TestCaseException& e) override { - std::lock_guard lock(mutex); - testCaseData.addError("exception", e.error_string.c_str()); - } - - void subcase_start(const SubcaseSignature& in) override { - std::lock_guard lock(mutex); - deepestSubcaseStackNames.push_back(in.m_name); - } - - void subcase_end() override {} - - void log_assert(const AssertData& rb) override { - if(!rb.m_failed) // report only failures & ignore the `success` option - return; - - std::lock_guard lock(mutex); - - std::ostringstream os; - os << skipPathFromFilename(rb.m_file) << (opt.gnu_file_line ? ":" : "(") - << line(rb.m_line) << (opt.gnu_file_line ? ":" : "):") << std::endl; - - fulltext_log_assert_to_stream(os, rb); - log_contexts(os); - testCaseData.addFailure(rb.m_decomp.c_str(), assertString(rb.m_at), os.str()); - } - - void log_message(const MessageData&) override {} - - void test_case_skipped(const TestCaseData&) override {} - - void log_contexts(std::ostringstream& s) { - int num_contexts = get_num_active_contexts(); - if(num_contexts) { - auto contexts = get_active_contexts(); - - s << " logged: "; - for(int i = 0; i < num_contexts; ++i) { - s << (i == 0 ? "" : " "); - contexts[i]->stringify(&s); - s << std::endl; - } - } - } - }; - - DOCTEST_REGISTER_REPORTER("junit", 0, JUnitReporter); - - struct Whitespace - { - int nrSpaces; - explicit Whitespace(int nr) - : nrSpaces(nr) {} - }; - - std::ostream& operator<<(std::ostream& out, const Whitespace& ws) { - if(ws.nrSpaces != 0) - out << std::setw(ws.nrSpaces) << ' '; - return out; - } - - struct ConsoleReporter : public IReporter - { - std::ostream& s; - bool hasLoggedCurrentTestStart; - std::vector subcasesStack; - size_t currentSubcaseLevel; - std::mutex mutex; - - // caching pointers/references to objects of these types - safe to do - const ContextOptions& opt; - const TestCaseData* tc; - - ConsoleReporter(const ContextOptions& co) - : s(*co.cout) - , opt(co) {} - - ConsoleReporter(const ContextOptions& co, std::ostream& ostr) - : s(ostr) - , opt(co) {} - - // ========================================================================================= - // WHAT FOLLOWS ARE HELPERS USED BY THE OVERRIDES OF THE VIRTUAL METHODS OF THE INTERFACE - // ========================================================================================= - - void separator_to_stream() { - s << Color::Yellow - << "===============================================================================" - "\n"; - } - - const char* getSuccessOrFailString(bool success, assertType::Enum at, - const char* success_str) { - if(success) - return success_str; - return failureString(at); - } - - Color::Enum getSuccessOrFailColor(bool success, assertType::Enum at) { - return success ? Color::BrightGreen : - (at & assertType::is_warn) ? Color::Yellow : Color::Red; - } - - void successOrFailColoredStringToStream(bool success, assertType::Enum at, - const char* success_str = "SUCCESS") { - s << getSuccessOrFailColor(success, at) - << getSuccessOrFailString(success, at, success_str) << ": "; - } - - void log_contexts() { - int num_contexts = get_num_active_contexts(); - if(num_contexts) { - auto contexts = get_active_contexts(); - - s << Color::None << " logged: "; - for(int i = 0; i < num_contexts; ++i) { - s << (i == 0 ? "" : " "); - contexts[i]->stringify(&s); - s << "\n"; - } - } - - s << "\n"; - } - - // this was requested to be made virtual so users could override it - virtual void file_line_to_stream(const char* file, int line, - const char* tail = "") { - s << Color::LightGrey << skipPathFromFilename(file) << (opt.gnu_file_line ? ":" : "(") - << (opt.no_line_numbers ? 0 : line) // 0 or the real num depending on the option - << (opt.gnu_file_line ? ":" : "):") << tail; - } - - void logTestStart() { - if(hasLoggedCurrentTestStart) - return; - - separator_to_stream(); - file_line_to_stream(tc->m_file.c_str(), tc->m_line, "\n"); - if(tc->m_description) - s << Color::Yellow << "DESCRIPTION: " << Color::None << tc->m_description << "\n"; - if(tc->m_test_suite && tc->m_test_suite[0] != '\0') - s << Color::Yellow << "TEST SUITE: " << Color::None << tc->m_test_suite << "\n"; - if(strncmp(tc->m_name, " Scenario:", 11) != 0) - s << Color::Yellow << "TEST CASE: "; - s << Color::None << tc->m_name << "\n"; - - for(size_t i = 0; i < currentSubcaseLevel; ++i) { - if(subcasesStack[i].m_name[0] != '\0') - s << " " << subcasesStack[i].m_name << "\n"; - } - - if(currentSubcaseLevel != subcasesStack.size()) { - s << Color::Yellow << "\nDEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):\n" << Color::None; - for(size_t i = 0; i < subcasesStack.size(); ++i) { - if(subcasesStack[i].m_name[0] != '\0') - s << " " << subcasesStack[i].m_name << "\n"; - } - } - - s << "\n"; - - hasLoggedCurrentTestStart = true; - } - - void printVersion() { - if(opt.no_version == false) - s << Color::Cyan << "[doctest] " << Color::None << "doctest version is \"" - << DOCTEST_VERSION_STR << "\"\n"; - } - - void printIntro() { - printVersion(); - s << Color::Cyan << "[doctest] " << Color::None - << "run with \"--" DOCTEST_OPTIONS_PREFIX_DISPLAY "help\" for options\n"; - } - - void printHelp() { - int sizePrefixDisplay = static_cast(strlen(DOCTEST_OPTIONS_PREFIX_DISPLAY)); - printVersion(); - // clang-format off - s << Color::Cyan << "[doctest]\n" << Color::None; - s << Color::Cyan << "[doctest] " << Color::None; - s << "boolean values: \"1/on/yes/true\" or \"0/off/no/false\"\n"; - s << Color::Cyan << "[doctest] " << Color::None; - s << "filter values: \"str1,str2,str3\" (comma separated strings)\n"; - s << Color::Cyan << "[doctest]\n" << Color::None; - s << Color::Cyan << "[doctest] " << Color::None; - s << "filters use wildcards for matching strings\n"; - s << Color::Cyan << "[doctest] " << Color::None; - s << "something passes a filter if any of the strings in a filter matches\n"; -#ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS - s << Color::Cyan << "[doctest]\n" << Color::None; - s << Color::Cyan << "[doctest] " << Color::None; - s << "ALL FLAGS, OPTIONS AND FILTERS ALSO AVAILABLE WITH A \"" DOCTEST_CONFIG_OPTIONS_PREFIX "\" PREFIX!!!\n"; -#endif - s << Color::Cyan << "[doctest]\n" << Color::None; - s << Color::Cyan << "[doctest] " << Color::None; - s << "Query flags - the program quits after them. Available:\n\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "?, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "help, -" DOCTEST_OPTIONS_PREFIX_DISPLAY "h " - << Whitespace(sizePrefixDisplay*0) << "prints this message\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "v, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "version " - << Whitespace(sizePrefixDisplay*1) << "prints the version\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "c, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "count " - << Whitespace(sizePrefixDisplay*1) << "prints the number of matching tests\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ltc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-test-cases " - << Whitespace(sizePrefixDisplay*1) << "lists all matching tests by name\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "lts, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-test-suites " - << Whitespace(sizePrefixDisplay*1) << "lists all matching test suites\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "lr, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-reporters " - << Whitespace(sizePrefixDisplay*1) << "lists all registered reporters\n\n"; - // ================================================================================== << 79 - s << Color::Cyan << "[doctest] " << Color::None; - s << "The available / options/filters are:\n\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-case= " - << Whitespace(sizePrefixDisplay*1) << "filters tests by their name\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tce, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-case-exclude= " - << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their name\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sf, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "source-file= " - << Whitespace(sizePrefixDisplay*1) << "filters tests by their file\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sfe, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "source-file-exclude= " - << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their file\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ts, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-suite= " - << Whitespace(sizePrefixDisplay*1) << "filters tests by their test suite\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tse, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-suite-exclude= " - << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their test suite\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase= " - << Whitespace(sizePrefixDisplay*1) << "filters subcases by their name\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sce, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase-exclude= " - << Whitespace(sizePrefixDisplay*1) << "filters OUT subcases by their name\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "r, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "reporters= " - << Whitespace(sizePrefixDisplay*1) << "reporters to use (console is default)\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "o, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "out= " - << Whitespace(sizePrefixDisplay*1) << "output filename\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ob, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "order-by= " - << Whitespace(sizePrefixDisplay*1) << "how the tests should be ordered\n"; - s << Whitespace(sizePrefixDisplay*3) << " - [file/suite/name/rand/none]\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "rs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "rand-seed= " - << Whitespace(sizePrefixDisplay*1) << "seed for random ordering\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "f, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "first= " - << Whitespace(sizePrefixDisplay*1) << "the first test passing the filters to\n"; - s << Whitespace(sizePrefixDisplay*3) << " execute - for range-based execution\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "l, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "last= " - << Whitespace(sizePrefixDisplay*1) << "the last test passing the filters to\n"; - s << Whitespace(sizePrefixDisplay*3) << " execute - for range-based execution\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "aa, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "abort-after= " - << Whitespace(sizePrefixDisplay*1) << "stop after failed assertions\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "scfl,--" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase-filter-levels= " - << Whitespace(sizePrefixDisplay*1) << "apply filters for the first levels\n"; - s << Color::Cyan << "\n[doctest] " << Color::None; - s << "Bool options - can be used like flags and true is assumed. Available:\n\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "s, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "success= " - << Whitespace(sizePrefixDisplay*1) << "include successful assertions in output\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "cs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "case-sensitive= " - << Whitespace(sizePrefixDisplay*1) << "filters being treated as case sensitive\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "e, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "exit= " - << Whitespace(sizePrefixDisplay*1) << "exits after the tests finish\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "d, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "duration= " - << Whitespace(sizePrefixDisplay*1) << "prints the time duration of each test\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nt, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-throw= " - << Whitespace(sizePrefixDisplay*1) << "skips exceptions-related assert checks\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ne, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-exitcode= " - << Whitespace(sizePrefixDisplay*1) << "returns (or exits) always with success\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nr, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-run= " - << Whitespace(sizePrefixDisplay*1) << "skips all runtime doctest operations\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nv, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-version= " - << Whitespace(sizePrefixDisplay*1) << "omit the framework version in the output\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-colors= " - << Whitespace(sizePrefixDisplay*1) << "disables colors in output\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "fc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "force-colors= " - << Whitespace(sizePrefixDisplay*1) << "use colors even when not in a tty\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nb, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-breaks= " - << Whitespace(sizePrefixDisplay*1) << "disables breakpoints in debuggers\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ns, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-skip= " - << Whitespace(sizePrefixDisplay*1) << "don't skip test cases marked as skip\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "gfl, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "gnu-file-line= " - << Whitespace(sizePrefixDisplay*1) << ":n: vs (n): for line numbers in output\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "npf, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-path-filenames= " - << Whitespace(sizePrefixDisplay*1) << "only filenames and no paths in output\n"; - s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nln, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-line-numbers= " - << Whitespace(sizePrefixDisplay*1) << "0 instead of real line numbers in output\n"; - // ================================================================================== << 79 - // clang-format on - - s << Color::Cyan << "\n[doctest] " << Color::None; - s << "for more information visit the project documentation\n\n"; - } - - void printRegisteredReporters() { - printVersion(); - auto printReporters = [this] (const reporterMap& reporters, const char* type) { - if(reporters.size()) { - s << Color::Cyan << "[doctest] " << Color::None << "listing all registered " << type << "\n"; - for(auto& curr : reporters) - s << "priority: " << std::setw(5) << curr.first.first - << " name: " << curr.first.second << "\n"; - } - }; - printReporters(getListeners(), "listeners"); - printReporters(getReporters(), "reporters"); - } - - void list_query_results() { - separator_to_stream(); - if(opt.count || opt.list_test_cases) { - s << Color::Cyan << "[doctest] " << Color::None - << "unskipped test cases passing the current filters: " - << g_cs->numTestCasesPassingFilters << "\n"; - } else if(opt.list_test_suites) { - s << Color::Cyan << "[doctest] " << Color::None - << "unskipped test cases passing the current filters: " - << g_cs->numTestCasesPassingFilters << "\n"; - s << Color::Cyan << "[doctest] " << Color::None - << "test suites with unskipped test cases passing the current filters: " - << g_cs->numTestSuitesPassingFilters << "\n"; - } - } - - // ========================================================================================= - // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE - // ========================================================================================= - - void report_query(const QueryData& in) override { - if(opt.version) { - printVersion(); - } else if(opt.help) { - printHelp(); - } else if(opt.list_reporters) { - printRegisteredReporters(); - } else if(opt.count || opt.list_test_cases) { - if(opt.list_test_cases) { - s << Color::Cyan << "[doctest] " << Color::None - << "listing all test case names\n"; - separator_to_stream(); - } - - for(unsigned i = 0; i < in.num_data; ++i) - s << Color::None << in.data[i]->m_name << "\n"; - - separator_to_stream(); - - s << Color::Cyan << "[doctest] " << Color::None - << "unskipped test cases passing the current filters: " - << g_cs->numTestCasesPassingFilters << "\n"; - - } else if(opt.list_test_suites) { - s << Color::Cyan << "[doctest] " << Color::None << "listing all test suites\n"; - separator_to_stream(); - - for(unsigned i = 0; i < in.num_data; ++i) - s << Color::None << in.data[i]->m_test_suite << "\n"; - - separator_to_stream(); - - s << Color::Cyan << "[doctest] " << Color::None - << "unskipped test cases passing the current filters: " - << g_cs->numTestCasesPassingFilters << "\n"; - s << Color::Cyan << "[doctest] " << Color::None - << "test suites with unskipped test cases passing the current filters: " - << g_cs->numTestSuitesPassingFilters << "\n"; - } - } - - void test_run_start() override { printIntro(); } - - void test_run_end(const TestRunStats& p) override { - separator_to_stream(); - s << std::dec; - - auto totwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters, static_cast(p.numAsserts))) + 1))); - auto passwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters - p.numTestCasesFailed, static_cast(p.numAsserts - p.numAssertsFailed))) + 1))); - auto failwidth = int(std::ceil(log10((std::max(p.numTestCasesFailed, static_cast(p.numAssertsFailed))) + 1))); - const bool anythingFailed = p.numTestCasesFailed > 0 || p.numAssertsFailed > 0; - s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(totwidth) - << p.numTestCasesPassingFilters << " | " - << ((p.numTestCasesPassingFilters == 0 || anythingFailed) ? Color::None : - Color::Green) - << std::setw(passwidth) << p.numTestCasesPassingFilters - p.numTestCasesFailed << " passed" - << Color::None << " | " << (p.numTestCasesFailed > 0 ? Color::Red : Color::None) - << std::setw(failwidth) << p.numTestCasesFailed << " failed" << Color::None << " |"; - if(opt.no_skipped_summary == false) { - const int numSkipped = p.numTestCases - p.numTestCasesPassingFilters; - s << " " << (numSkipped == 0 ? Color::None : Color::Yellow) << numSkipped - << " skipped" << Color::None; - } - s << "\n"; - s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(totwidth) - << p.numAsserts << " | " - << ((p.numAsserts == 0 || anythingFailed) ? Color::None : Color::Green) - << std::setw(passwidth) << (p.numAsserts - p.numAssertsFailed) << " passed" << Color::None - << " | " << (p.numAssertsFailed > 0 ? Color::Red : Color::None) << std::setw(failwidth) - << p.numAssertsFailed << " failed" << Color::None << " |\n"; - s << Color::Cyan << "[doctest] " << Color::None - << "Status: " << (p.numTestCasesFailed > 0 ? Color::Red : Color::Green) - << ((p.numTestCasesFailed > 0) ? "FAILURE!" : "SUCCESS!") << Color::None << std::endl; - } - - void test_case_start(const TestCaseData& in) override { - hasLoggedCurrentTestStart = false; - tc = ∈ - subcasesStack.clear(); - currentSubcaseLevel = 0; - } - - void test_case_reenter(const TestCaseData&) override { - subcasesStack.clear(); - } - - void test_case_end(const CurrentTestCaseStats& st) override { - if(tc->m_no_output) - return; - - // log the preamble of the test case only if there is something - // else to print - something other than that an assert has failed - if(opt.duration || - (st.failure_flags && st.failure_flags != TestCaseFailureReason::AssertFailure)) - logTestStart(); - - if(opt.duration) - s << Color::None << std::setprecision(6) << std::fixed << st.seconds - << " s: " << tc->m_name << "\n"; - - if(st.failure_flags & TestCaseFailureReason::Timeout) - s << Color::Red << "Test case exceeded time limit of " << std::setprecision(6) - << std::fixed << tc->m_timeout << "!\n"; - - if(st.failure_flags & TestCaseFailureReason::ShouldHaveFailedButDidnt) { - s << Color::Red << "Should have failed but didn't! Marking it as failed!\n"; - } else if(st.failure_flags & TestCaseFailureReason::ShouldHaveFailedAndDid) { - s << Color::Yellow << "Failed as expected so marking it as not failed\n"; - } else if(st.failure_flags & TestCaseFailureReason::CouldHaveFailedAndDid) { - s << Color::Yellow << "Allowed to fail so marking it as not failed\n"; - } else if(st.failure_flags & TestCaseFailureReason::DidntFailExactlyNumTimes) { - s << Color::Red << "Didn't fail exactly " << tc->m_expected_failures - << " times so marking it as failed!\n"; - } else if(st.failure_flags & TestCaseFailureReason::FailedExactlyNumTimes) { - s << Color::Yellow << "Failed exactly " << tc->m_expected_failures - << " times as expected so marking it as not failed!\n"; - } - if(st.failure_flags & TestCaseFailureReason::TooManyFailedAsserts) { - s << Color::Red << "Aborting - too many failed asserts!\n"; - } - s << Color::None; // lgtm [cpp/useless-expression] - } - - void test_case_exception(const TestCaseException& e) override { - if(tc->m_no_output) - return; - - logTestStart(); - - file_line_to_stream(tc->m_file.c_str(), tc->m_line, " "); - successOrFailColoredStringToStream(false, e.is_crash ? assertType::is_require : - assertType::is_check); - s << Color::Red << (e.is_crash ? "test case CRASHED: " : "test case THREW exception: ") - << Color::Cyan << e.error_string << "\n"; - - int num_stringified_contexts = get_num_stringified_contexts(); - if(num_stringified_contexts) { - auto stringified_contexts = get_stringified_contexts(); - s << Color::None << " logged: "; - for(int i = num_stringified_contexts; i > 0; --i) { - s << (i == num_stringified_contexts ? "" : " ") - << stringified_contexts[i - 1] << "\n"; - } - } - s << "\n" << Color::None; - } - - void subcase_start(const SubcaseSignature& subc) override { - std::lock_guard lock(mutex); - subcasesStack.push_back(subc); - ++currentSubcaseLevel; - hasLoggedCurrentTestStart = false; - } - - void subcase_end() override { - std::lock_guard lock(mutex); - --currentSubcaseLevel; - hasLoggedCurrentTestStart = false; - } - - void log_assert(const AssertData& rb) override { - if((!rb.m_failed && !opt.success) || tc->m_no_output) - return; - - std::lock_guard lock(mutex); - - logTestStart(); - - file_line_to_stream(rb.m_file, rb.m_line, " "); - successOrFailColoredStringToStream(!rb.m_failed, rb.m_at); - - fulltext_log_assert_to_stream(s, rb); - - log_contexts(); - } - - void log_message(const MessageData& mb) override { - if(tc->m_no_output) - return; - - std::lock_guard lock(mutex); - - logTestStart(); - - file_line_to_stream(mb.m_file, mb.m_line, " "); - s << getSuccessOrFailColor(false, mb.m_severity) - << getSuccessOrFailString(mb.m_severity & assertType::is_warn, mb.m_severity, - "MESSAGE") << ": "; - s << Color::None << mb.m_string << "\n"; - log_contexts(); - } - - void test_case_skipped(const TestCaseData&) override {} - }; - - DOCTEST_REGISTER_REPORTER("console", 0, ConsoleReporter); - -#ifdef DOCTEST_PLATFORM_WINDOWS - struct DebugOutputWindowReporter : public ConsoleReporter - { - DOCTEST_THREAD_LOCAL static std::ostringstream oss; - - DebugOutputWindowReporter(const ContextOptions& co) - : ConsoleReporter(co, oss) {} - -#define DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(func, type, arg) \ - void func(type arg) override { \ - bool with_col = g_no_colors; \ - g_no_colors = false; \ - ConsoleReporter::func(arg); \ - if(oss.tellp() != std::streampos{}) { \ - DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str()); \ - oss.str(""); \ - } \ - g_no_colors = with_col; \ - } - - DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_run_start, DOCTEST_EMPTY, DOCTEST_EMPTY) - DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_run_end, const TestRunStats&, in) - DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_start, const TestCaseData&, in) - DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_reenter, const TestCaseData&, in) - DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_end, const CurrentTestCaseStats&, in) - DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_exception, const TestCaseException&, in) - DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(subcase_start, const SubcaseSignature&, in) - DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(subcase_end, DOCTEST_EMPTY, DOCTEST_EMPTY) - DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(log_assert, const AssertData&, in) - DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(log_message, const MessageData&, in) - DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_skipped, const TestCaseData&, in) - }; - - DOCTEST_THREAD_LOCAL std::ostringstream DebugOutputWindowReporter::oss; -#endif // DOCTEST_PLATFORM_WINDOWS - - // the implementation of parseOption() - bool parseOptionImpl(int argc, const char* const* argv, const char* pattern, String* value) { - // going from the end to the beginning and stopping on the first occurrence from the end - for(int i = argc; i > 0; --i) { - auto index = i - 1; - auto temp = std::strstr(argv[index], pattern); - if(temp && (value || strlen(temp) == strlen(pattern))) { //!OCLINT prefer early exits and continue - // eliminate matches in which the chars before the option are not '-' - bool noBadCharsFound = true; - auto curr = argv[index]; - while(curr != temp) { - if(*curr++ != '-') { - noBadCharsFound = false; - break; - } - } - if(noBadCharsFound && argv[index][0] == '-') { - if(value) { - // parsing the value of an option - temp += strlen(pattern); - const unsigned len = strlen(temp); - if(len) { - *value = temp; - return true; - } - } else { - // just a flag - no value - return true; - } - } - } - } - return false; - } - - // parses an option and returns the string after the '=' character - bool parseOption(int argc, const char* const* argv, const char* pattern, String* value = nullptr, - const String& defaultVal = String()) { - if(value) - *value = defaultVal; -#ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS - // offset (normally 3 for "dt-") to skip prefix - if(parseOptionImpl(argc, argv, pattern + strlen(DOCTEST_CONFIG_OPTIONS_PREFIX), value)) - return true; -#endif // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS - return parseOptionImpl(argc, argv, pattern, value); - } - - // locates a flag on the command line - bool parseFlag(int argc, const char* const* argv, const char* pattern) { - return parseOption(argc, argv, pattern); - } - - // parses a comma separated list of words after a pattern in one of the arguments in argv - bool parseCommaSepArgs(int argc, const char* const* argv, const char* pattern, - std::vector& res) { - String filtersString; - if(parseOption(argc, argv, pattern, &filtersString)) { - // tokenize with "," as a separator - // cppcheck-suppress strtokCalled - DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") - auto pch = std::strtok(filtersString.c_str(), ","); // modifies the string - while(pch != nullptr) { - if(strlen(pch)) - res.push_back(pch); - // uses the strtok() internal state to go to the next token - // cppcheck-suppress strtokCalled - pch = std::strtok(nullptr, ","); - } - DOCTEST_CLANG_SUPPRESS_WARNING_POP - return true; - } - return false; - } - - enum optionType - { - option_bool, - option_int - }; - - // parses an int/bool option from the command line - bool parseIntOption(int argc, const char* const* argv, const char* pattern, optionType type, - int& res) { - String parsedValue; - if(!parseOption(argc, argv, pattern, &parsedValue)) - return false; - - if(type == 0) { - // boolean - const char positive[][5] = {"1", "true", "on", "yes"}; // 5 - strlen("true") + 1 - const char negative[][6] = {"0", "false", "off", "no"}; // 6 - strlen("false") + 1 - - // if the value matches any of the positive/negative possibilities - for(unsigned i = 0; i < 4; i++) { - if(parsedValue.compare(positive[i], true) == 0) { - res = 1; //!OCLINT parameter reassignment - return true; - } - if(parsedValue.compare(negative[i], true) == 0) { - res = 0; //!OCLINT parameter reassignment - return true; - } - } - } else { - // integer - // TODO: change this to use std::stoi or something else! currently it uses undefined behavior - assumes '0' on failed parse... - int theInt = std::atoi(parsedValue.c_str()); // NOLINT - if(theInt != 0) { - res = theInt; //!OCLINT parameter reassignment - return true; - } - } - return false; - } -} // namespace - -Context::Context(int argc, const char* const* argv) - : p(new detail::ContextState) { - parseArgs(argc, argv, true); - if(argc) - p->binary_name = argv[0]; -} - -Context::~Context() { - if(g_cs == p) - g_cs = nullptr; - delete p; -} - -void Context::applyCommandLine(int argc, const char* const* argv) { - parseArgs(argc, argv); - if(argc) - p->binary_name = argv[0]; -} - -// parses args -void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) { - using namespace detail; - - // clang-format off - parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "source-file=", p->filters[0]); - parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sf=", p->filters[0]); - parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "source-file-exclude=",p->filters[1]); - parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sfe=", p->filters[1]); - parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-suite=", p->filters[2]); - parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "ts=", p->filters[2]); - parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-suite-exclude=", p->filters[3]); - parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tse=", p->filters[3]); - parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-case=", p->filters[4]); - parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tc=", p->filters[4]); - parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-case-exclude=", p->filters[5]); - parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tce=", p->filters[5]); - parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "subcase=", p->filters[6]); - parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sc=", p->filters[6]); - parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "subcase-exclude=", p->filters[7]); - parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sce=", p->filters[7]); - parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "reporters=", p->filters[8]); - parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "r=", p->filters[8]); - // clang-format on - - int intRes = 0; - String strRes; - -#define DOCTEST_PARSE_AS_BOOL_OR_FLAG(name, sname, var, default) \ - if(parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", option_bool, intRes) || \ - parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", option_bool, intRes)) \ - p->var = static_cast(intRes); \ - else if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name) || \ - parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname)) \ - p->var = true; \ - else if(withDefaults) \ - p->var = default - -#define DOCTEST_PARSE_INT_OPTION(name, sname, var, default) \ - if(parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", option_int, intRes) || \ - parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", option_int, intRes)) \ - p->var = intRes; \ - else if(withDefaults) \ - p->var = default - -#define DOCTEST_PARSE_STR_OPTION(name, sname, var, default) \ - if(parseOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", &strRes, default) || \ - parseOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", &strRes, default) || \ - withDefaults) \ - p->var = strRes - - // clang-format off - DOCTEST_PARSE_STR_OPTION("out", "o", out, ""); - DOCTEST_PARSE_STR_OPTION("order-by", "ob", order_by, "file"); - DOCTEST_PARSE_INT_OPTION("rand-seed", "rs", rand_seed, 0); - - DOCTEST_PARSE_INT_OPTION("first", "f", first, 0); - DOCTEST_PARSE_INT_OPTION("last", "l", last, UINT_MAX); - - DOCTEST_PARSE_INT_OPTION("abort-after", "aa", abort_after, 0); - DOCTEST_PARSE_INT_OPTION("subcase-filter-levels", "scfl", subcase_filter_levels, INT_MAX); - - DOCTEST_PARSE_AS_BOOL_OR_FLAG("success", "s", success, false); - DOCTEST_PARSE_AS_BOOL_OR_FLAG("case-sensitive", "cs", case_sensitive, false); - DOCTEST_PARSE_AS_BOOL_OR_FLAG("exit", "e", exit, false); - DOCTEST_PARSE_AS_BOOL_OR_FLAG("duration", "d", duration, false); - DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-throw", "nt", no_throw, false); - DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-exitcode", "ne", no_exitcode, false); - DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-run", "nr", no_run, false); - DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-version", "nv", no_version, false); - DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-colors", "nc", no_colors, false); - DOCTEST_PARSE_AS_BOOL_OR_FLAG("force-colors", "fc", force_colors, false); - DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-breaks", "nb", no_breaks, false); - DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skip", "ns", no_skip, false); - DOCTEST_PARSE_AS_BOOL_OR_FLAG("gnu-file-line", "gfl", gnu_file_line, !bool(DOCTEST_MSVC)); - DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-path-filenames", "npf", no_path_in_filenames, false); - DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-line-numbers", "nln", no_line_numbers, false); - DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-debug-output", "ndo", no_debug_output, false); - DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skipped-summary", "nss", no_skipped_summary, false); - DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-time-in-output", "ntio", no_time_in_output, false); - // clang-format on - - if(withDefaults) { - p->help = false; - p->version = false; - p->count = false; - p->list_test_cases = false; - p->list_test_suites = false; - p->list_reporters = false; - } - if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "help") || - parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "h") || - parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "?")) { - p->help = true; - p->exit = true; - } - if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "version") || - parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "v")) { - p->version = true; - p->exit = true; - } - if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "count") || - parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "c")) { - p->count = true; - p->exit = true; - } - if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-test-cases") || - parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "ltc")) { - p->list_test_cases = true; - p->exit = true; - } - if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-test-suites") || - parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "lts")) { - p->list_test_suites = true; - p->exit = true; - } - if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-reporters") || - parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "lr")) { - p->list_reporters = true; - p->exit = true; - } -} - -// allows the user to add procedurally to the filters from the command line -void Context::addFilter(const char* filter, const char* value) { setOption(filter, value); } - -// allows the user to clear all filters from the command line -void Context::clearFilters() { - for(auto& curr : p->filters) - curr.clear(); -} - -// allows the user to override procedurally the int/bool options from the command line -void Context::setOption(const char* option, int value) { - setOption(option, toString(value).c_str()); - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) -} - -// allows the user to override procedurally the string options from the command line -void Context::setOption(const char* option, const char* value) { - auto argv = String("-") + option + "=" + value; - auto lvalue = argv.c_str(); - parseArgs(1, &lvalue); -} - -// users should query this in their main() and exit the program if true -bool Context::shouldExit() { return p->exit; } - -void Context::setAsDefaultForAssertsOutOfTestCases() { g_cs = p; } - -void Context::setAssertHandler(detail::assert_handler ah) { p->ah = ah; } - -// the main function that does all the filtering and test running -int Context::run() { - using namespace detail; - - // save the old context state in case such was setup - for using asserts out of a testing context - auto old_cs = g_cs; - // this is the current contest - g_cs = p; - is_running_in_test = true; - - g_no_colors = p->no_colors; - p->resetRunData(); - - // stdout by default - p->cout = &std::cout; - p->cerr = &std::cerr; - - // or to a file if specified - std::fstream fstr; - if(p->out.size()) { - fstr.open(p->out.c_str(), std::fstream::out); - p->cout = &fstr; - } - - FatalConditionHandler::allocateAltStackMem(); - - auto cleanup_and_return = [&]() { - FatalConditionHandler::freeAltStackMem(); - - if(fstr.is_open()) - fstr.close(); - - // restore context - g_cs = old_cs; - is_running_in_test = false; - - // we have to free the reporters which were allocated when the run started - for(auto& curr : p->reporters_currently_used) - delete curr; - p->reporters_currently_used.clear(); - - if(p->numTestCasesFailed && !p->no_exitcode) - return EXIT_FAILURE; - return EXIT_SUCCESS; - }; - - // setup default reporter if none is given through the command line - if(p->filters[8].empty()) - p->filters[8].push_back("console"); - - // check to see if any of the registered reporters has been selected - for(auto& curr : getReporters()) { - if(matchesAny(curr.first.second.c_str(), p->filters[8], false, p->case_sensitive)) - p->reporters_currently_used.push_back(curr.second(*g_cs)); - } - - // TODO: check if there is nothing in reporters_currently_used - - // prepend all listeners - for(auto& curr : getListeners()) - p->reporters_currently_used.insert(p->reporters_currently_used.begin(), curr.second(*g_cs)); - -#ifdef DOCTEST_PLATFORM_WINDOWS - if(isDebuggerActive() && p->no_debug_output == false) - p->reporters_currently_used.push_back(new DebugOutputWindowReporter(*g_cs)); -#endif // DOCTEST_PLATFORM_WINDOWS - - // handle version, help and no_run - if(p->no_run || p->version || p->help || p->list_reporters) { - DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, QueryData()); - - return cleanup_and_return(); - } - - std::vector testArray; - for(auto& curr : getRegisteredTests()) - testArray.push_back(&curr); - p->numTestCases = testArray.size(); - - // sort the collected records - if(!testArray.empty()) { - if(p->order_by.compare("file", true) == 0) { - std::sort(testArray.begin(), testArray.end(), fileOrderComparator); - } else if(p->order_by.compare("suite", true) == 0) { - std::sort(testArray.begin(), testArray.end(), suiteOrderComparator); - } else if(p->order_by.compare("name", true) == 0) { - std::sort(testArray.begin(), testArray.end(), nameOrderComparator); - } else if(p->order_by.compare("rand", true) == 0) { - std::srand(p->rand_seed); - - // random_shuffle implementation - const auto first = &testArray[0]; - for(size_t i = testArray.size() - 1; i > 0; --i) { - int idxToSwap = std::rand() % (i + 1); // NOLINT - - const auto temp = first[i]; - - first[i] = first[idxToSwap]; - first[idxToSwap] = temp; - } - } else if(p->order_by.compare("none", true) == 0) { - // means no sorting - beneficial for death tests which call into the executable - // with a specific test case in mind - we don't want to slow down the startup times - } - } - - std::set testSuitesPassingFilt; - - bool query_mode = p->count || p->list_test_cases || p->list_test_suites; - std::vector queryResults; - - if(!query_mode) - DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_start, DOCTEST_EMPTY); - - // invoke the registered functions if they match the filter criteria (or just count them) - for(auto& curr : testArray) { - const auto& tc = *curr; - - bool skip_me = false; - if(tc.m_skip && !p->no_skip) - skip_me = true; - - if(!matchesAny(tc.m_file.c_str(), p->filters[0], true, p->case_sensitive)) - skip_me = true; - if(matchesAny(tc.m_file.c_str(), p->filters[1], false, p->case_sensitive)) - skip_me = true; - if(!matchesAny(tc.m_test_suite, p->filters[2], true, p->case_sensitive)) - skip_me = true; - if(matchesAny(tc.m_test_suite, p->filters[3], false, p->case_sensitive)) - skip_me = true; - if(!matchesAny(tc.m_name, p->filters[4], true, p->case_sensitive)) - skip_me = true; - if(matchesAny(tc.m_name, p->filters[5], false, p->case_sensitive)) - skip_me = true; - - if(!skip_me) - p->numTestCasesPassingFilters++; - - // skip the test if it is not in the execution range - if((p->last < p->numTestCasesPassingFilters && p->first <= p->last) || - (p->first > p->numTestCasesPassingFilters)) - skip_me = true; - - if(skip_me) { - if(!query_mode) - DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_skipped, tc); - continue; - } - - // do not execute the test if we are to only count the number of filter passing tests - if(p->count) - continue; - - // print the name of the test and don't execute it - if(p->list_test_cases) { - queryResults.push_back(&tc); - continue; - } - - // print the name of the test suite if not done already and don't execute it - if(p->list_test_suites) { - if((testSuitesPassingFilt.count(tc.m_test_suite) == 0) && tc.m_test_suite[0] != '\0') { - queryResults.push_back(&tc); - testSuitesPassingFilt.insert(tc.m_test_suite); - p->numTestSuitesPassingFilters++; - } - continue; - } - - // execute the test if it passes all the filtering - { - p->currentTest = &tc; - - p->failure_flags = TestCaseFailureReason::None; - p->seconds = 0; - - // reset atomic counters - p->numAssertsFailedCurrentTest_atomic = 0; - p->numAssertsCurrentTest_atomic = 0; - - p->subcasesPassed.clear(); - - DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_start, tc); - - p->timer.start(); - - bool run_test = true; - - do { - // reset some of the fields for subcases (except for the set of fully passed ones) - p->should_reenter = false; - p->subcasesCurrentMaxLevel = 0; - p->subcasesStack.clear(); - - p->shouldLogCurrentException = true; - - // reset stuff for logging with INFO() - p->stringifiedContexts.clear(); - -#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS - try { -#endif // DOCTEST_CONFIG_NO_EXCEPTIONS -// MSVC 2015 diagnoses fatalConditionHandler as unused (because reset() is a static method) -DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4101) // unreferenced local variable - FatalConditionHandler fatalConditionHandler; // Handle signals - // execute the test - tc.m_test(); - fatalConditionHandler.reset(); -DOCTEST_MSVC_SUPPRESS_WARNING_POP -#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS - } catch(const TestFailureException&) { - p->failure_flags |= TestCaseFailureReason::AssertFailure; - } catch(...) { - DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, - {translateActiveException(), false}); - p->failure_flags |= TestCaseFailureReason::Exception; - } -#endif // DOCTEST_CONFIG_NO_EXCEPTIONS - - // exit this loop if enough assertions have failed - even if there are more subcases - if(p->abort_after > 0 && - p->numAssertsFailed + p->numAssertsFailedCurrentTest_atomic >= p->abort_after) { - run_test = false; - p->failure_flags |= TestCaseFailureReason::TooManyFailedAsserts; - } - - if(p->should_reenter && run_test) - DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_reenter, tc); - if(!p->should_reenter) - run_test = false; - } while(run_test); - - p->finalizeTestCaseData(); - - DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_end, *g_cs); - - p->currentTest = nullptr; - - // stop executing tests if enough assertions have failed - if(p->abort_after > 0 && p->numAssertsFailed >= p->abort_after) - break; - } - } - - if(!query_mode) { - DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_end, *g_cs); - } else { - QueryData qdata; - qdata.run_stats = g_cs; - qdata.data = queryResults.data(); - qdata.num_data = unsigned(queryResults.size()); - DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, qdata); - } - - // see these issues on the reasoning for this: - // - https://github.com/onqtam/doctest/issues/143#issuecomment-414418903 - // - https://github.com/onqtam/doctest/issues/126 - auto DOCTEST_FIX_FOR_MACOS_LIBCPP_IOSFWD_STRING_LINK_ERRORS = []() DOCTEST_NOINLINE - { std::cout << std::string(); }; - DOCTEST_FIX_FOR_MACOS_LIBCPP_IOSFWD_STRING_LINK_ERRORS(); - - return cleanup_and_return(); -} - -IReporter::~IReporter() = default; - -int IReporter::get_num_active_contexts() { return detail::g_infoContexts.size(); } -const IContextScope* const* IReporter::get_active_contexts() { - return get_num_active_contexts() ? &detail::g_infoContexts[0] : nullptr; -} - -int IReporter::get_num_stringified_contexts() { return detail::g_cs->stringifiedContexts.size(); } -const String* IReporter::get_stringified_contexts() { - return get_num_stringified_contexts() ? &detail::g_cs->stringifiedContexts[0] : nullptr; -} - -namespace detail { - void registerReporterImpl(const char* name, int priority, reporterCreatorFunc c, bool isReporter) { - if(isReporter) - getReporters().insert(reporterMap::value_type(reporterMap::key_type(priority, name), c)); - else - getListeners().insert(reporterMap::value_type(reporterMap::key_type(priority, name), c)); - } -} // namespace detail - -} // namespace doctest - -#endif // DOCTEST_CONFIG_DISABLE - -#ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN -DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007) // 'function' : must be 'attribute' - see issue #182 -int main(int argc, char** argv) { return doctest::Context(argc, argv).run(); } -DOCTEST_MSVC_SUPPRESS_WARNING_POP -#endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN - -DOCTEST_CLANG_SUPPRESS_WARNING_POP -DOCTEST_MSVC_SUPPRESS_WARNING_POP -DOCTEST_GCC_SUPPRESS_WARNING_POP - -#endif // DOCTEST_LIBRARY_IMPLEMENTATION -#endif // DOCTEST_CONFIG_IMPLEMENT diff --git a/test/fixtures/httpbin.cpp b/test/fixtures/httpbin.cpp new file mode 100644 index 0000000..7e0860f --- /dev/null +++ b/test/fixtures/httpbin.cpp @@ -0,0 +1,49 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "httpbin.hpp" + +boost::requests::connection httpbin::connect() +{ + using namespace boost; + const bool https = url().scheme_id() == urls::scheme::https; + auto hc = https ? requests::connection(ctx.get_executor(), sslctx) + : requests::connection(ctx.get_executor()); + hc.set_host(url().encoded_host_and_port()); + hc.use_ssl(https); + + asio::ip::tcp::resolver rslvr{ctx}; + core::string_view service = https ? "https" : "http"; + if (url().has_port()) + service = url().port(); + asio::ip::tcp::endpoint ep = *rslvr.resolve(url().encoded_host(), service).begin(); + + hc.connect(ep); + + return hc; +} + +boost::requests::connection httpbin::async_connect(boost::asio::yield_context yield) +{ + using namespace boost; + + const bool https = url().scheme_id() == urls::scheme::https; + auto hc = https ? requests::connection(yield.get_executor(), sslctx) + : requests::connection(yield.get_executor()); + hc.set_host(url().encoded_host_and_port()); + hc.use_ssl(https); + + asio::ip::tcp::resolver rslvr{yield.get_executor()}; + core::string_view service = https ? "https" : "http"; + if (url().has_port()) + service = url().port(); + asio::ip::tcp::endpoint ep = *rslvr.async_resolve(url().encoded_host(), service, yield).begin(); + + hc.async_connect(ep, yield); + + return hc; +} \ No newline at end of file diff --git a/test/fixtures/httpbin.hpp b/test/fixtures/httpbin.hpp new file mode 100644 index 0000000..9b7a272 --- /dev/null +++ b/test/fixtures/httpbin.hpp @@ -0,0 +1,44 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_REQUESTS_HTTPBIN_HPP +#define BOOST_REQUESTS_HTTPBIN_HPP + +#include +#include +#include +#include +#include + +struct httpbin +{ + + httpbin() + { + url_ = boost::urls::url_view("https://httpbin.org"); + if (auto p = ::getenv("BOOST_REQUEST_HTTPBIN")) + url_ = boost::urls::parse_uri(p).value(); + + sslctx.set_default_verify_paths(); + } + boost::urls::url_view url() const + { + return url_; + } + boost::requests::connection connect(); + boost::requests::connection async_connect(boost::asio::yield_context yield); + + + boost::asio::io_context ctx; + boost::asio::ssl::context sslctx{boost::asio::ssl::context_base::tlsv12_client}; + + private: + boost::urls::url url_; +}; + + +#endif // BOOST_REQUESTS_HTTPBIN_HPP diff --git a/test/fixtures/server.cpp b/test/fixtures/server.cpp new file mode 100644 index 0000000..1751160 --- /dev/null +++ b/test/fixtures/server.cpp @@ -0,0 +1,246 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "server.hpp" + +#include +#include +#include +#include + +using namespace boost; + +std::size_t test_server::cnt_ = 0u; + +template +void test_server::run_(Acceptor acc, boost::asio::yield_context ctx) +{ + while (ctx.cancelled() == asio::cancellation_type::none) + { + auto sock = acc.async_accept(ctx); + asio::spawn( + ctx, + [this, s = std::move(sock)](asio::yield_context ctx) mutable + { + run_session(std::move(s), ctx); + }, + asio::detached); + } +} + + + + +struct char_chunked_string_transfer +{ + beast::http::string_body inner; + + using value_type = std::string; + + + class writer + { + value_type const& body_; + value_type::const_iterator itr_{body_.begin()}; + + public: + using const_buffers_type = + asio::const_buffer; + + template + explicit + writer(beast::http::header const&, value_type const& b) + : body_(b) + { + } + + void + init(system::error_code& ec) + { + ec = {}; + } + + boost::optional> + get(system::error_code& ec) + { + ec = {}; + if (itr_ == body_.end()) + return {{const_buffers_type{} , false}}; + else + return {{const_buffers_type{&*itr_++, 1u} , true}}; + } + }; +}; + +template +void test_server::run_session(Socket sock, boost::asio::yield_context yield_) +{ + namespace http = boost::beast::http; + boost::beast::flat_buffer buf; + + boost::system::error_code ec; + auto yield = yield_[ec]; + + while (sock.is_open() && !ec) + { + + http::request req; + http::async_read(sock, buf, req, yield); + if (ec) + return; + + + if (req.target() == "/echo") + { + http::response res{http::status::ok, req.version()}; + res.set("Requests-Method", req.method_string()); + res.body() = std::move(req.body()); + + for (auto & hd : req) + if (hd.name() == beast::http::field::unknown) + res.set(hd.name_string(), hd.value()); + + res.prepare_payload(); + + http::async_write(sock, res, yield); + } + else if (req.target() == "/echo-chunked") + { + http::response res{http::status::ok, req.version()}; + http::response_serializer ser{res}; + res.set("Requests-Method", req.method_string()); + res.body() = std::move(req.body()); + + for (auto & hd : req) + if (hd.name() == beast::http::field::unknown) + res.set(hd.name_string(), hd.value()); + + res.prepare_payload(); + http::async_write(sock, ser, yield); + } + else if (req.target() == "/ws/echo") + { + if (beast::websocket::is_upgrade(req)) + { + namespace ws = boost::beast::websocket; + ws::stream wws{sock}; + wws.async_accept(req, yield); + wws.async_write(boost::asio::buffer(core::string_view("Hello World!")), yield); + } + else + { + http::response res{http::status::upgrade_required, req.version()}; + res.set("Requests-Method", req.method_string()); + res.body() = std::move(req.body()); + + for (auto & hd : req) + if (hd.name() == beast::http::field::unknown) + res.set(hd.name_string(), hd.value()); + + res.prepare_payload(); + http::async_write(sock, res, yield); + } + } + else if(req.target().starts_with("/redirect/")) + { + auto n = std::stoi(req.target().substr(10)); + + if (n == 0) + { + http::response res{http::status::ok, req.version()}; + res.set("Requests-Method", req.method_string()); + + for (auto & hd : req) + if (hd.name() == beast::http::field::unknown) + res.set(hd.name_string(), hd.value()); + + + res.body() = "Hello World!"; + res.prepare_payload(); + http::async_write(sock, res, yield); + } + else + { + http::response res{http::status::permanent_redirect, req.version()}; + res.set("Requests-Method", req.method_string()); + res.set(http::field::location, "/redirect/" + std::to_string(--n)); + + res.body() = "Redirecting..."; + + for (auto & hd : req) + if (hd.name() == beast::http::field::unknown) + res.set(hd.name_string(), hd.value()); + + res.prepare_payload(); + http::async_write(sock, res, yield); + } + } + else if(req.target() == "/invalid-redirect") + { + http::response res{http::status::permanent_redirect, req.version()}; + res.set("Requests-Method", req.method_string()); + + for (auto & hd : req) + if (hd.name() == beast::http::field::unknown) + res.set(hd.name_string(), hd.value()); + + res.prepare_payload(); + http::async_write(sock, res, yield); + } + else if(req.target() == "/boost-redirect") + { + http::response res{http::status::permanent_redirect, req.version()}; + res.set("Requests-Method", req.method_string()); + res.set(http::field::location, "http://boost.org"); + + for (auto & hd : req) + if (hd.name() == beast::http::field::unknown) + res.set(hd.name_string(), hd.value()); + + res.body() = "Redirecting..."; + res.prepare_payload(); + http::async_write(sock, res, yield); + } + else if(req.target() == "/set-cookie") + { + http::response res{http::status::permanent_redirect, req.version()}; + res.set("Requests-Method", req.method_string()); + res.set(http::field::location, "http://boost.org"); + + for (auto & hd : req) + if (hd.name() == beast::http::field::unknown) + res.set(hd.name_string(), hd.value()); + + res.prepare_payload(); + http::async_write(sock, res, yield); + } + } +} + +test_server::test_server() +{ + namespace asio = boost::asio; + namespace filesystem = boost::requests::filesystem; + auto tmp = filesystem::temp_directory_path(); + path_ = (tmp / ("boost_request_test_socket_" + + std::to_string(boost::process::v2::current_pid()) + + std::to_string(cnt_++))).string(); + + auto ep = boost::asio::local::stream_protocol::endpoint{path_}; + ep_ = ep; + { + asio::local::stream_protocol::acceptor acc{tp_.get_executor(), ep}; + acc.listen(); + asio::spawn( + tp_, + [this, ep, acc = std::move(acc)](auto ctx) mutable + { + run_(std::move(acc), ctx); + }, boost::asio::detached); + } +} + diff --git a/test/fixtures/server.hpp b/test/fixtures/server.hpp new file mode 100644 index 0000000..b9814e6 --- /dev/null +++ b/test/fixtures/server.hpp @@ -0,0 +1,45 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_REQUESTS_SERVER_HPP +#define BOOST_REQUESTS_SERVER_HPP + +#include +#include +#include +#include +#include +#include + + +struct test_server +{ + test_server(const test_server & ) = delete; + + using endpoint_type = typename boost::asio::generic::stream_protocol::endpoint; + endpoint_type endpoint() + { + return ep_; + } + + boost::core::string_view path() {return path_;} + + test_server(); + + template + void run_session(Socket sock, boost::asio::yield_context ctx); + +private: + static std::size_t cnt_; + template + void run_(Acceptor acc, boost::asio::yield_context ctx); + endpoint_type ep_; + boost::asio::thread_pool tp_{1u}; + std::string path_; +}; + +#endif // BOOST_REQUESTS_SERVER_HPP diff --git a/test/httpbin/cookie.cpp b/test/httpbin/cookie.cpp new file mode 100644 index 0000000..b73a836 --- /dev/null +++ b/test/httpbin/cookie.cpp @@ -0,0 +1,144 @@ +// Copyright (c) 2021 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include + +#include "../fixtures/httpbin.hpp" +#include "../coroutine_test_case.hpp" + + +using namespace boost; + +BOOST_FIXTURE_TEST_SUITE(cookie_, httpbin); + +BOOST_AUTO_TEST_CASE(http_request_cookie_connection) +{ + auto hc = connect(); + + requests::cookie_jar jar; + auto res = requests::json::get(hc, "/cookies", {{}, /*.opts=*/{false}, /*.jar=*/&jar}); + + BOOST_CHECK(res.value.at("cookies").as_object().empty()); + BOOST_CHECK(jar.content.empty()); + + res = requests::json::get(hc, "/cookies/set?cookie-1=foo", {{}, /*.opts=*/{false}, /*.jar=*/&jar}); + + BOOST_CHECK(res.value.at("cookies") == (json::object{{"cookie-1", "foo"}})); + BOOST_REQUIRE(!jar.content.empty()); + auto citr = jar.content.begin(); + BOOST_CHECK(citr->value == "foo"); + BOOST_CHECK(citr->name == "cookie-1"); + BOOST_CHECK(citr->secure_only_flag == false); + BOOST_CHECK(citr->path == "/"); + + res = requests::json::get(hc, "/cookies/set/cookie-2/bar", {{}, /*.opts=*/{false}, /*.jar=*/&jar}); + + BOOST_CHECK(res.value.at("cookies") == (json::object{{"cookie-1", "foo"}, {"cookie-2", "bar"}})); + BOOST_REQUIRE(jar.content.size() == 2u); + citr = jar.content.begin(); + if (citr->name == "cookie-1") + { + BOOST_CHECK(citr->value == "foo"); + BOOST_CHECK(citr->secure_only_flag == false); + BOOST_CHECK(citr->path == "/"); + citr ++ ; + BOOST_CHECK(citr->value == "bar"); + BOOST_CHECK(citr->secure_only_flag == false); + BOOST_CHECK(citr->path == "/"); + + } + else + { + BOOST_CHECK(citr->value == "bar"); + BOOST_CHECK(citr->secure_only_flag == false); + BOOST_CHECK(citr->path == "/"); + citr ++ ; + BOOST_CHECK(citr->value == "foo"); + BOOST_CHECK(citr->secure_only_flag == false); + BOOST_CHECK(citr->path == "/"); + } + res = requests::json::get(hc, urls::url_view{"/cookies/delete?cookie-1"}, {{}, /*.opts=*/{false}, /*.jar=*/&jar}); + + BOOST_CHECK(!jar.content.empty()); + BOOST_REQUIRE(jar.content.size() == 1u); + citr = jar.content.begin(); + BOOST_CHECK(citr->value == "bar"); + BOOST_CHECK(citr->name == "cookie-2"); + BOOST_CHECK(citr->secure_only_flag == false); + BOOST_CHECK(citr->path == "/"); + + res = requests::json::get(hc, urls::url_view{"/cookies/delete?cookie-2"}, {{}, /*.opts=*/{false}, /*.jar=*/&jar}); + BOOST_CHECK(jar.content.empty()); +} + +BOOST_COROUTINE_TEST_CASE(http_request_async_cookie_connection) +{ + auto hc = async_connect(yield); + + requests::cookie_jar jar; + BOOST_CHECK(jar.content.empty()); + auto res = requests::json::async_get(hc, "/cookies", {{}, /*.opts=*/{false}, /*.jar=*/&jar}, {}, yield); + + BOOST_CHECK(res.value.at("cookies").as_object().empty()); + BOOST_CHECK(jar.content.empty()); + + res = requests::json::async_get(hc, "/cookies/set?cookie-1=foo", {{}, /*.opts=*/{false}, /*.jar=*/&jar}, {}, yield); + + BOOST_CHECK(res.value.at("cookies") == (json::object{{"cookie-1", "foo"}})); + BOOST_REQUIRE(!jar.content.empty()); + auto citr = jar.content.begin(); + BOOST_CHECK(citr->value == "foo"); + BOOST_CHECK(citr->name == "cookie-1"); + BOOST_CHECK(citr->secure_only_flag == false); + BOOST_CHECK(citr->path == "/"); + + res = requests::json::async_get(hc, "/cookies/set/cookie-2/bar", {{}, /*.opts=*/{false}, /*.jar=*/&jar}, {}, yield); + + BOOST_CHECK(res.value.at("cookies") == (json::object{{"cookie-1", "foo"}, {"cookie-2", "bar"}})); + BOOST_REQUIRE(jar.content.size() == 2u); + citr = jar.content.begin(); + if (citr->name == "cookie-1") + { + BOOST_CHECK(citr->value == "foo"); + BOOST_CHECK(citr->secure_only_flag == false); + BOOST_CHECK(citr->path == "/"); + citr ++ ; + BOOST_CHECK(citr->value == "bar"); + BOOST_CHECK(citr->secure_only_flag == false); + BOOST_CHECK(citr->path == "/"); + + } + else + { + BOOST_CHECK(citr->value == "bar"); + BOOST_CHECK(citr->secure_only_flag == false); + BOOST_CHECK(citr->path == "/"); + citr ++ ; + BOOST_CHECK(citr->value == "foo"); + BOOST_CHECK(citr->secure_only_flag == false); + BOOST_CHECK(citr->path == "/"); + } + res = requests::json::async_get(hc, urls::url_view{"/cookies/delete?cookie-1"}, {{}, /*.opts=*/{false}, /*.jar=*/&jar}, {}, yield); + + BOOST_CHECK(!jar.content.empty()); + BOOST_REQUIRE(jar.content.size() == 1u); + citr = jar.content.begin(); + BOOST_CHECK(citr->value == "bar"); + BOOST_CHECK(citr->name == "cookie-2"); + BOOST_CHECK(citr->secure_only_flag == false); + BOOST_CHECK(citr->path == "/"); + + res = requests::json::async_get(hc, urls::url_view{"/cookies/delete?cookie-2"}, {{}, /*.opts=*/{false}, /*.jar=*/&jar}, {}, yield); + BOOST_CHECK(jar.content.empty()); +} + + +BOOST_AUTO_TEST_SUITE_END(); \ No newline at end of file diff --git a/test/httpbin/json.cpp b/test/httpbin/json.cpp new file mode 100644 index 0000000..b7cbd85 --- /dev/null +++ b/test/httpbin/json.cpp @@ -0,0 +1,270 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include + +#include +#include +#include + +#include + +#include "../fixtures/httpbin.hpp" +#include "../coroutine_test_case.hpp" + +#define BOOST_CHECK_HTTP_RESULT(Response) \ + BOOST_CHECK_MESSAGE(boost::requests::http::to_status_class(Response.result()) == requests::http::status_class::successful, Response) + + +namespace requests = boost::requests; +namespace filesystem = requests::filesystem; +namespace asio = boost::asio; +namespace json = boost::json; +namespace urls = boost::urls; +namespace beast = boost::beast; + +using boost::system::error_code; + + +BOOST_FIXTURE_TEST_SUITE(json_connection, httpbin); + +BOOST_AUTO_TEST_CASE(sync_http) +{ + auto hc = connect(); + + // headers + { + auto hdr = request(hc, requests::http::verb::get, urls::url_view("/headers"), + requests::empty{}, + {requests::http::headers({{"Test-Header", "it works"}}), {false}}); + + BOOST_CHECK_HTTP_RESULT(hdr.headers); + auto hd = as_json(hdr).at("headers"); + + BOOST_CHECK_EQUAL(hd.at("Host"), json::value(url().encoded_host_and_port())); + BOOST_CHECK(hd.at("Test-Header") == json::value("it works")); + } + + // stream + { + auto hd_ = requests::http::headers({{"Test-Header", "it works"}}); + auto src = requests::make_source(requests::empty{}); + auto str = hc.ropen(requests::http::verb::get, "/get", hd_, *src, nullptr); + BOOST_CHECK_HTTP_RESULT(str.headers()); + json::stream_parser sp; + + char buf[32]; + + error_code ec; + while (!str.done() && !ec) + { + auto sz = str.read_some(asio::buffer(buf), ec); + BOOST_CHECK(ec == error_code{}); + sp.write_some(buf, sz, ec); + BOOST_CHECK(ec == error_code{}); + } + + auto hd = sp.release().at("headers"); + + BOOST_CHECK(hd.at("Host") == json::value(url().encoded_host_and_port())); + BOOST_CHECK(hd.at("Test-Header") == json::value("it works")); + } + + // get-redirect + { + auto hdr = requests::json::get(hc, urls::url_view("/redirect-to?url=%2Fget"), + {requests::http::headers({{"Test-Header", "it works"}}), {false}}); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + BOOST_CHECK(hdr.history.size() == 1u); + BOOST_CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); + + auto hd = hdr.value.at("headers"); + + BOOST_CHECK(hd.at("Host") == json::value(url().encoded_host_and_port())); + BOOST_CHECK(hd.at("Test-Header") == json::value("it works")); + } + + // too-many-redirects + { + error_code ec; + auto res = requests::json::get(hc, "/redirect/10", + {{}, {false, requests::redirect_mode::private_domain, 5}}, {}, ec); + BOOST_CHECK(res.history.size() == 5); + BOOST_CHECK(res.headers.begin() == res.headers.end()); + BOOST_CHECK(ec == requests::error::too_many_redirects); + } + + // delete + { + auto hdr = requests::json::delete_(hc, urls::url_view("/delete"), json::value{{"test-key", "test-value"}}, {{}, {false}}); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto & js = hdr.value; + BOOST_CHECK(beast::http::to_status_class(hdr.headers.result()) == beast::http::status_class::successful); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/json"); + } + + // patch + { + json::value msg {{"test-key", "test-value"}}; + auto hdr = requests::json::patch(hc, urls::url_view("/patch"), msg, {{}, {false}}); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto & js = hdr.value; + BOOST_CHECK(hdr.headers.result() == beast::http::status::ok); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/json"); + BOOST_CHECK(js.at("json") == msg); + } + + // json + { + json::value msg {{"test-key", "test-value"}}; + auto hdr = requests::json::put(hc, urls::url_view("/put"), msg, {{}, {false}}); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto & js = hdr.value; + + BOOST_CHECK(hdr.headers.result() == beast::http::status::ok); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/json"); + BOOST_CHECK(js.at("json") == msg); + } + + // post + { + json::value msg {{"test-key", "test-value"}}; + auto hdr = requests::json::post(hc, urls::url_view("/post"), msg, {{}, {false}}); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto & js = hdr.value; + BOOST_CHECK(hdr.headers.result() == beast::http::status::ok); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/json"); + BOOST_CHECK(js.at("json") == msg); + } + +} + +BOOST_COROUTINE_TEST_CASE(async_http) +{ + auto hc = async_connect(yield); + + // headers + { + auto hdr = request(hc, requests::http::verb::get, urls::url_view("/headers"), + requests::empty{}, + {requests::http::headers({{"Test-Header", "it works"}}), {false}}); + + BOOST_CHECK_HTTP_RESULT(hdr.headers); + auto hd = as_json(hdr).at("headers"); + + BOOST_CHECK(hd.at("Host") == json::value(url().encoded_host_and_port())); + BOOST_CHECK(hd.at("Test-Header") == json::value("it works")); + } + + // stream + { + auto hd_ = requests::http::headers({{"Test-Header", "it works"}}); + auto src = requests::make_source(requests::empty{}); + auto str = hc.async_ropen(requests::http::verb::get, "/get", hd_, *src, nullptr, yield); + BOOST_CHECK_HTTP_RESULT(str.headers()); + json::stream_parser sp; + + char buf[32]; + + error_code ec; + while (!str.done() && !ec) + { + auto sz = str.read_some(asio::buffer(buf), ec); + BOOST_CHECK(ec == error_code{}); + sp.write_some(buf, sz, ec); + BOOST_CHECK(ec == error_code{}); + } + + auto hd = sp.release().at("headers"); + + BOOST_CHECK(hd.at("Host") == json::value(url().encoded_host_and_port())); + BOOST_CHECK(hd.at("Test-Header") == json::value("it works")); + } + + // get-redirect + { + auto hdr = requests::json::async_get(hc, "/redirect-to?url=%2Fget", + {requests::http::headers({{"Test-Header", "it works"}}), {false}}, {}, yield); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + BOOST_CHECK(hdr.history.size() == 1u); + BOOST_CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); + + auto hd = hdr.value.at("headers"); + + BOOST_CHECK(hd.at("Host") == json::value(url().encoded_host_and_port())); + BOOST_CHECK(hd.at("Test-Header") == json::value("it works")); + } + + // too-many-redirects + { + error_code ec; + auto res = requests::json::async_get(hc, "/redirect/10", + {{}, {false, requests::redirect_mode::private_domain, 5}}, {}, yield[ec]); + BOOST_CHECK(res.history.size() == 5); + BOOST_CHECK(res.headers.begin() == res.headers.end()); + BOOST_CHECK(ec == requests::error::too_many_redirects); + } + + // delete + { + auto hdr = requests::json::async_delete(hc, "/delete", + json::value{{"test-key", "test-value"}}, {{}, {false}}, {}, yield); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto & js = hdr.value; + BOOST_CHECK(beast::http::to_status_class(hdr.headers.result()) == beast::http::status_class::successful); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/json"); + } + + // patch + { + json::value msg {{"test-key", "test-value"}}; + auto hdr = requests::json::async_patch(hc, "/patch", msg, {{}, {false}}, {}, yield); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto & js = hdr.value; + BOOST_CHECK(hdr.headers.result() == beast::http::status::ok); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/json"); + BOOST_CHECK(js.at("json") == msg); + } + + // json + { + json::value msg {{"test-key", "test-value"}}; + auto hdr = requests::json::async_put(hc, "/put", msg, {{}, {false}}, {}, yield); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto & js = hdr.value; + + BOOST_CHECK(hdr.headers.result() == beast::http::status::ok); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/json"); + BOOST_CHECK(js.at("json") == msg); + } + + // post + { + json::value msg {{"test-key", "test-value"}}; + auto hdr = requests::json::async_post(hc, "/post", msg, {{}, {false}}, {}, yield); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto & js = hdr.value; + BOOST_CHECK(hdr.headers.result() == beast::http::status::ok); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/json"); + BOOST_CHECK(js.at("json") == msg); + } + +} + + + + +BOOST_AUTO_TEST_SUITE_END(); \ No newline at end of file diff --git a/test/httpbin/method.cpp b/test/httpbin/method.cpp new file mode 100644 index 0000000..4886404 --- /dev/null +++ b/test/httpbin/method.cpp @@ -0,0 +1,419 @@ +// Copyright (c) 2021 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../coroutine_test_case.hpp" +#include "../fixtures/httpbin.hpp" + +namespace requests = boost::requests; +namespace filesystem = requests::filesystem; +namespace asio = boost::asio; +namespace json = boost::json; +namespace urls = boost::urls; +namespace core = boost::core; + +using boost::system::error_code; +#if defined(BOOST_REQUESTS_USE_STD_FS) +using fs_error_code = std::error_code; +#else +using fs_error_code = boost::system::error_code; +#endif + +#define BOOST_CHECK_HTTP_RESULT(Response) \ + BOOST_CHECK_MESSAGE(boost::requests::http::to_status_class(Response.result()) == requests::http::status_class::successful, Response) + +BOOST_FIXTURE_TEST_SUITE(method, httpbin); + + +BOOST_AUTO_TEST_CASE(request) +{ + requests::default_options().enforce_tls = false; + requests::default_options().max_redirects = 5; + + auto u = [this](core::string_view path) { urls::url uu = url(); uu.set_encoded_path(path); return uu; }; + + // headers + { + auto hdr = requests::request(requests::http::verb::get, + u("/headers"), + requests::empty{}, + requests::http::headers({{"Test-Header", "it works"}})); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + auto hd = as_json(hdr).at("headers"); + + BOOST_CHECK_EQUAL(hd.at("Host"), json::value(url().encoded_host_and_port())); + BOOST_CHECK(hd.at("Test-Header") == json::value("it works")); + } + + + // get + { + auto hdr = requests::get(u("/get"), requests::http::headers({{"Test-Header", "it works"}})); + + BOOST_CHECK_HTTP_RESULT(hdr.headers); + auto hd = as_json(hdr).at("headers"); + + BOOST_CHECK(hd.at("Host") == json::value(url().encoded_host_and_port())); + BOOST_CHECK(hd.at("Test-Header") == json::value("it works")); + } +/* + // get-redirect + { + auto uu = u("/redirect-to"); + uu.set_encoded_query("url=%2Fget"); + auto hdr = requests::get(uu, requests::http::headers({{"Test-Header", "it works"}})); + + BOOST_CHECK_HTTP_RESULT(hdr.headers); + BOOST_CHECK(hdr.history.size() == 1u); + BOOST_CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); + + auto hd = as_json(hdr).at("headers"); + + BOOST_CHECK(hd.at("Host") == json::value(url().encoded_host_and_port())); + BOOST_CHECK(hd.at("Test-Header") == json::value("it works")); + } +*/ + // too-many-redirects + { + boost::system::error_code ec; + requests::default_session().options().max_redirects = 3; + auto res = requests::get(u("/redirect/10"), {}, ec); + BOOST_CHECK(res.history.size() == 3); + BOOST_CHECK(res.headers.begin() == res.headers.end()); + BOOST_CHECK(ec == requests::error::too_many_redirects); + } + + // download + { + const auto target = filesystem::temp_directory_path() / "requests-test.png"; + if (filesystem::exists(target)) + filesystem::remove(target); + + BOOST_CHECK(!filesystem::exists(target)); + auto res = requests::download(u("/image"), {}, target); + BOOST_CHECK_HTTP_RESULT(res.headers); + BOOST_CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); + BOOST_CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); + + BOOST_CHECK_MESSAGE(filesystem::exists(target), target); + fs_error_code ec; + filesystem::remove(target, ec); + } + + // download-too-many-redirects + { + error_code ec; + requests::default_options().max_redirects = 3; + requests::default_session().options().max_redirects = 3; + const auto target = filesystem::temp_directory_path() / "requests-test.html"; + if (filesystem::exists(target)) + filesystem::remove(target); + auto res = requests::download(u("/redirect/10"), {}, target, ec); + BOOST_CHECK(res.history.size() == 3); + BOOST_CHECK(res.headers.begin() == res.headers.end()); + + BOOST_CHECK(ec == requests::error::too_many_redirects); + BOOST_CHECK(!filesystem::exists(target)); + } + + // delete + { + auto hdr = requests::delete_(u("/delete"), json::value{{"test-key", "test-value"}}, {}); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + BOOST_CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/json"); + } + + // patch-json + { + json::value msg {{"test-key", "test-value"}}; + auto hdr = requests::patch(u("/patch"), msg, {}); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + BOOST_CHECK(hdr.headers.result() == requests::http::status::ok); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/json"); + BOOST_CHECK(js.at("json") == msg); + } + + // patch-form + { + auto hdr = requests::patch(u("/patch"), + requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, + {}); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + BOOST_CHECK(hdr.headers.result() == requests::http::status::ok); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); + BOOST_CHECK(js.at("form") == (json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}})); + } + + // put-json + { + json::value msg {{"test-key", "test-value"}}; + auto hdr = requests::put(u("/put"), msg, {}); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + BOOST_CHECK(hdr.headers.result() == requests::http::status::ok); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/json"); + BOOST_CHECK(js.at("json") == msg); + } + + // put-form + { + auto hdr = requests::put(u("/put"), + requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, + {}); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + BOOST_CHECK(hdr.headers.result() == requests::http::status::ok); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); + BOOST_CHECK(js.at("form") == (json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}})); + } + + // post-json + { + json::value msg {{"test-key", "test-value"}}; + auto hdr = requests::post(u("/post"), msg, {}); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + BOOST_CHECK(hdr.headers.result() == requests::http::status::ok); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/json"); + BOOST_CHECK(js.at("json") == msg); + } + + // post-form + { + auto hdr = requests::post(u("/post"), + requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, + {}); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + BOOST_CHECK(hdr.headers.result() == requests::http::status::ok); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); + BOOST_CHECK(js.at("form") == (json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}})); + } +} + + +BOOST_COROUTINE_TEST_CASE(async_request) +{ + requests::default_options().enforce_tls = false; + requests::default_options().max_redirects = 5; + + auto u = [this](core::string_view path) { urls::url uu = url(); uu.set_encoded_path(path); return uu; }; + + // headers + { + auto hdr = requests::async_request(requests::http::verb::get, + u("/headers"), + requests::empty{}, + requests::http::headers({{"Test-Header", "it works"}}), yield); + + BOOST_CHECK_HTTP_RESULT(hdr.headers); + auto hd = as_json(hdr).at("headers"); + + BOOST_CHECK_EQUAL(hd.at("Host"), json::value(url().encoded_host_and_port())); + BOOST_CHECK(hd.at("Test-Header") == json::value("it works")); + } + + + // get + { + auto hdr = requests::async_get(u("/get"), requests::http::headers({{"Test-Header", "it works"}}), yield); + + BOOST_CHECK_HTTP_RESULT(hdr.headers); + auto hd = boost::requests::as_json(hdr).at("headers"); + + BOOST_CHECK(hd.at("Host") == json::value(url().encoded_host_and_port())); + BOOST_CHECK(hd.at("Test-Header") == json::value("it works")); + } + /* + // get-redirect + { + auto hdr = requests::async_get(u("/redirect-to?url=%2Fget"), requests::http::headers({{"Test-Header", "it works"}}), yield); + + BOOST_CHECK_HTTP_RESULT(hdr.headers); + BOOST_CHECK(hdr.history.size() == 1u); + BOOST_CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); + + auto hd = boost::requests::as_json(hdr).at("headers"); + + BOOST_CHECK(hd.at("Host") == json::value(url().encoded_host_and_port())); + BOOST_CHECK(hd.at("Test-Header") == json::value("it works")); + } + */ + // too-many-redirects + { + boost::system::error_code ec; + requests::default_session(yield.get_executor()).options().max_redirects = 3; + auto res = requests::async_get(u("/redirect/10"), {}, yield[ec]); + BOOST_CHECK_EQUAL(res.history.size(), 3); + BOOST_CHECK(res.headers.begin() == res.headers.end()); + BOOST_CHECK_MESSAGE(ec == requests::error::too_many_redirects, ec.what()); + } + + // download + { + const auto target = filesystem::temp_directory_path() / "requests-test.png"; + if (filesystem::exists(target)) + filesystem::remove(target); + + BOOST_CHECK(!filesystem::exists(target)); + auto res = requests::async_download(u("/image"), {}, target, yield); + BOOST_CHECK_HTTP_RESULT(res.headers); + BOOST_CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); + BOOST_CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); + + BOOST_CHECK_MESSAGE(filesystem::exists(target), target); + fs_error_code ec; + filesystem::remove(target, ec); + } + +/* + // download-redirect + { + + const auto target = filesystem::temp_directory_path() / "requests-test.png"; + if (filesystem::exists(target)) + filesystem::remove(target); + + BOOST_CHECK(!filesystem::exists(target)); + auto res = requests::async_download(u("/redirect-to?url=%2Fimage"), {}, target, yield); + BOOST_CHECK_HTTP_RESULT(res.headers); + BOOST_CHECK(res.history.size() == 1u); + BOOST_CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); + + BOOST_CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); + BOOST_CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); + + BOOST_CHECK_MESSAGE(filesystem::exists(target), target); + fs_error_code ec; + filesystem::remove(target, ec); + }*/ + + + // download-too-many-redirects + { + error_code ec; + requests::default_options().max_redirects = 3; + requests::default_session().options().max_redirects = 3; + const auto target = filesystem::temp_directory_path() / "requests-test.html"; + if (filesystem::exists(target)) + filesystem::remove(target); + auto res = requests::async_download(u("/redirect/10"), {}, target, yield[ec]); + BOOST_CHECK(res.history.size() == 3); + BOOST_CHECK(requests::is_redirect(res.headers.result())); + BOOST_CHECK(res.headers.begin() == res.headers.end()); + + BOOST_CHECK(ec == requests::error::too_many_redirects); + BOOST_CHECK(!filesystem::exists(target)); + } + + // delete + { + auto hdr = requests::async_delete(u("/delete"), json::value{{"test-key", "test-value"}}, {}, yield); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + BOOST_CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/json"); + } + + // patch-json + { + json::value msg {{"test-key", "test-value"}}; + auto hdr = requests::async_patch(u("/patch"), msg, {}, yield); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + BOOST_CHECK(hdr.headers.result() == requests::http::status::ok); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/json"); + BOOST_CHECK(js.at("json") == msg); + } + + // patch-form + { + auto hdr = requests::async_patch(u("/patch"), + requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, + {}, yield); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + BOOST_CHECK(hdr.headers.result() == requests::http::status::ok); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); + BOOST_CHECK(js.at("form") == (json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}})); + } + + // put-json + { + json::value msg {{"test-key", "test-value"}}; + auto hdr = requests::async_put(u("/put"), msg, {}, yield); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + BOOST_CHECK(hdr.headers.result() == requests::http::status::ok); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/json"); + BOOST_CHECK(js.at("json") == msg); + } + + // put-form + { + auto hdr = requests::async_put(u("/put"), + requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, + {}, yield); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + BOOST_CHECK(hdr.headers.result() == requests::http::status::ok); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); + BOOST_CHECK(js.at("form") == (json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}})); + } + + // post-json + { + json::value msg {{"test-key", "test-value"}}; + auto hdr = requests::async_post(u("/post"), msg, {}, yield); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + BOOST_CHECK(hdr.headers.result() == requests::http::status::ok); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/json"); + BOOST_CHECK(js.at("json") == msg); + } + + // post-form + { + auto hdr = requests::async_post(u("/post"), + requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, + {}, yield); + BOOST_CHECK_HTTP_RESULT(hdr.headers); + + auto js = as_json(hdr); + BOOST_CHECK(hdr.headers.result() == requests::http::status::ok); + BOOST_CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); + BOOST_CHECK(js.at("form") == (json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}})); + } +} + +BOOST_AUTO_TEST_SUITE_END(); \ No newline at end of file diff --git a/test/json.cpp b/test/json.cpp deleted file mode 100644 index 97e5984..0000000 --- a/test/json.cpp +++ /dev/null @@ -1,333 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#include -#include - -#include -#include -#include - -#include "doctest.h" -#include "string_maker.hpp" - - -namespace requests = boost::requests; -namespace filesystem = requests::filesystem; -namespace asio = boost::asio; -namespace json = boost::json; -namespace urls = boost::urls; -namespace beast = boost::beast; - -using boost::system::error_code; - -inline std::string httpbin() -{ - std::string url = "httpbin.org"; - if (auto p = ::getenv("BOOST_REQUEST_HTTPBIN")) - url = p; - return url; -} - - -TEST_SUITE_BEGIN("json-connection"); - -TEST_CASE("sync-http") -{ - auto url = httpbin(); - - asio::io_context ctx; - - asio::ssl::context sslctx{asio::ssl::context_base::tls_client}; - - sslctx.set_verify_mode(asio::ssl::verify_peer); - sslctx.set_default_verify_paths(); - - requests::connection hc(ctx.get_executor(), sslctx); - - hc.set_host(url); - hc.use_ssl(false); - CHECK(hc.uses_ssl() == false); - - asio::ip::tcp::resolver rslvr{ctx}; - asio::ip::tcp::endpoint ep = *rslvr.resolve(url, false ? "https" : "http").begin(); - - hc.connect(ep); - - - // headers - { - auto hdr = request(hc, requests::http::verb::get, urls::url_view("/headers"), - requests::empty{}, - {requests::headers({{"Test-Header", "it works"}}), {false}}); - - CHECK_HTTP_RESULT(hdr.headers); - auto hd = as_json(hdr).at("headers"); - - CHECK(hd.at("Host") == json::value(url)); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - // stream - { - auto str = hc.ropen(requests::http::verb::get, urls::url_view("/get"), - requests::empty{}, {requests::headers({{"Test-Header", "it works"}}), {false}}); - CHECK_HTTP_RESULT(str.headers()); - json::stream_parser sp; - - char buf[32]; - - error_code ec; - while (!str.done() && !ec) - { - auto sz = str.read_some(asio::buffer(buf), ec); - CHECK(ec == error_code{}); - sp.write_some(buf, sz, ec); - CHECK(ec == error_code{}); - } - - auto hd = sp.release().at("headers"); - - CHECK(hd.at("Host") == json::value(url)); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - // get-redirect - { - auto hdr = requests::json::get(hc, urls::url_view("/redirect-to?url=%2Fget"), - {requests::headers({{"Test-Header", "it works"}}), {false}}); - CHECK_HTTP_RESULT(hdr.headers); - CHECK(hdr.history.size() == 1u); - CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); - - auto hd = hdr.value.at("headers"); - - CHECK(hd.at("Host") == json::value(url)); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - // too-many-redirects - { - error_code ec; - auto res = requests::json::get(hc, urls::url_view("/redirect/10"), - {{}, {false, requests::redirect_mode::private_domain, 5}}, {}, ec); - CHECK_HTTP_RESULT(res.headers); - CHECK(res.history.size() == 5); - CHECK(res.headers.begin() == res.headers.end()); - CHECK(ec == requests::error::too_many_redirects); - } - - // delete - { - auto hdr = requests::json::delete_(hc, urls::url_view("/delete"), json::value{{"test-key", "test-value"}}, {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - - auto & js = hdr.value; - CHECK(beast::http::to_status_class(hdr.headers.result()) == beast::http::status_class::successful); - REQUIRE(js); - CHECK(js->at("headers").at("Content-Type") == "application/json"); - } - - // patch - { - json::value msg {{"test-key", "test-value"}}; - auto hdr = requests::json::patch(hc, urls::url_view("/patch"), msg, {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - - auto & js = hdr.value; - CHECK(hdr.headers.result() == beast::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - CHECK(js.at("json") == msg); - } - - // json - { - json::value msg {{"test-key", "test-value"}}; - auto hdr = requests::json::put(hc, urls::url_view("/put"), msg, {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - - auto & js = hdr.value; - - CHECK(hdr.headers.result() == beast::http::status::ok); - REQUIRE(js); - CHECK(js->at("headers").at("Content-Type") == "application/json"); - CHECK(js->at("json") == msg); - } - - // post - { - json::value msg {{"test-key", "test-value"}}; - auto hdr = requests::json::post(hc, urls::url_view("/post"), msg, {{}, {false}}); - CHECK_HTTP_RESULT(hdr.headers); - - auto & js = hdr.value; - CHECK(hdr.headers.result() == beast::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - CHECK(js.at("json") == msg); - } - -} - - -void run_json_tests(error_code ec, - requests::connection & hc, - urls::url_view url) -{ - requests::json::async_get( - hc, urls::url_view("/get"), - requests::request_parameters{requests::headers({{"Test-Header", "it works"}}), {false}}, {}, - tracker( - [url](error_code ec, requests::json::response<> hdr) - { - check_ec(ec); - auto & hd = hdr.value.at("headers"); - CHECK_HTTP_RESULT(hdr.headers); - - CHECK(hd.at("Host") == json::value(url.encoded_host())); - CHECK(hd.at("Test-Header") == json::value("it works")); - })); - - - requests::json::async_get( - hc, urls::url_view("/redirect-to?url=%2Fget"), - requests::request_parameters{requests::headers({{"Test-Header", "it works"}}), {false}}, {}, - tracker( - [url](error_code ec, requests::json::response<> hdr) - { - check_ec(ec); - CHECK_HTTP_RESULT(hdr.headers); - CHECK(hdr.history.size() == 1u); - CHECK(hdr.history.at(0u).at(beast::http::field::location) == "/get"); - - auto & hd = hdr.value.at("headers"); - - CHECK(hd.at("Host") == json::value(url.encoded_host())); - CHECK(hd.at("Test-Header") == json::value("it works")); - })); - - - requests::json::async_get( - hc, urls::url_view("/redirect/10"), {{}, {false, requests::redirect_mode::private_domain, 5}},{}, - tracker( - [url](error_code ec, requests::json::response<> res) - { - CHECK_HTTP_RESULT(res.headers); - CHECK(res.history.size() == 5); - CHECK(res.headers.begin() == res.headers.end()); - CHECK(ec == requests::error::too_many_redirects); - })); - - - requests::json::async_delete( - hc, urls::url_view("/delete"), json::value{{"test-key", "test-value"}}, {{}, {false}}, {}, - tracker( - [url](error_code ec, requests::json::response<> hdr) - { - check_ec(ec); - CHECK_HTTP_RESULT(hdr.headers); - auto & js = hdr.value; - CHECK(beast::http::to_status_class(hdr.headers.result()) == beast::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - })); - - requests::json::async_patch( - hc, urls::url_view("/patch"), json::value {{"test-key", "test-value"}}, {{}, {false}}, {}, - tracker( - [url](error_code ec, requests::json::response<> hdr) - { - check_ec(ec); - auto & js = hdr.value; - CHECK_HTTP_RESULT(hdr.headers); - CHECK(hdr.headers.result() == beast::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - CHECK(js.at("json") == json::value{{"test-key", "test-value"}}); - })); - - requests::json::async_put( - hc, urls::url_view("/put"), json::value{{"test-key", "test-value"}}, {{}, {false}}, {}, - tracker( - [url](error_code ec, requests::json::response> hdr) - { - check_ec(ec); - auto & js = hdr.value; - CHECK_HTTP_RESULT(hdr.headers); - CHECK(hdr.headers.result() == beast::http::status::ok); - REQUIRE(js); - CHECK(js->at("headers").at("Content-Type") == "application/json"); - CHECK(js->at("json") == json::value{{"test-key", "test-value"}}); - })); - - requests::json::async_post( - hc, urls::url_view("/post"), json::value{{"test-key", "test-value"}}, {{}, {false}}, {}, - tracker( - [url](error_code ec, requests::json::response<> hdr) - { - check_ec(ec); - auto & js = hdr.value; - CHECK_HTTP_RESULT(hdr.headers); - CHECK(hdr.headers.result() == beast::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - CHECK(js.at("json") == json::value{{"test-key", "test-value"}}); - })); -} - - -TEST_CASE("async-json-request") -{ - urls::url url; - url.set_host(httpbin()); - asio::io_context ctx; - asio::ssl::context sslctx{asio::ssl::context_base::tls_client}; - sslctx.set_verify_mode(asio::ssl::verify_peer); - sslctx.set_default_verify_paths(); - requests::connection conn(ctx, sslctx); - - auto do_the_thing = asio::deferred( - [&](error_code ec, asio::ip::tcp::resolver::results_type res) - { - check_ec(ec); - asio::ip::tcp::endpoint ep; - if (!ec) - ep = res.begin()->endpoint(); - else - REQUIRE(!res.size()); - - return asio::deferred.when(!ec) - .then(conn.async_connect(ep, asio::deferred)) - .otherwise(asio::post(ctx, asio::append(asio::deferred, ec))); - }); - - SUBCASE("http") - { - conn = requests::connection(ctx); - asio::ip::tcp::resolver rslvr{ctx}; - url.set_scheme("http"); - conn.use_ssl(false); - CHECK(!conn.uses_ssl()); - conn.set_host(url.encoded_host()); - - rslvr.async_resolve(std::string(url.encoded_host().data(), url.encoded_host().size()), - "80", do_the_thing) - (asio::append(&run_json_tests, std::ref(conn), urls::url_view(url))); - ctx.run(); - } - - SUBCASE("https") - { - conn = requests::connection(ctx, sslctx); - asio::ip::tcp::resolver rslvr{ctx}; - url.set_scheme("https"); - CHECK(conn.uses_ssl()); - conn.set_host(url.encoded_host()); - rslvr.async_resolve(std::string(url.encoded_host().data(), url.encoded_host().size()), - "443", do_the_thing) - (asio::append(&run_json_tests, std::ref(conn), urls::url_view(url))); - ctx.run(); - } -} - -TEST_SUITE_END(); \ No newline at end of file diff --git a/test/keep_alive.cpp b/test/keep_alive.cpp deleted file mode 100644 index 9ef2e7f..0000000 --- a/test/keep_alive.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#include -#include - -#include "doctest.h" -#include "string_maker.hpp" - -TEST_SUITE_BEGIN("keep-alive"); - -TEST_CASE("parse") -{ - namespace br = boost::requests; - - const auto epoch = std::chrono::system_clock::time_point::min(); - - CHECK(br::parse_keep_alive_field("max=42", epoch).value().max == 42); - CHECK(br::parse_keep_alive_field("max=42", epoch).value().timeout - == std::chrono::system_clock::time_point::max()); - - CHECK(br::parse_keep_alive_field("timeout=23", epoch).value().max - == std::numeric_limits::max()); - CHECK(br::parse_keep_alive_field("timeout=23", epoch).value().timeout - == std::chrono::system_clock::time_point(std::chrono::seconds(23))); - - CHECK(br::parse_keep_alive_field("max=12, timeout=34", epoch).value().max == 12); - CHECK(br::parse_keep_alive_field("max=12, timeout=34", epoch).value().timeout - == std::chrono::system_clock::time_point(std::chrono::seconds(34))); - - CHECK(br::parse_keep_alive_field("timeout=12, max=34", epoch).value().max == 34); - CHECK(br::parse_keep_alive_field("timeout=12, max=34", epoch).value().timeout - == std::chrono::system_clock::time_point(std::chrono::seconds(12))); - -} - -TEST_SUITE_END(); \ No newline at end of file diff --git a/test/link.cpp b/test/link.cpp deleted file mode 100644 index 697f6bf..0000000 --- a/test/link.cpp +++ /dev/null @@ -1,121 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#include -#include -#include -#include "doctest.h" -#include "string_maker.hpp" - - -TEST_SUITE_BEGIN("rfc.link"); - -TEST_CASE("link") -{ - using namespace boost; - - namespace br = boost::requests; - - SUBCASE("single") - { - auto txt = R"()"; - auto lst = br::parse_link_field(txt); - CHECK_MESSAGE(!lst.has_error(), lst.error()); - REQUIRE(lst->size() == 1u); - CHECK((*lst->begin()).attributes.empty()); - CHECK((*lst->begin()).url == urls::parse_uri("https://one.example.com")); - } - - SUBCASE("multiple") - { - auto txt = R"(, )"; - auto lst = br::parse_link_field(txt); - CHECK_MESSAGE(!lst.has_error(), lst.error()); - - REQUIRE(lst->size() == 2u); - - CHECK((*lst->begin()).attributes.empty()); - CHECK((*lst->begin()).url == urls::parse_uri("https://one.example.com").value()); - - CHECK((*std::next(lst->begin())).attributes.empty()); - CHECK((*std::next(lst->begin())).url == urls::parse_uri("https://two.example.com").value()); - } - - - SUBCASE("single with param") - { - auto txt = R"(; foobar)"; - auto lst = br::parse_link_field(txt); - CHECK_MESSAGE(!lst.has_error(), lst.error()); - - CHECK(lst->size() == 1u); - - auto val = *lst->begin(); - CHECK(val.attributes.begin() != val.attributes.end()); - - std::vector vec; - vec.assign(val.attributes.begin(), val.attributes.end()); - CHECK(vec.size() == 1u); - if (!vec.empty()) - { - CHECK(val.url == urls::parse_uri("https://one.example.com")); - CHECK(vec.front().key == "foobar"); - CHECK(vec.front().value.empty()); - } - } - - SUBCASE("single with param") - { - auto txt = R"(; xyz=ctl)"; - auto lst = br::parse_link_field(txt); - CHECK_MESSAGE(!lst.has_error(), lst.error()); - - CHECK(lst->size() == 1u); - auto val = *lst->begin(); - CHECK(val.attributes.begin() != val.attributes.end()); - - std::vector vec; - vec.assign(val.attributes.begin(), val.attributes.end()); - CHECK(vec.size() == 1u); - if (!vec.empty()) - { - CHECK(val.url == urls::parse_uri("https://one.example.com")); - CHECK(vec.front().key == "xyz"); - CHECK(vec.front().value == "ctl"); - } - } - - - SUBCASE("single with param") - { - auto txt = R"(; rel="preconnect")"; - auto lst = br::parse_link_field(txt); - CHECK_MESSAGE(!lst.has_error(), lst.error()); - - - CHECK(lst->size() == 1u); - auto val = *lst->begin(); - CHECK(val.attributes.begin() != val.attributes.end()); - - auto rel = val.rel(); - CHECK_MESSAGE(!rel.has_error(), rel.error()); - CHECK(rel->size() == 1); - } - - SUBCASE("multiple with param") - { - auto txt = - R"(; rel="preconnect next", )" - R"(; rel="preconnect", )" - R"(; rel="preconnect")"; - auto lst = br::parse_link_field(txt); - CHECK_MESSAGE(!lst.has_error(), lst.error()); - } - -} - -TEST_SUITE_END(); \ No newline at end of file diff --git a/test/location.cpp b/test/location.cpp deleted file mode 100644 index 04bf606..0000000 --- a/test/location.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#include -#include -#include -#include "doctest.h" -#include "string_maker.hpp" - -#include - -TEST_SUITE_BEGIN("fields"); - -inline auto operator""_url( const char * ur, std::size_t sz) -{ - boost::core::string_view sv{ur, sz}; - return boost::urls::parse_uri_reference(sv); -} - -TEST_CASE("location") -{ - using boost::requests::interpret_location; - - CHECK(interpret_location("/api/user", "/api/team") == "/api/team"_url); - CHECK(interpret_location("/api/user", "../group") == "/api/group"_url); - CHECK(interpret_location("/api/user", "https://foo.com/api/team") == "https://foo.com/api/team"_url); - CHECK(interpret_location("/api/user", "avatar") == "/api/user/avatar"_url); - CHECK(interpret_location("/api/user#bio", "avatar#frag") == "/api/user/avatar#frag"_url); - CHECK(interpret_location("/api/user#bio", "avatar") == "/api/user/avatar#bio"_url); - CHECK(interpret_location("/api#user", "https://foo.com/api/team") == "https://foo.com/api/team#user"_url); -} - -TEST_SUITE_END(); \ No newline at end of file diff --git a/test/main_test.cpp b/test/main_test.cpp index ae56bbf..4415b45 100644 --- a/test/main_test.cpp +++ b/test/main_test.cpp @@ -1,7 +1,7 @@ -// Copyright (c) 2021 Klemens D. Morgenstern +// Copyright (c) 2023 Klemens D. Morgenstern // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN -#include "doctest.h" \ No newline at end of file +#define BOOST_TEST_MODULE requests_test +#include \ No newline at end of file diff --git a/test/method.cpp b/test/method.cpp deleted file mode 100644 index 8499e3a..0000000 --- a/test/method.cpp +++ /dev/null @@ -1,563 +0,0 @@ -// Copyright (c) 2021 Klemens D. Morgenstern -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "doctest.h" -#include "string_maker.hpp" - -namespace requests = boost::requests; -namespace filesystem = requests::filesystem; -namespace asio = boost::asio; -namespace json = boost::json; -namespace urls = boost::urls; -namespace core = boost::core; - -using boost::system::error_code; -#if defined(BOOST_REQUESTS_USE_STD_FS) -using fs_error_code = std::error_code; -#else -using fs_error_code = boost::system::error_code; -#endif - - -inline std::string httpbin() -{ - std::string url = "httpbin.org"; - if (auto p = ::getenv("BOOST_REQUEST_HTTPBIN")) - url = p; - MESSAGE(url); - return url; -} - -TEST_SUITE_BEGIN("method"); - - -struct http_maker -{ - urls::url url; - http_maker(urls::string_view target) - { - url = urls::parse_uri("http://" + httpbin() + std::string{target}).value(); - - } - - operator urls::url_view () const - { - return url; - } - - operator json::value () const - { - return json::string(url.buffer()); - } -}; - -struct https_maker -{ - urls::url url; - https_maker(boost::core::string_view target) - { - url = urls::parse_uri("https://" + httpbin() + std::string{target}).value(); - } - - operator urls::url_view () const - { - return url; - } - operator json::value () const - { - return json::string(url.buffer()); - } -}; - -TYPE_TO_STRING(http_maker); -TYPE_TO_STRING(https_maker); - -TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) -{ - requests::default_options().enforce_tls = false; - requests::default_options().max_redirects = 5; - - // headers - { - auto hdr = requests::request(requests::http::verb::get, - u("/headers"), - requests::empty{}, - requests::headers({{"Test-Header", "it works"}})); - CHECK_HTTP_RESULT(hdr.headers); - auto hd = as_json(hdr).at("headers"); - - CHECK(hd.at("Host") == json::value(httpbin())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - - // get - { - auto hdr = requests::get(u("/get"), requests::headers({{"Test-Header", "it works"}})); - - CHECK_HTTP_RESULT(hdr.headers); - auto hd = as_json(hdr).at("headers"); - - CHECK(hd.at("Host") == json::value(httpbin())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - // get-redirect - { - auto hdr = requests::get(u("/redirect-to?url=%2Fget"), requests::headers({{"Test-Header", "it works"}})); - - CHECK_HTTP_RESULT(hdr.headers); - CHECK(hdr.history.size() == 1u); - CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); - - auto hd = as_json(hdr).at("headers"); - - CHECK(hd.at("Host") == json::value(httpbin())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - // too-many-redirects - { - boost::system::error_code ec; - requests::default_session().options().max_redirects = 3; - auto res = requests::get(u("/redirect/10"), {}, ec); - CHECK_HTTP_RESULT(res.headers); - CHECK(res.history.size() == 3); - CHECK(res.headers.begin() == res.headers.end()); - CHECK(ec == requests::error::too_many_redirects); - } - - // download - { - const auto target = filesystem::temp_directory_path() / "requests-test.png"; - if (filesystem::exists(target)) - filesystem::remove(target); - - CHECK(!filesystem::exists(target)); - auto res = requests::download(u("/image"), {}, target); - CHECK_HTTP_RESULT(res.headers); - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec; - filesystem::remove(target, ec); - } - - - // download-redirect - { - - const auto target = filesystem::temp_directory_path() / "requests-test.png"; - if (filesystem::exists(target)) - filesystem::remove(target); - - CHECK(!filesystem::exists(target)); - auto res = requests::download(u("/redirect-to?url=%2Fimage"), {}, target); - CHECK_HTTP_RESULT(res.headers); - CHECK(res.history.size() == 1u); - CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); - - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec; - filesystem::remove(target, ec); - } - - - // download-too-many-redirects - { - error_code ec; - requests::default_options().max_redirects = 3; - requests::default_session().options().max_redirects = 3; - const auto target = filesystem::temp_directory_path() / "requests-test.html"; - if (filesystem::exists(target)) - filesystem::remove(target); - auto res = requests::download(u("/redirect/10"), {}, target, ec); - CHECK(res.history.size() == 3); - CHECK_HTTP_RESULT(res.headers); - CHECK(res.headers.begin() == res.headers.end()); - - CHECK(ec == requests::error::too_many_redirects); - CHECK(!filesystem::exists(target)); - } - - // delete - { - auto hdr = requests::delete_(u("/delete"), json::value{{"test-key", "test-value"}}, {}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - } - - // patch-json - { - json::value msg {{"test-key", "test-value"}}; - auto hdr = requests::patch(u("/patch"), msg, {}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - CHECK(js.at("json") == msg); - } - - // patch-form - { - auto hdr = requests::patch(u("/patch"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } - - // put-json - { - json::value msg {{"test-key", "test-value"}}; - auto hdr = requests::put(u("/put"), msg, {}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - CHECK(js.at("json") == msg); - } - - // put-form - { - auto hdr = requests::put(u("/put"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } - - // post-json - { - json::value msg {{"test-key", "test-value"}}; - auto hdr = requests::post(u("/post"), msg, {}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - CHECK(js.at("json") == msg); - } - - // post-form - { - auto hdr = requests::post(u("/post"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {}); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } - -} - - -void async_http_pool_request(asio::any_io_executor exec, - urls::url_view url, - std::vector & buffer) -{ - namespace http = requests::http; - namespace filesystem = requests::filesystem; - - auto u = - [&](core::string_view path, core::string_view query = "") -> urls::url_view - { - buffer.emplace_back(url); - auto & u = buffer.back(); - u.set_path(path); - u.set_query(query); - return u; - }; - - - - requests::async_request( - http::verb::get, u("/headers"), - requests::empty{}, requests::headers({{"Test-Header", "it works"}}), - tracker(exec, - [url](error_code ec, requests::response hdr) - { - // headers - - check_ec(ec); - auto hd = as_json(hdr).at("headers"); - CHECK_HTTP_RESULT(hdr.headers); - - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - })); - - requests::async_get(u("/get"), - requests::headers({{"Test-Header", "it works"}}), - tracker(exec, - [url](error_code ec, requests::response hdr) - { - // headers - check_ec(ec); - auto hd = as_json(hdr).at("headers"); - CHECK_HTTP_RESULT(hdr.headers); - - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - })); - - requests::async_get(u("/redirect-to", "url=/get"), - requests::headers({{"Test-Header", "it works"}}), - tracker(exec, - [url](error_code ec, requests::response hdr) - { - // get-redirect - check_ec(ec); - CHECK(hdr.history.size() == 1u); - CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); - CHECK_HTTP_RESULT(hdr.headers); - - auto hd = as_json(hdr).at("headers"); - - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - })); - - requests::async_get(u("/redirect/10"), {}, - tracker(exec, - [url](error_code ec, requests::response res) - { - // too-many-redirects - CHECK_HTTP_RESULT(res.headers); - - CHECK(res.history.size() == 3); - CHECK(res.headers.begin() == res.headers.end()); - CHECK(ec == requests::error::too_many_redirects); - - })); - - { - auto pt = filesystem::temp_directory_path(); - const auto target = pt / "requests-test.png"; - if (filesystem::exists(target)) - filesystem::remove(target); - - CHECK_MESSAGE(filesystem::exists(pt), pt); - CHECK(!filesystem::exists(target)); - requests::async_download(u("/image"), {}, target, - tracker(exec, - [url, target](error_code ec, requests::response_base res) - { - // download - CHECK_HTTP_RESULT(res.headers); - - check_ec(ec); - CHECK_MESSAGE(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u, res.headers); - CHECK_MESSAGE(res.headers.at(requests::http::field::content_type) == "image/png", res.headers); - - CHECK_MESSAGE(filesystem::exists(target), target); - filesystem::remove(target, ec); - })); - } - - { - const auto target = filesystem::temp_directory_path() / "requests-test-2.png"; - if (filesystem::exists(target)) - filesystem::remove(target); - - CHECK(!filesystem::exists(target)); - requests::async_download(u("/redirect-to", "url=/image"), {}, target, - tracker(exec, - [url, target](error_code ec, requests::response_base res) - { - // download-redirect - CHECK_HTTP_RESULT(res.headers); - - check_ec(ec); - CHECK(res.history.size() == 1u); - CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); - - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - filesystem::remove(target, ec); - - })); - - } - - requests::async_delete( u("/delete"), json::value{{"test-key", "test-value"}}, {}, - tracker(exec, - [url](error_code ec, requests::response hdr) - { - // delete - check_ec(ec); - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - })); - - - requests::async_patch(u("/patch"), json::value{{"test-key", "test-value"}}, {}, - tracker(exec, - [url](error_code ec, requests::response hdr) - { - // patch-json - - CHECK_HTTP_RESULT(hdr.headers); - check_ec(ec); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - - })); - - - - requests::async_patch(u("/patch"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {}, - tracker(exec, - [url](error_code ec, requests::response hdr) - { - // patch-form - check_ec(ec); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - CHECK_HTTP_RESULT(hdr.headers); - - })); - - - requests::async_put(u("/put"), json::value{{"test-key", "test-value"}}, {}, - tracker(exec, - [url](error_code ec, requests::response hdr) - { - // put-json - CHECK_HTTP_RESULT(hdr.headers); - - check_ec(ec); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - - })); - - - - requests::async_put(u("/put"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {}, - tracker(exec, - [url](error_code ec, requests::response hdr) - { - // put-form - CHECK_HTTP_RESULT(hdr.headers); - - check_ec(ec); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - - })); - - - requests::async_post(u("/post"), json::value{{"test-key", "test-value"}}, {}, - tracker(exec, - [url](error_code ec, requests::response hdr) - { - // post-json - CHECK_HTTP_RESULT(hdr.headers); - - check_ec(ec); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - - })); - - - - requests::async_post(u("/post"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {}, - tracker(exec, - [url](error_code ec, requests::response hdr) - { - // post-form - - CHECK_HTTP_RESULT(hdr.headers); - - check_ec(ec); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - - })); -} - -TEST_CASE("async-request") -{ - urls::url url; - url.set_host(httpbin()); - - - std::vector buffer; - - SUBCASE("http") - { - url.set_scheme("http"); - asio::thread_pool tp; - requests::default_session(tp.get_executor()).options().enforce_tls = false; - requests::default_session(tp.get_executor()).options().max_redirects = 3; - async_http_pool_request(tp.get_executor(), url, buffer); - tp.join(); - } - - SUBCASE("https") - { - url.set_scheme("https"); - asio::thread_pool tp; - requests::default_session(tp.get_executor()).options().enforce_tls = false; - requests::default_session(tp.get_executor()).options().max_redirects = 3; - async_http_pool_request(tp.get_executor(), url, buffer); - tp.join(); - } -} - -TEST_SUITE_END(); \ No newline at end of file diff --git a/test/options.cpp b/test/options.cpp deleted file mode 100644 index 9640f53..0000000 --- a/test/options.cpp +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) 2021 Klemens D. Morgenstern -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include \ No newline at end of file diff --git a/test/quoted_string.cpp b/test/quoted_string.cpp deleted file mode 100644 index a15537a..0000000 --- a/test/quoted_string.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - - -#include -#include - -#include "doctest.h" -#include "string_maker.hpp" - - -TEST_SUITE_BEGIN("rfc.quoted_string"); - -TEST_CASE("quoted_string") -{ - namespace br = boost::requests; - namespace ug = boost::urls::grammar; - - CHECK(ug::parse("\"foobar\"", br::rfc::quoted_string) == "\"foobar\""); - CHECK(ug::parse("foobar", br::rfc::quoted_string) == ug::error::mismatch); - CHECK(ug::parse("\"foo\\\"bar\"", br::rfc::quoted_string) == "\"foo\\\"bar\""); - CHECK(br::rfc::unquote_string(ug::parse("\"foo\\\"bar\"", br::rfc::quoted_string).value())== "foo\"bar"); -} - -TEST_SUITE_END(); diff --git a/test/redirect.cpp b/test/redirect.cpp deleted file mode 100644 index cf8e2dc..0000000 --- a/test/redirect.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) 2022 Klemens D. Morgenstern -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include - -#include "doctest.h" - -TEST_SUITE_BEGIN("redirect"); - - -TEST_CASE("none") -{ - using namespace boost::requests; - using namespace boost::urls; - CHECK(!should_redirect(redirect_mode::none, parse_uri("http://boost.org").value(), parse_uri("http://boost.org/index.html").value())); - CHECK(!should_redirect(redirect_mode::none, parse_uri("http://boost.org").value(), parse_uri_reference("/take-my-money.com").value())); -} - - -TEST_CASE("endpoint") -{ - using namespace boost::requests; - using namespace boost::urls; - CHECK(should_redirect(redirect_mode::endpoint, parse_uri("http://boost.org").value(), parse_uri("http://boost.org/index.html").value())); - CHECK(!should_redirect(redirect_mode::endpoint, parse_uri("http://boost.org").value(), parse_uri("https://boost.org").value())); - CHECK(!should_redirect(redirect_mode::endpoint, parse_uri("http://boost.org").value(), parse_uri("http://www.boost.org").value())); - CHECK(!should_redirect(redirect_mode::endpoint, parse_uri("http://boost.org").value(), parse_uri("http://boost.org:433").value())); - CHECK(should_redirect(redirect_mode::endpoint, parse_uri("http://boost.org").value(), parse_uri("https://boost.org:80").value())); - CHECK(should_redirect(redirect_mode::endpoint, parse_uri("http://boost.org").value(), parse_uri("http://boost.org:80").value())); - CHECK(should_redirect(redirect_mode::endpoint, parse_uri("http://boost.org").value(), parse_uri("ws://boost.org:80").value())); - CHECK(should_redirect(redirect_mode::endpoint, parse_uri("http://boost.org").value(), parse_uri("foo://boost.org:80").value())); - CHECK(should_redirect(redirect_mode::endpoint, parse_uri("http://boost.org").value(), parse_uri_reference("/take-my-money.com").value())); -} - -TEST_CASE("domain") -{ - using namespace boost::requests; - using namespace boost::urls; - CHECK(should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri("http://boost.org/index.html").value())); - CHECK(should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri("https://boost.org").value())); - CHECK(!should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri("http://www.boost.org").value())); - CHECK(!should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri("http://fakeboost.org").value())); - CHECK(!should_redirect(redirect_mode::domain, parse_uri("http://www.boost.org").value(), parse_uri("http://boost.org").value())); - CHECK(should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri("http://boost.org:433").value())); - CHECK(should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri("https://boost.org:80").value())); - CHECK(should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri("http://boost.org:80").value())); - CHECK(should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri("ws://boost.org:80").value())); - CHECK(should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri("foo://boost.org:80").value())); - CHECK(should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri_reference("/take-my-money.com").value())); - } - -TEST_CASE("subdomain") -{ - using namespace boost::requests; - using namespace boost::urls; - CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("http://boost.org/index.html").value())); - CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("https://boost.org").value())); - CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("http://www.boost.org").value())); - CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("https://www.boost.org").value())); - CHECK(!should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("http://fakeboost.org").value())); - CHECK(!should_redirect(redirect_mode::subdomain, parse_uri("http://www.boost.org").value(), parse_uri("http://boost.org").value())); - CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("http://boost.org:433").value())); - CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("https://boost.org:80").value())); - CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("http://boost.org:80").value())); - CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("ws://boost.org:80").value())); - CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("foo://boost.org:80").value())); - CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri_reference("/take-my-money.com").value())); -} - -TEST_CASE("private_domain") -{ - using namespace boost::requests; - using namespace boost::urls; - CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("http://boost.org/index.html").value())); - CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("https://boost.org").value())); - CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("http://www.boost.org").value())); - CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("https://www.boost.org").value())); - CHECK(!should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("http://fakeboost.org").value())); - CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://www.boost.org").value(), parse_uri("http://boost.org").value())); - CHECK(!should_redirect(redirect_mode::private_domain, parse_uri("http://www.boost.org").value(), parse_uri("http://org").value())); - CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("http://boost.org:433").value())); - CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("https://boost.org:80").value())); - CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("http://boost.org:80").value())); - CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("ws://boost.org:80").value())); - CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("foo://boost.org:80").value())); - CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://doc.boost.org").value(), parse_uri("http://docs.boost.org").value())); - CHECK(!should_redirect(redirect_mode::private_domain, parse_uri("http://doc.boost.org").value(), parse_uri("http://fakeboost.org").value())); - CHECK(!should_redirect(redirect_mode::private_domain, parse_uri("http://doc.boost.org").value(), parse_uri("http://ost.org").value())); - CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://api.boost.org").value(), parse_uri("http://pi.boost.org").value())); - CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri_reference("/take-my-money.com").value())); -} - - -TEST_CASE("any") -{ - using namespace boost::requests; - using namespace boost::urls; - CHECK(should_redirect(redirect_mode::any, parse_uri("http://boost.org").value(), parse_uri("https://take-my-money.com").value())); - CHECK(should_redirect(redirect_mode::any, parse_uri("http://boost.org").value(), parse_uri_reference("/take-my-money.com").value())); -} - - -TEST_SUITE_END(); \ No newline at end of file diff --git a/test/request.cpp b/test/request.cpp deleted file mode 100644 index 930409b..0000000 --- a/test/request.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#include - -#include "doctest.h" - -namespace br = boost::requests; diff --git a/test/response.cpp b/test/response.cpp deleted file mode 100644 index b4532cd..0000000 --- a/test/response.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#include -#include "doctest.h" - -TEST_SUITE_BEGIN("response"); - -TEST_CASE("resp") -{ - -} - -TEST_SUITE_END(); - diff --git a/test/rfc.cpp b/test/rfc.cpp deleted file mode 100644 index 83a99c7..0000000 --- a/test/rfc.cpp +++ /dev/null @@ -1,153 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#include "doctest.h" -#include "string_maker.hpp" -#include - -TEST_SUITE_BEGIN("rfc"); - -using namespace boost; - -TEST_CASE("date-1123") -{ - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(784111777)) == - urls::grammar::parse("Sun, 06 Nov 1994 08:49:37 GMT", requests::rfc::date_1123)); - CHECK(urls::grammar::error::mismatch == urls::grammar::parse("Mon, 06 Nov 1994 08:49:37 GMT", requests::rfc::date_1123)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1696335075)) == - urls::grammar::parse("Tue, 03 Oct 2023 12:11:15 GMT", requests::rfc::date_1123)); - - CHECK(urls::grammar::error::mismatch == - urls::grammar::parse("Sun, 03 Oct 2023 12:11:15 GMT", requests::rfc::date_1123)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1623233894)) == - urls::grammar::parse("Wed, 09 Jun 2021 10:18:14 GMT", requests::rfc::date_1123)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == - urls::grammar::parse("Thu, 01 Jan 1970 00:00:01 GMT", requests::rfc::date_1123)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == - urls::grammar::parse("Thu, 01 Jan 1970 00:00:01 GMT", requests::rfc::date_1123)); - -} - -TEST_CASE("date-850") -{ - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(784111777)) == - urls::grammar::parse("Sunday, 06-Nov-1994 08:49:37 GMT", requests::rfc::date_850)); - CHECK(urls::grammar::error::mismatch == urls::grammar::parse("Mon, 06 Nov 1994 08:49:37 GMT", requests::rfc::date_850)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1696335075)) == - urls::grammar::parse("Tuesday, 03-Oct-2023 12:11:15 GMT", requests::rfc::date_850)); - - CHECK(urls::grammar::error::mismatch == - urls::grammar::parse("Sunday, 03-Oct-2023 12:11:15 GMT", requests::rfc::date_850)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1623233894)) == - urls::grammar::parse("Wednesday, 09-Jun-2021 10:18:14 GMT", requests::rfc::date_850)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == - urls::grammar::parse("Thursday, 01-Jan-1970 00:00:01 GMT", requests::rfc::date_850)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == - urls::grammar::parse("Thursday, 01-Jan-1970 00:00:01 GMT", requests::rfc::date_850)); - -} - - - -TEST_CASE("date-asctime") -{ - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(784111777)) == - urls::grammar::parse("Sun Nov 6 08:49:37 1994", requests::rfc::date_asctime)); - CHECK(urls::grammar::error::mismatch == urls::grammar::parse("Mon, 06 Nov 1994 08:49:37 GMT", requests::rfc::date_asctime)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1696335075)) == - urls::grammar::parse("Tue Oct 03 12:11:15 2023", requests::rfc::date_asctime)); - - CHECK(urls::grammar::error::mismatch == - urls::grammar::parse("Sun Oct 03 12:11:15 2023", requests::rfc::date_asctime)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1623233894)) == - urls::grammar::parse("Wed Jun 9 10:18:14 2021", requests::rfc::date_asctime)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == - urls::grammar::parse("Thu Jan 1 00:00:01 1970", requests::rfc::date_asctime)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == - urls::grammar::parse("Thu Jan 01 00:00:01 1970", requests::rfc::date_asctime)); - -} - - -TEST_CASE("http-date") -{ - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(784111777)) == - urls::grammar::parse("Sun, 06 Nov 1994 08:49:37 GMT", requests::rfc::http_date)); - CHECK(urls::grammar::error::mismatch == urls::grammar::parse("Mon, 06 Nov 1994 08:49:37 GMT", requests::rfc::http_date)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1696335075)) == - urls::grammar::parse("Tue, 03 Oct 2023 12:11:15 GMT", requests::rfc::http_date)); - - CHECK(urls::grammar::error::mismatch == - urls::grammar::parse("Sun, 03 Oct 2023 12:11:15 GMT", requests::rfc::http_date)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1623233894)) == - urls::grammar::parse("Wed, 09 Jun 2021 10:18:14 GMT", requests::rfc::http_date)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == - urls::grammar::parse("Thu, 01 Jan 1970 00:00:01 GMT", requests::rfc::http_date)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == - urls::grammar::parse("Thu, 01 Jan 1970 00:00:01 GMT", requests::rfc::http_date)); - - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(784111777)) == - urls::grammar::parse("Sunday, 06-Nov-1994 08:49:37 GMT", requests::rfc::http_date)); - CHECK(urls::grammar::error::mismatch == urls::grammar::parse("Mon, 06 Nov 1994 08:49:37 GMT", requests::rfc::http_date)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1696335075)) == - urls::grammar::parse("Tuesday, 03-Oct-2023 12:11:15 GMT", requests::rfc::http_date)); - - CHECK(urls::grammar::error::mismatch == - urls::grammar::parse("Sunday, 03-Oct-2023 12:11:15 GMT", requests::rfc::http_date)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1623233894)) == - urls::grammar::parse("Wednesday, 09-Jun-2021 10:18:14 GMT", requests::rfc::http_date)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == - urls::grammar::parse("Thursday, 01-Jan-1970 00:00:01 GMT", requests::rfc::http_date)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == - urls::grammar::parse("Thursday, 01-Jan-1970 00:00:01 GMT", requests::rfc::http_date)); - - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(784111777)) == - urls::grammar::parse("Sun Nov 6 08:49:37 1994", requests::rfc::http_date)); - CHECK(urls::grammar::error::mismatch == urls::grammar::parse("Mon, 06 Nov 1994 08:49:37 GMT", requests::rfc::http_date)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1696335075)) == - urls::grammar::parse("Tue Oct 03 12:11:15 2023", requests::rfc::http_date)); - - CHECK(urls::grammar::error::mismatch == - urls::grammar::parse("Sun Oct 03 12:11:15 2023", requests::rfc::http_date)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1623233894)) == - urls::grammar::parse("Wed Jun 9 10:18:14 2021", requests::rfc::http_date)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == - urls::grammar::parse("Thu Jan 1 00:00:01 1970", requests::rfc::http_date)); - - CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == - urls::grammar::parse("Thu Jan 01 00:00:01 1970", requests::rfc::http_date)); - -} - - - -TEST_SUITE_END(); \ No newline at end of file diff --git a/test/session.cpp b/test/session.cpp deleted file mode 100644 index a7a3d91..0000000 --- a/test/session.cpp +++ /dev/null @@ -1,559 +0,0 @@ -// Copyright (c) 2021 Klemens D. Morgenstern -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "doctest.h" -#include "string_maker.hpp" - -namespace requests = boost::requests; -namespace filesystem = requests::filesystem; -namespace asio = boost::asio; -namespace json = boost::json; -namespace core = boost::core; -namespace urls = boost::urls; - -using boost::system::error_code; -#if defined(BOOST_REQUESTS_USE_STD_FS) -using fs_error_code = std::error_code; -#else -using fs_error_code = boost::system::error_code; -#endif - - - -inline std::string httpbin() -{ - std::string url = "httpbin.org"; - if (auto p = ::getenv("BOOST_REQUEST_HTTPBIN")) - url = p; - return url; -} - - -TEST_SUITE_BEGIN("session"); - - -struct http_maker -{ - urls::url url; - http_maker(boost::core::string_view target) - { - url = urls::parse_uri("http://" + httpbin() + std::string{target}).value(); - - } - - operator urls::url_view () const - { - return url; - } - - operator json::value () const - { - return json::string(url.buffer()); - } -}; - -struct https_maker -{ - urls::url url; - https_maker(boost::core::string_view target) - { - url = urls::parse_uri("https://" + httpbin() + std::string{target}).value(); - } - - operator urls::url_view () const - { - return url; - } - operator json::value () const - { - return json::string(url.buffer()); - } -}; - -TYPE_TO_STRING(http_maker); -TYPE_TO_STRING(https_maker); - -TEST_CASE_TEMPLATE("sync-request", u, http_maker, https_maker) -{ - asio::io_context ctx; - - requests::session hc{ctx}; - hc.options().enforce_tls = false; - hc.options().max_redirects = 5; - - // headers - { - auto hdr = request(hc, requests::http::verb::get, - u("/headers"), - requests::empty{}, - requests::headers({{"Test-Header", "it works"}})); - CHECK_HTTP_RESULT(hdr.headers); - auto hd = as_json(hdr).at("headers"); - - CHECK(hd.at("Host") == json::value(httpbin())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - - // get - { - auto hdr = get(hc, u("/get"), requests::headers({{"Test-Header", "it works"}})); - CHECK_HTTP_RESULT(hdr.headers); - auto hd = as_json(hdr).at("headers"); - - CHECK(hd.at("Host") == json::value(httpbin())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - // get-redirect - { - auto hdr = get(hc, u("/redirect-to?url=%2Fget"), requests::headers({{"Test-Header", "it works"}})); - - CHECK(hdr.history.size() == 1u); - CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); - - CHECK_HTTP_RESULT(hdr.headers); - auto hd = as_json(hdr).at("headers"); - - CHECK(hd.at("Host") == json::value(httpbin())); - CHECK(hd.at("Test-Header") == json::value("it works")); - } - - // too-many-redirects - { - error_code ec; - auto res = get(hc, u("/redirect/10"), {}, ec); - CHECK_HTTP_RESULT(res.headers); - CHECK(res.history.size() == 5); - CHECK(res.headers.begin() == res.headers.end()); - CHECK(ec == requests::error::too_many_redirects); - } - - // download - { - const auto target = filesystem::temp_directory_path() / "requests-test.png"; - if (filesystem::exists(target)) - filesystem::remove(target); - - CHECK(!filesystem::exists(target)); - auto res = download(hc, u("/image"), {}, target); - - CHECK_HTTP_RESULT(res.headers); - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec; - filesystem::remove(target, ec); - } - - - // download-redirect - { - - const auto target = filesystem::temp_directory_path() / "requests-test.png"; - if (filesystem::exists(target)) - filesystem::remove(target); - - CHECK(!filesystem::exists(target)); - auto res = download(hc, u("/redirect-to?url=%2Fimage"), {}, target); - CHECK_HTTP_RESULT(res.headers); - CHECK(res.history.size() == 1u); - CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); - - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec; - filesystem::remove(target, ec); - } - - - // download-too-many-redirects - { - error_code ec; - hc.options().max_redirects = 3; - const auto target = filesystem::temp_directory_path() / "requests-test.html"; - if (filesystem::exists(target)) - filesystem::remove(target); - auto res = download(hc, u("/redirect/10"), {}, target, ec); - CHECK(res.history.size() == 3); - CHECK_HTTP_RESULT(res.headers); - CHECK(res.headers.begin() == res.headers.end()); - - CHECK(ec == requests::error::too_many_redirects); - CHECK(!filesystem::exists(target)); - } - - // delete - { - auto hdr = delete_(hc, u("/delete"), json::value{{"test-key", "test-value"}}, {}); - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - } - - // patch-json - { - json::value msg {{"test-key", "test-value"}}; - auto hdr = patch(hc, u("/patch"), msg, {}); - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - CHECK(js.at("json") == msg); - } - - // patch-form - { - auto hdr = patch(hc, u("/patch"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {}); - - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } - - // put-json - { - json::value msg {{"test-key", "test-value"}}; - auto hdr = put(hc, u("/put"), msg, {}); - - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - CHECK(js.at("json") == msg); - } - - // put-form - { - auto hdr = put(hc, u("/put"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {}); - - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } - - // post-json - { - json::value msg {{"test-key", "test-value"}}; - auto hdr = post(hc, u("/post"), msg, {}); - - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - CHECK(js.at("json") == msg); - } - - // post-form - { - auto hdr = post(hc, u("/post"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {}); - - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - } - -} - - -void async_http_pool_request(requests::session & sess, - urls::url_view url, - std::vector & buffer) -{ - namespace http = requests::http; - namespace filesystem = requests::filesystem; - - auto u = - [&](core::string_view path, core::string_view query = "") -> urls::url_view - { - buffer.emplace_back(url); - auto & u = buffer.back(); - u.set_path(path); - u.set_query(query); - return u; - }; - - async_request( - sess, - http::verb::get, u("/headers"), - requests::empty{}, requests::headers({{"Test-Header", "it works"}}), - tracker( - [url](error_code ec, requests::response hdr) - { - // headers - check_ec(ec); - auto hd = as_json(hdr).at("headers"); - CHECK_HTTP_RESULT(hdr.headers); - - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - })); - - async_get(sess, - u("/get"), - requests::headers({{"Test-Header", "it works"}}), - tracker( - [url](error_code ec, requests::response hdr) - { - // headers - check_ec(ec); - CHECK_HTTP_RESULT(hdr.headers); - auto hd = as_json(hdr).at("headers"); - CHECK_HTTP_RESULT(hdr.headers); - - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - })); - - async_get(sess, u("/redirect-to", "url=/get"), - requests::headers({{"Test-Header", "it works"}}), - tracker( - [url](error_code ec, requests::response hdr) - { - // get-redirect - CHECK_HTTP_RESULT(hdr.headers); - CHECK(hdr.history.size() == 1u); - CHECK(hdr.history.at(0u).at(requests::http::field::location) == "/get"); - - auto hd = as_json(hdr).at("headers"); - - CHECK(hd.at("Host") == json::value(url.host_name())); - CHECK(hd.at("Test-Header") == json::value("it works")); - })); - - async_get(sess, u("/redirect/10"), {}, - tracker( - [url](error_code ec, requests::response res) - { - // too-many-redirects - CHECK_HTTP_RESULT(res.headers); - - CHECK(res.history.size() == 3); - CHECK(res.headers.begin() == res.headers.end()); - CHECK(ec == requests::error::too_many_redirects); - - })); - - { - auto pt = filesystem::temp_directory_path(); - const auto target = pt / "requests-test.png"; - if (filesystem::exists(target)) - filesystem::remove(target); - - CHECK(!filesystem::exists(target)); - async_download(sess, u("/image"), {}, target, - tracker( - [url, target](error_code ec, requests::response_base res) - { - // download - CHECK_HTTP_RESULT(res.headers); - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec_; - filesystem::remove(target, ec_); - - })); - } - - { - const auto target = filesystem::temp_directory_path() / "requests-test-2.png"; - if (filesystem::exists(target)) - filesystem::remove(target); - - CHECK(!filesystem::exists(target)); - async_download(sess, u("/redirect-to", "url=/image"), {}, target, - tracker( - [url, target](error_code ec, requests::response_base res) - { - // download-redirect - CHECK_HTTP_RESULT(res.headers); - - CHECK(res.history.size() == 1u); - CHECK(res.history.at(0u).at(requests::http::field::location) == "/image"); - - CHECK(std::stoull(res.headers.at(requests::http::field::content_length)) > 0u); - CHECK(res.headers.at(requests::http::field::content_type) == "image/png"); - - CHECK_MESSAGE(filesystem::exists(target), target); - fs_error_code ec_; - filesystem::remove(target, ec_); - - })); - - } - - async_delete(sess, u("/delete"), json::value{{"test-key", "test-value"}}, {}, - tracker( - [url](error_code ec, requests::response hdr) - { - // delete - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - - })); - - - async_patch(sess, u("/patch"), json::value{{"test-key", "test-value"}}, {}, - tracker( - [url](error_code ec, requests::response hdr) - { - // patch-json - CHECK_HTTP_RESULT(hdr.headers); - - check_ec(ec); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - - })); - - - - async_patch(sess, u("/patch"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {}, - tracker( - [url](error_code ec, requests::response hdr) - { - // patch-form - CHECK_HTTP_RESULT(hdr.headers); - - check_ec(ec); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - - })); - - - async_put(sess, u("/put"), json::value{{"test-key", "test-value"}}, {}, - tracker( - [url](error_code ec, requests::response hdr) - { - check_ec(ec); - // put-json - CHECK_HTTP_RESULT(hdr.headers); - - check_ec(ec); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - - })); - - - - async_put(sess, u("/put"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {}, - tracker( - [url](error_code ec, requests::response hdr) - { - check_ec(ec); - // put-form - CHECK_HTTP_RESULT(hdr.headers); - - check_ec(ec); - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - - })); - - - async_post(sess, u("/post"), json::value{{"test-key", "test-value"}}, {}, - tracker( - [url](error_code ec, requests::response hdr) - { - check_ec(ec); - // post-json - CHECK_HTTP_RESULT(hdr.headers); - auto js = as_json(hdr); - CHECK(requests::http::to_status_class(hdr.headers.result()) == requests::http::status_class::successful); - CHECK(js.at("headers").at("Content-Type") == "application/json"); - - })); - - - - async_post(sess, u("/post"), - requests::form{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}, - {}, - tracker( - [url](error_code ec, requests::response hdr) - { - // post-form - - check_ec(ec); - CHECK_HTTP_RESULT(hdr.headers); - - auto js = as_json(hdr); - CHECK(hdr.headers.result() == requests::http::status::ok); - CHECK(js.at("headers").at("Content-Type") == "application/x-www-form-urlencoded"); - CHECK(js.at("form") == json::value{{"foo", "42"}, {"bar", "21"}, {"foo bar" , "23"}}); - - })); -} - -TEST_CASE("async-session-request") -{ - urls::url url; - url.set_host(httpbin()); - asio::io_context ctx; - requests::session hc{ctx}; - hc.options().enforce_tls = false; - hc.options().max_redirects = 3; - - std::vector buffer; - - SUBCASE("http") - { - url.set_scheme("http"); - async_http_pool_request(hc, url, buffer); - ctx.run(); - } - - SUBCASE("https") - { - url.set_scheme("https"); - async_http_pool_request(hc, url, buffer); - ctx.run(); - - } -} - -TEST_SUITE_END(); \ No newline at end of file diff --git a/test/string_maker.hpp b/test/string_maker.hpp deleted file mode 100644 index 115cb5c..0000000 --- a/test/string_maker.hpp +++ /dev/null @@ -1,220 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef STRING_MAKER_HPP -#define STRING_MAKER_HPP - -#include "doctest.h" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace doctest -{ - - -template<> -struct StringMaker -{ - static String convert(const std::type_info & t) - { - return String(boost::core::demangle(t.name()).c_str()); - } -}; - -template<> -struct StringMaker -{ - static String convert(boost::core::string_view sv) - { - return String(sv.data(), sv.size()); - } -}; - -template<> -struct StringMaker -{ - static String convert(boost::system::error_code ec) - { - if (ec.has_location()) - return toString( - ec.location().to_string() + " [" + std::to_string(ec.value()) + "/" + ec.category().name() + "]: " + ec.message()); - else - return toString("[" + std::to_string(ec.value()) + "/" + ec.category().name() + "]: " + ec.message()); - } -}; - -template -struct StringMaker> -{ - static String convert(const boost::system::result & res) - { - if(res.has_value()) - return toString(*res); - else - return toString(res.error()); - } -}; - - -template<> -struct StringMaker -{ - static String convert(const boost::requests::set_cookie::extensions_type & res) - { - std::string val; - for (auto && attr : res) - { - val += attr; - val += "; "; - } - return String(val.data(), val.size()); - } -}; - -template<> -struct StringMaker -{ - static String convert(const boost::json::value & v) - { - auto sv = boost::json::serialize(v); - return String(sv.data(), sv.size()); - } -}; - -template<> -struct StringMaker -{ - static String convert(const boost::json::array & v) - { - auto sv = boost::json::serialize(v); - return String(sv.data(), sv.size()); - } -}; - -template<> -struct StringMaker -{ - static String convert(const boost::json::object & v) - { - auto sv = boost::json::serialize(v); - return String(sv.data(), sv.size()); - } -}; - -template<> -struct StringMaker -{ - static String convert(const boost::json::string & sv) - { - return String(sv.data(), sv.size()); - } -}; - - -template<> -struct StringMaker -{ - static String convert(std::exception_ptr e) - { - if (e) - try - { - std::rethrow_exception(e); - } - catch(boost::asio::multiple_exceptions & me) - { - return convert(me.first_exception()); - - } - catch(std::exception & ex) - { - return ex.what(); - } - else - return ""; - } -}; - -} - -inline void check_ec(boost::system::error_code ec, - boost::source_location loc = BOOST_CURRENT_LOCATION) -{ - doctest::detail::ResultBuilder rb(doctest::assertType::DT_REQUIRE, loc.file_name(), loc.line(), loc.function_name()); - rb.setResult(doctest::detail::Result{!ec, doctest::StringMaker< boost::system::error_code>::convert(ec)}); - DOCTEST_ASSERT_LOG_AND_REACT(rb); -} - -template -struct tracker_t -{ - Handler handler; - boost::source_location loc; - bool called = false; - bool moved_from = false; - - tracker_t( - Handler && handler, - const boost::source_location & loc = BOOST_CURRENT_LOCATION) - : handler(std::forward(handler)), loc(loc) - { - BOOST_ASIO_HANDLER_LOCATION((loc.file_name(), loc.line(), loc.function_name())); - } - - template - void operator()(Args && ... args) - { - BOOST_ASIO_HANDLER_LOCATION((loc.file_name(), loc.line(), loc.function_name())); - called = true; - std::move(handler)(std::forward(args)...); - } - - tracker_t(tracker_t && lhs) : handler(std::move(lhs.handler)), loc(lhs.loc), called(lhs.called) - { - lhs.moved_from = true; - } - - ~tracker_t() - { - if (!moved_from) - { - std::string dm = boost::core::demangle(typeid(handler).name()); - doctest::detail::ResultBuilder rb(doctest::assertType::DT_CHECK, - loc.file_name(), loc.line(), - dm.c_str()); - rb.setResult(doctest::detail::Result{called, "called"}); - DOCTEST_ASSERT_LOG_AND_REACT(rb); - } - } -}; - -template -auto tracker(Handler && handler, const boost::source_location & loc = BOOST_CURRENT_LOCATION) -> tracker_t> -{ - return tracker_t>(std::forward(handler), loc); -} - - -template -auto tracker(boost::asio::any_io_executor exec, Handler && handler, - const boost::source_location & loc = BOOST_CURRENT_LOCATION) -> - boost::asio::executor_binder>, boost::asio::any_io_executor> -{ - return boost::asio::bind_executor(exec, tracker_t>(std::forward(handler), loc)); -} - -#define CHECK_HTTP_RESULT(Response) \ - CHECK_MESSAGE(boost::requests::http::to_status_class(Response.result()) == requests::http::status_class::successful, Response) - - -#endif \ No newline at end of file diff --git a/test/unit/connection.cpp b/test/unit/connection.cpp new file mode 100644 index 0000000..7edcd5b --- /dev/null +++ b/test/unit/connection.cpp @@ -0,0 +1,298 @@ +// Copyright (c) 2021 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include "../coroutine_test_case.hpp" +#include "../fixtures/server.hpp" + +namespace requests = boost::requests; +namespace filesystem = requests::filesystem; +namespace asio = boost::asio; +namespace json = boost::json; +namespace urls = boost::urls; +using boost::system::error_code ; + +BOOST_FIXTURE_TEST_SUITE(connection, test_server); + +BOOST_AUTO_TEST_CASE(echo) +{ + asio::io_context ctx; + requests::connection conn{ctx}; + conn.connect(endpoint()); + + namespace http = boost::beast::http; + + requests::http::headers hd{{"test", "dummy"}}; + requests::string_source ss{"test-data"}; + auto s = conn.ropen(http::verb::post, "/echo", hd, ss, nullptr); + std::string str; + auto dbuf = asio::dynamic_buffer(str); + BOOST_CHECK(s.read(dbuf) == 9); + BOOST_CHECK(str == "test-data"); + BOOST_CHECK(s.headers().at("test") == "dummy"); + + conn.close(); +} + +BOOST_AUTO_TEST_CASE(echo_chunked) +{ + asio::io_context ctx; + requests::connection conn{ctx}; + conn.connect(endpoint()); + + namespace http = boost::beast::http; + + requests::http::headers hd{{"test", "dummy"}}; + requests::string_source ss{"test-data"}; + auto s = conn.ropen(http::verb::post, "/echo-chunked", hd, ss, nullptr); + BOOST_CHECK(s.headers().at(http::field::transfer_encoding) == "chunked"); + std::string str; + auto dbuf = asio::dynamic_buffer(str); + BOOST_CHECK_EQUAL(s.read(dbuf), 9); + BOOST_CHECK_EQUAL(str.size(), 9); + BOOST_CHECK(str == "test-data"); + BOOST_CHECK(s.headers().at("test") == "dummy"); + conn.close(); +} + +BOOST_AUTO_TEST_CASE(boost_get_http) +{ + asio::io_context ctx; + + requests::connection conn{ctx}; + asio::ip::tcp::resolver res{ctx}; + auto eps = res.resolve("boost.org", "http"); + conn.set_host("boost.org"); + conn.connect(eps.begin()->endpoint()); + + BOOST_REQUIRE(!conn.uses_ssl()); + namespace http = boost::beast::http; + + requests::http::headers hd; + auto src = requests::make_source(requests::empty{}); + conn.ropen(http::verb::get, "/", hd, *src, nullptr).dump(); + conn.close(); +} + +BOOST_AUTO_TEST_CASE(amazon_get_https) +{ + asio::io_context ctx; + asio::ssl::context sslctx{asio::ssl::context_base::tlsv12_client}; + sslctx.set_default_verify_paths(); + sslctx.set_verify_mode(asio::ssl::verify_fail_if_no_peer_cert); + + requests::connection conn{ctx, sslctx}; + asio::ip::tcp::resolver res{ctx}; + auto eps = res.resolve("amazon.com", "https"); + conn.set_host("amazon.com"); + conn.connect(eps.begin()->endpoint()); + + BOOST_REQUIRE(conn.uses_ssl()); + namespace http = boost::beast::http; + + requests::http::headers hd; + auto src = requests::make_source(requests::empty{}); + conn.ropen(http::verb::get, "/", hd, *src, nullptr).dump(); + conn.close(); +} + +BOOST_AUTO_TEST_CASE(amazon_get_https_invalid_host) +{ + asio::io_context ctx; + asio::ssl::context sslctx{asio::ssl::context_base::tlsv12_client}; + sslctx.set_default_verify_paths(); + sslctx.set_verify_mode(asio::ssl::verify_fail_if_no_peer_cert); + requests::connection conn{ctx, sslctx}; + asio::ip::tcp::resolver res{ctx}; + auto eps = res.resolve("amazon.com", "https"); + + BOOST_REQUIRE(conn.uses_ssl()); + conn.set_host("gitlab.com"); + boost::system::error_code ec; + conn.connect(eps.begin()->endpoint(), ec); + BOOST_CHECK(ec); +} + +BOOST_COROUTINE_TEST_CASE(async_echo) +{ + requests::connection conn{yield.get_executor()}; + conn.async_connect(endpoint(), yield); + + namespace http = boost::beast::http; + + requests::http::headers hd{{"test", "dummy"}}; + requests::string_source ss{"test-data"}; + auto s = conn.async_ropen(http::verb::post, "/echo", hd, ss, nullptr, yield); + std::string str; + auto dbuf = asio::dynamic_buffer(str); + BOOST_CHECK(s.async_read(dbuf, yield) == 9); + BOOST_CHECK(str == "test-data"); + BOOST_CHECK(s.headers().at("test") == "dummy"); + conn.async_close(yield); +} + +BOOST_COROUTINE_TEST_CASE(async_echo_chunked) +{ + requests::connection conn{yield.get_executor()}; + conn.async_connect(endpoint(), yield); + + namespace http = boost::beast::http; + + requests::http::headers hd{{"test", "dummy"}}; + requests::string_source ss{"test-data"}; + auto s = conn.async_ropen(http::verb::post, "/echo-chunked", hd, ss, nullptr, yield); + std::string str; + auto dbuf = asio::dynamic_buffer(str); + BOOST_CHECK(s.async_read(dbuf, yield) == 9); + BOOST_CHECK(str == "test-data"); + BOOST_CHECK(s.headers().at("test") == "dummy"); + conn.async_close(yield); +} + +BOOST_COROUTINE_TEST_CASE(async_boost_get_http) +{ + requests::connection conn{yield.get_executor()}; + asio::ip::tcp::resolver res{yield.get_executor()}; + auto eps = res.async_resolve("amazon.com", "http", yield); + conn.async_connect(eps.begin()->endpoint(), yield); + + BOOST_REQUIRE(!conn.uses_ssl()); + namespace http = boost::beast::http; + + requests::http::headers hd; + auto src = requests::make_source(requests::empty{}); + conn.async_ropen(http::verb::get, "/echo", hd, *src, nullptr, yield).async_dump(yield); + conn.close(); +} + +BOOST_COROUTINE_TEST_CASE(async_amazon_get_https) +{ + asio::ssl::context sslctx{asio::ssl::context_base::tlsv12_client}; + sslctx.set_verify_mode(asio::ssl::verify_fail_if_no_peer_cert); + sslctx.set_default_verify_paths(); + requests::connection conn{yield.get_executor(), sslctx}; + asio::ip::tcp::resolver res{yield.get_executor()}; + auto eps = res.async_resolve("amazon.com", "https", yield); + conn.async_connect(eps.begin()->endpoint(), yield); + + BOOST_REQUIRE(conn.uses_ssl()); + namespace http = boost::beast::http; + + requests::http::headers hd; + auto src = requests::make_source(requests::empty{}); + conn.async_ropen(http::verb::get, "/echo", hd, *src, nullptr, yield).async_dump(yield); + conn.close(); +} + +BOOST_AUTO_TEST_CASE(dump) +{ + asio::io_context ctx; + requests::connection conn{ctx}; + conn.connect(endpoint()); + + namespace http = boost::beast::http; + + requests::http::headers hd{{"test", "dummy"}}; + requests::string_source ss{"test-data"}; + auto s = conn.ropen(http::verb::post, "/echo", hd, ss, nullptr); + BOOST_CHECK(s.headers().at("test") == "dummy"); + s.dump(); + + conn.close(); +} + +BOOST_COROUTINE_TEST_CASE(async_dump) +{ + requests::connection conn{yield.get_executor()}; + conn.async_connect(endpoint(), yield); + + namespace http = boost::beast::http; + + requests::http::headers hd{{"test", "dummy"}}; + requests::string_source ss{"test-data"}; + auto s = conn.async_ropen(http::verb::post, "/echo", hd, ss, nullptr, yield); + s.async_dump(yield); + BOOST_CHECK(s.headers().at("test") == "dummy"); + conn.async_close(yield); +} + + +BOOST_AUTO_TEST_CASE(dump_chunked) +{ + asio::io_context ctx; + requests::connection conn{ctx}; + conn.connect(endpoint()); + + namespace http = boost::beast::http; + + requests::http::headers hd{{"test", "dummy"}}; + requests::string_source ss{"test-data"}; + auto s = conn.ropen(http::verb::post, "/echo-chunked", hd, ss, nullptr); + BOOST_CHECK(s.headers().at("test") == "dummy"); + s.dump(); + + conn.close(); +} + +BOOST_COROUTINE_TEST_CASE(async_dump_chunked) +{ + requests::connection conn{yield.get_executor()}; + conn.async_connect(endpoint(), yield); + + namespace http = boost::beast::http; + + requests::http::headers hd{{"test", "dummy"}}; + requests::string_source ss{"test-data"}; + auto s = conn.async_ropen(http::verb::post, "/echo-chunked", hd, ss, nullptr, yield); + s.async_dump(yield); + BOOST_CHECK(s.headers().at("test") == "dummy"); + conn.async_close(yield); +} + +BOOST_AUTO_TEST_CASE(upgrade) +{ + asio::io_context ctx; + requests::connection conn{ctx}; + conn.connect(endpoint()); + + namespace http = boost::beast::http; + + requests::http::headers hd{{"test", "dummy"}}; + requests::string_source ss{"test-data"}; + auto s = std::move(conn).upgrade("/ws/echo", hd, nullptr); + BOOST_REQUIRE(s.is_open()); + std::string str; + auto dbuf = asio::dynamic_buffer(str); + BOOST_CHECK_EQUAL(s.read(dbuf), 12); + BOOST_CHECK_EQUAL(str, "Hello World!"); + BOOST_CHECK(!conn.is_open()); +} + + + +BOOST_COROUTINE_TEST_CASE(async_upgrade) +{ + requests::connection conn{yield.get_executor()}; + conn.async_connect(endpoint(), yield); + + namespace http = boost::beast::http; + + requests::http::headers hd{{"test", "dummy"}}; + requests::string_source ss{"test-data"}; + auto s = std::move(conn).async_upgrade("/ws/echo", hd, nullptr, yield); + BOOST_REQUIRE(s.is_open()); + std::string str; + auto dbuf = asio::dynamic_buffer(str); + BOOST_CHECK_EQUAL(s.async_read(dbuf, yield), 12); + BOOST_CHECK_EQUAL(str, "Hello World!"); + BOOST_CHECK(!conn.is_open()); +} + +BOOST_AUTO_TEST_SUITE_END(); diff --git a/test/unit/connection_pool.cpp b/test/unit/connection_pool.cpp new file mode 100644 index 0000000..52878ad --- /dev/null +++ b/test/unit/connection_pool.cpp @@ -0,0 +1,179 @@ +// Copyright (c) 2021 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include + +#include "../coroutine_test_case.hpp" +#include "../fixtures/server.hpp" + +namespace requests = boost::requests; +namespace filesystem = requests::filesystem; +namespace asio = boost::asio; +namespace json = boost::json; +namespace urls = boost::urls; +using boost::system::error_code ; + +BOOST_FIXTURE_TEST_SUITE(connection_pool, test_server); + +BOOST_AUTO_TEST_CASE(sync) +{ + asio::io_context ctx; + requests::connection_pool pool{ctx, 3u}; + pool.lookup("unix://" + std::string(path())); + + BOOST_CHECK(pool.active() == 0u); + auto c1 = pool.borrow_connection(); + BOOST_CHECK(c1.is_open()); + BOOST_CHECK(pool.active() == 1u); + BOOST_CHECK(pool.free() == 0u); + + pool.return_connection(std::move(c1)); + BOOST_CHECK(pool.active() == 1u); + BOOST_CHECK(pool.free() == 1u); + BOOST_CHECK(!c1.is_open()); + + auto c2 = pool.steal_connection(); + BOOST_CHECK(c2.is_open()); + BOOST_CHECK(pool.active() == 0u); + BOOST_CHECK(pool.free() == 0u); + c2.close(); + BOOST_CHECK(!c2.is_open()); + + c1 = pool.borrow_connection(); + BOOST_CHECK(c1.is_open()); + BOOST_CHECK(pool.active() == 1u); + BOOST_CHECK(pool.free() == 0u); + + c2 = pool.borrow_connection(); + BOOST_CHECK(c2.is_open()); + BOOST_CHECK(pool.active() == 2u); + BOOST_CHECK(pool.free() == 0u); + + auto c3 = pool.borrow_connection(); + BOOST_CHECK(c3.is_open()); + BOOST_CHECK(pool.active() == 3u); + BOOST_CHECK(pool.free() == 0u); + + auto c3p = &c3.next_layer(); + decltype(c3p) c4p = nullptr; + std::atomic_bool bl{false}; + std::thread thr { + [&] + { + auto c4 = pool.borrow_connection(); + c4p = &c4.next_layer(); + pool.remove_connection(std::move(c4)); + bl = true; + } + }; + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + BOOST_CHECK(!bl); + pool.return_connection(std::move(c3)); + BOOST_CHECK_EQUAL(pool.active(), 3u); + // BOOST_CHECK_EQUAL(pool.free(), 1u);// race condition + BOOST_CHECK(!c3.is_open()); + + + while (!bl) + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + BOOST_CHECK_EQUAL(pool.active(), 2u); + BOOST_CHECK_EQUAL(pool.free(), 0u); + + + BOOST_CHECK(c3p == c4p); + thr.join(); + + BOOST_CHECK(!c3.is_open()); + BOOST_CHECK_EQUAL(pool.active(), 2u); + BOOST_CHECK_EQUAL(pool.free(), 0u); + + pool.return_connection(std::move(c2)); + + BOOST_CHECK(pool.active() == 2u); + BOOST_CHECK(pool.free() == 1u); +} + +BOOST_COROUTINE_TEST_CASE(async) +{ + requests::connection_pool pool{yield.get_executor(), 2u}; + pool.async_lookup("unix://" + std::string(path()), yield); + + BOOST_CHECK(pool.active() == 0u); + auto c1 = pool.async_borrow_connection(yield); + BOOST_CHECK(c1.is_open()); + BOOST_CHECK(pool.active() == 1u); + BOOST_CHECK(pool.free() == 0u); + + pool.return_connection(std::move(c1)); + BOOST_CHECK(pool.active() == 1u); + BOOST_CHECK(pool.free() == 1u); + BOOST_CHECK(!c1.is_open()); + + auto c2 = pool.async_steal_connection(yield); + BOOST_CHECK(c2.is_open()); + BOOST_CHECK(pool.active() == 0u); + BOOST_CHECK(pool.free() == 0u); + c2.close(); + BOOST_CHECK(!c2.is_open()); + c1 = pool.async_borrow_connection(yield); + BOOST_CHECK(c1.is_open()); + BOOST_CHECK(pool.active() == 1u); + BOOST_CHECK(pool.free() == 0u); + + c2 = pool.async_borrow_connection(yield); + BOOST_CHECK(c2.is_open()); + BOOST_CHECK(pool.active() == 2u); + BOOST_CHECK(pool.free() == 0u); + + auto c2p = &c2.next_layer(); + decltype(c2p) c3p = nullptr; + + + bool bl = false; + pool.async_borrow_connection( + [&](boost::system::error_code ec, requests::connection conn) + { + c3p = &conn.next_layer(); + pool.remove_connection(std::move(conn)); + bl = true; + }); + + + BOOST_CHECK(!bl); + pool.return_connection(std::move(c2)); + BOOST_CHECK_EQUAL(pool.active(), 2u); + // BOOST_CHECK_EQUAL(pool.free(), 1u);// race condition + BOOST_CHECK(!c2.is_open()); + + + while (!bl) + asio::post(yield); + + BOOST_CHECK_EQUAL(pool.active(), 1u); + BOOST_CHECK_EQUAL(pool.free(), 0u); + + + BOOST_CHECK(c3p == c2p); + + BOOST_CHECK(!c2.is_open()); + BOOST_CHECK_EQUAL(pool.active(), 1u); + BOOST_CHECK_EQUAL(pool.free(), 0u); + + pool.return_connection(std::move(c1)); + + BOOST_CHECK(pool.active() == 1u); + BOOST_CHECK(pool.free() == 1u); +} + + +BOOST_AUTO_TEST_SUITE_END(); \ No newline at end of file diff --git a/test/unit/cookie_grammar.cpp b/test/unit/cookie_grammar.cpp new file mode 100644 index 0000000..f73cccd --- /dev/null +++ b/test/unit/cookie_grammar.cpp @@ -0,0 +1,218 @@ +// Copyright (c) 2022 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace boost; + +#include +#include + +BOOST_AUTO_TEST_SUITE(cookie_grammar); + +BOOST_AUTO_TEST_CASE(non_zero_digit) +{ + BOOST_CHECK(urls::grammar::parse("1234", urls::grammar::token_rule(requests::grammar::non_zero_digit))); +} + +BOOST_AUTO_TEST_CASE(any_char_except) +{ + BOOST_CHECK(urls::grammar::parse("1234", urls::grammar::token_rule(requests::grammar::any_char_except_ctl_semicolon))); + + auto res = urls::grammar::parse("1234;", urls::grammar::token_rule(requests::grammar::any_char_except_ctl_semicolon)); + BOOST_CHECK(res == urls::grammar::error::leftover); +} + +BOOST_AUTO_TEST_CASE(path_av) +{ + constexpr auto rule = urls::grammar::tuple_rule( + urls::grammar::squelch(urls::grammar::literal_rule("Path=")), + urls::grammar::token_rule(requests::grammar::any_char_except_ctl_semicolon) + ); + + + BOOST_CHECK(!urls::grammar::parse("1234", rule)); + + auto res = urls::grammar::parse("Path=foobar;", rule); + BOOST_CHECK(res == urls::grammar::error::leftover); + + res = urls::grammar::parse("Path=foobar", rule); + BOOST_CHECK(res.value() == "foobar"); +} + + +BOOST_AUTO_TEST_CASE(sane_cookie_date) +{ + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(784111777)) == + urls::grammar::parse("Sun, 06 Nov 1994 08:49:37 GMT", requests::rfc::sane_cookie_date)); + BOOST_CHECK(urls::grammar::error::mismatch == urls::grammar::parse("Mon, 06 Nov 1994 08:49:37 GMT", requests::rfc::sane_cookie_date)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1696335075)) == + urls::grammar::parse("Tue, 03 Oct 2023 12:11:15 GMT", requests::rfc::sane_cookie_date)); + + BOOST_CHECK(urls::grammar::error::mismatch == + urls::grammar::parse("Sun, 03 Oct 2023 12:11:15 GMT", requests::rfc::sane_cookie_date)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1623233894)) == + urls::grammar::parse("Wed, 09 Jun 2021 10:18:14 GMT", requests::rfc::sane_cookie_date)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == + urls::grammar::parse("Thu, 01 Jan 1970 00:00:01 GMT", requests::rfc::sane_cookie_date)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == + urls::grammar::parse("Thu, 01 Jan 1970 00:00:01 GMT", requests::rfc::sane_cookie_date)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(0)) == + urls::grammar::parse("Thu, 01-Jan-1970 00:00:00 GMT", requests::rfc::sane_cookie_date)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::hours(1)) == + urls::grammar::parse("Thu, 01-Jan-1970 01:00:00 GMT", requests::rfc::sane_cookie_date)); + +} + + +BOOST_AUTO_TEST_CASE(fixed_token) +{ + constexpr auto rule = + requests::grammar::fixed_token_rule<2>(urls::grammar::digit_chars); + + BOOST_CHECK("12" == urls::grammar::parse("12", rule)); + BOOST_CHECK(urls::grammar::error::leftover == urls::grammar::parse("123", rule )); + BOOST_CHECK(urls::grammar::error::need_more == urls::grammar::parse("1", rule )); +} + +BOOST_AUTO_TEST_CASE(domain) +{ + constexpr auto & rule = requests::grammar::domain; + + BOOST_CHECK(!urls::grammar::parse("12", rule)); + BOOST_CHECK("b12" == urls::grammar::parse("b12", rule)); + BOOST_CHECK("foo.bar" == urls::grammar::parse("foo.bar", rule)); + BOOST_CHECK(!urls::grammar::parse("foo.bar;", rule)); + BOOST_CHECK("foo.bar-23-2" == urls::grammar::parse("foo.bar-23-2", rule)); +} + +BOOST_AUTO_TEST_CASE(set_cookie) +{ + auto v1 = requests::parse_set_cookie_field("theme=light"); + BOOST_CHECK(v1); + BOOST_CHECK(v1->name == "theme"); + BOOST_CHECK(v1->value == "light"); + BOOST_CHECK(v1->extensions().empty()); + BOOST_CHECK(v1->expires == std::chrono::system_clock::time_point::max()); + BOOST_CHECK(v1->max_age == std::chrono::seconds::max()); + BOOST_CHECK(v1->domain == ""); + BOOST_CHECK(v1->path == ""); + BOOST_CHECK(!v1->secure); + BOOST_CHECK(!v1->http_only); + + auto v2 = requests::parse_set_cookie_field("sessionToken=abc123; Expires=Wed, 09 Jun 2021 10:18:14 GMT"); + BOOST_CHECK(v2); + BOOST_CHECK(v2->name == "sessionToken"); + BOOST_CHECK(v2->value == "abc123"); + BOOST_CHECK(v2->extensions().empty()); + BOOST_CHECK(v2->expires == std::chrono::system_clock::time_point(std::chrono::seconds(1623233894))); + BOOST_CHECK(v2->max_age == std::chrono::seconds::max()); + BOOST_CHECK(v2->domain == ""); + BOOST_CHECK(v2->path == ""); + BOOST_CHECK(!v2->secure); + BOOST_CHECK(!v2->http_only); + + + auto v4 = requests::parse_set_cookie_field("LSID=DQAAAKEaem_vYg; Path=/accounts; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly"); + BOOST_CHECK(v4); + BOOST_CHECK(v4->name == "LSID"); + BOOST_CHECK(v4->value == "DQAAAKEaem_vYg"); + BOOST_CHECK(v4->extensions().empty()); + BOOST_CHECK(v4->expires == std::chrono::system_clock::time_point(std::chrono::seconds(1610576581))); + BOOST_CHECK(v4->max_age == std::chrono::seconds::max()); + BOOST_CHECK(v4->domain == ""); + BOOST_CHECK(v4->path == "/accounts"); + BOOST_CHECK(v4->secure); + BOOST_CHECK(v4->http_only); + + auto v5 = requests::parse_set_cookie_field("HSID=AYQEVnDKrdst; Domain=.foo.com; Path=/; Expires=Wed, 13 Jan 2021 22:23:01 GMT; HttpOnly"); + BOOST_CHECK(v5); + BOOST_CHECK(v5->name == "HSID"); + BOOST_CHECK(v5->value == "AYQEVnDKrdst"); + BOOST_CHECK(v5->extensions().empty()); + BOOST_CHECK(v5->expires == std::chrono::system_clock::time_point(std::chrono::seconds(1610576581))); + BOOST_CHECK(v5->max_age == std::chrono::seconds::max()); + BOOST_CHECK(v5->domain == "foo.com"); + BOOST_CHECK(v5->path == "/"); + BOOST_CHECK(!v5->secure); + BOOST_CHECK(v5->http_only); + + auto v6 = requests::parse_set_cookie_field("SSID=Ap4PGTEq; Domain=foo.com; Path=/; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly"); + BOOST_CHECK(v6); + BOOST_CHECK(v6->name == "SSID"); + BOOST_CHECK(v6->value == "Ap4PGTEq"); + BOOST_CHECK(v6->extensions().empty()); + BOOST_CHECK(v6->expires == std::chrono::system_clock::time_point(std::chrono::seconds(1610576581))); + BOOST_CHECK(v6->max_age == std::chrono::seconds::max()); + BOOST_CHECK(v6->domain == "foo.com"); + BOOST_CHECK(v6->path == "/"); + BOOST_CHECK(v6->secure); + BOOST_CHECK(v6->http_only); + + auto v7 = requests::parse_set_cookie_field("lu=Rg3vHJZnehYLjVg7qi3bZjzg; Expires=Tue, 15 Jan 2013 21:47:38 GMT; Path=/; Domain=.example.com; HttpOnly"); + BOOST_CHECK(v7); + BOOST_CHECK(v7->name == "lu"); + BOOST_CHECK(v7->value == "Rg3vHJZnehYLjVg7qi3bZjzg"); + BOOST_CHECK(v7->extensions().empty()); + BOOST_CHECK(v7->expires == std::chrono::system_clock::time_point(std::chrono::seconds(1358286458))); + BOOST_CHECK(v7->max_age == std::chrono::seconds::max()); + BOOST_CHECK(v7->domain == "example.com"); + BOOST_CHECK(v7->path == "/"); + BOOST_CHECK(!v7->secure); + BOOST_CHECK(v7->http_only); + + + auto v8 = requests::parse_set_cookie_field("made_write_conn=1295214458; Path=/; Domain=.example.com"); + BOOST_CHECK(v8); + BOOST_CHECK(v8->name == "made_write_conn"); + BOOST_CHECK(v8->value == "1295214458"); + BOOST_CHECK(v8->extensions().empty()); + BOOST_CHECK(v8->expires == std::chrono::system_clock::time_point::max()); + BOOST_CHECK(v8->max_age == std::chrono::seconds::max()); + BOOST_CHECK(v8->domain == "example.com"); + BOOST_CHECK(v8->path == "/"); + BOOST_CHECK(!v8->secure); + BOOST_CHECK(!v8->http_only); + + + auto v9 = requests::parse_set_cookie_field("reg_fb_gate=deleted; Expires=Thu, 01 Jan 1970 00:00:01 GMT; Path=/; Domain=.example.thingy; HttpOnly"); + BOOST_CHECK(v9); + BOOST_CHECK(v9->name == "reg_fb_gate"); + BOOST_CHECK(v9->value == "deleted"); + BOOST_CHECK(v9->extensions().empty()); + BOOST_CHECK(v9->expires == std::chrono::system_clock::time_point(std::chrono::seconds(1))); + BOOST_CHECK(v9->max_age == std::chrono::seconds::max()); + BOOST_CHECK(v9->domain == "example.thingy"); + BOOST_CHECK(v9->path == "/"); + BOOST_CHECK(!v9->secure); + BOOST_CHECK(v9->http_only); + + std::array cks = {*v1, *v2, *v4, *v5, *v6, *v7, *v8, *v9}; + + BOOST_CHECK(requests::detail::make_cookie_field(cks) + == "theme=light; sessionToken=abc123; LSID=DQAAAKEaem_vYg; HSID=AYQEVnDKrdst; SSID=Ap4PGTEq; lu=Rg3vHJZnehYLjVg7qi3bZjzg; made_write_conn=1295214458; reg_fb_gate=deleted" + ); +} + + +BOOST_AUTO_TEST_SUITE_END(); diff --git a/test/cookie_jar.cpp b/test/unit/cookie_jar.cpp similarity index 51% rename from test/cookie_jar.cpp rename to test/unit/cookie_jar.cpp index 73c85e1..49b26f4 100644 --- a/test/cookie_jar.cpp +++ b/test/unit/cookie_jar.cpp @@ -6,33 +6,33 @@ #include #include #include +#include namespace rcp = boost::requests; -#include "doctest.h" -TEST_SUITE_BEGIN("cookie-jar"); +BOOST_AUTO_TEST_SUITE(cookie_jar); -TEST_CASE("public-list") +BOOST_AUTO_TEST_CASE(public_list) { auto l = rcp::default_public_suffix_list(); - CHECK(rcp::is_public_suffix("com", l)); - CHECK(rcp::is_public_suffix("org", l)); - CHECK(!rcp::is_public_suffix("boost.org", l)); - CHECK(!rcp::is_public_suffix("x.yz.bd", l)); - CHECK(!rcp::is_public_suffix("city.kobe.jp", l)); + BOOST_CHECK(rcp::is_public_suffix("com", l)); + BOOST_CHECK(rcp::is_public_suffix("org", l)); + BOOST_CHECK(!rcp::is_public_suffix("boost.org", l)); + BOOST_CHECK(!rcp::is_public_suffix("x.yz.bd", l)); + BOOST_CHECK(!rcp::is_public_suffix("city.kobe.jp", l)); } -TEST_CASE("domain-match") +BOOST_AUTO_TEST_CASE(domain_match) { - CHECK(rcp::domain_match("foo.com", "com")); - CHECK(rcp::domain_match("com", "com")); - CHECK(!rcp::domain_match("foocom", "com")); + BOOST_CHECK(rcp::domain_match("foo.com", "com")); + BOOST_CHECK(rcp::domain_match("com", "com")); + BOOST_CHECK(!rcp::domain_match("foocom", "com")); } -TEST_CASE("cookie-jar") +BOOST_AUTO_TEST_CASE(cookie_jar) { rcp::cookie_jar j; j.set(rcp::parse_set_cookie_field("userid=sup3r4n0m-us3r-1d3nt1f13r").value(), "boost.org"); @@ -40,7 +40,7 @@ TEST_CASE("cookie-jar") j.set(rcp::parse_set_cookie_field("doc=foobar; Max-Age=0").value(), "boost.org"); std::this_thread::sleep_for(std::chrono::seconds(1)); - CHECK(j.get("boost.org") == "lib=requests; userid=sup3r4n0m-us3r-1d3nt1f13r"); + BOOST_CHECK(j.get("boost.org") == "lib=requests; userid=sup3r4n0m-us3r-1d3nt1f13r"); } @@ -48,5 +48,5 @@ TEST_CASE("cookie-jar") -TEST_SUITE_END(); +BOOST_AUTO_TEST_SUITE_END(); diff --git a/test/detail/condition_variable.cpp b/test/unit/detail/condition_variable.cpp similarity index 77% rename from test/detail/condition_variable.cpp rename to test/unit/detail/condition_variable.cpp index 65f2317..6734a91 100644 --- a/test/detail/condition_variable.cpp +++ b/test/unit/detail/condition_variable.cpp @@ -9,15 +9,14 @@ #include #include -#include "../string_maker.hpp" +#include - -TEST_SUITE_BEGIN("condition_variable"); +BOOST_AUTO_TEST_SUITE(condition_variable); namespace asio = boost::asio; namespace requests = boost::requests; -TEST_CASE("sync") +BOOST_AUTO_TEST_CASE(sync) { asio::thread_pool ctx; std::thread thr; @@ -34,26 +33,26 @@ TEST_CASE("sync") pos = 1; cv.wait(lock); pos = 2; - CHECK_THROWS(cv.wait(lock)); + BOOST_CHECK_THROW(cv.wait(lock), boost::system::system_error); pos = 3; }}; - CHECK(pos == 0); + BOOST_CHECK(pos == 0); std::this_thread::sleep_for(std::chrono::milliseconds(50)); cv.notify_one(); std::this_thread::sleep_for(std::chrono::milliseconds(50)); - CHECK(pos == 1); + BOOST_CHECK(pos == 1); cv.notify_all(); std::this_thread::sleep_for(std::chrono::milliseconds(50)); - CHECK(pos == 2); + BOOST_CHECK(pos == 2); } std::this_thread::sleep_for(std::chrono::milliseconds(50)); - CHECK(pos == 3); + BOOST_CHECK(pos == 3); thr.join(); ctx.join(); } -TEST_SUITE_END(); \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END(); \ No newline at end of file diff --git a/test/fixed_token_rule.cpp b/test/unit/fixed_token_rule.cpp similarity index 50% rename from test/fixed_token_rule.cpp rename to test/unit/fixed_token_rule.cpp index 63b84cc..083ffd3 100644 --- a/test/fixed_token_rule.cpp +++ b/test/unit/fixed_token_rule.cpp @@ -9,28 +9,27 @@ #include #include -#include "doctest.h" -#include "string_maker.hpp" +#include -TEST_SUITE_BEGIN("grammar"); +BOOST_AUTO_TEST_SUITE(grammar); -TEST_CASE("fixed-token-rule") +BOOST_AUTO_TEST_CASE(fixed_token_rule) { namespace br = boost::requests; namespace ug = boost::urls::grammar; auto tk1 = br::grammar::fixed_token_rule<3u>(ug::alpha_chars); - CHECK(ug::parse("ab", tk1) == ug::error::need_more); - CHECK(ug::parse("abc", tk1) == "abc"); - CHECK(ug::parse("abcd", tk1) == ug::error::leftover); + BOOST_CHECK(ug::parse("ab", tk1) == ug::error::need_more); + BOOST_CHECK(ug::parse("abc", tk1) == "abc"); + BOOST_CHECK(ug::parse("abcd", tk1) == ug::error::leftover); auto tk2 = br::grammar::fixed_token_rule<3u, 5u>(ug::alpha_chars); - CHECK(ug::parse("ab", tk2) == ug::error::need_more); - CHECK(ug::parse("abc", tk2) == "abc"); - CHECK(ug::parse("abcd", tk2) == "abcd"); - CHECK(ug::parse("abcde", tk2) == "abcde"); - CHECK(ug::parse("abcdef", tk2) == ug::error::leftover); + BOOST_CHECK(ug::parse("ab", tk2) == ug::error::need_more); + BOOST_CHECK(ug::parse("abc", tk2) == "abc"); + BOOST_CHECK(ug::parse("abcd", tk2) == "abcd"); + BOOST_CHECK(ug::parse("abcde", tk2) == "abcde"); + BOOST_CHECK(ug::parse("abcdef", tk2) == ug::error::leftover); } -TEST_SUITE_END(); \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END(); \ No newline at end of file diff --git a/test/form.cpp b/test/unit/form.cpp similarity index 82% rename from test/form.cpp rename to test/unit/form.cpp index 8191f7f..8b71b27 100644 --- a/test/form.cpp +++ b/test/unit/form.cpp @@ -12,8 +12,7 @@ #include #include -#include "doctest.h" -#include "string_maker.hpp" +#include using namespace boost; @@ -36,8 +35,8 @@ static auto cmp = "test-string2\r\n" "--01234567890123456789012345678901--"; -TEST_SUITE_BEGIN("form"); -TEST_CASE("multi-part") +BOOST_AUTO_TEST_SUITE(form); +BOOST_AUTO_TEST_CASE(multi_part) { filesystem::path pt = filesystem::temp_directory_path() / "form-test.txt"; std::ofstream(pt.string()) << "test-string2"; @@ -48,7 +47,7 @@ TEST_CASE("multi-part") requests::multi_part_form_source bd{mpf}; std::copy(std::begin(boundary), std::end(boundary) - 1, bd.boundary_and_type.begin() + 30); - REQUIRE(bd.current == bd.mpf.storage.cbegin()); + BOOST_REQUIRE(bd.current == bd.mpf.storage.cbegin()); auto sz = bd.size(); std::string data; @@ -58,15 +57,15 @@ TEST_CASE("multi-part") char buf[4096]; boost::system::error_code ec; auto rr = bd.read_some(&buf[0], sizeof(buf), ec); - REQUIRE(ec == boost::system::error_code{}); + BOOST_REQUIRE(ec == boost::system::error_code{}); data.append(buf, rr.first); if (!rr.second) break; } - CHECK(sz == data.size()); - CHECK(data.size() == string_view(cmp).size()); - CHECK(data == string_view(cmp)); + BOOST_CHECK(sz == data.size()); + BOOST_CHECK(data.size() == string_view(cmp).size()); + BOOST_CHECK(data == string_view(cmp)); } -TEST_SUITE_END(); \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END(); \ No newline at end of file diff --git a/test/unit/keep_alive.cpp b/test/unit/keep_alive.cpp new file mode 100644 index 0000000..1377ff9 --- /dev/null +++ b/test/unit/keep_alive.cpp @@ -0,0 +1,40 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include + + +BOOST_AUTO_TEST_SUITE(keep_alive); + +BOOST_AUTO_TEST_CASE(parse) +{ + namespace br = boost::requests; + + const auto epoch = std::chrono::system_clock::time_point::min(); + + BOOST_CHECK(br::parse_keep_alive_field("max=42", epoch).value().max == 42); + BOOST_CHECK(br::parse_keep_alive_field("max=42", epoch).value().timeout + == std::chrono::system_clock::time_point::max()); + + BOOST_CHECK(br::parse_keep_alive_field("timeout=23", epoch).value().max + == std::numeric_limits::max()); + BOOST_CHECK(br::parse_keep_alive_field("timeout=23", epoch).value().timeout + == std::chrono::system_clock::time_point(std::chrono::seconds(23))); + + BOOST_CHECK(br::parse_keep_alive_field("max=12, timeout=34", epoch).value().max == 12); + BOOST_CHECK(br::parse_keep_alive_field("max=12, timeout=34", epoch).value().timeout + == std::chrono::system_clock::time_point(std::chrono::seconds(34))); + + BOOST_CHECK(br::parse_keep_alive_field("timeout=12, max=34", epoch).value().max == 34); + BOOST_CHECK(br::parse_keep_alive_field("timeout=12, max=34", epoch).value().timeout + == std::chrono::system_clock::time_point(std::chrono::seconds(12))); + +} + +BOOST_AUTO_TEST_SUITE_END(); diff --git a/test/unit/link.cpp b/test/unit/link.cpp new file mode 100644 index 0000000..8e558c1 --- /dev/null +++ b/test/unit/link.cpp @@ -0,0 +1,117 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include + +#include + + +BOOST_AUTO_TEST_SUITE(rfc_link); + +using namespace boost; + +namespace br = boost::requests; + +BOOST_AUTO_TEST_CASE(single) +{ + auto txt = R"()"; + auto lst = br::parse_link_field(txt); + BOOST_CHECK_MESSAGE(!lst.has_error(), lst.error()); + BOOST_REQUIRE(lst->size() == 1u); + BOOST_CHECK((*lst->begin()).attributes.empty()); + BOOST_CHECK((*lst->begin()).url == urls::parse_uri("https://one.example.com")); +} + +BOOST_AUTO_TEST_CASE(multiple) +{ + auto txt = R"(, )"; + auto lst = br::parse_link_field(txt); + BOOST_CHECK_MESSAGE(!lst.has_error(), lst.error()); + + BOOST_REQUIRE(lst->size() == 2u); + + BOOST_CHECK((*lst->begin()).attributes.empty()); + BOOST_CHECK((*lst->begin()).url == urls::parse_uri("https://one.example.com").value()); + + BOOST_CHECK((*std::next(lst->begin())).attributes.empty()); + BOOST_CHECK((*std::next(lst->begin())).url == urls::parse_uri("https://two.example.com").value()); +} + + +BOOST_AUTO_TEST_CASE(single_with_param) +{ + auto txt = R"(; foobar)"; + auto lst = br::parse_link_field(txt); + BOOST_CHECK_MESSAGE(!lst.has_error(), lst.error()); + + BOOST_CHECK(lst->size() == 1u); + + auto val = *lst->begin(); + BOOST_CHECK(val.attributes.begin() != val.attributes.end()); + + std::vector vec; + vec.assign(val.attributes.begin(), val.attributes.end()); + BOOST_CHECK(vec.size() == 1u); + if (!vec.empty()) + { + BOOST_CHECK(val.url == urls::parse_uri("https://one.example.com")); + BOOST_CHECK(vec.front().key == "foobar"); + BOOST_CHECK(vec.front().value.empty()); + } +} + +BOOST_AUTO_TEST_CASE(single_with_param_eq) +{ + auto txt = R"(; xyz=ctl)"; + auto lst = br::parse_link_field(txt); + BOOST_CHECK_MESSAGE(!lst.has_error(), lst.error()); + + BOOST_CHECK(lst->size() == 1u); + auto val = *lst->begin(); + BOOST_CHECK(val.attributes.begin() != val.attributes.end()); + + std::vector vec; + vec.assign(val.attributes.begin(), val.attributes.end()); + BOOST_CHECK(vec.size() == 1u); + if (!vec.empty()) + { + BOOST_CHECK(val.url == urls::parse_uri("https://one.example.com")); + BOOST_CHECK(vec.front().key == "xyz"); + BOOST_CHECK(vec.front().value == "ctl"); + } +} + + +BOOST_AUTO_TEST_CASE(single_with_param_rel) +{ + auto txt = R"(; rel="preconnect")"; + auto lst = br::parse_link_field(txt); + BOOST_CHECK_MESSAGE(!lst.has_error(), lst.error()); + + + BOOST_CHECK(lst->size() == 1u); + auto val = *lst->begin(); + BOOST_CHECK(val.attributes.begin() != val.attributes.end()); + + auto rel = val.rel(); + BOOST_CHECK_MESSAGE(!rel.has_error(), rel.error()); + BOOST_CHECK(rel->size() == 1); +} + +BOOST_AUTO_TEST_CASE(multiple_with_param) +{ + auto txt = + R"(; rel="preconnect next", )" + R"(; rel="preconnect", )" + R"(; rel="preconnect")"; + auto lst = br::parse_link_field(txt); + BOOST_CHECK_MESSAGE(!lst.has_error(), lst.error()); +} + +BOOST_AUTO_TEST_SUITE_END(); \ No newline at end of file diff --git a/test/unit/quoted_string.cpp b/test/unit/quoted_string.cpp new file mode 100644 index 0000000..c076d3c --- /dev/null +++ b/test/unit/quoted_string.cpp @@ -0,0 +1,27 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + + +#include +#include +#include + + +BOOST_AUTO_TEST_SUITE(rfc_quoted_string); + +BOOST_AUTO_TEST_CASE(quoted_string) +{ + namespace br = boost::requests; + namespace ug = boost::urls::grammar; + + BOOST_CHECK(ug::parse("\"foobar\"", br::rfc::quoted_string) == "\"foobar\""); + BOOST_CHECK(ug::parse("foobar", br::rfc::quoted_string) == ug::error::mismatch); + BOOST_CHECK(ug::parse("\"foo\\\"bar\"", br::rfc::quoted_string) == "\"foo\\\"bar\""); + BOOST_CHECK(br::rfc::unquote_string(ug::parse("\"foo\\\"bar\"", br::rfc::quoted_string).value())== "foo\"bar"); +} + +BOOST_AUTO_TEST_SUITE_END(); diff --git a/test/raw_string.cpp b/test/unit/raw_string.cpp similarity index 73% rename from test/raw_string.cpp rename to test/unit/raw_string.cpp index 6ffe5c5..1f5ba87 100644 --- a/test/raw_string.cpp +++ b/test/unit/raw_string.cpp @@ -6,6 +6,7 @@ // #include +#include #include #include #include @@ -13,12 +14,9 @@ #include #include -#include "doctest.h" -#include "string_maker.hpp" +BOOST_AUTO_TEST_SUITE(grammar); -TEST_SUITE_BEGIN("grammar"); - -TEST_CASE("fixed-token-rule") +BOOST_AUTO_TEST_CASE(raw_string_rule) { namespace br = boost::requests; namespace ug = boost::urls::grammar; @@ -27,9 +25,8 @@ TEST_CASE("fixed-token-rule") ug::squelch(ug::literal_rule("=")), ug::token_rule(ug::digit_chars)); - CHECK(ug::parse("x=1234", rl).value() == std::make_tuple("x", "1234")); - CHECK(ug::parse("x=1234", br::grammar::raw_string(rl)) == "x=1234"); - + BOOST_CHECK(ug::parse("x=1234", rl).value() == std::make_tuple("x", "1234")); + BOOST_CHECK(ug::parse("x=1234", br::grammar::raw_string(rl)) == "x=1234"); } -TEST_SUITE_END(); \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END(); diff --git a/test/unit/redirect.cpp b/test/unit/redirect.cpp new file mode 100644 index 0000000..db73967 --- /dev/null +++ b/test/unit/redirect.cpp @@ -0,0 +1,104 @@ +// Copyright (c) 2022 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +BOOST_AUTO_TEST_SUITE(redirect); + +BOOST_AUTO_TEST_CASE(none) +{ + using namespace boost::requests; + using namespace boost::urls; + BOOST_CHECK(!should_redirect(redirect_mode::none, parse_uri("http://boost.org").value(), parse_uri("http://boost.org/index.html").value())); + BOOST_CHECK(!should_redirect(redirect_mode::none, parse_uri("http://boost.org").value(), parse_uri_reference("/take-my-money.com").value())); +} + + +BOOST_AUTO_TEST_CASE(endpoint) +{ + using namespace boost::requests; + using namespace boost::urls; + BOOST_CHECK(should_redirect(redirect_mode::endpoint, parse_uri("http://boost.org").value(), parse_uri("http://boost.org/index.html").value())); + BOOST_CHECK(!should_redirect(redirect_mode::endpoint, parse_uri("http://boost.org").value(), parse_uri("https://boost.org").value())); + BOOST_CHECK(!should_redirect(redirect_mode::endpoint, parse_uri("http://boost.org").value(), parse_uri("http://www.boost.org").value())); + BOOST_CHECK(!should_redirect(redirect_mode::endpoint, parse_uri("http://boost.org").value(), parse_uri("http://boost.org:433").value())); + BOOST_CHECK(should_redirect(redirect_mode::endpoint, parse_uri("http://boost.org").value(), parse_uri("https://boost.org:80").value())); + BOOST_CHECK(should_redirect(redirect_mode::endpoint, parse_uri("http://boost.org").value(), parse_uri("http://boost.org:80").value())); + BOOST_CHECK(should_redirect(redirect_mode::endpoint, parse_uri("http://boost.org").value(), parse_uri("ws://boost.org:80").value())); + BOOST_CHECK(should_redirect(redirect_mode::endpoint, parse_uri("http://boost.org").value(), parse_uri("foo://boost.org:80").value())); + BOOST_CHECK(should_redirect(redirect_mode::endpoint, parse_uri("http://boost.org").value(), parse_uri_reference("/take-my-money.com").value())); +} + +BOOST_AUTO_TEST_CASE(domain) +{ + using namespace boost::requests; + using namespace boost::urls; + BOOST_CHECK(should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri("http://boost.org/index.html").value())); + BOOST_CHECK(should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri("https://boost.org").value())); + BOOST_CHECK(!should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri("http://www.boost.org").value())); + BOOST_CHECK(!should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri("http://fakeboost.org").value())); + BOOST_CHECK(!should_redirect(redirect_mode::domain, parse_uri("http://www.boost.org").value(), parse_uri("http://boost.org").value())); + BOOST_CHECK(should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri("http://boost.org:433").value())); + BOOST_CHECK(should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri("https://boost.org:80").value())); + BOOST_CHECK(should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri("http://boost.org:80").value())); + BOOST_CHECK(should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri("ws://boost.org:80").value())); + BOOST_CHECK(should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri("foo://boost.org:80").value())); + BOOST_CHECK(should_redirect(redirect_mode::domain, parse_uri("http://boost.org").value(), parse_uri_reference("/take-my-money.com").value())); + } + +BOOST_AUTO_TEST_CASE(subdomain) +{ + using namespace boost::requests; + using namespace boost::urls; + BOOST_CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("http://boost.org/index.html").value())); + BOOST_CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("https://boost.org").value())); + BOOST_CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("http://www.boost.org").value())); + BOOST_CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("https://www.boost.org").value())); + BOOST_CHECK(!should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("http://fakeboost.org").value())); + BOOST_CHECK(!should_redirect(redirect_mode::subdomain, parse_uri("http://www.boost.org").value(), parse_uri("http://boost.org").value())); + BOOST_CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("http://boost.org:433").value())); + BOOST_CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("https://boost.org:80").value())); + BOOST_CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("http://boost.org:80").value())); + BOOST_CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("ws://boost.org:80").value())); + BOOST_CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri("foo://boost.org:80").value())); + BOOST_CHECK(should_redirect(redirect_mode::subdomain, parse_uri("http://boost.org").value(), parse_uri_reference("/take-my-money.com").value())); +} + +BOOST_AUTO_TEST_CASE(private_domain) +{ + using namespace boost::requests; + using namespace boost::urls; + BOOST_CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("http://boost.org/index.html").value())); + BOOST_CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("https://boost.org").value())); + BOOST_CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("http://www.boost.org").value())); + BOOST_CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("https://www.boost.org").value())); + BOOST_CHECK(!should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("http://fakeboost.org").value())); + BOOST_CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://www.boost.org").value(), parse_uri("http://boost.org").value())); + BOOST_CHECK(!should_redirect(redirect_mode::private_domain, parse_uri("http://www.boost.org").value(), parse_uri("http://org").value())); + BOOST_CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("http://boost.org:433").value())); + BOOST_CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("https://boost.org:80").value())); + BOOST_CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("http://boost.org:80").value())); + BOOST_CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("ws://boost.org:80").value())); + BOOST_CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri("foo://boost.org:80").value())); + BOOST_CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://doc.boost.org").value(), parse_uri("http://docs.boost.org").value())); + BOOST_CHECK(!should_redirect(redirect_mode::private_domain, parse_uri("http://doc.boost.org").value(), parse_uri("http://fakeboost.org").value())); + BOOST_CHECK(!should_redirect(redirect_mode::private_domain, parse_uri("http://doc.boost.org").value(), parse_uri("http://ost.org").value())); + BOOST_CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://api.boost.org").value(), parse_uri("http://pi.boost.org").value())); + BOOST_CHECK(should_redirect(redirect_mode::private_domain, parse_uri("http://boost.org").value(), parse_uri_reference("/take-my-money.com").value())); +} + + +BOOST_AUTO_TEST_CASE(any) +{ + using namespace boost::requests; + using namespace boost::urls; + BOOST_CHECK(should_redirect(redirect_mode::any, parse_uri("http://boost.org").value(), parse_uri("https://take-my-money.com").value())); + BOOST_CHECK(should_redirect(redirect_mode::any, parse_uri("http://boost.org").value(), parse_uri_reference("/take-my-money.com").value())); +} + + +BOOST_AUTO_TEST_SUITE_END(); \ No newline at end of file diff --git a/test/unit/request_stream.cpp b/test/unit/request_stream.cpp new file mode 100644 index 0000000..fc15413 --- /dev/null +++ b/test/unit/request_stream.cpp @@ -0,0 +1,281 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include +#include +#include +#include + +#include "../coroutine_test_case.hpp" +#include "../fixtures/server.hpp" + +namespace requests = boost::requests; +namespace filesystem = requests::filesystem; +namespace asio = boost::asio; +namespace json = boost::json; +namespace urls = boost::urls; +using boost::system::error_code ; + +BOOST_FIXTURE_TEST_SUITE(request_stream, test_server); + +BOOST_AUTO_TEST_CASE(request_connection) +{ + namespace http = boost::beast::http; + + asio::io_context ctx; + requests::connection conn{ctx}; + conn.connect(endpoint()); + + auto res = requests::request_stream( + conn, + http::verb::get, + "/redirect/3", + "test-data", + { + /*.headers = */ {{"test", "dummy"}}, + }); + + auto & s = res.first; + auto & h = res.second; + + std::string str; + auto dbuf = asio::dynamic_buffer(str); + BOOST_CHECK_EQUAL(s.read(dbuf), 12); + BOOST_CHECK(str == "Hello World!"); + BOOST_CHECK(s.headers().at("test") == "dummy"); + + BOOST_CHECK(h.size() == 3); +} + + +BOOST_AUTO_TEST_CASE(request_connection_too_many_redirects) +{ + namespace http = boost::beast::http; + + asio::io_context ctx; + requests::connection conn{ctx}; + conn.connect(endpoint()); + + boost::system::error_code ec; + + auto res = requests::request_stream( + conn, + http::verb::get, + "/redirect/3", + "test-data", + { + /*.headers = */ {{"test", "dummy"}}, + {true, requests::redirect_mode::private_domain, 2} + }, ec); + + auto & h = res.second; + BOOST_CHECK(ec == requests::error::too_many_redirects); + BOOST_CHECK(h.size() == 2); +} + +BOOST_AUTO_TEST_CASE(request_connection_forbidden_redirect) +{ + namespace http = boost::beast::http; + + asio::io_context ctx; + requests::connection conn{ctx}; + conn.connect(endpoint()); + + boost::system::error_code ec; + + auto res = requests::request_stream( + conn, + http::verb::get, + "/redirect/3", + "test-data", + {/*.headers = */ {{"test", "dummy"}}, + {true, requests::redirect_mode::none, 12} + }, ec); + + auto & s = res.first; + auto & h = res.second; + BOOST_CHECK_MESSAGE(ec == requests::error::forbidden_redirect, ec.what()); + BOOST_CHECK(h.size() == 1); +} + +BOOST_AUTO_TEST_CASE(request_connection_invalid_redirect) +{ + namespace http = boost::beast::http; + + asio::io_context ctx; + requests::connection conn{ctx}; + conn.connect(endpoint()); + + boost::system::error_code ec; + + auto res = requests::request_stream( + conn, + http::verb::get, + "/invalid-redirect", + "test-data", + {/*.headers = */ {{"test", "dummy"}}}, ec); + + auto & s = res.first; + auto & h = res.second; + BOOST_CHECK_MESSAGE(ec == requests::error::invalid_redirect, ec.what()); + BOOST_CHECK(h.size() == 1); +} + + +BOOST_AUTO_TEST_CASE(request_connection_offsite) +{ + namespace http = boost::beast::http; + + asio::io_context ctx; + requests::connection conn{ctx}; + conn.connect(endpoint()); + + boost::system::error_code ec; + + auto res = requests::request_stream( + conn, + http::verb::get, + "/boost-redirect", + "test-data", + {/*.headers = */ {{"test", "dummy"}}}, ec); + + auto & s = res.first; + auto & h = res.second; + BOOST_CHECK_MESSAGE(ec == requests::error::forbidden_redirect, ec.what()); + BOOST_CHECK(h.size() == 1); +} + + +BOOST_COROUTINE_TEST_CASE(async_request_connection) +{ + namespace http = boost::beast::http; + + requests::connection conn{yield.get_executor()}; + conn.async_connect(endpoint(), yield); + + auto res = requests::async_request_stream( + conn, + http::verb::get, + "/redirect/3", + "test-data", + { + /*.headers = */ {{"test", "dummy"}}, + }, yield); + + auto & s = std::get<0>(res); + auto & h = std::get<1>(res); + + std::string str; + auto dbuf = asio::dynamic_buffer(str); + BOOST_CHECK_EQUAL(s.async_read(dbuf, yield), 12); + BOOST_CHECK(str == "Hello World!"); + BOOST_CHECK(s.headers().at("test") == "dummy"); + + BOOST_CHECK(h.size() == 3); +} + + +BOOST_COROUTINE_TEST_CASE(async_request_connection_too_many_redirects) +{ + namespace http = boost::beast::http; + + requests::connection conn{yield.get_executor()}; + conn.async_connect(endpoint(), yield); + + boost::system::error_code ec; + + auto res = requests::async_request_stream( + conn, + http::verb::get, + "/redirect/3", + "test-data", + {/*.headers = */ {{"test", "dummy"}}, + {true, requests::redirect_mode::private_domain, 2} + }, yield[ec]); + + auto & h = std::get<1>(res); + BOOST_CHECK(ec == requests::error::too_many_redirects); + BOOST_CHECK(h.size() == 2); +} + +BOOST_COROUTINE_TEST_CASE(async_request_connection_forbidden_redirect) +{ + namespace http = boost::beast::http; + + requests::connection conn{yield.get_executor()}; + conn.async_connect(endpoint(), yield); + + boost::system::error_code ec; + + auto res = requests::async_request_stream( + conn, + http::verb::get, + "/redirect/3", + "test-data", + {/*.headers = */ {{"test", "dummy"}}, + {true, requests::redirect_mode::none, 12} + }, yield[ec]); + + auto & s = std::get<0>(res); + auto & h = std::get<1>(res); + + BOOST_CHECK_MESSAGE(ec == requests::error::forbidden_redirect, ec.what()); + BOOST_CHECK_EQUAL(h.size(), 1); +} + +BOOST_COROUTINE_TEST_CASE(async_request_connection_invalid_redirect) +{ + namespace http = boost::beast::http; + + requests::connection conn{yield.get_executor()}; + conn.async_connect(endpoint(), yield); + + boost::system::error_code ec; + + auto res = requests::async_request_stream( + conn, + http::verb::get, + "/invalid-redirect", + "test-data", + {/*.headers = */ {{"test", "dummy"}}}, yield[ec]); + + auto & s = std::get<0>(res); + auto & h = std::get<1>(res); + + BOOST_CHECK_MESSAGE(ec == requests::error::invalid_redirect, ec.what()); + BOOST_CHECK(h.size() == 1); +} + + +BOOST_COROUTINE_TEST_CASE(async_request_connection_offsite) +{ + namespace http = boost::beast::http; + + requests::connection conn{yield.get_executor()}; + conn.async_connect(endpoint(), yield); + + boost::system::error_code ec; + + auto res = requests::async_request_stream( + conn, + http::verb::get, + "/boost-redirect", + "test-data", + {/*.headers = */ {{"test", "dummy"}}}, yield[ec]); + + auto & s = std::get<0>(res); + auto & h = std::get<1>(res); + + BOOST_CHECK_MESSAGE(ec == requests::error::forbidden_redirect, ec.what()); + BOOST_CHECK(h.size() == 1); +} + +// pool & session will be tested in the httpbin tests. + +BOOST_AUTO_TEST_SUITE_END(); diff --git a/test/unit/rfc.cpp b/test/unit/rfc.cpp new file mode 100644 index 0000000..9cba022 --- /dev/null +++ b/test/unit/rfc.cpp @@ -0,0 +1,153 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include + + +BOOST_AUTO_TEST_SUITE(rfc); + +using namespace boost; + +BOOST_AUTO_TEST_CASE(date_1123) +{ + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(784111777)) == + urls::grammar::parse("Sun, 06 Nov 1994 08:49:37 GMT", requests::rfc::date_1123)); + BOOST_CHECK(urls::grammar::error::mismatch == urls::grammar::parse("Mon, 06 Nov 1994 08:49:37 GMT", requests::rfc::date_1123)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1696335075)) == + urls::grammar::parse("Tue, 03 Oct 2023 12:11:15 GMT", requests::rfc::date_1123)); + + BOOST_CHECK(urls::grammar::error::mismatch == + urls::grammar::parse("Sun, 03 Oct 2023 12:11:15 GMT", requests::rfc::date_1123)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1623233894)) == + urls::grammar::parse("Wed, 09 Jun 2021 10:18:14 GMT", requests::rfc::date_1123)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == + urls::grammar::parse("Thu, 01 Jan 1970 00:00:01 GMT", requests::rfc::date_1123)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == + urls::grammar::parse("Thu, 01 Jan 1970 00:00:01 GMT", requests::rfc::date_1123)); + +} + +BOOST_AUTO_TEST_CASE(date_850) +{ + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(784111777)) == + urls::grammar::parse("Sunday, 06-Nov-1994 08:49:37 GMT", requests::rfc::date_850)); + BOOST_CHECK(urls::grammar::error::mismatch == urls::grammar::parse("Mon, 06 Nov 1994 08:49:37 GMT", requests::rfc::date_850)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1696335075)) == + urls::grammar::parse("Tuesday, 03-Oct-2023 12:11:15 GMT", requests::rfc::date_850)); + + BOOST_CHECK(urls::grammar::error::mismatch == + urls::grammar::parse("Sunday, 03-Oct-2023 12:11:15 GMT", requests::rfc::date_850)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1623233894)) == + urls::grammar::parse("Wednesday, 09-Jun-2021 10:18:14 GMT", requests::rfc::date_850)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == + urls::grammar::parse("Thursday, 01-Jan-1970 00:00:01 GMT", requests::rfc::date_850)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == + urls::grammar::parse("Thursday, 01-Jan-1970 00:00:01 GMT", requests::rfc::date_850)); + +} + + + +BOOST_AUTO_TEST_CASE(date_asctime) +{ + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(784111777)) == + urls::grammar::parse("Sun Nov 6 08:49:37 1994", requests::rfc::date_asctime)); + BOOST_CHECK(urls::grammar::error::mismatch == urls::grammar::parse("Mon, 06 Nov 1994 08:49:37 GMT", requests::rfc::date_asctime)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1696335075)) == + urls::grammar::parse("Tue Oct 03 12:11:15 2023", requests::rfc::date_asctime)); + + BOOST_CHECK(urls::grammar::error::mismatch == + urls::grammar::parse("Sun Oct 03 12:11:15 2023", requests::rfc::date_asctime)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1623233894)) == + urls::grammar::parse("Wed Jun 9 10:18:14 2021", requests::rfc::date_asctime)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == + urls::grammar::parse("Thu Jan 1 00:00:01 1970", requests::rfc::date_asctime)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == + urls::grammar::parse("Thu Jan 01 00:00:01 1970", requests::rfc::date_asctime)); + +} + + +BOOST_AUTO_TEST_CASE(http_date) +{ + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(784111777)) == + urls::grammar::parse("Sun, 06 Nov 1994 08:49:37 GMT", requests::rfc::http_date)); + BOOST_CHECK(urls::grammar::error::mismatch == urls::grammar::parse("Mon, 06 Nov 1994 08:49:37 GMT", requests::rfc::http_date)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1696335075)) == + urls::grammar::parse("Tue, 03 Oct 2023 12:11:15 GMT", requests::rfc::http_date)); + + BOOST_CHECK(urls::grammar::error::mismatch == + urls::grammar::parse("Sun, 03 Oct 2023 12:11:15 GMT", requests::rfc::http_date)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1623233894)) == + urls::grammar::parse("Wed, 09 Jun 2021 10:18:14 GMT", requests::rfc::http_date)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == + urls::grammar::parse("Thu, 01 Jan 1970 00:00:01 GMT", requests::rfc::http_date)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == + urls::grammar::parse("Thu, 01 Jan 1970 00:00:01 GMT", requests::rfc::http_date)); + + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(784111777)) == + urls::grammar::parse("Sunday, 06-Nov-1994 08:49:37 GMT", requests::rfc::http_date)); + BOOST_CHECK(urls::grammar::error::mismatch == urls::grammar::parse("Mon, 06 Nov 1994 08:49:37 GMT", requests::rfc::http_date)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1696335075)) == + urls::grammar::parse("Tuesday, 03-Oct-2023 12:11:15 GMT", requests::rfc::http_date)); + + BOOST_CHECK(urls::grammar::error::mismatch == + urls::grammar::parse("Sunday, 03-Oct-2023 12:11:15 GMT", requests::rfc::http_date)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1623233894)) == + urls::grammar::parse("Wednesday, 09-Jun-2021 10:18:14 GMT", requests::rfc::http_date)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == + urls::grammar::parse("Thursday, 01-Jan-1970 00:00:01 GMT", requests::rfc::http_date)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == + urls::grammar::parse("Thursday, 01-Jan-1970 00:00:01 GMT", requests::rfc::http_date)); + + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(784111777)) == + urls::grammar::parse("Sun Nov 6 08:49:37 1994", requests::rfc::http_date)); + BOOST_CHECK(urls::grammar::error::mismatch == urls::grammar::parse("Mon, 06 Nov 1994 08:49:37 GMT", requests::rfc::http_date)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1696335075)) == + urls::grammar::parse("Tue Oct 03 12:11:15 2023", requests::rfc::http_date)); + + BOOST_CHECK(urls::grammar::error::mismatch == + urls::grammar::parse("Sun Oct 03 12:11:15 2023", requests::rfc::http_date)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1623233894)) == + urls::grammar::parse("Wed Jun 9 10:18:14 2021", requests::rfc::http_date)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == + urls::grammar::parse("Thu Jan 1 00:00:01 1970", requests::rfc::http_date)); + + BOOST_CHECK(std::chrono::system_clock::time_point(std::chrono::seconds(1)) == + urls::grammar::parse("Thu Jan 01 00:00:01 1970", requests::rfc::http_date)); + +} + + + +BOOST_AUTO_TEST_SUITE_END(); \ No newline at end of file diff --git a/test/service.cpp b/test/unit/service.cpp similarity index 56% rename from test/service.cpp rename to test/unit/service.cpp index 86cab37..aec9607 100644 --- a/test/service.cpp +++ b/test/unit/service.cpp @@ -5,18 +5,15 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include #include #include #include -#include -#include "doctest.h" -#include "string_maker.hpp" +#include -TEST_SUITE_BEGIN("service"); +BOOST_AUTO_TEST_SUITE(service); -TEST_CASE("session") +BOOST_AUTO_TEST_CASE(session) { using namespace boost; @@ -25,9 +22,12 @@ TEST_CASE("session") asio::any_io_executor exec{ctx.get_executor()}; - get(requests::default_session(exec), urls::parse_uri("https://httpbin.org").value(), {}); - requests::default_session(tp.get_executor()); - requests::default_session(ctx.get_executor() ); + auto & s1 = requests::default_session(tp.get_executor()); + auto & s2 = requests::default_session(ctx.get_executor()); + auto & s3 = requests::default_session(exec); + + BOOST_CHECK(&s1 != & s2); + BOOST_CHECK(&s2 == & s3); } -TEST_SUITE_END(); \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END(); diff --git a/test/unit/session.cpp b/test/unit/session.cpp new file mode 100644 index 0000000..c427a72 --- /dev/null +++ b/test/unit/session.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2021 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include + +#include "../coroutine_test_case.hpp" +#include "../fixtures/server.hpp" + +namespace requests = boost::requests; +namespace filesystem = requests::filesystem; +namespace asio = boost::asio; +namespace json = boost::json; +namespace urls = boost::urls; +using boost::system::error_code ; + +BOOST_FIXTURE_TEST_SUITE(session, test_server); + +BOOST_AUTO_TEST_CASE(sync) +{ + asio::io_context ctx; + requests::session sess{ctx}; + auto & pool = *sess.get_pool("unix://" + std::string(path())); + BOOST_CHECK(pool.active() == 0u); + BOOST_CHECK(pool.free() == 0u); + BOOST_CHECK(pool.endpoints().size() == 1u); + + auto & p2 = *sess.get_pool("unix://" + std::string(path())); + BOOST_CHECK(&pool == &p2); +} + +BOOST_COROUTINE_TEST_CASE(async) +{ + requests::session sess{yield.get_executor()}; + auto & pool = *sess.async_get_pool("unix://" + std::string(path()), yield); + BOOST_CHECK(pool.active() == 0u); + BOOST_CHECK(pool.free() == 0u); + BOOST_CHECK(pool.endpoints().size() == 1u); + + auto & p2 = *sess.async_get_pool("unix://" + std::string(path()), yield); + BOOST_CHECK(&pool == &p2); +} + + +BOOST_AUTO_TEST_SUITE_END(); \ No newline at end of file diff --git a/test/source.cpp b/test/unit/source.cpp similarity index 67% rename from test/source.cpp rename to test/unit/source.cpp index f8e49d1..50810d6 100644 --- a/test/source.cpp +++ b/test/unit/source.cpp @@ -7,9 +7,7 @@ #include #include -#include "doctest.h" -#include "string_maker.hpp" - +#include #include #include #include @@ -19,9 +17,9 @@ using namespace boost; -TEST_SUITE_BEGIN("source"); +BOOST_AUTO_TEST_SUITE(source); -TEST_CASE("sync") +BOOST_AUTO_TEST_CASE(sync) { asio::io_context ctx; asio::readable_pipe rp{ctx}; @@ -37,14 +35,14 @@ TEST_CASE("sync") requests::http::verb::post, "/test", hd, *sp, ec); - CHECK(ec == system::error_code{}); + BOOST_CHECK(ec == system::error_code{}); hd.clear(); auto ep = requests::make_source(requests::empty()); write_request(wp, requests::http::verb::get, "/test2", hd, *ep, ec); - CHECK(ec == system::error_code{}); + BOOST_CHECK(ec == system::error_code{}); }}; @@ -52,21 +50,21 @@ TEST_CASE("sync") beast::flat_buffer buf; system::error_code ec; beast::http::read(rp, buf, req, ec); - CHECK(ec == system::error_code{}); - CHECK(req.method() == requests::http::verb::post); - CHECK(req.target() == "/test"); - CHECK(req.at(boost::beast::http::field::content_type) == "application/json"); - CHECK(json::parse(req.body()) == json::value{"foobaria"}); + BOOST_CHECK(ec == system::error_code{}); + BOOST_CHECK(req.method() == requests::http::verb::post); + BOOST_CHECK(req.target() == "/test"); + BOOST_CHECK(req.at(boost::beast::http::field::content_type) == "application/json"); + BOOST_CHECK(json::parse(req.body()) == json::value{"foobaria"}); requests::http::request re2; beast::http::read(rp, buf, re2, ec); - CHECK(ec == system::error_code{}); - CHECK(re2.method() == requests::http::verb::get); - CHECK(re2.target() == "/test2"); - CHECK(re2.count(boost::beast::http::field::content_type) == 0); + BOOST_CHECK(ec == system::error_code{}); + BOOST_CHECK(re2.method() == requests::http::verb::get); + BOOST_CHECK(re2.target() == "/test2"); + BOOST_CHECK(re2.count(boost::beast::http::field::content_type) == 0); thr.join(); } -TEST_SUITE_END(); \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END(); \ No newline at end of file diff --git a/test/token_rule.cpp b/test/unit/token_rule.cpp similarity index 68% rename from test/token_rule.cpp rename to test/unit/token_rule.cpp index a7596f7..519c288 100644 --- a/test/token_rule.cpp +++ b/test/unit/token_rule.cpp @@ -6,15 +6,14 @@ // #include +#include #include #include #include -#include "doctest.h" +BOOST_AUTO_TEST_SUITE(grammar); -TEST_SUITE_BEGIN("grammar"); - -TEST_CASE("token_rule") +BOOST_AUTO_TEST_CASE(token_rule) { using namespace boost; constexpr auto tk = requests::grammar::token_rule( @@ -22,8 +21,8 @@ TEST_CASE("token_rule") urls::grammar::alnum_chars ); - CHECK(urls::grammar::error::mismatch == urls::grammar::parse("1234", tk)); - CHECK("a123" == urls::grammar::parse("a123", tk)); + BOOST_CHECK(urls::grammar::error::mismatch == urls::grammar::parse("1234", tk)); + BOOST_CHECK("a123" == urls::grammar::parse("a123", tk)); } -TEST_SUITE_END(); \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END(); \ No newline at end of file From 272947d2fbe1fb8a608180d1693dd5ebac85b00c Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 28 Nov 2023 14:34:43 +0800 Subject: [PATCH 73/76] example fixes. --- example/Jamfile | 10 +- example/basic-auth-future.cpp | 4 +- example/basic-auth.cpp | 4 +- example/docker.cpp | 6 +- example/github-issues.hpp | 764 ------------------ example/github.cpp | 37 - example/methods-future.cpp | 8 +- .../requests/detail/impl/connection_impl.hpp | 1 - include/boost/requests/method.hpp | 4 +- src/request.cpp | 15 +- test/Jamfile | 9 +- 11 files changed, 30 insertions(+), 832 deletions(-) delete mode 100644 example/github-issues.hpp delete mode 100644 example/github.cpp diff --git a/example/Jamfile b/example/Jamfile index 9991fa1..256ee06 100644 --- a/example/Jamfile +++ b/example/Jamfile @@ -17,16 +17,12 @@ requirements ../../.. off:/boost//filesystem ; +using openssl ; -alias asio : ../../beast//lib-asio ; -alias asio-ssl : ../../beast//lib-asio-ssl ; -alias beast : ../../beast//lib-beast ; -alias ssl : ../../beast//ssl ; - -import regex ; +import regex ; for local src in [ glob *.cpp : xml.cpp ] { local name = [ regex.match "([^.]*)\\..*" : $(src) ] ; - exe $(name) : $(src) /boost//json /boost//url /boost//program_options asio asio-ssl beast ssl ; + exe $(name) : $(src) /boost//json /boost//url /boost//program_options /openssl//ssl ; } diff --git a/example/basic-auth-future.cpp b/example/basic-auth-future.cpp index 707349c..4148949 100644 --- a/example/basic-auth-future.cpp +++ b/example/basic-auth-future.cpp @@ -33,7 +33,7 @@ int main(int argc, char * argv[]) { asio::io_context ctx; auto fr = requests::async_get(urls::url_view("https://httpbin.org/basic-auth/user/pass"), - requests::headers({requests::basic_auth("user", "pass")}), + requests::http::headers({requests::basic_auth("user", "pass")}), asio::bind_executor(ctx, asio::use_future)); ctx.run(); @@ -47,7 +47,7 @@ int main(int argc, char * argv[]) std::cout << r.string_view() << std::endl; // {"authenticated": true, ... - std::cout << as_json(r) << std::endl; + std::cout << requests::as_json(r) << std::endl; // {'authenticated': True, ...} return 0; diff --git a/example/basic-auth.cpp b/example/basic-auth.cpp index e5f9707..8b60c1e 100644 --- a/example/basic-auth.cpp +++ b/example/basic-auth.cpp @@ -33,7 +33,7 @@ int main(int argc, char * argv[]) // tag::motivatingExample[] auto r = requests::get(urls::url_view("https://httpbin.org/basic-auth/user/pass"), - requests::headers({requests::basic_auth("user", "pass")})); + requests::http::headers({requests::basic_auth("user", "pass")})); std::cout << r.result_code() << std::endl; // 200 @@ -43,7 +43,7 @@ int main(int argc, char * argv[]) std::cout << r.string_view() << std::endl; // {"authenticated": true, ... - std::cout << as_json(r) << std::endl; + std::cout << requests::as_json(r) << std::endl; // {'authenticated': True, ...} // end::motivatingExample[] return 0; diff --git a/example/docker.cpp b/example/docker.cpp index 836711d..b3818d9 100644 --- a/example/docker.cpp +++ b/example/docker.cpp @@ -26,13 +26,13 @@ int main(int argc, char * argv[]) sock.connect(asio::local::stream_protocol::endpoint{url.path()}); sock.set_host("localhost"); - auto res = requests::json::get(sock, urls::url_view{"/containers/json"}); + auto res = requests::json::get(sock, urls::url_view{"/containers/json"}); // list all containers std::cout << "Response: " << res.headers << std::endl; - std::cout << "Amount of containers: " << res.value.size() << std::endl; + std::cout << "Amount of containers: " << res.value.as_array().size() << std::endl; - for (auto val : res.value) + for (auto val : res.value.as_array()) std::cout << "Container[" << val.at("Id") << "]: " << json::serialize(val.at("Names")) << std::endl; diff --git a/example/github-issues.hpp b/example/github-issues.hpp deleted file mode 100644 index c415814..0000000 --- a/example/github-issues.hpp +++ /dev/null @@ -1,764 +0,0 @@ -// -// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BOOST_REQUESTS_EXAMPLE_GITHUB_ISSUES_HPP -#define BOOST_REQUESTS_EXAMPLE_GITHUB_ISSUES_HPP - -#include -#include -#include -#include - -namespace github -{ - -using boost::variant2::monostate; -using boost::variant2::variant; -using boost::optional; -using boost::system::error_code; -using boost::system::result; - -namespace urls = boost::urls; - -// https://docs.github.com/en/rest/issues/issues -using string_view = boost::core::string_view; - - -#define GET_MEMBER(Name, Type) \ - if (auto p = obj.if_contains(#Name)) \ - { \ - if (auto v = boost::json::try_value_to(*p)) \ - res.Name = *v; \ - else \ - return v.error(); \ - } \ - else \ - { \ - static constexpr auto loc((BOOST_CURRENT_LOCATION)); \ - return ::boost::system::error_code(boost::json::error::not_found, &loc); \ - } - -#define GET_OPTIONAL_MEMBER(Name, Type) \ - if (auto p = obj.if_contains(#Name)) \ - { \ - if (!p->is_null()) \ - { \ - if (auto v = boost::json::try_value_to(*p)) \ - res.Name = *v; \ - else \ - return v.error(); \ - } \ - } - - -struct user_t -{ - std::string avatar_url; - optional email; // optional - std::string events_url; - std::string followers_url; - std::string following_url; - std::string gists_url; - optional gravatar_id; - std::string html_url; - int id; - std::string login; - optional name; // optional - std::string node_id; - std::string organizations_url; - std::string received_events_url; - std::string repos_url; - bool site_admin; - optional starred_at; // optional - std::string starred_url; - std::string subscriptions_url; - std::string type; - std::string url; -}; - -inline result tag_invoke(boost::json::try_value_to_tag, const boost::json::value & val) -{ - if (!val.is_object()) - { - static constexpr auto loc((BOOST_CURRENT_LOCATION)); - return ::boost::system::error_code(boost::json::error::not_object, &loc); - } - - auto & obj = val.get_object(); - - user_t res; - - GET_MEMBER(avatar_url, std::string); - GET_OPTIONAL_MEMBER(email, std::string); - GET_MEMBER(events_url, std::string); - GET_MEMBER(followers_url, std::string); - GET_MEMBER(following_url, std::string); - GET_MEMBER(gists_url, std::string); - - GET_OPTIONAL_MEMBER(gravatar_id, std::string); - GET_MEMBER(html_url, std::string); - - GET_MEMBER(id, int); - GET_OPTIONAL_MEMBER(name, std::string); - GET_MEMBER(node_id, std::string); - - GET_MEMBER(organizations_url, std::string); - GET_MEMBER(received_events_url, std::string); - GET_MEMBER(repos_url, std::string); - GET_MEMBER(site_admin, bool); - GET_OPTIONAL_MEMBER(starred_at, std::string); - GET_MEMBER(starred_url, std::string); - GET_MEMBER(subscriptions_url, std::string); - GET_MEMBER(type, std::string); - GET_MEMBER(url, std::string); - - return res; -} - -struct label // all members are optional -{ - optional color; - bool default_{false}; - optional description; - optional id; - optional name; - optional node_id; - optional url; -}; - - -inline result