#include "catch2/catch_test_macros.hpp" #include "Address.hpp" #include "protocol/quicr/QuicrConnection.hpp" #include "protocol/quicr/QuicrConnectionListener.hpp" #include "protocol/quicr/QuicrEncoder.hpp" #include "protocol/quicr/QuicrReliability.hpp" #include #include using namespace tw::net::quicr; TEST_CASE("Client begins with Hello datagram", "[quicr2]") { QuicrConnection connection(0, 0, tw::net::Address({}), nullptr); connection.send_initial_hello(); // should contain only the hello frame REQUIRE(connection.has_next_datagram() == true); auto buffer = connection.pop_datagram(); QuicrPacket header = QuicrDecoder::decode_packet(buffer); REQUIRE(header.type == QuicrPacketType::Initial); REQUIRE(header.destination_id == connection.peer_id()); REQUIRE(header.local_id == connection.self_id()); REQUIRE(header.frames.size() == 1); REQUIRE(header.frames[0].type == FrameType::Hello); REQUIRE(connection.has_next_datagram() == false); } TEST_CASE("Client wants to resend the Hello", "[quicr2]") { QuicrConnection connection(0, 0, tw::net::Address({}), nullptr); connection.send_initial_hello(); // should contain only the hello frame REQUIRE(connection.has_next_datagram() == true); auto buffer = connection.pop_datagram(); QuicrPacket header = QuicrDecoder::decode_packet(buffer); REQUIRE(connection.has_next_datagram() == false); std::this_thread::sleep_for(std::chrono::milliseconds(TW_NET_HELLO_RETRY_INTERVAL_IN_MILLIS)); REQUIRE(connection.has_next_datagram() == true); buffer = connection.pop_datagram(); header = QuicrDecoder::decode_packet(buffer); REQUIRE(header.type == QuicrPacketType::Initial); REQUIRE(header.destination_id == connection.peer_id()); REQUIRE(header.local_id == connection.self_id()); REQUIRE(header.frames.size() == 1); REQUIRE(header.frames[0].type == FrameType::Hello); REQUIRE(connection.has_next_datagram() == false); } TEST_CASE("Closed connection will setup connection IDs after Hello", "[quicr2]") { } TEST_CASE("Connection reacts to Hello with ACK & Hello", "[quicr2]") { QuicrConnection client(0, 0, tw::net::Address({}), nullptr); client.send_initial_hello(); auto hello = client.pop_datagram(); QuicrConnection server(0, 0, tw::net::Address({}), nullptr); server.process_datagram(hello); REQUIRE(server.has_next_datagram() == true); auto dgram = server.pop_datagram(); QuicrPacket packet = QuicrDecoder::decode_packet(dgram); REQUIRE(packet.type == QuicrPacketType::Initial); REQUIRE(packet.destination_id == server.peer_id()); REQUIRE(packet.local_id == server.self_id()); REQUIRE(packet.frames.size() == 2); REQUIRE(std::any_of(packet.frames.begin(), packet.frames.end(), [](const QuicrFrame& f) { return f.type == FrameType::Ack; })); REQUIRE(std::any_of(packet.frames.begin(), packet.frames.end(), [](const QuicrFrame& f) { return f.type == FrameType::Hello; })); } TEST_CASE("Both connections have correct IDs after Initial exchange", "[quicr2]") { QuicrConnection client(0, 0, tw::net::Address({}), nullptr); client.send_initial_hello(); auto client_hello = client.pop_datagram(); QuicrConnection server(0, 0, tw::net::Address({}), nullptr); server.process_datagram(client_hello); auto server_hello = server.pop_datagram(); client.process_datagram(server_hello); REQUIRE(client.self_id() == server.peer_id()); REQUIRE(client.peer_id() == server.self_id()); } TEST_CASE("When client receives ACK, it won't send the packet again", "[quicr2]") { QuicrConnection connection(0, 0, tw::net::Address({}), nullptr); connection.send_initial_hello(); // should contain only the hello frame REQUIRE(connection.has_next_datagram() == true); auto buffer = connection.pop_datagram(); QuicrPacket header = QuicrDecoder::decode_packet(buffer); REQUIRE(connection.has_next_datagram() == false); std::vector target(1200); size_t offset = 0; std::vector acks = { header.packet_number.value() }; QuicrFrame hello_frame = QuicrFrame::make_hello(); QuicrPacketEncoder encoder(target, offset, QuicrPacketType::Initial, 0, connection); encoder .encode_ack_frame(acks); connection.process_datagram(std::span(target).subspan(0, encoder.size())); std::this_thread::sleep_for(std::chrono::milliseconds(TW_NET_HELLO_RETRY_INTERVAL_IN_MILLIS)); REQUIRE(connection.has_next_datagram() == false); } TEST_CASE("Connection don't send ACK when packet has no reliable frames", "[quicr3]") { QuicrConnection connection(0, 0, tw::net::Address({}), nullptr); auto buffer = connection.pop_datagram(); QuicrPacket header = QuicrDecoder::decode_packet(buffer); std::vector target(1200); size_t offset = 0; std::string message = "Hello world"; QuicrPacketEncoder encoder(target, offset, QuicrPacketType::Initial, 0, connection); encoder .encode_stream_frame(std::as_writable_bytes(std::span(message)), false); REQUIRE(connection.has_next_datagram() == false); connection.process_datagram(std::span(target).subspan(0, encoder.size())); REQUIRE(connection.has_next_datagram() == false); std::this_thread::sleep_for(std::chrono::milliseconds(TW_NET_HELLO_RETRY_INTERVAL_IN_MILLIS)); REQUIRE(connection.has_next_datagram() == false); } TEST_CASE("Connection sends ACK when the packet has reliable frames", "[quicr3]") { QuicrConnection connection(0, 0, tw::net::Address({}), nullptr); auto buffer = connection.pop_datagram(); QuicrPacket header = QuicrDecoder::decode_packet(buffer); std::vector target(1200); size_t offset = 0; std::string message = "Hello world"; QuicrPacketEncoder encoder(target, offset, QuicrPacketType::Initial, 0, connection); encoder .encode_stream_frame(std::as_writable_bytes(std::span(message)), true); REQUIRE(connection.has_next_datagram() == false); connection.process_datagram(std::span(target).subspan(0, encoder.size())); REQUIRE(connection.has_next_datagram() == true); std::this_thread::sleep_for(std::chrono::milliseconds(TW_NET_HELLO_RETRY_INTERVAL_IN_MILLIS)); REQUIRE(connection.has_next_datagram() == true); } TEST_CASE("Connection applies to ACK to all packets that sent the frame", "[quicr2]") { QuicrConnection connection(0, 0, tw::net::Address({}), nullptr); connection.send_initial_hello(); auto dgram1 = connection.pop_datagram(); auto packet1 = QuicrDecoder::decode_packet(dgram1); std::this_thread::sleep_for(std::chrono::milliseconds(TW_NET_HELLO_RETRY_INTERVAL_IN_MILLIS)); auto dgram2 = connection.pop_datagram(); auto packet2 = QuicrDecoder::decode_packet(dgram2); REQUIRE(packet1.packet_number.value() != packet2.packet_number.value()); std::this_thread::sleep_for(std::chrono::milliseconds(TW_NET_HELLO_RETRY_INTERVAL_IN_MILLIS)); std::vector target(1200); size_t offset = 0; QuicrConnection connection2(0, 0, tw::net::Address({}), nullptr); std::vector acks = { packet1.packet_number.value() }; QuicrPacketEncoder encoder(target, offset, QuicrPacketType::Initial, 0, connection2); encoder .encode_ack_frame(acks); connection.process_datagram(std::span(target).subspan(0, encoder.size())); REQUIRE(!connection.has_next_datagram()); } TEST_CASE("Connection can be established", "[quicr2]") { std::barrier create_sync_point(2); std::barrier send_sync_point(2); std::barrier client_send_sync_point(2); std::string mesg = "Hello world"; std::string client_msg = "Client hello"; std::thread server_thread([&]() { auto endpoint_r = QuicrEndpoint::create(); REQUIRE(endpoint_r); auto endpoint = std::move(endpoint_r.value()); REQUIRE(endpoint->bind(6971)); auto listener_r = QuicrConnectionListener::listen(endpoint.get()); REQUIRE(listener_r); auto listener = std::move(listener_r.value()); create_sync_point.arrive_and_wait(); QuicrConnection* connection = nullptr; // wait for connection while(connection == nullptr) { endpoint->poll(); connection = listener->listen(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } endpoint->poll(); spdlog::info("Connection established with peer id: 0x{:x}", connection->peer_id()); // write whole message auto bytes = std::as_writable_bytes(std::span(mesg.begin(), mesg.end())); auto r = connection->send_message(bytes, true); if(!r) { spdlog::error("Failed to write to connection"); } REQUIRE(r); endpoint->poll(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); send_sync_point.arrive_and_wait(); client_send_sync_point.arrive_and_wait(); endpoint->poll(); }); std::thread client_thread([&]() { create_sync_point.arrive_and_wait(); auto endpoint_r = QuicrEndpoint::create(); REQUIRE(endpoint_r); auto endpoint = std::move(*endpoint_r); auto connection_result = endpoint->connect(tw::net::Address {"127.0.0.1", 6971}); // QuicrConnection::connect(Address{"127.0.0.1", 6970}); REQUIRE(connection_result); auto conn = std::move(*connection_result); spdlog::info("Client ID: {}", conn->self_id()); while(conn->state() != QuicrConnectionState::Established) { endpoint->poll(); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } endpoint->poll(); spdlog::info("Connection established"); send_sync_point.arrive_and_wait(); endpoint->poll(); std::string buffer(1024, '\0'); spdlog::info("Waiting to receive message from server..."); auto r = conn->read_into(std::as_writable_bytes(std::span(buffer.data(), buffer.size()))); if(!r) { spdlog::error("Failed to read from connection: {}", r.error().message()); } spdlog::info("Received: [{}], {}", r.value(), buffer.substr(0, r.value())); REQUIRE(buffer.substr(0, r.value()) == mesg); auto bytes = std::as_writable_bytes(std::span(client_msg.begin(), client_msg.end())); conn->send_message(bytes, true); std::this_thread::sleep_for(std::chrono::milliseconds(100)); client_send_sync_point.arrive_and_wait(); }); client_thread.join(); server_thread.join(); } TEST_CASE("Send large datagram", "[quicr2]") { std::barrier create_sync_point(2); std::barrier send_sync_point(2); std::barrier client_send_sync_point(2); std::string mesg = std::string(2000, 'a'); std::string client_msg = "Client hello"; std::thread server_thread([&]() { auto endpoint_r = QuicrEndpoint::create(); REQUIRE(endpoint_r); auto endpoint = std::move(endpoint_r.value()); REQUIRE(endpoint->bind(6970)); auto listener_r = QuicrConnectionListener::listen(endpoint.get()); REQUIRE(listener_r); auto listener = std::move(listener_r.value()); create_sync_point.arrive_and_wait(); QuicrConnection* connection = nullptr; // wait for connection while(connection == nullptr) { endpoint->poll(); connection = listener->listen(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } endpoint->poll(); spdlog::info("Connection established with peer id: 0x{:x}", connection->peer_id()); // write whole message auto bytes = std::as_writable_bytes(std::span(mesg.begin(), mesg.end())); auto r = connection->send_message(bytes, true); if(!r) { spdlog::error("Failed to write to connection"); } REQUIRE(r); endpoint->poll(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); send_sync_point.arrive_and_wait(); client_send_sync_point.arrive_and_wait(); endpoint->poll(); }); std::thread client_thread([&]() { create_sync_point.arrive_and_wait(); auto endpoint_r = QuicrEndpoint::create(); REQUIRE(endpoint_r); auto endpoint = std::move(*endpoint_r); spdlog::info("Connecting"); auto connection_result = endpoint->connect(tw::net::Address {"127.0.0.1", 6970}); // QuicrConnection::connect(Address{"127.0.0.1", 6970}); if(!connection_result) { spdlog::error("Failed to connect to server: {}", connection_result.error().message()); } auto conn = std::move(*connection_result); spdlog::info("Client ID: {}", conn->self_id()); while(conn->state() != QuicrConnectionState::Established) { endpoint->poll(); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } endpoint->poll(); spdlog::info("Connection established"); send_sync_point.arrive_and_wait(); endpoint->poll(); std::string buffer(64 * 1024, '\0'); spdlog::info("Waiting to receive message from server..."); auto r = conn->read_into(std::as_writable_bytes(std::span(buffer.data(), buffer.size()))); if(!r) { spdlog::error("Failed to read from connection: {}", r.error().message()); } spdlog::info("Received: [{}], {}", r.value(), buffer.substr(0, r.value())); REQUIRE(buffer.substr(0, r.value()) == mesg); auto bytes = std::as_writable_bytes(std::span(client_msg.begin(), client_msg.end())); conn->send_message(bytes, true); std::this_thread::sleep_for(std::chrono::milliseconds(100)); client_send_sync_point.arrive_and_wait(); }); client_thread.join(); server_thread.join(); } TEST_CASE("Sending message through closed connection returns error", "[quicr2]") { QuicrConnection connection(0, 0, tw::net::Address({}), nullptr); REQUIRE(connection.state() == QuicrConnectionState::Closed); std::string mesg = "Hello world"; auto bytes = std::as_writable_bytes(std::span(mesg.begin(), mesg.end())); auto send_r = connection.send_message(bytes, true); REQUIRE(!send_r); REQUIRE(send_r.error().type() == QuicrErrorType::ConnectionClosed); } TEST_CASE("Frame can close the connection", "[quicr3]") { }