diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..6313b56c
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+* text=auto eol=lf
diff --git a/CMakePresets.json b/CMakePresets.json
index ca4abfb1..5aa94bfe 100644
--- a/CMakePresets.json
+++ b/CMakePresets.json
@@ -1,16 +1,10 @@
{
- "version": 8,
- "configurePresets": [
- {
- "name": "Custom configure preset",
- "displayName": "Custom configure preset",
- "description": "Sets Ninja generator, build and install directory",
- "generator": "Ninja",
- "binaryDir": "${sourceDir}/out/build/${presetName}",
- "cacheVariables": {
- "CMAKE_BUILD_TYPE": "Debug",
- "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}"
- }
- }
- ]
-}
\ No newline at end of file
+ "version": 6,
+ "cmakeMinimumRequired": {
+ "major": 3,
+ "minor": 25,
+ "patch": 0
+ },
+ "configurePresets": [],
+ "buildPresets": []
+}
diff --git a/doc/tagfiles/boost-http-doxygen.tag.xml b/doc/tagfiles/boost-http-doxygen.tag.xml
index 48273eab..b0d10ebc 100644
--- a/doc/tagfiles/boost-http-doxygen.tag.xml
+++ b/doc/tagfiles/boost-http-doxygen.tag.xml
@@ -7,7 +7,7 @@
boost::http::is_sink
boost::http::is_source
boost::http::route_result
- boost::http::router
+ boost::http::basic_router
boost::http::acceptor_config
boost::http::any_router
boost::http::basic_router
@@ -149,8 +149,8 @@
boost/http/route_result.adoc
- boost::http::router
- boost/http/router.adoc
+ boost::http::basic_router
+ boost/http/basic_router.adoc
boost::http::acceptor_config
diff --git a/example/client/burl/CMakeLists.txt b/example/client/burl/CMakeLists.txt
index 921ecea6..8c5956e0 100644
--- a/example/client/burl/CMakeLists.txt
+++ b/example/client/burl/CMakeLists.txt
@@ -35,12 +35,12 @@ if (WIN32)
target_link_libraries(beast2_example_client_burl crypt32)
endif()
-if (TARGET Boost::capy_zlib)
- target_link_libraries(beast2_example_client_burl Boost::capy_zlib)
+if (TARGET Boost::http_zlib)
+ target_link_libraries(beast2_example_client_burl Boost::http_zlib)
endif()
-if (TARGET Boost::capy_brotli)
- target_link_libraries(beast2_example_client_burl Boost::capy_brotli)
+if (TARGET Boost::http_brotli)
+ target_link_libraries(beast2_example_client_burl Boost::http_brotli)
endif()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
diff --git a/example/client/burl/connect.cpp b/example/client/burl/connect.cpp
index b9855d17..836bb7c2 100644
--- a/example/client/burl/connect.cpp
+++ b/example/client/burl/connect.cpp
@@ -153,7 +153,6 @@ connect_socks5_proxy(
asio::awaitable
connect_http_proxy(
const operation_config& oc,
- capy::polystore& capy_ctx,
asio::ip::tcp::socket& stream,
const urls::url_view& url,
const urls::url_view& proxy)
@@ -190,8 +189,8 @@ connect_http_proxy(
request.set(field::proxy_authorization, basic_auth);
}
- auto serializer = http::serializer{ capy_ctx };
- auto parser = http::response_parser{ capy_ctx };
+ auto serializer = http::serializer{};
+ auto parser = http::response_parser{};
serializer.start(request);
co_await beast2::async_write(stream, serializer);
@@ -225,7 +224,6 @@ asio::awaitable
connect(
const operation_config& oc,
ssl::context& ssl_ctx,
- capy::polystore& capy_ctx,
any_stream& stream,
urls::url url)
{
@@ -261,7 +259,7 @@ connect(
{
if(oc.proxy.scheme() == "http")
{
- co_await connect_http_proxy(oc, capy_ctx, socket, url, oc.proxy);
+ co_await connect_http_proxy(oc, socket, url, oc.proxy);
}
else if(oc.proxy.scheme() == "socks5")
{
diff --git a/example/client/burl/connect.hpp b/example/client/burl/connect.hpp
index ca4aefb5..f51416ea 100644
--- a/example/client/burl/connect.hpp
+++ b/example/client/burl/connect.hpp
@@ -15,11 +15,10 @@
#include
#include
-#include
#include
namespace asio = boost::asio;
-namespace capy = boost::capy;
+namespace http = boost::http;
namespace ssl = boost::asio::ssl;
namespace urls = boost::urls;
@@ -27,7 +26,6 @@ asio::awaitable
connect(
const operation_config& oc,
ssl::context& ssl_ctx,
- capy::polystore& capy_ctx,
any_stream& stream,
urls::url url);
diff --git a/example/client/burl/main.cpp b/example/client/burl/main.cpp
index 1f318507..98cffcce 100644
--- a/example/client/burl/main.cpp
+++ b/example/client/burl/main.cpp
@@ -31,8 +31,8 @@
#include
#include
#include
-#include
-#include
+#include
+#include
#include
#include
#include
@@ -45,13 +45,13 @@ namespace capy = boost::capy;
namespace scope = boost::scope;
using system_error = boost::system::system_error;
-#ifdef BOOST_CAPY_HAS_ZLIB
+#ifdef BOOST_HTTP_HAS_ZLIB
constexpr bool capy_has_zlib = true;
#else
constexpr bool capy_has_zlib = false;
#endif
-#ifdef BOOST_CAPY_HAS_BROTLI
+#ifdef BOOST_HTTP_HAS_BROTLI
constexpr bool capy_has_brotli = true;
#else
constexpr bool capy_has_brotli = false;
@@ -345,15 +345,14 @@ perform_request(
boost::optional& cookie_jar,
core::string_view exp_cookies,
ssl::context& ssl_ctx,
- capy::polystore& capy_ctx,
message msg,
request_opt request_opt)
{
using field = http::field;
auto executor = co_await asio::this_coro::executor;
auto stream = any_stream{ asio::ip::tcp::socket{ executor } };
- auto parser = http::response_parser{ capy_ctx };
- auto serializer = http::serializer{ capy_ctx };
+ auto parser = http::response_parser{};
+ auto serializer = http::serializer{};
urls::url url = [&]()
{
@@ -376,26 +375,7 @@ perform_request(
if(!request_opt.input.empty())
{
- msg = [&]() -> message
- {
- if(request_opt.input == "-")
- return stdin_body{};
-
- auto path = request_opt.input;
-
- // Append filename to URL if missing
- auto segs = url.encoded_segments();
- if(segs.empty())
- {
- segs.push_back(path.filename().string());
- }
- else if(auto back = --segs.end(); back->empty())
- {
- segs.replace(back, path.filename().string());
- }
-
- return file_body{ path.string() };
- }();
+ throw std::runtime_error{ "File upload is not available" };
}
fs::path output_path = [&]()
@@ -465,7 +445,7 @@ perform_request(
co_await asio::co_spawn(
executor,
- connect(oc, ssl_ctx, capy_ctx, stream, url),
+ connect(oc, ssl_ctx, stream, url),
asio::cancel_after(oc.connect_timeout));
if(oc.recvpersecond)
@@ -759,7 +739,6 @@ co_main(int argc, char* argv[])
auto executor = co_await asio::this_coro::executor;
auto task_group = ::task_group{ executor, oc.parallel_max };
- auto capy_ctx = capy::polystore{};
auto cookie_jar = boost::optional<::cookie_jar>{};
auto header_output = boost::optional{};
auto exp_cookies = std::string{};
@@ -778,22 +757,22 @@ co_main(int argc, char* argv[])
if constexpr(capy_has_brotli)
{
cfg.apply_brotli_decoder = true;
- capy::brotli::install_decode_service(capy_ctx);
+ http::brotli::install_decode_service();
}
if constexpr(capy_has_zlib)
{
cfg.apply_deflate_decoder = true;
cfg.apply_gzip_decoder = true;
- capy::zlib::install_inflate_service(capy_ctx);
+ http::zlib::install_inflate_service();
}
- http::install_parser_service(capy_ctx, cfg);
+ http::install_parser_service(cfg);
}
// serializer service
{
http::serializer::config cfg;
cfg.payload_buffer = 1024 * 1024;
- http::install_serializer_service(capy_ctx, cfg);
+ http::install_serializer_service(cfg);
}
if(!oc.headerfile.empty())
@@ -831,7 +810,6 @@ co_main(int argc, char* argv[])
cookie_jar,
exp_cookies,
ssl_ctx,
- capy_ctx,
oc.msg,
ropt.value());
};
diff --git a/example/client/burl/message.cpp b/example/client/burl/message.cpp
index d9601dc6..ddeff2c8 100644
--- a/example/client/burl/message.cpp
+++ b/example/client/burl/message.cpp
@@ -8,18 +8,10 @@
//
#include "message.hpp"
-#include "mime_type.hpp"
-#include
#include
-#include
-#include
-#include
-
-namespace capy = boost::capy;
-namespace fs = std::filesystem;
-using system_error = boost::system::system_error;
+namespace capy = boost::capy;
string_body::string_body(std::string body, std::string content_type)
: body_{ std::move(body) }
@@ -53,79 +45,6 @@ string_body::body() const noexcept
// -----------------------------------------------------------------------------
-file_body::file_body(std::string path)
- : path_{ std::move(path) }
-{
-}
-
-http::method
-file_body::method() const noexcept
-{
- return http::method::put;
-}
-
-core::string_view
-file_body::content_type() const noexcept
-{
- return mime_type(path_);
-}
-
-std::uint64_t
-file_body::content_length() const
-{
- return fs::file_size(path_);
-}
-
-http::file_source
-file_body::body() const
-{
- boost::capy::file file;
- error_code ec;
- file.open(path_.c_str(), boost::capy::file_mode::read, ec);
- if(ec)
- throw system_error{ ec };
-
- return http::file_source{ std::move(file), content_length() };
-}
-
-// -----------------------------------------------------------------------------
-
-boost::http::source::results
-stdin_body::source::on_read(capy::mutable_buffer mb)
-{
- std::cin.read(static_cast(mb.data()), mb.size());
-
- return { .ec = {},
- .bytes = static_cast(std::cin.gcount()),
- .finished = std::cin.eof() };
-}
-
-http::method
-stdin_body::method() const noexcept
-{
- return http::method::put;
-}
-
-core::string_view
-stdin_body::content_type() const noexcept
-{
- return "application/octet-stream";
-}
-
-boost::optional
-stdin_body::content_length() const noexcept
-{
- return boost::none;
-}
-
-stdin_body::source
-stdin_body::body() const
-{
- return {};
-}
-
-// -----------------------------------------------------------------------------
-
void
message::set_headers(http::request& request) const
{
@@ -138,20 +57,11 @@ message::set_headers(http::request& request) const
request.set_method(f.method());
request.set(field::content_type, f.content_type());
- boost::optional content_length =
- f.content_length();
- if(content_length.has_value())
- {
- request.set_content_length(content_length.value());
- if(content_length.value() >= 1024 * 1024 &&
- request.version() == http::version::http_1_1)
- request.set(field::expect, "100-continue");
- }
- else
- {
- request.set_chunked(true);
+ std::size_t content_length = f.content_length();
+ request.set_content_length(content_length);
+ if(content_length >= 1024 * 1024 &&
+ request.version() == http::version::http_1_1)
request.set(field::expect, "100-continue");
- }
}
},
body_);
@@ -167,8 +77,7 @@ message::start_serializer(
{
if constexpr(!std::is_same_v)
{
- serializer.start>(
- request, f.body());
+ serializer.start(request, f.body());
}
else
{
diff --git a/example/client/burl/message.hpp b/example/client/burl/message.hpp
index 3d3caef4..1e5387d3 100644
--- a/example/client/burl/message.hpp
+++ b/example/client/burl/message.hpp
@@ -10,9 +10,6 @@
#ifndef BURL_MESSAGE_HPP
#define BURL_MESSAGE_HPP
-#include "multipart_form.hpp"
-
-#include
#include
#include
@@ -41,57 +38,11 @@ class string_body
body() const noexcept;
};
-class file_body
-{
- std::string path_;
-
-public:
- file_body(std::string path);
-
- http::method
- method() const noexcept;
-
- core::string_view
- content_type() const noexcept;
-
- std::uint64_t
- content_length() const;
-
- http::file_source
- body() const;
-};
-
-class stdin_body
-{
-public:
- class source : public http::source
- {
- public:
- results
- on_read(capy::mutable_buffer mb) override;
- };
-
- http::method
- method() const noexcept;
-
- core::string_view
- content_type() const noexcept;
-
- boost::optional
- content_length() const noexcept;
-
- source
- body() const;
-};
-
class message
{
std::variant<
std::monostate,
- string_body,
- multipart_form,
- file_body,
- stdin_body>
+ string_body>
body_;
public:
diff --git a/example/client/burl/multipart_form.cpp b/example/client/burl/multipart_form.cpp
deleted file mode 100644
index 16c0edab..00000000
--- a/example/client/burl/multipart_form.cpp
+++ /dev/null
@@ -1,305 +0,0 @@
-//
-// Copyright (c) 2024 Mohammad Nejati
-//
-// 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)
-//
-// Official repository: https://github.com/cppalliance/beast2
-//
-
-#include "multipart_form.hpp"
-
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-namespace capy = boost::capy;
-namespace core = boost::core;
-namespace fs = std::filesystem;
-using system_error = boost::system::system_error;
-
-namespace
-{
-std::array
-generate_boundary()
-{
- std::array rs;
- constexpr static char chars[] =
- "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
- static std::random_device rd;
- std::uniform_int_distribution dist{ 0, sizeof(chars) - 2 };
- std::fill(rs.begin(), rs.end(), '-');
- std::generate(
- rs.begin() + 2 + 24, rs.end() - 2, [&] { return chars[dist(rd)]; });
- return rs;
-}
-
-std::string
-serialize_headers(std::vector& headers)
-{
- std::string rs;
- for(const auto& h : headers)
- {
- rs.append("\r\n");
- rs.append(h);
- }
- return rs;
-}
-
-core::string_view content_disposition_ =
- "\r\nContent-Disposition: form-data; name=\"";
-core::string_view filename_ = "; filename=\"";
-core::string_view content_type_ = "\r\nContent-Type: ";
-} // namespace
-
-// -----------------------------------------------------------------------------
-
-multipart_form::multipart_form()
- : storage_{ generate_boundary() }
-{
-}
-
-void
-multipart_form::append(
- bool is_file,
- std::string name,
- std::string value,
- boost::optional filename,
- boost::optional content_type,
- std::vector headers)
-{
- auto size = is_file ? fs::file_size(value) : value.size();
-
- pacapy_.push_back(
- { is_file,
- std::move(name),
- std::move(value),
- size,
- std::move(filename),
- std::move(content_type),
- serialize_headers(headers) });
-}
-
-http::method
-multipart_form::method() const noexcept
-{
- return http::method::post;
-}
-
-std::string
-multipart_form::content_type() const
-{
- std::string res = "multipart/form-data; boundary=";
- // append boundary
- res.append(storage_.begin() + 2, storage_.end() - 2);
- return res;
-}
-
-std::uint64_t
-multipart_form::content_length() const noexcept
-{
- auto rs = std::uint64_t{};
- for(const auto& part : pacapy_)
- {
- rs += storage_.size() - 2; // --boundary
- rs += content_disposition_.size();
- rs += part.name.size();
- rs += 1; // closing double quote
-
- if(part.content_type)
- {
- rs += content_type_.size();
- rs += part.content_type->size();
- }
-
- if(part.filename)
- {
- rs += filename_.size();
- rs += part.filename->size();
- rs += 1; // closing double quote
- }
-
- rs += part.headers.size();
-
- rs += 4; // after headers
- rs += part.size;
- rs += 2; // after content
- }
- rs += storage_.size(); // --boundary--
- rs += 2; //
- return rs;
-}
-
-multipart_form::source
-multipart_form::body() const
-{
- return source{ this };
-}
-
-// -----------------------------------------------------------------------------
-
-multipart_form::source::source(const multipart_form* form) noexcept
- : form_{ form }
-{
-}
-
-multipart_form::source::results
-multipart_form::source::on_read(capy::mutable_buffer mb)
-{
- auto rs = results{};
-
- auto copy = [&](core::string_view sv)
- {
- capy::const_buffer source(sv.data(), sv.size());
- capy::remove_prefix(mb, static_cast(skip_));
-
- auto copied = capy::copy(mb, source);
-
- capy::remove_prefix(mb, copied);
- rs.bytes += copied;
- skip_ += copied;
-
- if(skip_ != sv.size())
- return false;
-
- skip_ = 0;
- return true;
- };
-
- auto read = [&](const std::string& path, uint64_t size)
- {
- capy::file file;
-
- file.open(path.c_str(), capy::file_mode::read, rs.ec);
- if(rs.ec)
- return false;
-
- file.seek(skip_, rs.ec);
- if(rs.ec)
- return false;
-
- auto read = file.read(
- mb.data(),
- (std::min)(static_cast(mb.size()), size),
- rs.ec);
- if(rs.ec)
- return false;
-
- capy::remove_prefix(mb, read);
- rs.bytes += read;
- skip_ += read;
-
- if(skip_ != size)
- return false;
-
- skip_ = 0;
- return true;
- };
-
- while(it_ != form_->pacapy_.end())
- {
- switch(step_)
- {
- case 0:
- // --boundary
- if(!copy({ form_->storage_.data(), form_->storage_.size() - 2 }))
- return rs;
- step_ = 1;
- BOOST_FALLTHROUGH;
- case 1:
- if(!copy(content_disposition_))
- return rs;
- step_ = 2;
- BOOST_FALLTHROUGH;
- case 2:
- if(!copy(it_->name))
- return rs;
- step_ = 3;
- BOOST_FALLTHROUGH;
- case 3:
- if(!copy("\""))
- return rs;
- step_ = 4;
- BOOST_FALLTHROUGH;
- case 4:
- if(!it_->filename)
- goto content_type;
- if(!copy(filename_))
- return rs;
- step_ = 5;
- BOOST_FALLTHROUGH;
- case 5:
- if(!copy(it_->filename.value()))
- return rs;
- step_ = 6;
- BOOST_FALLTHROUGH;
- case 6:
- if(!copy("\""))
- return rs;
- step_ = 7;
- BOOST_FALLTHROUGH;
- case 7:
- content_type:
- if(!it_->content_type)
- goto headers;
- if(!copy(content_type_))
- return rs;
- step_ = 8;
- BOOST_FALLTHROUGH;
- case 8:
- if(!copy(it_->content_type.value()))
- return rs;
- step_ = 9;
- BOOST_FALLTHROUGH;
- case 9:
- headers:
- if(!copy(it_->headers))
- return rs;
- step_ = 10;
- BOOST_FALLTHROUGH;
- case 10:
- if(!copy("\r\n\r\n"))
- return rs;
- step_ = 11;
- BOOST_FALLTHROUGH;
- case 11:
- if(it_->is_file)
- {
- if(!read(it_->value, it_->size))
- return rs;
- }
- else
- {
- if(!copy(it_->value))
- return rs;
- }
- step_ = 12;
- BOOST_FALLTHROUGH;
- case 12:
- if(!copy("\r\n"))
- return rs;
- ++it_;
- step_ = 0;
- }
- }
-
- switch(step_)
- {
- case 0:
- // --boundary--
- if(!copy({ form_->storage_.data(), form_->storage_.size() }))
- return rs;
- step_ = 1;
- BOOST_FALLTHROUGH;
- case 1:
- if(!copy("\r\n"))
- return rs;
- }
-
- rs.finished = true;
- return rs;
-}
diff --git a/example/client/burl/multipart_form.hpp b/example/client/burl/multipart_form.hpp
deleted file mode 100644
index 09a0068f..00000000
--- a/example/client/burl/multipart_form.hpp
+++ /dev/null
@@ -1,85 +0,0 @@
-//
-// Copyright (c) 2024 Mohammad Nejati
-//
-// 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)
-//
-// Official repository: https://github.com/cppalliance/beast2
-//
-
-#ifndef BURL_MULTIPART_FORM_HPP
-#define BURL_MULTIPART_FORM_HPP
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-namespace capy = boost::capy;
-namespace http = boost::http;
-using error_code = boost::system::error_code;
-
-class multipart_form
-{
- struct part
- {
- bool is_file = false;
- std::string name;
- std::string value;
- std::uint64_t size;
- boost::optional filename;
- boost::optional content_type;
- std::string headers;
- };
-
- // boundary with extra "--" prefix and postfix.
- std::array storage_;
- std::vector pacapy_;
-
-public:
- class source;
-
- multipart_form();
-
- void
- append(
- bool is_file,
- std::string name,
- std::string value,
- boost::optional filename = {},
- boost::optional content_type = {},
- std::vector headers = {});
-
- http::method
- method() const noexcept;
-
- std::string
- content_type() const;
-
- std::uint64_t
- content_length() const noexcept;
-
- source
- body() const;
-};
-
-class multipart_form::source : public http::source
-{
- const multipart_form* form_;
- std::vector::const_iterator it_{ form_->pacapy_.begin() };
- int step_ = 0;
- std::uint64_t skip_ = 0;
-
-public:
- explicit source(const multipart_form* form) noexcept;
-
- results
- on_read(capy::mutable_buffer mb) override;
-};
-
-#endif
diff --git a/example/client/burl/options.cpp b/example/client/burl/options.cpp
index f0dc12fa..3371afc7 100644
--- a/example/client/burl/options.cpp
+++ b/example/client/burl/options.cpp
@@ -984,57 +984,7 @@ parse_args(int argc, char* argv[])
if(vm.contains("form") || vm.contains("form-string"))
{
- auto form = multipart_form{};
- if(vm.contains("form"))
- {
- for(core::string_view sv :
- vm.at("form").as>())
- {
- auto [name, prefix, value, filename, type, headers] =
- parse_form_option(sv);
-
- auto is_file = false;
-
- if(prefix == '@' || prefix == '<')
- {
- is_file = true;
-
- if(!filename && prefix != '<')
- filename = fs::path{ value }.filename().string();
-
- if(value == "-")
- {
- value.clear();
- any_istream{ core::string_view{ "-" } }.append_to(
- value);
- is_file = false;
- }
- else if(!type)
- {
- type = ::mime_type(value);
- }
- }
- form.append(
- is_file,
- std::move(name),
- std::move(value),
- std::move(filename),
- std::move(type),
- std::move(headers));
- }
- }
- if(vm.contains("form-string"))
- {
- for(core::string_view sv :
- vm.at("form-string").as>())
- {
- if(auto pos = sv.find('='); pos != sv.npos)
- form.append(false, sv.substr(0, pos), sv.substr(pos + 1));
- else
- throw std::runtime_error("Illegally formatted input field");
- }
- }
- oc.msg = std::move(form);
+ throw std::runtime_error{ "Multipart form support is not available" };
}
if(vm.contains("json"))
diff --git a/example/client/get/CMakeLists.txt b/example/client/get/CMakeLists.txt
index 9e296095..93aa4c14 100644
--- a/example/client/get/CMakeLists.txt
+++ b/example/client/get/CMakeLists.txt
@@ -33,10 +33,10 @@ if (WIN32)
target_link_libraries(beast2_example_client_get crypt32)
endif()
-if (TARGET Boost::capy_zlib)
- target_link_libraries(beast2_example_client_get Boost::capy_zlib)
+if (TARGET Boost::http_zlib)
+ target_link_libraries(beast2_example_client_get Boost::http_zlib)
endif()
-if (TARGET Boost::capy_brotli)
- target_link_libraries(beast2_example_client_get Boost::capy_brotli)
+if (TARGET Boost::http_brotli)
+ target_link_libraries(beast2_example_client_get Boost::http_brotli)
endif()
diff --git a/example/client/get/main.cpp b/example/client/get/main.cpp
index a9bca59b..59d4e93a 100644
--- a/example/client/get/main.cpp
+++ b/example/client/get/main.cpp
@@ -17,9 +17,8 @@
#include
#include
#include
-#include
-#include
-#include
+#include
+#include
#include
#include
@@ -49,13 +48,10 @@ class session
public:
session(
asio::io_context& ioc,
- asio::ssl::context& ssl_ctx,
- capy::polystore& capy_ctx)
+ asio::ssl::context& ssl_ctx)
: ssl_ctx_(ssl_ctx)
, stream_(ioc, ssl_ctx)
, resolver_(ioc)
- , sr_(capy_ctx)
- , pr_(capy_ctx)
{
}
@@ -74,10 +70,10 @@ class session
url.authority().encoded_host_and_port().decode());
// Enable compression
- #ifdef BOOST_CAPY_HAS_BROTLI
+ #ifdef BOOST_HTTP_HAS_BROTLI
req_.append(field::accept_encoding, "br");
#endif
- #ifdef BOOST_CAPY_HAS_ZLIB
+ #ifdef BOOST_HTTP_HAS_ZLIB
req_.append(field::accept_encoding, "deflate, gzip");
#endif
@@ -440,29 +436,25 @@ main(int argc, char* argv[])
// The SSL context is required, and holds certificates
asio::ssl::context ssl_ctx(asio::ssl::context::tls_client);
- // holds optional deflate and
- // required configuration services
- capy::polystore capy_ctx;
-
// Install parser service
{
http::response_parser::config cfg;
cfg.body_limit = std::uint64_t(-1);
cfg.min_buffer = 64 * 1024;
- #ifdef BOOST_CAPY_HAS_BROTLI
+ #ifdef BOOST_HTTP_HAS_BROTLI
cfg.apply_brotli_decoder = true;
- capy::brotli::install_decode_service(capy_ctx);
+ http::brotli::install_decode_service();
#endif
- #ifdef BOOST_CAPY_HAS_ZLIB
+ #ifdef BOOST_HTTP_HAS_ZLIB
cfg.apply_deflate_decoder = true;
cfg.apply_gzip_decoder = true;
- capy::zlib::install_inflate_service(capy_ctx);
+ http::zlib::install_inflate_service();
#endif
- http::install_parser_service(capy_ctx, cfg);
+ http::install_parser_service(cfg);
}
// Install serializer service with default configuration
- http::install_serializer_service(capy_ctx, {});
+ http::install_serializer_service({});
// Root certificates used for verification
ssl_ctx.set_default_verify_paths();
@@ -476,7 +468,7 @@ main(int argc, char* argv[])
if(!url.has_authority())
goto help;
- session s(ioc, ssl_ctx, capy_ctx);
+ session s(ioc, ssl_ctx);
s.run(url);
ioc.run();
diff --git a/example/client/jsonrpc/CMakeLists.txt b/example/client/jsonrpc/CMakeLists.txt
index 998f4895..e596fb74 100644
--- a/example/client/jsonrpc/CMakeLists.txt
+++ b/example/client/jsonrpc/CMakeLists.txt
@@ -43,9 +43,9 @@ set_property(TARGET beast2_example_client_jsonrpc
target_link_libraries(beast2_example_client_jsonrpc
PRIVATE
beast2_example_client_jsonrpc_lib)
-if (TARGET Boost::capy_zlib)
- target_link_libraries(beast2_example_client_jsonrpc PRIVATE Boost::capy_zlib)
+if (TARGET Boost::http_zlib)
+ target_link_libraries(beast2_example_client_jsonrpc PRIVATE Boost::http_zlib)
endif()
-if (TARGET Boost::capy_brotli)
- target_link_libraries(beast2_example_client_jsonrpc PRIVATE Boost::capy_brotli)
+if (TARGET Boost::http_brotli)
+ target_link_libraries(beast2_example_client_jsonrpc PRIVATE Boost::http_brotli)
endif()
diff --git a/example/client/jsonrpc/jsonrpc/client.cpp b/example/client/jsonrpc/jsonrpc/client.cpp
index 5823c29a..c4f5529c 100644
--- a/example/client/jsonrpc/jsonrpc/client.cpp
+++ b/example/client/jsonrpc/jsonrpc/client.cpp
@@ -18,11 +18,12 @@
#include
#include
#include
+#include
#include
#include
-#include
-#include
-#include
+#include
+#include
+#include
#include
using namespace boost;
@@ -250,33 +251,6 @@ class json_sink : public http::sink
}
};
-class json_source : public http::source
-{
- json::serializer& jsr_;
- json::value v_;
-
-public:
- json_source(json::serializer& jsr, json::value v)
- : jsr_(jsr)
- , v_(std::move(v))
- {
- jsr_.reset(&v_);
- }
-
-private:
- results
- on_read(
- capy::mutable_buffer b) override
- {
- results ret;
- ret.bytes = jsr_.read(
- static_cast(b.data()),
- b.size()).size();
- ret.finished = jsr_.done();
- return ret;
- }
-};
-
} //namespace
// ----------------------------------------------
@@ -286,6 +260,7 @@ class client::request_op
{
client& client_;
std::uint64_t id_;
+ std::string body_;
public:
request_op(client& c) noexcept
@@ -310,9 +285,10 @@ class client::request_op
{ "id", id_ }
},
sp);
- client_.req_.set_chunked(true);
- client_.sr_.start(
- client_.req_, client_.jsr_, std::move(value));
+ body_ = json::serialize(value);
+ client_.req_.set_content_length(body_.size());
+ client_.sr_.start(
+ client_.req_, http::string_body(std::move(body_)));
beast2::async_write(
*client_.stream_, client_.sr_, std::move(self));
@@ -385,12 +361,10 @@ class client::request_op
client::
client(
urls::url endpoint,
- capy::polystore& capy_ctx,
asio::any_io_executor exec,
asio::ssl::context& ssl_ctx)
: client(
std::move(endpoint),
- capy_ctx,
std::unique_ptr(new stream_impl(exec, ssl_ctx)))
{
}
@@ -398,12 +372,9 @@ client(
client::
client(
urls::url endpoint,
- capy::polystore& capy_ctx,
std::unique_ptr stream)
: stream_(std::move(stream))
, endpoint_(std::move(endpoint))
- , sr_(capy_ctx)
- , pr_(capy_ctx)
, req_(http::method::post, "/")
{
using field = http::field;
@@ -416,10 +387,11 @@ client(
req_.append(field::accept, "application/json");
req_.append(field::user_agent, "Boost.Http.Io");
- if(capy_ctx.find() != nullptr)
+ auto& ctx = capy::get_system_context();
+ if(ctx.find_service() != nullptr)
req_.append(field::accept_encoding, "br");
- if(capy_ctx.find() != nullptr)
+ if(ctx.find_service() != nullptr)
req_.append(field::accept_encoding, "deflate, gzip");
pr_.reset();
diff --git a/example/client/jsonrpc/jsonrpc/client.hpp b/example/client/jsonrpc/jsonrpc/client.hpp
index 4265cfd4..7bd9a322 100644
--- a/example/client/jsonrpc/jsonrpc/client.hpp
+++ b/example/client/jsonrpc/jsonrpc/client.hpp
@@ -25,7 +25,6 @@
#include
#include
#include
-#include
#include
namespace jsonrpc {
@@ -56,7 +55,6 @@ class client
*/
client(
boost::urls::url endpoint,
- boost::capy::polystore& capy_ctx,
boost::asio::any_io_executor exec,
boost::asio::ssl::context& ssl_ctx);
@@ -67,7 +65,6 @@ class client
*/
client(
boost::urls::url endpoint,
- boost::capy::polystore& capy_ctx,
std::unique_ptr stream);
/// Get the executor associated with the object.
diff --git a/example/client/jsonrpc/main.cpp b/example/client/jsonrpc/main.cpp
index 0b7c9f54..b2d406f7 100644
--- a/example/client/jsonrpc/main.cpp
+++ b/example/client/jsonrpc/main.cpp
@@ -16,9 +16,8 @@
#include
#include
#include
-#include
-#include
-#include
+#include
+#include
#include
@@ -26,8 +25,7 @@ using namespace boost;
asio::awaitable
co_main(
- asio::ssl::context& ssl_ctx,
- capy::polystore& capy_ctx)
+ asio::ssl::context& ssl_ctx)
{
using dec_float = multiprecision::cpp_dec_float_50;
const auto to_int = [](std::string_view s)
@@ -37,7 +35,6 @@ co_main(
jsonrpc::client client(
urls::url("https://ethereum.publicnode.com"),
- capy_ctx,
co_await asio::this_coro::executor,
ssl_ctx);
@@ -145,28 +142,24 @@ main(int, char*[])
// The SSL context is required, and holds certificates
asio::ssl::context ssl_ctx(asio::ssl::context::tls_client);
- // CAPY context holds optional deflate and
- // required configuration services
- capy::polystore capy_ctx;
-
// Install parser service
{
http::response_parser::config cfg;
cfg.min_buffer = 64 * 1024;
- #ifdef BOOST_CAPY_HAS_BROTLI
+ #ifdef BOOST_HTTP_HAS_BROTLI
cfg.apply_brotli_decoder = true;
- capy::brotli::install_decode_service(capy_ctx);
+ http::brotli::install_decode_service();
#endif
- #ifdef BOOST_CAPY_HAS_ZLIB
+ #ifdef BOOST_HTTP_HAS_ZLIB
cfg.apply_deflate_decoder = true;
cfg.apply_gzip_decoder = true;
- capy::zlib::install_inflate_service(capy_ctx);
+ http::zlib::install_inflate_service();
#endif
- http::install_parser_service(capy_ctx, cfg);
+ http::install_parser_service(cfg);
}
// Install serializer service with default configuration
- http::install_serializer_service(capy_ctx, {});
+ http::install_serializer_service({});
// Root certificates used for verification
ssl_ctx.set_default_verify_paths();
@@ -176,7 +169,7 @@ main(int, char*[])
asio::co_spawn(
ioc,
- co_main(ssl_ctx, capy_ctx),
+ co_main(ssl_ctx),
[](auto eptr)
{
if(eptr)
diff --git a/example/client/visit/main.cpp b/example/client/visit/main.cpp
index c5f3c15e..ee2ae215 100644
--- a/example/client/visit/main.cpp
+++ b/example/client/visit/main.cpp
@@ -16,7 +16,6 @@
#include
#include
#include
-#include
#include
//------------------------------------------------
@@ -201,10 +200,8 @@ struct worker
explicit
worker(
- executor_type ex,
- boost::capy::polystore& ctx)
+ executor_type ex)
: sock(ex)
- , pr(ctx)
{
sock.open(boost::asio::ip::tcp::v4());
}
@@ -276,13 +273,12 @@ main(int argc, char* argv[])
(void)argc;
(void)argv;
- boost::capy::polystore ctx;
boost::http::parser::config_base cfg;
- boost::http::install_parser_service(ctx, cfg);
+ boost::http::install_parser_service(cfg);
boost::asio::io_context ioc;
- worker w(ioc.get_executor(), ctx);
+ worker w(ioc.get_executor());
w.do_next();
diff --git a/example/server/CMakeLists.txt b/example/server/CMakeLists.txt
index f2f1508b..cf7aae0b 100644
--- a/example/server/CMakeLists.txt
+++ b/example/server/CMakeLists.txt
@@ -26,10 +26,10 @@ target_link_libraries(
Boost::url
)
-if (TARGET Boost::capy_zlib)
- target_link_libraries(beast2_server_example Boost::capy_zlib)
+if (TARGET Boost::http_zlib)
+ target_link_libraries(beast2_server_example Boost::http_zlib)
endif()
-if (TARGET Boost::capy_brotli)
- target_link_libraries(beast2_server_example Boost::capy_brotli)
+if (TARGET Boost::http_brotli)
+ target_link_libraries(beast2_server_example Boost::http_brotli)
endif()
diff --git a/example/server/main.cpp b/example/server/main.cpp
index 6c1d0f7e..22fb912a 100644
--- a/example/server/main.cpp
+++ b/example/server/main.cpp
@@ -8,22 +8,21 @@
//
#include "serve_log_admin.hpp"
-//#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
@@ -35,23 +34,15 @@ namespace beast2 {
capy::thread_pool g_tp;
-void install_services(capy::application& app)
+void install_services()
{
-#ifdef BOOST_CAPY_HAS_BROTLI
- capy::brotli::install_decode_service(app);
- capy::brotli::install_encode_service(app);
+#ifdef BOOST_HTTP_HAS_BROTLI
+ http::brotli::install_brotli_service();
#endif
-#ifdef BOOST_CAPY_HAS_ZLIB
- capy::zlib::install_deflate_service(app);
- capy::zlib::install_inflate_service(app);
+#ifdef BOOST_HTTP_HAS_ZLIB
+ http::zlib::install_zlib_service();
#endif
-
- // VFALCO These ugly incantations are needed for http and will hopefully go away soon.
- http::install_parser_service(app,
- http::request_parser::config());
- http::install_serializer_service(app,
- http::serializer::config());
}
#if 0
@@ -159,10 +150,10 @@ int server_main( int argc, char* argv[] )
return EXIT_FAILURE;
}
- capy::application app;
+ http::application app;
corosio::io_context ioc;
- install_services(app);
+ install_services();
auto addr = urls::parse_ipv4_address(argv[1]);
if(addr.has_error())
@@ -173,15 +164,25 @@ int server_main( int argc, char* argv[] )
auto port = static_cast(std::atoi(argv[2]));
corosio::endpoint ep(addr.value(), port);
- http::router rr;
+ http::basic_router rr;
+ rr.use( "/", http::serve_static( argv[3] ) );
+#if 0
rr.use(
[](http::route_params& rp) -> capy::task
{
- (void)rp;
- co_return http::route::next;
+ co_return co_await rp.send( "Hello, coworld!" );
});
- http_server hsrv(ioc, std::atoi(argv[4]), std::move(rr));
+#endif
+ auto parser_cfg = http::make_parser_config(http::parser_config(true));
+ auto serializer_cfg = http::make_serializer_config(http::serializer_config());
+
+ http_server hsrv(
+ ioc,
+ std::atoi(argv[4]),
+ std::move(rr),
+ std::move(parser_cfg),
+ std::move(serializer_cfg));
auto ec = hsrv.bind(ep);
if(ec)
{
diff --git a/example/server/serve_log_admin.cpp b/example/server/serve_log_admin.cpp
index 743d27da..9980c008 100644
--- a/example/server/serve_log_admin.cpp
+++ b/example/server/serve_log_admin.cpp
@@ -22,9 +22,8 @@ namespace {
class serve_log_page
{
public:
- serve_log_page(
- capy::polystore& ps)
- : ls_(use_log_service(ps))
+ serve_log_page()
+ : ls_(use_log_service())
{
}
@@ -91,9 +90,8 @@ class serve_log_page
class handle_submit
{
public:
- handle_submit(
- capy::polystore& ps)
- : ls_(use_log_service(ps))
+ handle_submit()
+ : ls_(use_log_service())
{
}
@@ -120,12 +118,11 @@ class handle_submit
#if 0
router
-serve_log_admin(
- capy::polystore& ps)
+serve_log_admin()
{
router r;
- r.add(http::method::get, "/", serve_log_page(ps));
- r.add(http::method::get, "/submit", handle_submit(ps));
+ r.add(http::method::get, "/", serve_log_page());
+ r.add(http::method::get, "/submit", handle_submit());
return r;
}
#endif
diff --git a/example/server/serve_log_admin.hpp b/example/server/serve_log_admin.hpp
index 1851833c..cdad56e4 100644
--- a/example/server/serve_log_admin.hpp
+++ b/example/server/serve_log_admin.hpp
@@ -12,14 +12,12 @@
#include
#include
-#include
namespace boost {
namespace beast2 {
router
-serve_log_admin(
- capy::polystore& ps);
+serve_log_admin();
} // beast2
} // boost
diff --git a/include/boost/beast2/http_server.hpp b/include/boost/beast2/http_server.hpp
index 303f347f..450f0d28 100644
--- a/include/boost/beast2/http_server.hpp
+++ b/include/boost/beast2/http_server.hpp
@@ -13,12 +13,41 @@
#include
#include
#include
+#include
#include
namespace boost {
namespace http { class flat_router; }
namespace beast2 {
+/** An HTTP server for handling requests with coroutine-based I/O.
+
+ This class provides a complete HTTP server implementation that
+ accepts connections, parses HTTP requests, and dispatches them
+ through a router. Each connection is handled by a worker that
+ processes requests using coroutines.
+
+ @par Thread Safety
+ Distinct objects: Safe.
+ Shared objects: Unsafe.
+
+ @par Example
+ @code
+ corosio::io_context ctx;
+ http::flat_router router;
+ router.add( http::verb::get, "/", my_handler );
+
+ http_server srv(
+ ctx,
+ 4, // workers
+ std::move( router ),
+ http::shared_parser_config::make(),
+ http::shared_serializer_config::make() );
+
+ srv.listen( "0.0.0.0", 8080 );
+ ctx.run();
+ @endcode
+*/
class BOOST_BEAST2_DECL
http_server : public corosio::tcp_server
{
@@ -26,12 +55,25 @@ class BOOST_BEAST2_DECL
impl* impl_;
public:
+ /// Destroy the server.
~http_server();
+ /** Construct an HTTP server.
+
+ @param ctx The I/O context for asynchronous operations.
+ @param num_workers Number of worker objects for handling
+ connections concurrently.
+ @param router The router for dispatching requests to handlers.
+ @param parser_cfg Shared configuration for request parsers.
+ @param serializer_cfg Shared configuration for response
+ serializers.
+ */
http_server(
corosio::io_context& ctx,
std::size_t num_workers,
- http::flat_router router);
+ http::flat_router router,
+ http::shared_parser_config parser_cfg,
+ http::shared_serializer_config serializer_cfg);
private:
struct worker;
diff --git a/include/boost/beast2/log_service.hpp b/include/boost/beast2/log_service.hpp
index 9a975330..f847c421 100644
--- a/include/boost/beast2/log_service.hpp
+++ b/include/boost/beast2/log_service.hpp
@@ -12,7 +12,6 @@
#include
#include
-#include
#include
#include
@@ -38,16 +37,16 @@ class BOOST_SYMBOL_VISIBLE
std::vector = 0;
};
-/** Return the log service attached to the polystore
- If the polystore does not already contain the
+/** Return the log service from the system context
+
+ If the system context does not already contain the
service, it is created.
- @param ps The polystore.
+
@return The log service.
*/
BOOST_BEAST2_DECL
log_service&
-use_log_service(
- capy::polystore& ps);
+use_log_service();
} // beast2
} // boost
diff --git a/include/boost/beast2/server/body_source.hpp b/include/boost/beast2/server/body_source.hpp
deleted file mode 100644
index 09cc805c..00000000
--- a/include/boost/beast2/server/body_source.hpp
+++ /dev/null
@@ -1,432 +0,0 @@
-//
-// Copyright (c) 2025 Vinnie Falco (vinnie dot falco 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)
-//
-// Official repository: https://github.com/cppalliance/beast2
-//
-
-#ifndef BOOST_BEAST2_SERVER_BODY_SOURCE_HPP
-#define BOOST_BEAST2_SERVER_BODY_SOURCE_HPP
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace boost {
-namespace beast2 {
-
-/** Tag for customizing body construction
-*/
-struct construct_body_tag {};
-
-//-----------------------------------------------
-
-/** A type erased HTTP message body.
-
- This type erases the specific body type used to represent the message body.
- It provides a uniform interface for reading the body data regardless of
- how the body is implemented. Accessing the bytes is achieved by calling
- @ref read which reads data into a caller-provided buffer. Alternatively,
- when @ref has_buffers returns `true` the body consists of buffers in memory,
- and they can be accessed directly by calling @ref get_buffers.
-
- Example bodies include:
- - in-memory buffers
- - file-backed bodies
- - generated bodies
-
- @note A body_source is movable but not copyable.
-
- Type-erased bodies can always be rewound to the beginning by calling
- @ref rewind. Therefore, bodies can be read multiple times.
-
- @par Thread Safety
- Unsafe.
-*/
-class body_source
-{
-public:
- /** Destructor.
- */
- BOOST_BEAST2_DECL ~body_source();
-
- /** Constructor
-
- Default-constructed bodies are empty.
- */
- body_source() = default;
-
- /** Constructor
-
- The content of @p other is transferred to `*this`. The
- moved-from body is left empty, as if default-constructed.
- */
- body_source(body_source&& other) noexcept
- : impl_(other.impl_)
- {
- other.impl_ = nullptr;
- }
-
- /** Assignment
-
- The previous body, if any, is released and the
- content of @p other is transferred to `*this`.
- */
- BOOST_BEAST2_DECL
- body_source& operator=(body_source&& other) noexcept;
-
- /** Construct a streaming body source.
- */
- template::type, body_source>::value &&
- capy::is_read_source::type>::value
- , int>::type = 0>
- body_source(ReadSource&& body);
-
- /** Construct a streaming body source with a known size.
- */
- template::type, body_source>::value &&
- capy::is_read_source::type>::value
- , int>::type = 0>
- body_source(
- std::size_t known_size,
- ReadSource&& body);
-
- /** Construct a buffers body source.
- */
- template::type, body_source>::value &&
- capy::is_data_source::type>::value
- , int>::type = 0>
- body_source(DataSource&& body);
-
- //template
- //body(T&& t);
-
- /** Return `true` if the size of the body is known.
- */
- bool has_size() const noexcept
- {
- if(impl_)
- return impl_->has_size();
- return true; // empty
- }
-
- /** Return `true` if the body consists of buffers in memory.
- When the body consists of buffers in memory, they can
- also be accessed directly using @ref get_buffers.
- */
- bool has_buffers() const noexcept
- {
- if(impl_)
- return impl_->has_buffers();
- return true; // empty
- }
-
- /** Return the size of the body, if available.
- @throw std::invalid_argument if @ref has_size returns `false`.
- @return The size of the body in bytes.
- */
- auto size() const -> std::size_t
- {
- if(impl_)
- return impl_->size();
- return 0; // empty
- }
-
- /** Return the buffers representing the body, if available.
- @throw std::invalid_argument if @ref has_buffers returns `false`.
- @return A span of buffers representing the body.
- */
- auto data() const ->
- span
- {
- if(impl_)
- return impl_->data();
- return {}; // empty
- }
-
- /** Rewind the body to the beginning.
- This allows the body to be accessed from the start when calling @read.
- */
- void rewind()
- {
- if(impl_)
- return impl_->rewind();
- // empty
- }
-
- /** Read from the body into a caller-provided buffer.
-
- @param dest A pointer to the buffer to read into.
- @param n The maximum number of bytes to read.
- @param ec Set to the error, if any occurred.
- @return The number of bytes read, which may be
- less than the number requested. A return value of
- zero indicates end-of-body.
- */
- auto
- read(void* dest, std::size_t n,
- system::error_code& ec) ->
- std::size_t
- {
- if(impl_)
- return impl_->read(dest, n, ec);
- // empty
- ec = http::error::end_of_stream;
- return 0;
- }
-
-private:
- struct impl
- {
- virtual ~impl() = default;
- virtual bool has_size() const noexcept { return false; }
- virtual bool has_buffers() const noexcept { return false; }
- virtual std::size_t size() const
- {
- detail::throw_invalid_argument();
- }
- virtual auto data() const ->
- span
- {
- detail::throw_invalid_argument();
- }
- virtual void rewind() = 0;
- virtual std::size_t read(
- void* dest, std::size_t n,
- system::error_code& ec) = 0;
- };
-
- impl* impl_ = nullptr;
-};
-
-//-----------------------------------------------
-
-template::type, body_source>::value &&
- capy::is_read_source::type>::value
- , int>::type>
-body_source::
-body_source(
- ReadSource&& body)
-{
- struct model : impl
- {
- system::error_code ec_;
- typename std::decay::type body_;
-
- explicit model(ReadSource&& body)
- : body_(std::forward(body))
- {
- }
-
- void rewind() override
- {
- ec_ = {};
- body_.rewind();
- }
-
- std::size_t read(
- void* dest,
- std::size_t size,
- system::error_code& ec) override
- {
- if(ec_.failed())
- {
- ec = ec_;
- return 0;
- }
- auto nread = body_.read(
- capy::mutable_buffer(dest, size), ec);
- ec_ = ec;
- return nread;
- }
- };
-
- auto p = ::operator new(sizeof(model));
- impl_ = ::new(p) model(
- std::forward(body));
-}
-
-/** Construct a streaming body source with a known size.
-*/
-template::type, body_source>::value &&
- capy::is_read_source::type>::value
- , int>::type>
-body_source::
-body_source(
- std::size_t known_size,
- ReadSource&& body)
-{
- struct model : impl
- {
- std::size_t size_;
- system::error_code ec_;
- typename std::decay::type body_;
-
- model(
- ReadSource&& body,
- std::size_t known_size)
- : size_(known_size)
- , body_(std::forward(body))
- {
- }
-
- bool has_size() const noexcept override
- {
- return true;
- }
-
- std::size_t size() const override
- {
- return size_;
- }
-
- void rewind() override
- {
- ec_ = {};
- body_.rewind();
- }
-
- std::size_t read(
- void* dest,
- std::size_t size,
- system::error_code& ec) override
- {
- if(ec_.failed())
- {
- ec = ec_;
- return 0;
- }
- auto nread = body_.read(
- capy::mutable_buffer(dest, size), ec);
- ec_ = ec;
- return nread;
- }
- };
-
- auto p = ::operator new(sizeof(model));
- impl_ = ::new(p) model(
- std::forward(body), known_size);
-}
-
-/** Construct a buffers body source.
-*/
-template::type, body_source>::value &&
- capy::is_data_source::type>::value
- , int>::type>
-body_source::
-body_source(
- DataSource&& body)
-{
- struct model : impl
- {
- typename std::decay::type body_;
- std::size_t size_ = 0;
- span bs_;
- std::size_t nread_ = 0;
-
- explicit model(
- DataSource&& body) noexcept
- : body_(std::forward(body))
- {
- auto const& data = body_.data();
- auto const& end = capy::end(data);
- auto p = reinterpret_cast<
- capy::const_buffer*>(this+1);
- std::size_t length = 0;
- for(auto it = capy::begin(data); it != end; ++it)
- {
- boost::capy::const_buffer cb(*it);
- size_ += cb.size();
- *p++ = cb;
- ++length;
- }
- bs_ = { reinterpret_cast<
- capy::const_buffer*>(this + 1), length };
- }
-
- bool has_size() const noexcept override
- {
- return true;
- }
-
- bool has_buffers() const noexcept override
- {
- return true;
- }
-
- std::size_t size() const override
- {
- return size_;
- }
-
- span
- data() const override
- {
- return bs_;
- }
-
- void rewind() override
- {
- nread_ = 0;
- }
-
- std::size_t read(
- void* dest,
- std::size_t n0,
- system::error_code& ec) override
- {
- std::size_t n = capy::copy(
- capy::mutable_buffer(dest, n0),
- capy::sans_prefix(bs_, nread_));
- nread_ += n;
- if(nread_ >= size_)
- ec = http::error::end_of_stream;
- else
- ec = {};
- return n;
- }
- };
-
- std::size_t length = 0;
- auto const& data = body.data();
- auto const& end = capy::end(data);
- for(auto it = capy::begin(data);
- it != end; ++it)
- {
- ++length;
- }
-
- // VFALCO this requires DataSource to be nothrow
- // move constructible for strong exception safety.
- auto p = ::operator new(sizeof(model) +
- length * sizeof(capy::const_buffer));
- impl_ = ::new(p) model(
- std::forward(body));
-}
-
-} // beast2
-} // boost
-
-#endif
diff --git a/include/boost/beast2/server/http_stream.hpp b/include/boost/beast2/server/http_stream.hpp
index f48cf853..1b1a2c70 100644
--- a/include/boost/beast2/server/http_stream.hpp
+++ b/include/boost/beast2/server/http_stream.hpp
@@ -19,7 +19,7 @@
#include
#include
#include
-#include
+#include
#include
#include
#include
@@ -29,7 +29,7 @@
#include
#include
#include
-#include
+#include
#include
#include
@@ -61,7 +61,7 @@ class http_stream
@param routes The router used to dispatch incoming HTTP requests.
*/
http_stream(
- capy::application& app,
+ http::application& app,
corosio::socket& sock,
http::flat_router& routes);
@@ -112,7 +112,7 @@ class http_stream
inline
http_stream::
http_stream(
- capy::application& app,
+ http::application& app,
corosio::socket& sock,
http::flat_router& /*routes*/)
: sect_(use_log_service(app).get_section("http_stream"))
diff --git a/include/boost/beast2/server/route_handler_corosio.hpp b/include/boost/beast2/server/route_handler_corosio.hpp
index dcda4305..8e6e84c4 100644
--- a/include/boost/beast2/server/route_handler_corosio.hpp
+++ b/include/boost/beast2/server/route_handler_corosio.hpp
@@ -11,8 +11,13 @@
#define BOOST_BEAST2_SERVER_ROUTE_HANDLER_COROSIO_HPP
#include
-#include
+#include
+#include
+#include
#include
+#include
+#include
+#include
namespace boost {
namespace beast2 {
@@ -22,6 +27,8 @@ namespace beast2 {
class corosio_route_params
: public http::route_params
{
+ http::serializer::stream srs_;
+
public:
using stream_type = corosio::socket;
@@ -33,6 +40,77 @@ class corosio_route_params
: stream(s)
{
}
+
+ http::route_task
+ end() override
+ {
+ // Close the serializer stream if active
+ if(srs_.is_open())
+ srs_.close();
+
+ // Drain all remaining serializer output
+ while(!serializer.is_done())
+ {
+ auto cbs = serializer.prepare();
+ if(cbs.has_error())
+ {
+ if(cbs.error() == http::error::need_data)
+ continue;
+ co_return cbs.error();
+ }
+
+ if(capy::buffer_empty(*cbs))
+ {
+ serializer.consume(0);
+ continue;
+ }
+
+ auto [ec, n] = co_await capy::write(stream, *cbs);
+ if(ec)
+ co_return ec;
+ serializer.consume(capy::buffer_size(*cbs));
+ }
+
+ co_return {};
+ }
+
+protected:
+ http::route_task
+ write_impl(capy::const_buffer_param buffers) override
+ {
+ // Initialize streaming on first call
+ if(!srs_.is_open())
+ srs_ = serializer.start_stream(res);
+
+ // Loop until all input buffers are consumed
+ while(true)
+ {
+ auto bufs = buffers.data();
+ if(bufs.empty())
+ break;
+
+ // Copy data to serializer stream
+ std::size_t bytes = capy::buffer_copy(srs_.prepare(), bufs);
+ srs_.commit(bytes);
+ buffers.consume(bytes);
+
+ // Write serializer output to socket
+ auto cbs = serializer.prepare();
+ if(cbs.has_error())
+ {
+ if(cbs.error() != http::error::need_data)
+ co_return cbs.error();
+ continue;
+ }
+
+ auto [ec, n] = co_await capy::write(stream, *cbs);
+ if(ec)
+ co_return ec;
+ serializer.consume(capy::buffer_size(*cbs));
+ }
+
+ co_return {};
+ }
};
} // beast2
diff --git a/include/boost/beast2/server/router.hpp b/include/boost/beast2/server/router.hpp
index ed94d146..91caaaa2 100644
--- a/include/boost/beast2/server/router.hpp
+++ b/include/boost/beast2/server/router.hpp
@@ -11,15 +11,15 @@
#define BOOST_BEAST2_SERVER_ROUTER_HPP
#include
+#include
#include
-#include
namespace boost {
namespace beast2 {
/** The sans-IO router type
*/
-using router = http::router;
+using router = http::basic_router;
} // beast2
} // boost
diff --git a/include/boost/beast2/server/router_corosio.hpp b/include/boost/beast2/server/router_corosio.hpp
index d091128d..c0d89e4a 100644
--- a/include/boost/beast2/server/router_corosio.hpp
+++ b/include/boost/beast2/server/router_corosio.hpp
@@ -11,7 +11,7 @@
#define BOOST_BEAST2_SERVER_ROUTER_COROSIO_HPP
#include
-#include
+#include
#include
namespace boost {
@@ -19,7 +19,7 @@ namespace beast2 {
/** The Corosio-aware router type
*/
-using router_corosio = http::router;
+using router_corosio = http::basic_router;
} // beast2
} // boost
diff --git a/include/boost/beast2/server/serve_static.hpp b/include/boost/beast2/server/serve_static.hpp
deleted file mode 100644
index abe09da5..00000000
--- a/include/boost/beast2/server/serve_static.hpp
+++ /dev/null
@@ -1,178 +0,0 @@
-//
-// Copyright (c) 2025 Vinnie Falco (vinnie dot falco 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)
-//
-// Official repository: https://github.com/cppalliance/beast2
-//
-
-#ifndef BOOST_BEAST2_SERVER_SERVE_STATIC_HPP
-#define BOOST_BEAST2_SERVER_SERVE_STATIC_HPP
-
-#include
-#include
-
-namespace boost {
-namespace beast2 {
-
-/** A route handler which serves static files from a document root
-
- This handler operates similary to the npm package serve-static.
-*/
-struct serve_static
-{
- /** Policy for handling dotfiles
- */
- enum class dotfiles_policy
- {
- allow,
- deny,
- ignore
- };
-
- /** Options for the static file server
- */
- struct options
- {
- /** How to handle dotfiles
-
- Dotfiles are files or directories whose names begin with a dot (.)
- The default is to ignore dotfiles.
- */
- dotfiles_policy dotfiles = dotfiles_policy::ignore;
-
- // VFALCO extensions fallbacks vector
-
- // VFALCO vector of index file names
-
- /** Maximum cache age in milliseconds
- */
- // VFALCO
- // std::chrono::duration max_age = std::chrono::milliseconds(0); ?
- std::size_t max_age_ms = 0;
-
- // VFALCO set_headers callback
-
- /** Enable accepting range requests.
-
- When this is false, the "Accept-Ranges" field will not be
- sent, and any "Range" field in the request will be ignored.
- */
- bool accept_ranges = true;
-
- /** Enable sending cache-control headers.
-
- When this is set to `false`, the @ref immutable
- and @ref max_age options are ignored.
- */
- bool cache_control = true;
-
- /** Enable etag header generation.
- */
- bool etag = true;
-
- /** Treat client errors as unhandled requests.
-
- When this value is `true`, all error codes will be
- treated as if unhandled. Otherwise, errors (including
- file not found) will go through the error handling routes.
-
- Typically true is desired such that multiple physical
- directories can be mapped to the same web address or for
- routes to fill in non-existent files.
-
- The value false can be used if this handler is mounted
- at a path that is designed to be strictly a single file system
- directory, which allows for short-circuiting 404s for less
- overhead. This handler will also reply to all methods.
-
- @note This handler replies to all HTTP methods.
- */
- bool fallthrough = true;
-
- /** Enable the immutable directive in cache control headers.
-
- When this is true, the "immutable" directive will be
- added to the "Cache-Control" field.
- This indicates to clients that the resource will not
- change during its freshness lifetime.
- This is typically used when the filenames contain
- a hash of the content, such as when using a build
- tool which fingerprints static assets.
- The @ref max_age value must also be set to a non-zero value.
- */
- bool immutable = false;
-
- /** Enable a default index file for directory requests.
- When a request is made for a directory path, such as
- "/docs/", the file "index.html" will be served if it
- exists within that directory.
- */
- bool index = true; // "index.html" default
-
- /** Enable the "Last-Modified" header.
-
- The file system's last modified value is used.
- */
- bool last_modified = true;
-
- /** Enable redirection for directories missing a trailing slash.
-
- When a request is made for a directory path without a trailing
- slash, the client is redirected to the same path with the slash
- appended. This is useful for relative links to work correctly
- in browsers.
- For example, a request for `/docs` when `/docs/index.html` exists
- will be redirected to `/docs/`.
- @note This requires that the client accepts redirections.
- */
- bool redirect = true;
- };
-
- BOOST_BEAST2_DECL
- ~serve_static();
-
- /** Constructor
- @param path The document root path
- @param options The options to use
- */
- BOOST_BEAST2_DECL
- serve_static(
- core::string_view path,
- options const& opt);
-
- /** Constructor
- @param path The document root path
- */
- explicit
- serve_static(
- core::string_view path)
- : serve_static(path, options{})
- {
- }
-
- /** Constructor
- */
- BOOST_BEAST2_DECL
- serve_static(serve_static&&) noexcept;
-
- /** Handle a request
- @param req The request
- @param res The response
- @return `true` if the request was handled, `false` to
- indicate the request was not handled.
- */
- BOOST_BEAST2_DECL
- system::error_code operator()(
- http::route_params&) const;
-
-private:
- struct impl;
- impl* impl_;
-};
-
-} // beast2
-} // boost
-
-#endif
diff --git a/src/http_server.cpp b/src/http_server.cpp
index 9bacd76c..7835e5ea 100644
--- a/src/http_server.cpp
+++ b/src/http_server.cpp
@@ -17,11 +17,11 @@
#include
#include
#include
-#include
+#include
#include
#include
-#include
+#include
namespace boost {
namespace beast2 {
@@ -29,20 +29,18 @@ namespace beast2 {
struct http_server::impl
{
http::flat_router router;
- capy::application app;
+ http::application app;
+ http::shared_parser_config parser_cfg;
+ http::shared_serializer_config serializer_cfg;
impl(http::flat_router r)
: router(std::move(r))
{
- // VFALCO These ugly incantations are needed
- // for http and will hopefully go away soon.
- http::install_parser_service(app,
- http::request_parser::config());
- http::install_serializer_service(app,
- http::serializer::config());
}
};
+// Each worker owns its own socket and parser/serializer state,
+// allowing concurrent connection handling without synchronization.
struct http_server::
worker : tcp_server::worker_base
{
@@ -62,8 +60,8 @@ struct http_server::
, rp(sock)
{
sock.open();
- rp.parser = http::request_parser(srv->impl_->app);
- rp.serializer = http::serializer(srv->impl_->app);
+ rp.parser = http::request_parser(srv->impl_->parser_cfg);
+ rp.serializer = http::serializer(srv->impl_->serializer_cfg);
}
corosio::socket& socket() override
@@ -76,7 +74,7 @@ struct http_server::
launch(ctx.get_executor(), srv->do_session(*this));
}
- capy::task>
+ capy::task>
read_header()
{
std::size_t total_bytes = 0;
@@ -151,10 +149,14 @@ http_server::
http_server(
corosio::io_context& ctx,
std::size_t num_workers,
- http::flat_router router)
+ http::flat_router router,
+ http::shared_parser_config parser_cfg,
+ http::shared_serializer_config serializer_cfg)
: tcp_server(ctx, ctx.get_executor())
, impl_(new impl(std::move(router)))
{
+ impl_->parser_cfg = std::move(parser_cfg);
+ impl_->serializer_cfg = std::move(serializer_cfg);
wv_.reserve(num_workers);
for(std::size_t i = 0; i < num_workers; ++i)
wv_.emplace