///|
#cfg(target="native")
#warnings("-unused_value")
async fn quic_send_client_initial(
addr : @socket.Addr,
destination_connection_id : Bytes,
source_connection_id : Bytes,
crypto_data : Bytes,
) -> QuicProtectedPacket {
let conn = @socket.UdpClient(addr)
defer conn.close()
let datagram = quic_build_client_initial_datagram(
destination_connection_id, source_connection_id, 0, crypto_data,
)
conn.send(datagram.packet)
datagram
}
///|
#cfg(target="native")
#warnings("-unused_field")
priv struct QuicTlsClientInitial {
datagram : QuicProtectedPacket
client_hello : Bytes
client_public_key : Bytes
transport_parameters : Bytes
}
///|
#cfg(target="native")
#warnings("-unused_value")
async fn quic_send_tls13_client_initial(
addr : @socket.Addr,
server_name : String,
destination_connection_id : Bytes,
source_connection_id : Bytes,
client_private_key : Bytes,
random : Bytes,
legacy_session_id : Bytes,
) -> QuicTlsClientInitial {
let client_public_key = x25519_base(client_private_key)
let transport_parameters = quic_encode_client_transport_parameters({
initial_source_connection_id: source_connection_id,
max_idle_timeout: 30000,
max_udp_payload_size: 1200,
initial_max_data: 1048576,
initial_max_stream_data_bidi_local: 262144,
initial_max_stream_data_bidi_remote: 262144,
initial_max_stream_data_uni: 262144,
initial_max_streams_bidi: 100,
initial_max_streams_uni: 3,
active_connection_id_limit: 2,
})
let client_hello = tls13_encode_client_hello({
server_name,
alpn: "h3",
random,
legacy_session_id,
x25519_public_key: client_public_key,
transport_parameters,
})
{
datagram: quic_send_client_initial(
addr, destination_connection_id, source_connection_id, client_hello,
),
client_hello,
client_public_key,
transport_parameters,
}
}
///|
#cfg(target="native")
#warnings("-unused_value")
async fn quic_send_tls13_client_initial_on_conn(
conn : @socket.UdpClient,
server_name : String,
destination_connection_id : Bytes,
source_connection_id : Bytes,
client_private_key : Bytes,
random : Bytes,
legacy_session_id : Bytes,
token? : Bytes = b"",
) -> QuicTlsClientInitial {
let client_public_key = x25519_base(client_private_key)
let transport_parameters = quic_encode_client_transport_parameters({
initial_source_connection_id: source_connection_id,
max_idle_timeout: 30000,
max_udp_payload_size: 1200,
initial_max_data: 1048576,
initial_max_stream_data_bidi_local: 262144,
initial_max_stream_data_bidi_remote: 262144,
initial_max_stream_data_uni: 262144,
initial_max_streams_bidi: 100,
initial_max_streams_uni: 3,
active_connection_id_limit: 2,
})
let client_hello = tls13_encode_client_hello({
server_name,
alpn: "h3",
random,
legacy_session_id,
x25519_public_key: client_public_key,
transport_parameters,
})
let datagram = quic_build_client_initial_datagram(
destination_connection_id,
source_connection_id,
0,
client_hello,
token~,
)
conn.send(datagram.packet)
{ datagram, client_hello, client_public_key, transport_parameters }
}
///|
#cfg(target="native")
#warnings("-unused_value")
fn quic_build_tls13_client_finished_packet(
destination_connection_id : Bytes,
source_connection_id : Bytes,
packet_number : Int,
keys : QuicPacketKeys,
finished_message : Bytes,
ack_largest? : Int = -1,
) -> QuicProtectedPacket raise {
let payload = @buffer.new()
if ack_largest >= 0 {
payload.write_bytes(quic_encode_ack(ack_largest, 0, 0))
}
payload.write_bytes(quic_encode_crypto_frame(0, finished_message))
quic_build_handshake_packet(
destination_connection_id,
source_connection_id,
packet_number,
payload.contents(),
keys,
)
}
///|
#cfg(target="native")
#warnings("-unused_value")
async fn quic_send_tls13_client_finished(
conn : @socket.UdpClient,
destination_connection_id : Bytes,
source_connection_id : Bytes,
packet_number : Int,
keys : QuicPacketKeys,
finished_message : Bytes,
ack_largest? : Int = -1,
) -> QuicProtectedPacket {
let packet = quic_build_tls13_client_finished_packet(
destination_connection_id,
source_connection_id,
packet_number,
keys,
finished_message,
ack_largest~,
)
conn.send(packet.packet)
packet
}
///|
#cfg(target="native")
#warnings("-unused_value")
fn quic_build_h3_client_request_packet(
destination_connection_id : Bytes,
packet_number : Int,
keys : QuicPacketKeys,
stream_id : Int,
headers : Array[(String, String)],
body : Bytes,
ack_largest? : Int = -1,
) -> QuicProtectedPacket raise {
let payload = @buffer.new()
if ack_largest >= 0 {
payload.write_bytes(quic_encode_ack(ack_largest, 0, 0))
}
payload.write_bytes(
quic_encode_stream_frame(2, 0, false, h3_encode_control_stream([])),
)
payload.write_bytes(
quic_encode_stream_frame(
stream_id,
0,
true,
h3_encode_request_stream(headers, body),
),
)
quic_build_short_packet(
destination_connection_id,
packet_number,
payload.contents(),
keys,
)
}
///|
#cfg(target="native")
#warnings("-unused_value")
async fn quic_recv_datagram_packets(conn : @socket.UdpClient) -> Array[Bytes] {
let buf = FixedArray::make(4096, b'\x00')
let n = conn.recv(buf)
quic_split_datagram(quic_copy_slice(buf.unsafe_reinterpret_as_bytes(), 0, n))
}
///|
#cfg(target="native")
#warnings("-unused_value")
async fn quic_recv_tls13_server_handshake(
conn : @socket.UdpClient,
client_private_key : Bytes,
client_hello : Bytes,
original_destination_connection_id : Bytes,
max_datagrams? : Int = 8,
verify_certificate? : Bool = true,
retry_source_connection_id? : Bytes = b"",
) -> QuicTlsServerHandshakeResult {
let mut initial_destination_connection_id = original_destination_connection_id
let mut server_initial_packet : Bytes? = None
let server_handshake_packets = []
let mut retried = false
for _ in 0.. continue }
if long_header.packet_type == 3 &&
!retried &&
retry_source_connection_id.length() > 0 {
let retry = quic_parse_retry_packet(
packet,
original_destination_connection_id~,
)
let retry_initial = quic_build_client_initial_datagram(
retry.source_connection_id,
retry_source_connection_id,
0,
client_hello,
token=retry.token,
)
conn.send(retry_initial.packet)
initial_destination_connection_id = retry.source_connection_id
retried = true
server_initial_packet = None
server_handshake_packets.clear()
} else if long_header.packet_type == 0 && server_initial_packet is None {
server_initial_packet = Some(packet)
} else if long_header.packet_type == 2 {
server_handshake_packets.push(packet)
}
}
if server_initial_packet is Some(initial) &&
server_handshake_packets.length() > 0 {
try
quic_process_tls13_server_handshake(
client_private_key,
client_hello,
initial_destination_connection_id,
initial,
server_handshake_packets,
verify_certificate~,
)
catch {
_ => ()
} noraise {
result => return result
}
}
}
raise Http3HandshakeFailed
}
///|
#cfg(target="native")
#warnings("-unused_value")
async fn quic_recv_h3_response_loop(
conn : @socket.UdpClient,
destination_connection_id_length : Int,
keys : QuicPacketKeys,
stream_id? : Int = 0,
max_datagrams? : Int = 16,
) -> H3DecodedResponse {
let packets = []
for _ in 0.. 0 && (packet[0].to_int() & 0x80) == 0 {
packets.push(packet)
}
}
try
quic_decode_h3_response_packets(
packets,
destination_connection_id_length,
keys,
stream_id~,
)
catch {
_ => ()
} noraise {
response => return response
}
}
raise Http3ResponseIncomplete
}
///|
#cfg(target="native")
#warnings("-unused_value")
fn quic_build_h3_request_packet(
destination_connection_id : Bytes,
packet_number : Int,
keys : QuicPacketKeys,
stream_id : Int,
headers : Array[(String, String)],
body : Bytes,
) -> QuicProtectedPacket raise {
quic_build_short_packet(
destination_connection_id,
packet_number,
quic_encode_stream_frame(
stream_id,
0,
true,
h3_encode_request_stream(headers, body),
),
keys,
)
}
///|
#cfg(target="native")
#warnings("-unused_value")
async fn quic_send_h3_request_packet(
conn : @socket.UdpClient,
destination_connection_id : Bytes,
packet_number : Int,
keys : QuicPacketKeys,
stream_id : Int,
headers : Array[(String, String)],
body : Bytes,
) -> QuicProtectedPacket {
let packet = quic_build_h3_request_packet(
destination_connection_id, packet_number, keys, stream_id, headers, body,
)
conn.send(packet.packet)
packet
}
///|
#cfg(target="native")
#warnings("-unused_value")
async fn quic_recv_h3_response_packet(
conn : @socket.UdpClient,
destination_connection_id_length : Int,
keys : QuicPacketKeys,
stream_id? : Int = 0,
) -> H3DecodedResponse {
let buf = FixedArray::make(4096, b'\x00')
let n = conn.recv(buf)
let packet = quic_copy_slice(buf.unsafe_reinterpret_as_bytes(), 0, n)
quic_decode_h3_response_packets(
[packet],
destination_connection_id_length,
keys,
stream_id~,
)
}
///|
#cfg(target="native")
#warnings("-unused_value")
async fn quic_send_h3_request_and_recv_response(
conn : @socket.UdpClient,
request_destination_connection_id : Bytes,
response_destination_connection_id_length : Int,
packet_number : Int,
request_keys : QuicPacketKeys,
response_keys : QuicPacketKeys,
stream_id : Int,
headers : Array[(String, String)],
body : Bytes,
) -> H3DecodedResponse {
let _ = quic_send_h3_request_packet(
conn, request_destination_connection_id, packet_number, request_keys, stream_id,
headers, body,
)
quic_recv_h3_response_packet(
conn,
response_destination_connection_id_length,
response_keys,
stream_id~,
)
}