http_quik/client/
connector.rs1use bytes::Bytes;
2use foreign_types::ForeignTypeRef;
3use http2::client::SendRequest;
4use std::net::SocketAddr;
5use tokio::net::TcpStream;
6
7use crate::client::proxy::{dial_proxy, Proxy};
8use crate::client::response::Response;
9use crate::error::Result;
10use crate::http2::configure_builder;
11use crate::profile::ChromeProfile;
12use crate::tls::build_connector;
13
14pub struct QuikConnection {
21 pub h2: SendRequest<Bytes>,
23 pub profile: ChromeProfile,
25}
26
27pub async fn connect(
40 host: &str,
41 port: u16,
42 addr: SocketAddr,
43 profile: &ChromeProfile,
44 proxy: Option<&Proxy>,
45) -> Result<QuikConnection> {
46 let tcp = if let Some(p) = proxy {
48 dial_proxy(p, host, port).await?
49 } else {
50 TcpStream::connect(addr).await?
51 };
52
53 let connector = build_connector(&profile.tls)?;
55 let mut config = connector.configure()?;
56
57 config.set_status_type(boring::ssl::StatusType::OCSP)?;
59
60 let ssl_ptr = config.as_ptr();
62
63 fn build_alps_payload(settings: &crate::profile::SettingsFrame) -> [u8; 24] {
65 let mut payload = [0u8; 24];
66 payload[0..2].copy_from_slice(&1u16.to_be_bytes());
67 payload[2..6].copy_from_slice(&settings.header_table_size.to_be_bytes());
68 payload[6..8].copy_from_slice(&2u16.to_be_bytes());
69 payload[8..12].copy_from_slice(&(settings.enable_push as u32).to_be_bytes());
70 payload[12..14].copy_from_slice(&4u16.to_be_bytes());
71 payload[14..18].copy_from_slice(&settings.initial_window_size.to_be_bytes());
72 payload[18..20].copy_from_slice(&6u16.to_be_bytes());
73 payload[20..24].copy_from_slice(&settings.max_header_list_size.to_be_bytes());
74 payload
75 }
76
77 unsafe {
82 if profile.tls.enable_ech_grease {
83 boring_sys::SSL_set_enable_ech_grease(ssl_ptr, 1);
84 }
85 if profile.tls.alps_enabled {
86 let alps_data = build_alps_payload(&profile.h2.settings);
87
88 boring_sys::SSL_add_application_settings(
89 ssl_ptr,
90 b"h2".as_ptr(),
91 2,
92 alps_data.as_ptr(),
93 alps_data.len(),
94 );
95 }
96 }
97
98 let tls_stream = tokio_boring::connect(config, host, tcp)
100 .await
101 .map_err(|e| {
102 tracing::error!("TLS handshake failed: {:?}", e);
103 e
104 })?;
105
106 let mut h2_builder = http2::client::Builder::new();
108 configure_builder(&mut h2_builder, &profile.h2);
109
110 let (h2, connection) = h2_builder.handshake(tls_stream).await?;
111
112 tokio::spawn(async move {
115 if let Err(e) = connection.await {
116 tracing::error!("HTTP/2 connection driver failed: {:?}", e);
117 }
118 });
119
120 Ok(QuikConnection {
121 h2,
122 profile: profile.clone(),
123 })
124}
125
126impl QuikConnection {
127 pub async fn send(
129 &mut self,
130 request: http::Request<()>,
131 body: Option<Bytes>,
132 ) -> Result<Response> {
133 let url_str = request.uri().to_string();
134 if let Some(data) = body {
135 let (response_future, mut send_stream) = self.h2.send_request(request, false)?;
136 send_stream.send_data(data, true)?;
137 let response = response_future.await?;
138 Ok(Response::new(response, url_str))
139 } else {
140 let (response_future, _) = self.h2.send_request(request, true)?;
141 let response = response_future.await?;
142 Ok(Response::new(response, url_str))
143 }
144 }
145}