1use crate::config::ClientConfig;
2use crate::config::DnsResolver;
3use crate::config::ServerConfig;
4use crate::connection::Connection;
5use crate::driver::streams::session::StreamSession;
6use crate::driver::streams::ProtoReadError;
7use crate::driver::streams::ProtoWriteError;
8use crate::driver::utils::varint_w2q;
9use crate::driver::Driver;
10use crate::error::ConnectingError;
11use crate::error::ConnectionError;
12use crate::VarInt;
13use quinn::TokioRuntime;
14use std::collections::HashMap;
15use std::future::Future;
16use std::future::IntoFuture;
17use std::marker::PhantomData;
18use std::net::SocketAddr;
19use std::net::SocketAddrV4;
20use std::net::SocketAddrV6;
21use std::pin::Pin;
22use std::sync::Arc;
23use std::task::Context;
24use std::task::Poll;
25use tracing::debug;
26use url::Host;
27use url::Url;
28use wtransport_proto::error::ErrorCode;
29use wtransport_proto::frame::FrameKind;
30use wtransport_proto::headers::Headers;
31use wtransport_proto::session::ReservedHeader;
32use wtransport_proto::session::SessionRequest as SessionRequestProto;
33use wtransport_proto::session::SessionResponse as SessionResponseProto;
34
35pub mod endpoint_side {
37 use super::*;
38
39 pub struct Server {
43 pub(super) _marker: PhantomData<()>,
44 }
45
46 pub struct Client {
50 pub(super) dns_resolver: Arc<dyn DnsResolver + Send + Sync>,
51 }
52}
53
54pub struct Endpoint<Side> {
100 endpoint: quinn::Endpoint,
101 side: Side,
102}
103
104impl<Side> Endpoint<Side> {
105 pub fn close(&self, error_code: VarInt, reason: &[u8]) {
107 self.endpoint.close(varint_w2q(error_code), reason);
108 }
109
110 pub async fn wait_idle(&self) {
112 self.endpoint.wait_idle().await;
113 }
114
115 pub fn local_addr(&self) -> std::io::Result<SocketAddr> {
117 self.endpoint.local_addr()
118 }
119
120 pub fn open_connections(&self) -> usize {
122 self.endpoint.open_connections()
123 }
124}
125
126impl Endpoint<endpoint_side::Server> {
127 pub fn server(server_config: ServerConfig) -> std::io::Result<Self> {
129 let endpoint_config = server_config.endpoint_config;
130 let quic_config = server_config.quic_config;
131 let socket = server_config.bind_address_config.bind_socket()?;
132 let runtime = Arc::new(TokioRuntime);
133
134 let endpoint = quinn::Endpoint::new(endpoint_config, Some(quic_config), socket, runtime)?;
135
136 Ok(Self {
137 endpoint,
138 side: endpoint_side::Server {
139 _marker: PhantomData,
140 },
141 })
142 }
143
144 pub async fn accept(&self) -> IncomingSession {
146 let quic_incoming = self
147 .endpoint
148 .accept()
149 .await
150 .expect("Endpoint cannot be closed");
151
152 debug!("New incoming QUIC connection");
153
154 IncomingSession(quic_incoming)
155 }
156
157 pub fn reload_config(&self, server_config: ServerConfig, rebind: bool) -> std::io::Result<()> {
168 if rebind {
169 let socket = server_config.bind_address_config.bind_socket()?;
170 self.endpoint.rebind(socket)?;
171 }
172
173 let quic_config = server_config.quic_config;
174 self.endpoint.set_server_config(Some(quic_config));
175
176 Ok(())
177 }
178}
179
180impl Endpoint<endpoint_side::Client> {
181 pub fn client(client_config: ClientConfig) -> std::io::Result<Self> {
183 let endpoint_config = client_config.endpoint_config;
184 let quic_config = client_config.quic_config;
185 let socket = client_config.bind_address_config.bind_socket()?;
186 let runtime = Arc::new(TokioRuntime);
187
188 let mut endpoint = quinn::Endpoint::new(endpoint_config, None, socket, runtime)?;
189
190 endpoint.set_default_client_config(quic_config);
191
192 Ok(Self {
193 endpoint,
194 side: endpoint_side::Client {
195 dns_resolver: client_config.dns_resolver,
196 },
197 })
198 }
199
200 pub async fn connect<O>(&self, options: O) -> Result<Connection, ConnectingError>
256 where
257 O: IntoConnectOptions,
258 {
259 let options = options.into_options();
260
261 let url = Url::parse(&options.url)
262 .map_err(|parse_error| ConnectingError::InvalidUrl(parse_error.to_string()))?;
263
264 if url.scheme() != "https" {
265 return Err(ConnectingError::InvalidUrl(
266 "WebTransport URL scheme must be 'https'".to_string(),
267 ));
268 }
269
270 let host = url.host().expect("https scheme must have an host");
271 let port = url.port().unwrap_or(443);
272
273 let (socket_address, server_name) = match host {
274 Host::Domain(domain) => {
275 let socket_address = self
276 .side
277 .dns_resolver
278 .resolve(&format!("{domain}:{port}"))
279 .await
280 .map_err(ConnectingError::DnsLookup)?
281 .ok_or(ConnectingError::DnsNotFound)?;
282
283 (socket_address, domain.to_string())
284 }
285 Host::Ipv4(address) => {
286 let socket_address = SocketAddr::V4(SocketAddrV4::new(address, port));
287 (socket_address, address.to_string())
288 }
289 Host::Ipv6(address) => {
290 let socket_address = SocketAddr::V6(SocketAddrV6::new(address, port, 0, 0));
291 (socket_address, address.to_string())
292 }
293 };
294
295 let quic_connection = self
296 .endpoint
297 .connect(socket_address, &server_name)
298 .map_err(ConnectingError::with_connect_error)?
299 .await
300 .map_err(|connection_error| {
301 ConnectingError::ConnectionError(connection_error.into())
302 })?;
303
304 let driver = Driver::init(quic_connection.clone());
305
306 let settings = driver.accept_settings().await.map_err(|driver_error| {
307 ConnectingError::ConnectionError(ConnectionError::with_driver_error(
308 driver_error,
309 &quic_connection,
310 ))
311 })?;
312
313 if settings.get(wtransport_proto::settings::SettingId::EnableConnectProtocol)
315 != Some(VarInt::from_u32(1))
316 {
317 return Err(ConnectingError::ConnectionError(
318 ConnectionError::local_h3_error(ErrorCode::Settings),
319 ));
320 }
321
322 let mut session_request_proto =
323 SessionRequestProto::new(url.as_ref()).expect("Url has been already validate");
324
325 for (k, v) in options.additional_headers {
326 session_request_proto
327 .insert(k.clone(), v)
328 .map_err(|ReservedHeader| ConnectingError::ReservedHeader(k))?;
329 }
330
331 let mut stream_session = match driver.open_session(session_request_proto).await {
332 Ok(stream_session) => stream_session,
333 Err(driver_error) => {
334 return Err(ConnectingError::ConnectionError(
335 ConnectionError::with_driver_error(driver_error, &quic_connection),
336 ))
337 }
338 };
339
340 let session_id = stream_session.session_id();
341
342 match stream_session
343 .write_frame(stream_session.request().headers().generate_frame())
344 .await
345 {
346 Ok(()) => {}
347 Err(ProtoWriteError::Stopped) => {
348 return Err(ConnectingError::SessionRejected);
349 }
350 Err(ProtoWriteError::NotConnected) => {
351 return Err(ConnectingError::with_no_connection(&quic_connection));
352 }
353 }
354
355 let frame = loop {
356 let frame = match stream_session.read_frame().await {
357 Ok(frame) => frame,
358 Err(ProtoReadError::H3(error_code)) => {
359 quic_connection.close(varint_w2q(error_code.to_code()), b"");
360 return Err(ConnectingError::ConnectionError(
361 ConnectionError::local_h3_error(error_code),
362 ));
363 }
364 Err(ProtoReadError::IO(_io_error)) => {
365 return Err(ConnectingError::with_no_connection(&quic_connection));
366 }
367 };
368
369 if let FrameKind::Exercise(_) = frame.kind() {
370 continue;
371 }
372 break frame;
373 };
374
375 if !matches!(frame.kind(), FrameKind::Headers) {
376 quic_connection.close(varint_w2q(ErrorCode::FrameUnexpected.to_code()), b"");
377 return Err(ConnectingError::ConnectionError(
378 ConnectionError::local_h3_error(ErrorCode::FrameUnexpected),
379 ));
380 }
381
382 let headers = match Headers::with_frame(&frame) {
383 Ok(headers) => headers,
384 Err(error_code) => {
385 quic_connection.close(varint_w2q(error_code.to_code()), b"");
386 return Err(ConnectingError::ConnectionError(
387 ConnectionError::local_h3_error(error_code),
388 ));
389 }
390 };
391
392 let Ok(session_response) = SessionResponseProto::try_from(headers) else {
393 quic_connection.close(varint_w2q(ErrorCode::Message.to_code()), b"");
394 return Err(ConnectingError::ConnectionError(
395 ConnectionError::local_h3_error(ErrorCode::Message),
396 ));
397 };
398
399 if session_response.code().is_successful() {
400 match driver.register_session(stream_session).await {
401 Ok(()) => {}
402 Err(driver_error) => {
403 return Err(ConnectingError::ConnectionError(
404 ConnectionError::with_driver_error(driver_error, &quic_connection),
405 ))
406 }
407 }
408 } else {
409 return Err(ConnectingError::SessionRejected);
410 }
411
412 Ok(Connection::new(quic_connection, driver, session_id))
413 }
414}
415
416#[derive(Debug, Clone)]
435pub struct ConnectOptions {
436 url: String,
437 additional_headers: HashMap<String, String>,
438}
439
440impl ConnectOptions {
441 pub fn builder<S>(url: S) -> ConnectRequestBuilder
452 where
453 S: ToString,
454 {
455 ConnectRequestBuilder {
456 url: url.to_string(),
457 additional_headers: Default::default(),
458 }
459 }
460
461 pub fn url(&self) -> &str {
463 &self.url
464 }
465
466 pub fn additional_headers(&self) -> &HashMap<String, String> {
468 &self.additional_headers
469 }
470}
471
472pub trait IntoConnectOptions {
474 fn into_options(self) -> ConnectOptions;
476}
477
478pub struct ConnectRequestBuilder {
482 url: String,
483 additional_headers: HashMap<String, String>,
484}
485
486impl ConnectRequestBuilder {
487 pub fn add_header<K, V>(mut self, key: K, value: V) -> Self
499 where
500 K: ToString,
501 V: ToString,
502 {
503 self.additional_headers
504 .insert(key.to_string(), value.to_string());
505 self
506 }
507
508 pub fn build(self) -> ConnectOptions {
510 ConnectOptions {
511 url: self.url,
512 additional_headers: self.additional_headers,
513 }
514 }
515}
516
517impl IntoConnectOptions for ConnectRequestBuilder {
518 fn into_options(self) -> ConnectOptions {
519 self.build()
520 }
521}
522
523impl IntoConnectOptions for ConnectOptions {
524 fn into_options(self) -> ConnectOptions {
525 self
526 }
527}
528
529impl<S> IntoConnectOptions for S
530where
531 S: ToString,
532{
533 fn into_options(self) -> ConnectOptions {
534 ConnectOptions::builder(self).build()
535 }
536}
537
538type DynFutureIncomingSession =
539 dyn Future<Output = Result<SessionRequest, ConnectionError>> + Send + Sync;
540
541pub struct IncomingSession(quinn::Incoming);
545
546impl IncomingSession {
547 pub fn remote_address(&self) -> SocketAddr {
549 self.0.remote_address()
550 }
551
552 pub fn remote_address_validated(&self) -> bool {
557 self.0.remote_address_validated()
558 }
559
560 pub fn retry(self) {
566 self.0.retry().expect("remote address already verified");
567 }
568
569 pub fn refuse(self) {
571 self.0.refuse();
572 }
573
574 pub fn ignore(self) {
576 self.0.ignore();
577 }
578}
579
580impl IntoFuture for IncomingSession {
581 type IntoFuture = IncomingSessionFuture;
582 type Output = Result<SessionRequest, ConnectionError>;
583
584 fn into_future(self) -> Self::IntoFuture {
585 IncomingSessionFuture::new(self.0)
586 }
587}
588
589pub struct IncomingSessionFuture(Pin<Box<DynFutureIncomingSession>>);
593
594impl IncomingSessionFuture {
595 #[cfg(feature = "quinn")]
600 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
601 pub fn with_quic_incoming(quic_incoming: quinn::Incoming) -> Self {
602 Self::new(quic_incoming)
603 }
604
605 #[cfg(feature = "quinn")]
613 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
614 pub fn with_quic_connecting(quic_connecting: quinn::Connecting) -> Self {
615 Self(Box::pin(async move {
616 let quic_connection = quic_connecting.await?;
617 Self::accept(quic_connection).await
618 }))
619 }
620
621 fn new(quic_incoming: quinn::Incoming) -> Self {
622 Self(Box::pin(async move {
623 let quic_connection = quic_incoming.await?;
624 Self::accept(quic_connection).await
625 }))
626 }
627
628 async fn accept(quic_connection: quinn::Connection) -> Result<SessionRequest, ConnectionError> {
629 let driver = Driver::init(quic_connection.clone());
630
631 let _settings = driver.accept_settings().await.map_err(|driver_error| {
632 ConnectionError::with_driver_error(driver_error, &quic_connection)
633 })?;
634
635 let stream_session = driver.accept_session().await.map_err(|driver_error| {
638 ConnectionError::with_driver_error(driver_error, &quic_connection)
639 })?;
640
641 Ok(SessionRequest::new(quic_connection, driver, stream_session))
642 }
643}
644
645impl Future for IncomingSessionFuture {
646 type Output = Result<SessionRequest, ConnectionError>;
647
648 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
649 Future::poll(self.0.as_mut(), cx)
650 }
651}
652
653pub struct SessionRequest {
658 quic_connection: quinn::Connection,
659 driver: Driver,
660 stream_session: StreamSession,
661}
662
663impl SessionRequest {
664 pub(crate) fn new(
665 quic_connection: quinn::Connection,
666 driver: Driver,
667 stream_session: StreamSession,
668 ) -> Self {
669 Self {
670 quic_connection,
671 driver,
672 stream_session,
673 }
674 }
675
676 #[inline(always)]
682 pub fn remote_address(&self) -> SocketAddr {
683 self.quic_connection.remote_address()
684 }
685
686 pub fn authority(&self) -> &str {
688 self.stream_session.request().authority()
689 }
690
691 pub fn path(&self) -> &str {
693 self.stream_session.request().path()
694 }
695
696 pub fn origin(&self) -> Option<&str> {
698 self.stream_session.request().origin()
699 }
700
701 pub fn user_agent(&self) -> Option<&str> {
703 self.stream_session.request().user_agent()
704 }
705
706 pub fn headers(&self) -> &HashMap<String, String> {
708 self.stream_session.request().headers().as_ref()
709 }
710
711 pub async fn accept_with_headers<I, K, V>(
713 self,
714 headers: I,
715 ) -> Result<Connection, ConnectionError>
716 where
717 I: IntoIterator<Item = (K, V)>,
718 K: Into<String>,
719 V: Into<String>,
720 {
721 self.accept_impl(
722 headers
723 .into_iter()
724 .map(|(k, v)| (k.into(), v.into()))
725 .collect(),
726 )
727 .await
728 }
729
730 pub async fn accept(self) -> Result<Connection, ConnectionError> {
732 self.accept_impl(HashMap::new()).await
733 }
734
735 pub async fn forbidden(self) {
737 self.reject(SessionResponseProto::forbidden()).await;
738 }
739
740 pub async fn not_found(self) {
742 self.reject(SessionResponseProto::not_found()).await;
743 }
744
745 pub async fn too_many_requests(self) {
747 self.reject(SessionResponseProto::too_many_requests()).await;
748 }
749
750 async fn accept_impl(
751 mut self,
752 headers: HashMap<String, String>,
753 ) -> Result<Connection, ConnectionError> {
754 let mut response = SessionResponseProto::ok();
755
756 for (key, value) in headers {
757 response.add(key, value);
758 }
759
760 self.send_response(response).await?;
761
762 let session_id = self.stream_session.session_id();
763
764 self.driver
765 .register_session(self.stream_session)
766 .await
767 .map_err(|driver_error| {
768 ConnectionError::with_driver_error(driver_error, &self.quic_connection)
769 })?;
770
771 Ok(Connection::new(
772 self.quic_connection,
773 self.driver,
774 session_id,
775 ))
776 }
777
778 async fn reject(mut self, response: SessionResponseProto) {
779 let _ = self.send_response(response).await;
780 self.stream_session.finish().await;
781 }
782
783 async fn send_response(
784 &mut self,
785 response: SessionResponseProto,
786 ) -> Result<(), ConnectionError> {
787 let frame = response.headers().generate_frame();
788
789 match self.stream_session.write_frame(frame).await {
790 Ok(()) => Ok(()),
791 Err(ProtoWriteError::NotConnected) => {
792 Err(ConnectionError::no_connect(&self.quic_connection))
793 }
794 Err(ProtoWriteError::Stopped) => {
795 self.quic_connection
796 .close(varint_w2q(ErrorCode::ClosedCriticalStream.to_code()), b"");
797
798 Err(ConnectionError::local_h3_error(
799 ErrorCode::ClosedCriticalStream,
800 ))
801 }
802 }
803 }
804}