wtransport_lightyear_patch/
endpoint.rs1use crate::config::ClientConfig;
2use crate::config::DnsResolver;
3use crate::config::Ipv6DualStackConfig;
4use crate::config::ServerConfig;
5use crate::connection::Connection;
6use crate::driver::streams::session::StreamSession;
7use crate::driver::streams::ProtoReadError;
8use crate::driver::streams::ProtoWriteError;
9use crate::driver::utils::varint_w2q;
10use crate::driver::Driver;
11use crate::error::ConnectingError;
12use crate::error::ConnectionError;
13use quinn::TokioRuntime;
14use socket2::Domain as SocketDomain;
15use socket2::Protocol as SocketProtocol;
16use socket2::Socket;
17use socket2::Type as SocketType;
18use std::collections::HashMap;
19use std::future::Future;
20use std::marker::PhantomData;
21use std::net::SocketAddr;
22use std::net::SocketAddrV4;
23use std::net::SocketAddrV6;
24use std::pin::Pin;
25use std::sync::Arc;
26use std::task::Context;
27use std::task::Poll;
28use tracing::debug;
29use url::Host;
30use url::Url;
31use wtransport_proto::error::ErrorCode;
32use wtransport_proto::frame::FrameKind;
33use wtransport_proto::headers::Headers;
34use wtransport_proto::session::ReservedHeader;
35use wtransport_proto::session::SessionRequest as SessionRequestProto;
36use wtransport_proto::session::SessionResponse as SessionResponseProto;
37
38pub mod endpoint_side {
40 use super::*;
41
42 pub struct Server {
46 pub(super) _marker: PhantomData<()>,
47 }
48
49 pub struct Client {
53 pub(super) dns_resolver: Box<dyn DnsResolver + Send + Sync>,
54 }
55}
56
57pub struct Endpoint<Side> {
104 endpoint: quinn::Endpoint,
105 side: Side,
106}
107
108impl<Side> Endpoint<Side> {
109 fn bind_socket(
110 bind_address: SocketAddr,
111 dual_stack_config: Ipv6DualStackConfig,
112 ) -> std::io::Result<Socket> {
113 let domain = match bind_address {
114 SocketAddr::V4(_) => SocketDomain::IPV4,
115 SocketAddr::V6(_) => SocketDomain::IPV6,
116 };
117
118 let socket = Socket::new(domain, SocketType::DGRAM, Some(SocketProtocol::UDP))?;
119
120 match dual_stack_config {
121 Ipv6DualStackConfig::OsDefault => {}
122 Ipv6DualStackConfig::Deny => socket.set_only_v6(true)?,
123 Ipv6DualStackConfig::Allow => socket.set_only_v6(false)?,
124 }
125
126 socket.bind(&bind_address.into())?;
127
128 Ok(socket)
129 }
130
131 pub async fn wait_idle(&self) {
133 self.endpoint.wait_idle().await;
134 }
135
136 pub fn local_addr(&self) -> std::io::Result<SocketAddr> {
138 self.endpoint.local_addr()
139 }
140}
141
142impl Endpoint<endpoint_side::Server> {
143 pub fn server(server_config: ServerConfig) -> std::io::Result<Self> {
145 let quic_config = server_config.quic_config;
146 let socket =
147 Self::bind_socket(server_config.bind_address, server_config.dual_stack_config)?;
148 let runtime = Arc::new(TokioRuntime);
149
150 let endpoint = quinn::Endpoint::new(
151 quinn::EndpointConfig::default(),
152 Some(quic_config),
153 socket.into(),
154 runtime,
155 )?;
156
157 Ok(Self {
158 endpoint,
159 side: endpoint_side::Server {
160 _marker: PhantomData,
161 },
162 })
163 }
164
165 pub async fn accept(&self) -> IncomingSession {
167 let quic_connecting = self
168 .endpoint
169 .accept()
170 .await
171 .expect("Endpoint cannot be closed");
172
173 debug!("New incoming QUIC connection");
174
175 IncomingSession::new(quic_connecting)
176 }
177
178 pub fn reload_config(&self, server_config: ServerConfig, rebind: bool) -> std::io::Result<()> {
189 if rebind {
190 let socket =
191 Self::bind_socket(server_config.bind_address, server_config.dual_stack_config)?;
192 self.endpoint.rebind(socket.into())?;
193 }
194
195 let quic_config = server_config.quic_config;
196 self.endpoint.set_server_config(Some(quic_config));
197
198 Ok(())
199 }
200}
201
202impl Endpoint<endpoint_side::Client> {
203 pub fn client(client_config: ClientConfig) -> std::io::Result<Self> {
205 let quic_config = client_config.quic_config;
206 let socket =
207 Self::bind_socket(client_config.bind_address, client_config.dual_stack_config)?;
208 let runtime = Arc::new(TokioRuntime);
209
210 let mut endpoint = quinn::Endpoint::new(
211 quinn::EndpointConfig::default(),
212 None,
213 socket.into(),
214 runtime,
215 )?;
216
217 endpoint.set_default_client_config(quic_config);
218
219 Ok(Self {
220 endpoint,
221 side: endpoint_side::Client {
222 dns_resolver: client_config.dns_resolver,
223 },
224 })
225 }
226
227 pub async fn connect<O>(&self, options: O) -> Result<Connection, ConnectingError>
283 where
284 O: IntoConnectOptions,
285 {
286 let options = options.into_options();
287
288 let url = Url::parse(&options.url)
289 .map_err(|parse_error| ConnectingError::InvalidUrl(parse_error.to_string()))?;
290
291 if url.scheme() != "https" {
292 return Err(ConnectingError::InvalidUrl(
293 "WebTransport URL scheme must be 'https'".to_string(),
294 ));
295 }
296
297 let host = url.host().expect("https scheme must have an host");
298 let port = url.port().unwrap_or(443);
299
300 let (socket_address, server_name) = match host {
301 Host::Domain(domain) => {
302 let socket_address = self
303 .side
304 .dns_resolver
305 .resolve(&format!("{domain}:{port}"))
306 .await
307 .map_err(ConnectingError::DnsLookup)?
308 .ok_or(ConnectingError::DnsNotFound)?;
309
310 (socket_address, domain.to_string())
311 }
312 Host::Ipv4(address) => {
313 let socket_address = SocketAddr::V4(SocketAddrV4::new(address, port));
314 (socket_address, address.to_string())
315 }
316 Host::Ipv6(address) => {
317 let socket_address = SocketAddr::V6(SocketAddrV6::new(address, port, 0, 0));
318 (socket_address, address.to_string())
319 }
320 };
321
322 let quic_connection = self
323 .endpoint
324 .connect(socket_address, &server_name)
325 .expect("QUIC connection parameters must be validated")
326 .await
327 .map_err(|connection_error| {
328 ConnectingError::ConnectionError(connection_error.into())
329 })?;
330
331 let driver = Driver::init(quic_connection.clone());
332
333 let _settings = driver.accept_settings().await.map_err(|driver_error| {
334 ConnectingError::ConnectionError(ConnectionError::with_driver_error(
335 driver_error,
336 &quic_connection,
337 ))
338 })?;
339
340 let mut session_request_proto =
343 SessionRequestProto::new(url.as_ref()).expect("Url has been already validate");
344
345 for (k, v) in options.additional_headers {
346 session_request_proto
347 .insert(k.clone(), v)
348 .map_err(|ReservedHeader| ConnectingError::ReservedHeader(k))?;
349 }
350
351 let mut stream_session = match driver.open_session(session_request_proto).await {
352 Ok(stream_session) => stream_session,
353 Err(driver_error) => {
354 return Err(ConnectingError::ConnectionError(
355 ConnectionError::with_driver_error(driver_error, &quic_connection),
356 ))
357 }
358 };
359
360 let stream_id = stream_session.id();
361 let session_id = stream_session.session_id();
362
363 match stream_session
364 .write_frame(stream_session.request().headers().generate_frame(stream_id))
365 .await
366 {
367 Ok(()) => {}
368 Err(ProtoWriteError::Stopped) => {
369 return Err(ConnectingError::SessionRejected);
370 }
371 Err(ProtoWriteError::NotConnected) => {
372 return Err(ConnectingError::with_no_connection(&quic_connection));
373 }
374 }
375
376 let frame = loop {
377 let frame = match stream_session.read_frame().await {
378 Ok(frame) => frame,
379 Err(ProtoReadError::H3(error_code)) => {
380 quic_connection.close(varint_w2q(error_code.to_code()), b"");
381 return Err(ConnectingError::ConnectionError(
382 ConnectionError::local_h3_error(error_code),
383 ));
384 }
385 Err(ProtoReadError::IO(_io_error)) => {
386 return Err(ConnectingError::with_no_connection(&quic_connection));
387 }
388 };
389
390 if let FrameKind::Exercise(_) = frame.kind() {
391 continue;
392 }
393 break frame;
394 };
395
396 if !matches!(frame.kind(), FrameKind::Headers) {
397 quic_connection.close(varint_w2q(ErrorCode::FrameUnexpected.to_code()), b"");
398 return Err(ConnectingError::ConnectionError(
399 ConnectionError::local_h3_error(ErrorCode::FrameUnexpected),
400 ));
401 }
402
403 let headers = match Headers::with_frame(&frame, stream_id) {
404 Ok(headers) => headers,
405 Err(error_code) => {
406 quic_connection.close(varint_w2q(error_code.to_code()), b"");
407 return Err(ConnectingError::ConnectionError(
408 ConnectionError::local_h3_error(error_code),
409 ));
410 }
411 };
412
413 let session_response = match SessionResponseProto::try_from(headers) {
414 Ok(session_response) => session_response,
415 Err(_) => {
416 quic_connection.close(varint_w2q(ErrorCode::Message.to_code()), b"");
417 return Err(ConnectingError::ConnectionError(
418 ConnectionError::local_h3_error(ErrorCode::Message),
419 ));
420 }
421 };
422
423 if session_response.code().is_successful() {
424 match driver.register_session(stream_session).await {
425 Ok(()) => {}
426 Err(driver_error) => {
427 return Err(ConnectingError::ConnectionError(
428 ConnectionError::with_driver_error(driver_error, &quic_connection),
429 ))
430 }
431 }
432 } else {
433 return Err(ConnectingError::SessionRejected);
434 }
435
436 Ok(Connection::new(quic_connection, driver, session_id))
437 }
438}
439
440pub struct ConnectOptions {
459 url: String,
460 additional_headers: HashMap<String, String>,
461}
462
463impl ConnectOptions {
464 pub fn builder<S>(url: S) -> ConnectRequestBuilder
475 where
476 S: ToString,
477 {
478 ConnectRequestBuilder {
479 url: url.to_string(),
480 additional_headers: Default::default(),
481 }
482 }
483}
484
485pub trait IntoConnectOptions {
487 fn into_options(self) -> ConnectOptions;
489}
490
491pub struct ConnectRequestBuilder {
495 url: String,
496 additional_headers: HashMap<String, String>,
497}
498
499impl ConnectRequestBuilder {
500 pub fn add_header<K, V>(mut self, key: K, value: V) -> Self
512 where
513 K: ToString,
514 V: ToString,
515 {
516 self.additional_headers
517 .insert(key.to_string(), value.to_string());
518 self
519 }
520
521 pub fn build(self) -> ConnectOptions {
523 ConnectOptions {
524 url: self.url,
525 additional_headers: self.additional_headers,
526 }
527 }
528}
529
530impl IntoConnectOptions for ConnectRequestBuilder {
531 fn into_options(self) -> ConnectOptions {
532 self.build()
533 }
534}
535
536impl IntoConnectOptions for ConnectOptions {
537 fn into_options(self) -> ConnectOptions {
538 self
539 }
540}
541
542impl<S> IntoConnectOptions for S
543where
544 S: ToString,
545{
546 fn into_options(self) -> ConnectOptions {
547 ConnectOptions::builder(self).build()
548 }
549}
550
551type DynFutureIncomingSession =
552 dyn Future<Output = Result<SessionRequest, ConnectionError>> + Send + Sync;
553
554pub struct IncomingSession(Pin<Box<DynFutureIncomingSession>>);
558
559impl IncomingSession {
560 fn new(quic_connecting: quinn::Connecting) -> Self {
561 Self(Box::pin(Self::accept(quic_connecting)))
562 }
563
564 async fn accept(quic_connecting: quinn::Connecting) -> Result<SessionRequest, ConnectionError> {
565 let quic_connection = quic_connecting.await?;
566
567 let driver = Driver::init(quic_connection.clone());
568
569 let _settings = driver.accept_settings().await.map_err(|driver_error| {
570 ConnectionError::with_driver_error(driver_error, &quic_connection)
571 })?;
572
573 let stream_session = driver.accept_session().await.map_err(|driver_error| {
576 ConnectionError::with_driver_error(driver_error, &quic_connection)
577 })?;
578
579 Ok(SessionRequest::new(quic_connection, driver, stream_session))
580 }
581}
582
583impl Future for IncomingSession {
584 type Output = Result<SessionRequest, ConnectionError>;
585
586 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
587 Future::poll(self.0.as_mut(), cx)
588 }
589}
590
591pub struct SessionRequest {
596 quic_connection: quinn::Connection,
597 driver: Driver,
598 stream_session: StreamSession,
599}
600
601impl SessionRequest {
602 pub(crate) fn new(
603 quic_connection: quinn::Connection,
604 driver: Driver,
605 stream_session: StreamSession,
606 ) -> Self {
607 Self {
608 quic_connection,
609 driver,
610 stream_session,
611 }
612 }
613
614 pub fn authority(&self) -> &str {
616 self.stream_session.request().authority()
617 }
618
619 pub fn path(&self) -> &str {
621 self.stream_session.request().path()
622 }
623
624 pub fn origin(&self) -> Option<&str> {
626 self.stream_session.request().origin()
627 }
628
629 pub fn user_agent(&self) -> Option<&str> {
631 self.stream_session.request().user_agent()
632 }
633
634 pub fn headers(&self) -> &HashMap<String, String> {
636 self.stream_session.request().headers().as_ref()
637 }
638
639 pub async fn accept(mut self) -> Result<Connection, ConnectionError> {
641 let user_agent = self.user_agent().unwrap_or_default();
642
643 let mut response = SessionResponseProto::ok();
644
645 if !user_agent.contains("firefox") {
647 response.add("sec-webtransport-http3-draft", "draft02");
648 }
649
650 self.send_response(response).await?;
651
652 let session_id = self.stream_session.session_id();
653
654 self.driver
655 .register_session(self.stream_session)
656 .await
657 .map_err(|driver_error| {
658 ConnectionError::with_driver_error(driver_error, &self.quic_connection)
659 })?;
660
661 Ok(Connection::new(
662 self.quic_connection,
663 self.driver,
664 session_id,
665 ))
666 }
667
668 pub async fn forbidden(self) {
670 self.reject(SessionResponseProto::forbidden()).await;
671 }
672
673 pub async fn not_found(self) {
675 self.reject(SessionResponseProto::not_found()).await;
676 }
677
678 async fn reject(mut self, mut response: SessionResponseProto) {
679 let user_agent = self.user_agent().unwrap_or_default();
680
681 if !user_agent.contains("firefox") {
683 response.add("sec-webtransport-http3-draft", "draft02");
684 }
685
686 let _ = self.send_response(response).await;
687 self.stream_session.finish().await;
688 }
689
690 async fn send_response(
691 &mut self,
692 response: SessionResponseProto,
693 ) -> Result<(), ConnectionError> {
694 let frame = response.headers().generate_frame(self.stream_session.id());
695
696 match self.stream_session.write_frame(frame).await {
697 Ok(()) => Ok(()),
698 Err(ProtoWriteError::NotConnected) => {
699 Err(ConnectionError::no_connect(&self.quic_connection))
700 }
701 Err(ProtoWriteError::Stopped) => {
702 self.quic_connection
703 .close(varint_w2q(ErrorCode::ClosedCriticalStream.to_code()), b"");
704
705 Err(ConnectionError::local_h3_error(
706 ErrorCode::ClosedCriticalStream,
707 ))
708 }
709 }
710 }
711}