14#pragma comment(lib, "ws2_32.lib")
19#include <netinet/in.h>
21#include <sys/socket.h>
29auto generate_random_cid() -> protocols::quic::connection_id
31 std::random_device rd;
32 std::mt19937 gen(rd());
35 std::uniform_int_distribution<unsigned short> dist(0, 255);
37 std::vector<uint8_t> cid_bytes(8);
38 for (
auto&
byte : cid_bytes) {
39 byte =
static_cast<uint8_t
>(dist(gen));
42 return protocols::quic::connection_id(cid_bytes);
45auto close_socket(
int fd) ->
void
54auto set_nonblocking(
int fd) ->
bool
58 return ioctlsocket(fd, FIONBIO, &mode) == 0;
60 int flags = fcntl(fd, F_GETFL, 0);
64 return fcntl(fd, F_SETFL, flags | O_NONBLOCK) != -1;
72 std::string_view connection_id)
73 : connection_id_(connection_id)
85 if (!is_connected_.load()) {
87 static_cast<int>(std::errc::not_connected),
88 "QUIC connection is not established"
94 static_cast<int>(std::errc::not_connected),
95 "QUIC connection object is null"
100 std::vector<uint8_t> buffer(data.size());
101 std::memcpy(buffer.data(), data.data(), data.size());
104 std::lock_guard lock(send_queue_mutex_);
105 send_queue_.push_back(std::move(buffer));
113 if (!is_connected_.load()) {
115 static_cast<int>(std::errc::not_connected),
116 "QUIC connection is not established"
122 static_cast<int>(std::errc::not_connected),
123 "QUIC connection object is null"
128 std::lock_guard lock(send_queue_mutex_);
129 send_queue_.push_back(std::move(data));
159 if (is_connected_.load() || running_.load()) {
161 static_cast<int>(std::errc::already_connected),
162 "Already connected or connecting"
167 std::lock_guard lock(endpoint_mutex_);
168 remote_endpoint_ = endpoint;
174 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
176 static_cast<int>(std::errc::io_error),
177 "Failed to initialize Winsock"
182 socket_fd_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
183 if (socket_fd_ < 0) {
185 static_cast<int>(std::errc::io_error),
186 "Failed to create UDP socket"
190 if (!set_nonblocking(socket_fd_)) {
191 close_socket(socket_fd_);
194 static_cast<int>(std::errc::io_error),
195 "Failed to set socket to non-blocking mode"
200 auto initial_dcid = generate_random_cid();
201 quic_conn_ = std::make_unique<protocols::quic::connection>(
false, initial_dcid);
212 if (config_.idle_timeout.count() > 0) {
214 static_cast<uint64_t
>(config_.idle_timeout.count());
217 quic_conn_->set_local_params(local_params);
220 if (config_.enable_pmtud) {
221 quic_conn_->enable_pmtud();
225 std::string server_name = config_.server_name;
226 if (server_name.empty()) {
227 server_name = endpoint.host;
230 auto hs_result = quic_conn_->start_handshake(server_name);
231 if (!hs_result.is_ok()) {
233 close_socket(socket_fd_);
236 static_cast<int>(std::errc::connection_refused),
237 "Failed to start QUIC handshake"
241 is_connecting_ =
true;
243 stop_requested_ =
false;
253 auto [host, port] = parse_url(url);
254 if (host.empty() || port == 0) {
256 static_cast<int>(std::errc::invalid_argument),
257 "Invalid QUIC URL format (expected: quic://host:port or host:port)"
269 (void)
quic_conn_->close(0,
"Connection closed by client");
294 std::lock_guard lock(callbacks_mutex_);
295 callbacks_ = std::move(callbacks);
305 options_.connect_timeout = timeout;
315 std::unique_lock lock(stop_mutex_);
316 stop_cv_.wait(lock, [
this] {
return !running_.load(); });
321 constexpr int POLL_TIMEOUT_MS = 10;
322 constexpr size_t MAX_PACKET_SIZE = 65535;
324 std::vector<uint8_t> recv_buffer(MAX_PACKET_SIZE);
325 sockaddr_in remote_addr{};
326 remote_addr.sin_family = AF_INET;
330 std::lock_guard lock(endpoint_mutex_);
331 remote_addr.sin_port = htons(remote_endpoint_.port);
333 struct addrinfo hints{}, *result =
nullptr;
334 hints.ai_family = AF_INET;
335 hints.ai_socktype = SOCK_DGRAM;
337 if (getaddrinfo(remote_endpoint_.host.c_str(),
nullptr, &hints, &result) == 0
339 remote_addr.sin_addr =
340 reinterpret_cast<sockaddr_in*
>(result->ai_addr)->sin_addr;
341 freeaddrinfo(result);
343 inet_pton(AF_INET, remote_endpoint_.host.c_str(), &remote_addr.sin_addr);
347 while (!stop_requested_.load()) {
354 int poll_result = WSAPoll(&pfd, 1, POLL_TIMEOUT_MS);
360 int poll_result = poll(&pfd, 1, POLL_TIMEOUT_MS);
363 if (poll_result > 0 && (pfd.revents & POLLIN)) {
365 sockaddr_in from_addr{};
366 socklen_t from_len =
sizeof(from_addr);
368 auto received = recvfrom(socket_fd_,
reinterpret_cast<char*
>(recv_buffer.data()),
369 static_cast<int>(recv_buffer.size()), 0,
370 reinterpret_cast<sockaddr*
>(&from_addr), &from_len);
373 std::span<const uint8_t> packet_data(recv_buffer.data(),
374 static_cast<size_t>(received));
375 auto result = quic_conn_->receive_packet(packet_data);
379 handle_state_change();
388 auto timeout = quic_conn_->next_timeout();
389 if (timeout && std::chrono::steady_clock::now() >= *timeout) {
390 quic_conn_->on_timeout();
395 handle_state_change();
399 auto packets = quic_conn_->generate_packets();
400 for (
const auto& packet : packets) {
401 sendto(socket_fd_,
reinterpret_cast<const char*
>(packet.data()),
402 static_cast<int>(packet.size()), 0,
403 reinterpret_cast<const sockaddr*
>(&remote_addr),
404 sizeof(remote_addr));
409 if (quic_conn_ && quic_conn_->is_closed()) {
415 is_connected_ =
false;
416 is_connecting_ =
false;
420 std::lock_guard lock(callbacks_mutex_);
421 if (callbacks_.on_disconnected) {
422 callbacks_.on_disconnected();
427 std::lock_guard lock(stop_mutex_);
429 stop_cv_.notify_all();
434 if (!quic_conn_ || !quic_conn_->is_established()) {
438 std::deque<std::vector<uint8_t>> data_to_send;
440 std::lock_guard lock(send_queue_mutex_);
441 data_to_send.swap(send_queue_);
444 for (
auto& data : data_to_send) {
446 auto& stream_mgr = quic_conn_->streams();
447 auto stream_result = stream_mgr.create_bidirectional_stream();
449 if (stream_result.is_ok()) {
450 uint64_t stream_id = stream_result.value();
451 auto* stream = stream_mgr.get_stream(stream_id);
453 (void)stream->write(data);
461 if (!quic_conn_ || !quic_conn_->is_established()) {
465 auto& stream_mgr = quic_conn_->streams();
468 for (uint64_t stream_id = 0; stream_id < 1000; stream_id += 4) {
469 auto* stream = stream_mgr.get_stream(stream_id);
470 if (stream && stream->has_data()) {
471 std::vector<uint8_t> buffer(4096);
472 auto read_result = stream->read(buffer);
473 if (read_result.is_ok() && read_result.value() > 0) {
474 buffer.resize(read_result.value());
476 std::lock_guard lock(callbacks_mutex_);
477 if (callbacks_.on_data) {
478 std::span<const std::byte> byte_span(
479 reinterpret_cast<const std::byte*
>(buffer.data()),
482 callbacks_.on_data(byte_span);
495 bool was_connecting = is_connecting_.load();
496 bool is_now_established = quic_conn_->is_established();
498 if (was_connecting && is_now_established) {
499 is_connecting_ =
false;
500 is_connected_ =
true;
502 std::lock_guard lock(callbacks_mutex_);
503 if (callbacks_.on_connected) {
504 callbacks_.on_connected();
508 if (quic_conn_->is_draining() || quic_conn_->is_closed()) {
509 is_connected_ =
false;
511 auto error_code = quic_conn_->close_error_code();
512 if (error_code && *error_code != 0) {
513 std::lock_guard lock(callbacks_mutex_);
514 if (callbacks_.on_error) {
515 callbacks_.on_error(std::make_error_code(std::errc::connection_reset));
525 -> std::pair<std::string, uint16_t>
527 std::string url_str(url);
530 const std::string quic_prefix =
"quic://";
531 if (url_str.substr(0, quic_prefix.size()) == quic_prefix) {
532 url_str = url_str.substr(quic_prefix.size());
536 auto colon_pos = url_str.rfind(
':');
537 if (colon_pos == std::string::npos) {
541 std::string host = url_str.substr(0, colon_pos);
542 std::string port_str = url_str.substr(colon_pos + 1);
546 port =
static_cast<uint16_t
>(std::stoi(port_str));
std::condition_variable stop_cv_
endpoint_info remote_endpoint_
endpoint_info local_endpoint_
auto local_endpoint() const noexcept -> endpoint_info override
Gets the local endpoint information.
auto connect(const endpoint_info &endpoint) -> VoidResult override
Connects to a remote endpoint using host/port.
auto is_connected() const noexcept -> bool override
Checks if the transport is currently connected.
std::string connection_id_
auto set_timeout(std::chrono::milliseconds timeout) -> void override
Sets the connection timeout.
auto close() noexcept -> void override
Closes the connection gracefully.
~quic_connection_adapter() override
Destructor ensures proper cleanup.
auto parse_url(std::string_view url) -> std::pair< std::string, uint16_t >
Parse URL to extract host and port.
auto remote_endpoint() const noexcept -> endpoint_info override
Gets the remote endpoint information.
auto is_connecting() const noexcept -> bool override
Checks if the connection is in the process of connecting.
std::atomic< bool > is_connecting_
std::unique_ptr< protocols::quic::connection > quic_conn_
std::atomic< bool > stop_requested_
auto io_thread_func() -> void
I/O processing thread function.
auto set_options(connection_options options) -> void override
Sets connection options.
std::atomic< bool > running_
auto wait_for_stop() -> void override
Blocks until the component has stopped.
auto handle_state_change() -> void
Handle connection state changes.
auto process_incoming() -> void
Process incoming packets.
auto process_outgoing() -> void
Process outgoing packets.
auto set_callbacks(connection_callbacks callbacks) -> void override
Sets all connection callbacks at once.
auto send(std::span< const std::byte > data) -> VoidResult override
Sends raw data to the remote endpoint.
std::mutex endpoint_mutex_
auto id() const noexcept -> std::string_view override
Gets the unique identifier for this transport/connection.
quic_connection_adapter(const protocol::quic::quic_config &config, std::string_view connection_id)
Constructs an adapter with QUIC configuration.
std::atomic< bool > is_connected_
VoidResult error_void(int code, const std::string &message, const std::string &source="network_system", const std::string &details="")
Configuration options for QUIC connections.
QUIC transport parameters (RFC 9000 Section 18)
uint64_t max_idle_timeout
Maximum idle timeout in milliseconds (0 = disabled)
uint64_t initial_max_data
Initial maximum data for connection (default: 0)
uint64_t initial_max_streams_uni
Initial maximum unidirectional streams.
uint64_t initial_max_streams_bidi
Initial maximum bidirectional streams.
uint64_t initial_max_stream_data_bidi_remote
Initial maximum data for remotely-initiated bidirectional streams.
uint64_t initial_max_stream_data_bidi_local
Initial maximum data for locally-initiated bidirectional streams.
uint64_t initial_max_stream_data_uni
Initial maximum data for unidirectional streams.
Callback functions for connection events.
Configuration options for connections.
Network endpoint information (host/port or URL)