17 : server_id_(server_id)
18 , stop_future_(stop_promise_.get_future())
36 "Server already running",
41 io_context_ = std::make_unique<asio::io_context>();
42 work_guard_ = std::make_unique<asio::executor_work_guard<asio::io_context::executor_type>>(
43 io_context_->get_executor());
45 acceptor_ = std::make_unique<asio::ip::tcp::acceptor>(
47 asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port));
53 io_future_ = std::async(std::launch::async, [
this]() { run_io(); });
57 start_cleanup_timer();
60 }
catch (
const std::exception& e) {
63 std::string(
"Failed to start server: ") + e.what(),
73 "Server already running",
78 io_context_ = std::make_unique<asio::io_context>();
79 work_guard_ = std::make_unique<asio::executor_work_guard<asio::io_context::executor_type>>(
80 io_context_->get_executor());
83 ssl_context_ = std::make_unique<asio::ssl::context>(asio::ssl::context::tls_server);
84 ssl_context_->set_options(
85 asio::ssl::context::default_workarounds |
86 asio::ssl::context::no_sslv2 |
87 asio::ssl::context::no_sslv3 |
88 asio::ssl::context::no_tlsv1 |
89 asio::ssl::context::no_tlsv1_1);
91 ssl_context_->use_certificate_file(
config.cert_file, asio::ssl::context::pem);
92 ssl_context_->use_private_key_file(
config.key_file, asio::ssl::context::pem);
94 if (!
config.ca_file.empty()) {
95 ssl_context_->load_verify_file(
config.ca_file);
98 if (
config.verify_client) {
99 ssl_context_->set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert);
103 SSL_CTX_set_alpn_select_cb(
104 ssl_context_->native_handle(),
105 [](
SSL* ,
const unsigned char** out,
unsigned char* outlen,
106 const unsigned char* in,
unsigned int inlen,
void* ) ->
int {
108 const unsigned char* client = in;
109 const unsigned char* end = in + inlen;
110 while (client < end) {
111 unsigned char len = *client++;
112 if (len == 2 && client + 2 <= end &&
113 client[0] ==
'h' && client[1] ==
'2') {
116 return SSL_TLSEXT_ERR_OK;
120 return SSL_TLSEXT_ERR_NOACK;
124 acceptor_ = std::make_unique<asio::ip::tcp::acceptor>(
126 asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port));
132 io_future_ = std::async(std::launch::async, [
this]() { run_io(); });
136 start_cleanup_timer();
139 }
catch (
const std::exception& e) {
142 std::string(
"Failed to start TLS server: ") + e.what(),
156 if (acceptor_ && acceptor_->is_open()) {
158 acceptor_->close(ec);
163 std::lock_guard<std::mutex> lock(connections_mutex_);
164 for (
auto& [
id, conn] : connections_) {
167 connections_.clear();
171 if (cleanup_timer_) {
172 cleanup_timer_->cancel();
179 stop_promise_.set_value();
187 auto http2_server::is_running() const ->
bool
192 auto http2_server::wait() ->
void
199 request_handler_ = std::move(handler);
204 error_handler_ = std::move(handler);
210 encoder_->set_max_table_size(
settings.header_table_size);
211 decoder_->set_max_table_size(
settings.header_table_size);
219 auto http2_server::active_connections() const ->
size_t
221 std::lock_guard<std::mutex> lock(
const_cast<std::mutex&
>(connections_mutex_));
222 return connections_.size();
225 auto http2_server::active_streams() const ->
size_t
227 std::lock_guard<std::mutex> lock(
const_cast<std::mutex&
>(connections_mutex_));
229 for (
const auto& [
id, conn] : connections_) {
230 total += conn->stream_count();
235 auto http2_server::server_id() const -> std::string_view
240 auto http2_server::do_accept() ->
void
242 if (!is_running_ || !acceptor_) {
246 acceptor_->async_accept(
247 [
this](std::error_code ec, asio::ip::tcp::socket socket) {
248 handle_accept(ec, std::move(socket));
252 auto http2_server::do_accept_tls() ->
void
254 if (!is_running_ || !acceptor_) {
258 acceptor_->async_accept(
259 [
this](std::error_code ec, asio::ip::tcp::socket socket) {
260 handle_accept_tls(ec, std::move(socket));
264 auto http2_server::handle_accept(std::error_code ec, asio::ip::tcp::socket socket) ->
void
271 if (error_handler_) {
272 error_handler_(std::string(
"Accept error: ") + ec.message());
275 uint64_t conn_id = next_connection_id_++;
276 auto conn = std::make_shared<http2_server_connection>(
283 add_connection(conn);
291 auto http2_server::handle_accept_tls(std::error_code ec, asio::ip::tcp::socket socket) ->
void
298 if (error_handler_) {
299 error_handler_(std::string(
"Accept error: ") + ec.message());
305 auto tls_socket = std::make_unique<asio::ssl::stream<asio::ip::tcp::socket>>(
306 std::move(socket), *ssl_context_);
308 auto* raw_socket = tls_socket.get();
309 raw_socket->async_handshake(
310 asio::ssl::stream_base::server,
311 [
this, tls_socket = std::move(tls_socket)](std::error_code ec)
mutable {
313 if (error_handler_) {
314 error_handler_(std::string(
"TLS handshake error: ") + ec.message());
317 uint64_t conn_id = next_connection_id_++;
318 auto conn = std::make_shared<http2_server_connection>(
320 std::move(tls_socket),
325 add_connection(conn);
334 auto http2_server::add_connection(std::shared_ptr<http2_server_connection> conn) ->
void
336 std::lock_guard<std::mutex> lock(connections_mutex_);
337 connections_[conn->connection_id()] = std::move(conn);
340 auto http2_server::remove_connection(uint64_t connection_id) ->
void
342 std::lock_guard<std::mutex> lock(connections_mutex_);
343 connections_.erase(connection_id);
346 auto http2_server::cleanup_dead_connections() ->
void
348 std::lock_guard<std::mutex> lock(connections_mutex_);
349 for (
auto it = connections_.begin(); it != connections_.end();) {
350 if (!it->second->is_alive()) {
351 it = connections_.erase(it);
358 auto http2_server::start_cleanup_timer() ->
void
364 cleanup_timer_ = std::make_unique<asio::steady_timer>(*io_context_);
365 cleanup_timer_->expires_after(std::chrono::seconds(30));
366 cleanup_timer_->async_wait([
this](std::error_code ec) {
367 if (!ec && is_running_) {
368 cleanup_dead_connections();
369 start_cleanup_timer();
374 auto http2_server::run_io() ->
void
381 auto http2_server::stop_io() ->
void
391 if (io_future_.valid()) {
400 http2_server_connection::http2_server_connection(
401 uint64_t connection_id,
402 asio::ip::tcp::socket socket,
406 : connection_id_(connection_id)
408 , plain_socket_(std::make_unique<asio::ip::tcp::socket>(std::move(socket)))
412 , request_handler_(std::move(request_handler))
413 , error_handler_(std::move(error_handler))
414 , frame_header_buffer_{}
419 uint64_t connection_id,
420 std::unique_ptr<asio::ssl::stream<asio::ip::tcp::socket>> socket,
424 : connection_id_(connection_id)
426 , tls_socket_(std::move(socket))
430 , request_handler_(std::move(request_handler))
431 , error_handler_(std::move(error_handler))
432 , frame_header_buffer_{}
444 read_connection_preface();
458 if (use_tls_ && tls_socket_) {
459 tls_socket_->lowest_layer().close(ec);
460 }
else if (plain_socket_) {
461 plain_socket_->close(ec);
479 std::lock_guard<std::mutex> lock(
const_cast<std::mutex&
>(
streams_mutex_));
485 constexpr size_t PREFACE_SIZE = 24;
486 auto buffer = std::make_shared<std::vector<uint8_t>>(PREFACE_SIZE);
488 auto read_handler = [
this, buffer](std::error_code ec, std::size_t bytes_read) {
489 if (ec || bytes_read != 24) {
490 if (error_handler_) {
491 error_handler_(
"Failed to read connection preface");
498 const std::string expected =
"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
499 if (std::string(buffer->begin(), buffer->end()) != expected) {
500 if (error_handler_) {
501 error_handler_(
"Invalid connection preface");
507 preface_received_ =
true;
510 auto result = send_settings();
511 if (result.is_err()) {
512 if (error_handler_) {
513 error_handler_(
"Failed to send settings");
524 asio::async_read(*tls_socket_, asio::buffer(*buffer), read_handler);
526 asio::async_read(*plain_socket_, asio::buffer(*buffer), read_handler);
532 std::vector<setting_parameter> params = {
534 local_settings_.max_concurrent_streams},
536 local_settings_.initial_window_size},
538 local_settings_.max_frame_size},
540 local_settings_.header_table_size},
544 return send_frame(
frame);
549 if (
frame.is_ack()) {
555 for (
const auto& param :
frame.settings()) {
558 remote_settings_.header_table_size = param.value;
559 encoder_.set_max_table_size(param.value);
562 remote_settings_.enable_push = (param.value != 0);
565 remote_settings_.max_concurrent_streams = param.value;
568 remote_settings_.initial_window_size = param.value;
571 remote_settings_.max_frame_size = param.value;
574 remote_settings_.max_header_list_size = param.value;
580 return send_settings_ack();
586 return send_frame(
frame);
600 auto read_handler = [
this](std::error_code ec, std::size_t bytes_read) {
601 if (ec || bytes_read != 9) {
602 if (ec != asio::error::eof && ec != asio::error::operation_aborted) {
603 if (error_handler_) {
604 error_handler_(std::string(
"Read error: ") + ec.message());
612 if (header_result.is_err()) {
613 if (error_handler_) {
614 error_handler_(
"Failed to parse frame header");
620 auto header = header_result.value();
621 if (header.length > 0) {
622 read_frame_payload(header.length);
626 if (frame_result.is_ok()) {
627 process_frame(std::move(frame_result.value()));
634 asio::async_read(*tls_socket_, asio::buffer(frame_header_buffer_), read_handler);
636 asio::async_read(*plain_socket_, asio::buffer(frame_header_buffer_), read_handler);
646 read_buffer_.resize(9 + length);
647 std::copy(frame_header_buffer_.begin(), frame_header_buffer_.end(), read_buffer_.begin());
649 auto payload_buffer = asio::buffer(read_buffer_.data() + 9, length);
651 auto read_handler = [
this](std::error_code ec, std::size_t ) {
653 if (ec != asio::error::eof && ec != asio::error::operation_aborted) {
654 if (error_handler_) {
655 error_handler_(std::string(
"Read payload error: ") + ec.message());
663 if (frame_result.is_ok()) {
664 process_frame(std::move(frame_result.value()));
666 if (error_handler_) {
667 error_handler_(
"Failed to parse frame");
675 asio::async_read(*tls_socket_, payload_buffer, read_handler);
677 asio::async_read(*plain_socket_, payload_buffer, read_handler);
683 auto data = f.serialize();
687 asio::write(*tls_socket_, asio::buffer(
data), ec);
689 asio::write(*plain_socket_, asio::buffer(
data), ec);
695 std::string(
"Failed to send frame: ") + ec.message(),
696 "http2_server_connection");
704 auto& header = f->header();
706 switch (header.type) {
710 return handle_settings_frame(*settings_f);
717 return handle_headers_frame(*headers_f);
722 auto* data_f =
dynamic_cast<data_frame*
>(f.get());
724 return handle_data_frame(*data_f);
731 return handle_rst_stream_frame(*rst_f);
736 auto* ping_f =
dynamic_cast<ping_frame*
>(f.get());
738 return handle_ping_frame(*ping_f);
745 return handle_goaway_frame(*goaway_f);
752 return handle_window_update_frame(*wu_f);
766 std::lock_guard<std::mutex> lock(streams_mutex_);
768 auto it = streams_.find(stream_id);
769 if (it != streams_.end()) {
777 stream.
window_size =
static_cast<int32_t
>(local_settings_.initial_window_size);
779 auto [iter, inserted] = streams_.emplace(stream_id, std::move(stream));
780 if (stream_id > last_stream_id_) {
781 last_stream_id_ = stream_id;
784 return &iter->second;
789 std::lock_guard<std::mutex> lock(streams_mutex_);
790 streams_.erase(stream_id);
795 uint32_t stream_id = f.header().stream_id;
796 auto* stream = get_or_create_stream(stream_id);
801 "Failed to create stream",
802 "http2_server_connection");
806 auto header_block = f.header_block();
807 std::vector<uint8_t> block_data(header_block.begin(), header_block.end());
808 auto headers_result = decoder_.decode(block_data);
810 if (headers_result.is_err()) {
817 headers_result.error().code,
818 headers_result.error().message,
819 "http2_server_connection");
822 stream->request_headers = headers_result.value();
824 if (f.is_end_stream()) {
826 dispatch_request(stream_id);
827 }
else if (f.is_end_headers()) {
828 stream->headers_complete =
true;
836 uint32_t stream_id = f.header().stream_id;
838 std::lock_guard<std::mutex> lock(streams_mutex_);
839 auto it = streams_.find(stream_id);
840 if (it == streams_.end()) {
843 return send_frame(rst);
846 auto& stream = it->second;
847 auto data = f.data();
848 stream.request_body.insert(stream.request_body.end(),
data.begin(),
data.end());
851 size_t data_size =
data.size();
859 send_frame(stream_wu);
862 if (f.is_end_stream()) {
864 stream.body_complete =
true;
868 dispatch_request(stream_id);
876 close_stream(f.header().stream_id);
888 return send_frame(ack);
900 uint32_t stream_id = f.header().stream_id;
901 int32_t increment =
static_cast<int32_t
>(f.window_size_increment());
903 if (stream_id == 0) {
905 connection_window_size_ += increment;
908 std::lock_guard<std::mutex> lock(streams_mutex_);
909 auto it = streams_.find(stream_id);
910 if (it != streams_.end()) {
911 it->second.window_size += increment;
920 if (!request_handler_) {
926 std::lock_guard<std::mutex> lock(streams_mutex_);
927 auto it = streams_.find(stream_id);
928 if (it == streams_.end()) {
931 stream = &it->second;
939 auto encoder_ptr = std::make_shared<hpack_encoder>(encoder_);
940 auto weak_this = weak_from_this();
947 auto self = weak_this.lock();
952 "http2_server_stream");
954 return self->send_frame(f);
956 remote_settings_.max_frame_size);
960 request_handler_(server_stream, server_stream.request());
961 }
catch (
const std::exception& e) {
962 if (error_handler_) {
963 error_handler_(std::string(
"Request handler exception: ") + e.what());
968 close_stream(stream_id);
DATA frame (RFC 7540 Section 6.1)
Base class for HTTP/2 frames.
static auto parse(std::span< const uint8_t > data) -> Result< std::unique_ptr< frame > >
Parse frame from raw bytes.
GOAWAY frame (RFC 7540 Section 6.8)
HPACK header decoder (RFC 7541)
HPACK header encoder (RFC 7541)
http2_server_connection(uint64_t connection_id, asio::ip::tcp::socket socket, const http2_settings &settings, http2_server::request_handler_t request_handler, http2_server::error_handler_t error_handler)
Construct server connection with plain socket.
auto handle_data_frame(const data_frame &f) -> VoidResult
auto close_stream(uint32_t stream_id) -> void
auto start_reading() -> void
auto get_or_create_stream(uint32_t stream_id) -> http2_stream *
auto read_frame_payload(uint32_t length) -> void
~http2_server_connection()
Destructor.
auto read_connection_preface() -> void
auto start() -> VoidResult
Start connection handling.
auto stream_count() const -> size_t
Get number of active streams.
auto handle_goaway_frame(const goaway_frame &f) -> VoidResult
auto stop() -> VoidResult
Stop connection.
auto connection_id() const -> uint64_t
Get connection identifier.
auto send_settings_ack() -> VoidResult
auto handle_window_update_frame(const window_update_frame &f) -> VoidResult
auto process_frame(std::unique_ptr< frame > f) -> VoidResult
auto handle_settings_frame(const settings_frame &frame) -> VoidResult
auto handle_rst_stream_frame(const rst_stream_frame &f) -> VoidResult
auto send_frame(const frame &f) -> VoidResult
auto send_settings() -> VoidResult
auto handle_headers_frame(const headers_frame &f) -> VoidResult
auto read_frame_header() -> void
auto is_alive() const -> bool
Check if connection is alive.
std::atomic< bool > is_alive_
auto dispatch_request(uint32_t stream_id) -> void
uint64_t connection_id_
Connection identifier.
std::map< uint32_t, http2_stream > streams_
auto handle_ping_frame(const ping_frame &f) -> VoidResult
std::mutex streams_mutex_
Server-side HTTP/2 stream for sending responses.
http2_server(std::string_view server_id)
Construct HTTP/2 server.
std::function< void( const std::string &error_message)> error_handler_t
Error handler function type.
~http2_server()
Destructor - stops server gracefully.
auto stop() -> VoidResult
Stop the server.
std::atomic< bool > is_running_
auto start(unsigned short port) -> VoidResult
Start HTTP/2 server without TLS (h2c - HTTP/2 cleartext)
auto start_tls(unsigned short port, const tls_config &config) -> VoidResult
Start HTTP/2 server with TLS.
std::function< void( http2_server_stream &stream, const http2_request &request)> request_handler_t
Request handler function type.
PING frame (RFC 7540 Section 6.7)
RST_STREAM frame (RFC 7540 Section 6.4)
SETTINGS frame (RFC 7540 Section 6.5)
WINDOW_UPDATE frame (RFC 7540 Section 6.9)
constexpr int internal_error
constexpr int already_exists
constexpr int bind_failed
constexpr int send_failed
@ compression_error
Compression state not updated.
@ stream_closed
Frame received for closed stream.
setting_identifier
SETTINGS frame parameter identifiers (RFC 7540 Section 6.5.2)
@ max_frame_size
SETTINGS_MAX_FRAME_SIZE.
@ max_header_list_size
SETTINGS_MAX_HEADER_LIST_SIZE.
@ max_concurrent_streams
SETTINGS_MAX_CONCURRENT_STREAMS.
@ enable_push
SETTINGS_ENABLE_PUSH.
@ header_table_size
SETTINGS_HEADER_TABLE_SIZE.
@ initial_window_size
SETTINGS_INITIAL_WINDOW_SIZE.
@ open
Stream open and active.
@ half_closed_remote
Remote end closed, local can send.
@ settings
SETTINGS frame.
@ rst_stream
RST_STREAM frame.
@ window_update
WINDOW_UPDATE frame.
VoidResult error_void(int code, const std::string &message, const std::string &source="network_system", const std::string &details="")
static auto from_headers(const std::vector< http_header > &parsed_headers) -> http2_request
Create http2_request from parsed headers.
HTTP/2 connection settings.
HTTP/2 stream state and data.
std::vector< uint8_t > request_body
Request body.
std::vector< http_header > request_headers
Request headers.
stream_state state
Current state.
uint32_t stream_id
Stream identifier.
int32_t window_size
Flow control window.
TLS configuration for HTTP/2 server.