Network System 0.1.1
High-performance modular networking library for scalable client-server applications
Loading...
Searching...
No Matches
http2_server.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2024, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
6
7#include <algorithm>
8#include <stdexcept>
9
11{
12 // =========================================================================
13 // http2_server implementation
14 // =========================================================================
15
16 http2_server::http2_server(std::string_view server_id)
17 : server_id_(server_id)
18 , stop_future_(stop_promise_.get_future())
19 , encoder_(std::make_shared<hpack_encoder>(settings_.header_table_size))
20 , decoder_(std::make_shared<hpack_decoder>(settings_.header_table_size))
21 {
22 }
23
25 {
26 if (is_running_) {
27 stop();
28 }
29 }
30
31 auto http2_server::start(unsigned short port) -> VoidResult
32 {
33 if (is_running_) {
34 return error_void(
36 "Server already running",
37 "http2_server");
38 }
39
40 try {
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());
44
45 acceptor_ = std::make_unique<asio::ip::tcp::acceptor>(
46 *io_context_,
47 asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port));
48
49 use_tls_ = false;
50 is_running_ = true;
51
52 // Start I/O thread
53 io_future_ = std::async(std::launch::async, [this]() { run_io(); });
54
55 // Start accepting connections
56 do_accept();
57 start_cleanup_timer();
58
59 return ok();
60 } catch (const std::exception& e) {
61 return error_void(
63 std::string("Failed to start server: ") + e.what(),
64 "http2_server");
65 }
66 }
67
68 auto http2_server::start_tls(unsigned short port, const tls_config& config) -> VoidResult
69 {
70 if (is_running_) {
71 return error_void(
73 "Server already running",
74 "http2_server");
75 }
76
77 try {
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());
81
82 // Setup TLS context
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);
90
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);
93
94 if (!config.ca_file.empty()) {
95 ssl_context_->load_verify_file(config.ca_file);
96 }
97
98 if (config.verify_client) {
99 ssl_context_->set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert);
100 }
101
102 // Set ALPN callback for HTTP/2
103 SSL_CTX_set_alpn_select_cb(
104 ssl_context_->native_handle(),
105 [](SSL* /*ssl*/, const unsigned char** out, unsigned char* outlen,
106 const unsigned char* in, unsigned int inlen, void* /*arg*/) -> int {
107 // Look for "h2" in client's ALPN list
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') {
114 *out = client;
115 *outlen = 2;
116 return SSL_TLSEXT_ERR_OK;
117 }
118 client += len;
119 }
120 return SSL_TLSEXT_ERR_NOACK;
121 },
122 nullptr);
123
124 acceptor_ = std::make_unique<asio::ip::tcp::acceptor>(
125 *io_context_,
126 asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port));
127
128 use_tls_ = true;
129 is_running_ = true;
130
131 // Start I/O thread
132 io_future_ = std::async(std::launch::async, [this]() { run_io(); });
133
134 // Start accepting connections
135 do_accept_tls();
136 start_cleanup_timer();
137
138 return ok();
139 } catch (const std::exception& e) {
140 return error_void(
142 std::string("Failed to start TLS server: ") + e.what(),
143 "http2_server");
144 }
145 }
146
147 auto http2_server::stop() -> VoidResult
148 {
149 if (!is_running_) {
150 return ok();
151 }
152
153 is_running_ = false;
154
155 // Close acceptor
156 if (acceptor_ && acceptor_->is_open()) {
157 std::error_code ec;
158 acceptor_->close(ec);
159 }
160
161 // Stop all connections
162 {
163 std::lock_guard<std::mutex> lock(connections_mutex_);
164 for (auto& [id, conn] : connections_) {
165 conn->stop();
166 }
167 connections_.clear();
168 }
169
170 // Stop cleanup timer
171 if (cleanup_timer_) {
172 cleanup_timer_->cancel();
173 }
174
175 stop_io();
176
177 // Signal stop
178 try {
179 stop_promise_.set_value();
180 } catch (...) {
181 // Already set
182 }
183
184 return ok();
185 }
186
187 auto http2_server::is_running() const -> bool
188 {
189 return is_running_;
190 }
191
192 auto http2_server::wait() -> void
193 {
194 stop_future_.wait();
195 }
196
197 auto http2_server::set_request_handler(request_handler_t handler) -> void
198 {
199 request_handler_ = std::move(handler);
200 }
201
202 auto http2_server::set_error_handler(error_handler_t handler) -> void
203 {
204 error_handler_ = std::move(handler);
205 }
206
207 auto http2_server::set_settings(const http2_settings& settings) -> void
208 {
209 settings_ = settings;
210 encoder_->set_max_table_size(settings.header_table_size);
211 decoder_->set_max_table_size(settings.header_table_size);
212 }
213
214 auto http2_server::get_settings() const -> http2_settings
215 {
216 return settings_;
217 }
218
219 auto http2_server::active_connections() const -> size_t
220 {
221 std::lock_guard<std::mutex> lock(const_cast<std::mutex&>(connections_mutex_));
222 return connections_.size();
223 }
224
225 auto http2_server::active_streams() const -> size_t
226 {
227 std::lock_guard<std::mutex> lock(const_cast<std::mutex&>(connections_mutex_));
228 size_t total = 0;
229 for (const auto& [id, conn] : connections_) {
230 total += conn->stream_count();
231 }
232 return total;
233 }
234
235 auto http2_server::server_id() const -> std::string_view
236 {
237 return server_id_;
238 }
239
240 auto http2_server::do_accept() -> void
241 {
242 if (!is_running_ || !acceptor_) {
243 return;
244 }
245
246 acceptor_->async_accept(
247 [this](std::error_code ec, asio::ip::tcp::socket socket) {
248 handle_accept(ec, std::move(socket));
249 });
250 }
251
252 auto http2_server::do_accept_tls() -> void
253 {
254 if (!is_running_ || !acceptor_) {
255 return;
256 }
257
258 acceptor_->async_accept(
259 [this](std::error_code ec, asio::ip::tcp::socket socket) {
260 handle_accept_tls(ec, std::move(socket));
261 });
262 }
263
264 auto http2_server::handle_accept(std::error_code ec, asio::ip::tcp::socket socket) -> void
265 {
266 if (!is_running_) {
267 return;
268 }
269
270 if (ec) {
271 if (error_handler_) {
272 error_handler_(std::string("Accept error: ") + ec.message());
273 }
274 } else {
275 uint64_t conn_id = next_connection_id_++;
276 auto conn = std::make_shared<http2_server_connection>(
277 conn_id,
278 std::move(socket),
279 settings_,
280 request_handler_,
281 error_handler_);
282
283 add_connection(conn);
284 conn->start();
285 }
286
287 // Continue accepting
288 do_accept();
289 }
290
291 auto http2_server::handle_accept_tls(std::error_code ec, asio::ip::tcp::socket socket) -> void
292 {
293 if (!is_running_) {
294 return;
295 }
296
297 if (ec) {
298 if (error_handler_) {
299 error_handler_(std::string("Accept error: ") + ec.message());
300 }
301 do_accept_tls();
302 return;
303 }
304
305 auto tls_socket = std::make_unique<asio::ssl::stream<asio::ip::tcp::socket>>(
306 std::move(socket), *ssl_context_);
307
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 {
312 if (ec) {
313 if (error_handler_) {
314 error_handler_(std::string("TLS handshake error: ") + ec.message());
315 }
316 } else {
317 uint64_t conn_id = next_connection_id_++;
318 auto conn = std::make_shared<http2_server_connection>(
319 conn_id,
320 std::move(tls_socket),
321 settings_,
322 request_handler_,
323 error_handler_);
324
325 add_connection(conn);
326 conn->start();
327 }
328
329 // Continue accepting
330 do_accept_tls();
331 });
332 }
333
334 auto http2_server::add_connection(std::shared_ptr<http2_server_connection> conn) -> void
335 {
336 std::lock_guard<std::mutex> lock(connections_mutex_);
337 connections_[conn->connection_id()] = std::move(conn);
338 }
339
340 auto http2_server::remove_connection(uint64_t connection_id) -> void
341 {
342 std::lock_guard<std::mutex> lock(connections_mutex_);
343 connections_.erase(connection_id);
344 }
345
346 auto http2_server::cleanup_dead_connections() -> void
347 {
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);
352 } else {
353 ++it;
354 }
355 }
356 }
357
358 auto http2_server::start_cleanup_timer() -> void
359 {
360 if (!io_context_) {
361 return;
362 }
363
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();
370 }
371 });
372 }
373
374 auto http2_server::run_io() -> void
375 {
376 if (io_context_) {
377 io_context_->run();
378 }
379 }
380
381 auto http2_server::stop_io() -> void
382 {
383 if (work_guard_) {
384 work_guard_.reset();
385 }
386
387 if (io_context_) {
388 io_context_->stop();
389 }
390
391 if (io_future_.valid()) {
392 io_future_.wait();
393 }
394 }
395
396 // =========================================================================
397 // http2_server_connection implementation
398 // =========================================================================
399
400 http2_server_connection::http2_server_connection(
401 uint64_t connection_id,
402 asio::ip::tcp::socket socket,
403 const http2_settings& settings,
404 http2_server::request_handler_t request_handler,
405 http2_server::error_handler_t error_handler)
406 : connection_id_(connection_id)
407 , use_tls_(false)
408 , plain_socket_(std::make_unique<asio::ip::tcp::socket>(std::move(socket)))
409 , local_settings_(settings)
410 , encoder_(settings.header_table_size)
411 , decoder_(settings.header_table_size)
412 , request_handler_(std::move(request_handler))
413 , error_handler_(std::move(error_handler))
414 , frame_header_buffer_{}
415 {
416 }
417
419 uint64_t connection_id,
420 std::unique_ptr<asio::ssl::stream<asio::ip::tcp::socket>> socket,
422 http2_server::request_handler_t request_handler,
423 http2_server::error_handler_t error_handler)
424 : connection_id_(connection_id)
425 , use_tls_(true)
426 , tls_socket_(std::move(socket))
427 , local_settings_(settings)
428 , encoder_(settings.header_table_size)
429 , decoder_(settings.header_table_size)
430 , request_handler_(std::move(request_handler))
431 , error_handler_(std::move(error_handler))
432 , frame_header_buffer_{}
433 {
434 }
435
440
442 {
443 // Read connection preface from client
444 read_connection_preface();
445 return ok();
446 }
447
449 {
450 if (!is_alive_) {
451 return ok();
452 }
453
454 is_alive_ = false;
455
456 // Close socket
457 std::error_code ec;
458 if (use_tls_ && tls_socket_) {
459 tls_socket_->lowest_layer().close(ec);
460 } else if (plain_socket_) {
461 plain_socket_->close(ec);
462 }
463
464 return ok();
465 }
466
468 {
469 return is_alive_;
470 }
471
473 {
474 return connection_id_;
475 }
476
478 {
479 std::lock_guard<std::mutex> lock(const_cast<std::mutex&>(streams_mutex_));
480 return streams_.size();
481 }
482
484 {
485 constexpr size_t PREFACE_SIZE = 24; // "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
486 auto buffer = std::make_shared<std::vector<uint8_t>>(PREFACE_SIZE);
487
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");
492 }
493 stop();
494 return;
495 }
496
497 // Verify 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");
502 }
503 stop();
504 return;
505 }
506
507 preface_received_ = true;
508
509 // Send our SETTINGS frame
510 auto result = send_settings();
511 if (result.is_err()) {
512 if (error_handler_) {
513 error_handler_("Failed to send settings");
514 }
515 stop();
516 return;
517 }
518
519 // Start reading frames
520 start_reading();
521 };
522
523 if (use_tls_) {
524 asio::async_read(*tls_socket_, asio::buffer(*buffer), read_handler);
525 } else {
526 asio::async_read(*plain_socket_, asio::buffer(*buffer), read_handler);
527 }
528 }
529
531 {
532 std::vector<setting_parameter> params = {
533 {static_cast<uint16_t>(setting_identifier::max_concurrent_streams),
534 local_settings_.max_concurrent_streams},
535 {static_cast<uint16_t>(setting_identifier::initial_window_size),
536 local_settings_.initial_window_size},
537 {static_cast<uint16_t>(setting_identifier::max_frame_size),
538 local_settings_.max_frame_size},
539 {static_cast<uint16_t>(setting_identifier::header_table_size),
540 local_settings_.header_table_size},
541 };
542
543 settings_frame frame(params, false);
544 return send_frame(frame);
545 }
546
548 {
549 if (frame.is_ack()) {
550 // Settings acknowledgment received
551 return ok();
552 }
553
554 // Apply peer settings
555 for (const auto& param : frame.settings()) {
556 switch (static_cast<setting_identifier>(param.identifier)) {
558 remote_settings_.header_table_size = param.value;
559 encoder_.set_max_table_size(param.value);
560 break;
562 remote_settings_.enable_push = (param.value != 0);
563 break;
565 remote_settings_.max_concurrent_streams = param.value;
566 break;
568 remote_settings_.initial_window_size = param.value;
569 break;
571 remote_settings_.max_frame_size = param.value;
572 break;
574 remote_settings_.max_header_list_size = param.value;
575 break;
576 }
577 }
578
579 // Send ACK
580 return send_settings_ack();
581 }
582
584 {
585 settings_frame frame({}, true);
586 return send_frame(frame);
587 }
588
590 {
591 read_frame_header();
592 }
593
595 {
596 if (!is_alive_) {
597 return;
598 }
599
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());
605 }
606 }
607 stop();
608 return;
609 }
610
611 auto header_result = frame_header::parse(frame_header_buffer_);
612 if (header_result.is_err()) {
613 if (error_handler_) {
614 error_handler_("Failed to parse frame header");
615 }
616 stop();
617 return;
618 }
619
620 auto header = header_result.value();
621 if (header.length > 0) {
622 read_frame_payload(header.length);
623 } else {
624 // Process frame with empty payload
625 auto frame_result = frame::parse(frame_header_buffer_);
626 if (frame_result.is_ok()) {
627 process_frame(std::move(frame_result.value()));
628 }
629 read_frame_header();
630 }
631 };
632
633 if (use_tls_) {
634 asio::async_read(*tls_socket_, asio::buffer(frame_header_buffer_), read_handler);
635 } else {
636 asio::async_read(*plain_socket_, asio::buffer(frame_header_buffer_), read_handler);
637 }
638 }
639
641 {
642 if (!is_alive_) {
643 return;
644 }
645
646 read_buffer_.resize(9 + length);
647 std::copy(frame_header_buffer_.begin(), frame_header_buffer_.end(), read_buffer_.begin());
648
649 auto payload_buffer = asio::buffer(read_buffer_.data() + 9, length);
650
651 auto read_handler = [this](std::error_code ec, std::size_t /*bytes_read*/) {
652 if (ec) {
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());
656 }
657 }
658 stop();
659 return;
660 }
661
662 auto frame_result = frame::parse(read_buffer_);
663 if (frame_result.is_ok()) {
664 process_frame(std::move(frame_result.value()));
665 } else {
666 if (error_handler_) {
667 error_handler_("Failed to parse frame");
668 }
669 }
670
671 read_frame_header();
672 };
673
674 if (use_tls_) {
675 asio::async_read(*tls_socket_, payload_buffer, read_handler);
676 } else {
677 asio::async_read(*plain_socket_, payload_buffer, read_handler);
678 }
679 }
680
682 {
683 auto data = f.serialize();
684
685 std::error_code ec;
686 if (use_tls_) {
687 asio::write(*tls_socket_, asio::buffer(data), ec);
688 } else {
689 asio::write(*plain_socket_, asio::buffer(data), ec);
690 }
691
692 if (ec) {
693 return error_void(
695 std::string("Failed to send frame: ") + ec.message(),
696 "http2_server_connection");
697 }
698
699 return ok();
700 }
701
702 auto http2_server_connection::process_frame(std::unique_ptr<frame> f) -> VoidResult
703 {
704 auto& header = f->header();
705
706 switch (header.type) {
708 auto* settings_f = dynamic_cast<settings_frame*>(f.get());
709 if (settings_f) {
710 return handle_settings_frame(*settings_f);
711 }
712 break;
713 }
714 case frame_type::headers: {
715 auto* headers_f = dynamic_cast<headers_frame*>(f.get());
716 if (headers_f) {
717 return handle_headers_frame(*headers_f);
718 }
719 break;
720 }
721 case frame_type::data: {
722 auto* data_f = dynamic_cast<data_frame*>(f.get());
723 if (data_f) {
724 return handle_data_frame(*data_f);
725 }
726 break;
727 }
729 auto* rst_f = dynamic_cast<rst_stream_frame*>(f.get());
730 if (rst_f) {
731 return handle_rst_stream_frame(*rst_f);
732 }
733 break;
734 }
735 case frame_type::ping: {
736 auto* ping_f = dynamic_cast<ping_frame*>(f.get());
737 if (ping_f) {
738 return handle_ping_frame(*ping_f);
739 }
740 break;
741 }
742 case frame_type::goaway: {
743 auto* goaway_f = dynamic_cast<goaway_frame*>(f.get());
744 if (goaway_f) {
745 return handle_goaway_frame(*goaway_f);
746 }
747 break;
748 }
750 auto* wu_f = dynamic_cast<window_update_frame*>(f.get());
751 if (wu_f) {
752 return handle_window_update_frame(*wu_f);
753 }
754 break;
755 }
756 default:
757 // Ignore unknown frame types
758 break;
759 }
760
761 return ok();
762 }
763
765 {
766 std::lock_guard<std::mutex> lock(streams_mutex_);
767
768 auto it = streams_.find(stream_id);
769 if (it != streams_.end()) {
770 return &it->second;
771 }
772
773 // Create new stream
774 http2_stream stream;
775 stream.stream_id = stream_id;
776 stream.state = stream_state::open;
777 stream.window_size = static_cast<int32_t>(local_settings_.initial_window_size);
778
779 auto [iter, inserted] = streams_.emplace(stream_id, std::move(stream));
780 if (stream_id > last_stream_id_) {
781 last_stream_id_ = stream_id;
782 }
783
784 return &iter->second;
785 }
786
787 auto http2_server_connection::close_stream(uint32_t stream_id) -> void
788 {
789 std::lock_guard<std::mutex> lock(streams_mutex_);
790 streams_.erase(stream_id);
791 }
792
794 {
795 uint32_t stream_id = f.header().stream_id;
796 auto* stream = get_or_create_stream(stream_id);
797
798 if (!stream) {
799 return error_void(
801 "Failed to create stream",
802 "http2_server_connection");
803 }
804
805 // Decode headers
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);
809
810 if (headers_result.is_err()) {
811 // Send GOAWAY with COMPRESSION_ERROR
812 goaway_frame goaway(last_stream_id_,
813 static_cast<uint32_t>(error_code::compression_error));
814 send_frame(goaway);
815 stop();
816 return error_void(
817 headers_result.error().code,
818 headers_result.error().message,
819 "http2_server_connection");
820 }
821
822 stream->request_headers = headers_result.value();
823
824 if (f.is_end_stream()) {
825 stream->state = stream_state::half_closed_remote;
826 dispatch_request(stream_id);
827 } else if (f.is_end_headers()) {
828 stream->headers_complete = true;
829 }
830
831 return ok();
832 }
833
835 {
836 uint32_t stream_id = f.header().stream_id;
837
838 std::lock_guard<std::mutex> lock(streams_mutex_);
839 auto it = streams_.find(stream_id);
840 if (it == streams_.end()) {
841 // Stream doesn't exist
842 rst_stream_frame rst(stream_id, static_cast<uint32_t>(error_code::stream_closed));
843 return send_frame(rst);
844 }
845
846 auto& stream = it->second;
847 auto data = f.data();
848 stream.request_body.insert(stream.request_body.end(), data.begin(), data.end());
849
850 // Send WINDOW_UPDATE if needed
851 size_t data_size = data.size();
852 if (data_size > 0) {
853 // Update connection window
854 window_update_frame conn_wu(0, static_cast<uint32_t>(data_size));
855 send_frame(conn_wu);
856
857 // Update stream window
858 window_update_frame stream_wu(stream_id, static_cast<uint32_t>(data_size));
859 send_frame(stream_wu);
860 }
861
862 if (f.is_end_stream()) {
864 stream.body_complete = true;
865
866 // Need to unlock before dispatching
867 lock.~lock_guard();
868 dispatch_request(stream_id);
869 }
870
871 return ok();
872 }
873
875 {
876 close_stream(f.header().stream_id);
877 return ok();
878 }
879
881 {
882 if (f.is_ack()) {
883 return ok();
884 }
885
886 // Send PING ACK
887 ping_frame ack(f.opaque_data(), true);
888 return send_frame(ack);
889 }
890
892 {
893 // Client is closing connection
894 stop();
895 return ok();
896 }
897
899 {
900 uint32_t stream_id = f.header().stream_id;
901 int32_t increment = static_cast<int32_t>(f.window_size_increment());
902
903 if (stream_id == 0) {
904 // Connection-level flow control
905 connection_window_size_ += increment;
906 } else {
907 // Stream-level flow control
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;
912 }
913 }
914
915 return ok();
916 }
917
918 auto http2_server_connection::dispatch_request(uint32_t stream_id) -> void
919 {
920 if (!request_handler_) {
921 return;
922 }
923
924 http2_stream* stream = nullptr;
925 {
926 std::lock_guard<std::mutex> lock(streams_mutex_);
927 auto it = streams_.find(stream_id);
928 if (it == streams_.end()) {
929 return;
930 }
931 stream = &it->second;
932 }
933
934 // Create request from headers
935 auto request = http2_request::from_headers(stream->request_headers);
936 request.body = stream->request_body;
937
938 // Create server stream for response
939 auto encoder_ptr = std::make_shared<hpack_encoder>(encoder_);
940 auto weak_this = weak_from_this();
941
942 http2_server_stream server_stream(
943 stream_id,
944 std::move(request),
945 encoder_ptr,
946 [weak_this](const frame& f) -> VoidResult {
947 auto self = weak_this.lock();
948 if (!self) {
949 return error_void(
951 "Connection closed",
952 "http2_server_stream");
953 }
954 return self->send_frame(f);
955 },
956 remote_settings_.max_frame_size);
957
958 // Call request handler
959 try {
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());
964 }
965 }
966
967 // Close stream after handler completes
968 close_stream(stream_id);
969 }
970
971} // namespace kcenon::network::protocols::http2
DATA frame (RFC 7540 Section 6.1)
Definition frame.h:139
Base class for HTTP/2 frames.
Definition frame.h:82
static auto parse(std::span< const uint8_t > data) -> Result< std::unique_ptr< frame > >
Parse frame from raw bytes.
Definition frame.cpp:94
GOAWAY frame (RFC 7540 Section 6.8)
Definition frame.h:387
HEADERS frame (RFC 7540 Section 6.2)
Definition frame.h:189
HPACK header decoder (RFC 7541)
Definition hpack.h:209
HPACK header encoder (RFC 7541)
Definition hpack.h:159
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 get_or_create_stream(uint32_t stream_id) -> http2_stream *
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 connection_id() const -> uint64_t
Get connection identifier.
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 handle_headers_frame(const headers_frame &f) -> VoidResult
auto is_alive() const -> bool
Check if connection is alive.
auto handle_ping_frame(const ping_frame &f) -> VoidResult
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.
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)
Definition frame.h:344
RST_STREAM frame (RFC 7540 Section 6.4)
Definition frame.h:307
SETTINGS frame (RFC 7540 Section 6.5)
Definition frame.h:265
WINDOW_UPDATE frame (RFC 7540 Section 6.9)
Definition frame.h:438
struct ssl_st SSL
Definition crypto.h:21
tracing_config config
Definition exporters.cpp:29
@ 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)
Definition frame.h:248
@ max_header_list_size
SETTINGS_MAX_HEADER_LIST_SIZE.
@ max_concurrent_streams
SETTINGS_MAX_CONCURRENT_STREAMS.
@ initial_window_size
SETTINGS_INITIAL_WINDOW_SIZE.
@ half_closed_remote
Remote end closed, local can send.
VoidResult error_void(int code, const std::string &message, const std::string &source="network_system", const std::string &details="")
VoidResult ok()
static auto parse(std::span< const uint8_t > data) -> Result< frame_header >
Parse frame header from raw bytes.
Definition frame.cpp:52
static auto from_headers(const std::vector< http_header > &parsed_headers) -> http2_request
Create http2_request from parsed headers.
std::vector< uint8_t > request_body
Request body.
std::vector< http_header > request_headers
Request headers.
TLS configuration for HTTP/2 server.