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, ResponseBody};
9use crate::error::Result;
10use crate::http2::configure_builder;
11use crate::profile::ChromeProfile;
12use crate::tls::build_connector;
13
14#[derive(Clone)]
23pub struct QuikConnection {
24 pub h2: SendRequest<Bytes>,
26 pub profile: ChromeProfile,
28 pub session: Option<boring::ssl::SslSession>,
30}
31
32pub async fn connect(
45 host: &str,
46 port: u16,
47 addr: SocketAddr,
48 profile: &ChromeProfile,
49 proxy: Option<&Proxy>,
50 session: Option<boring::ssl::SslSession>,
51) -> Result<QuikConnection> {
52 let tcp = if let Some(p) = proxy {
54 dial_proxy(p, host, port).await?
55 } else {
56 TcpStream::connect(addr).await?
57 };
58
59 let connector = build_connector(&profile.tls)?;
61 let mut config = connector.configure()?;
62
63 if profile.tls.session_ticket_enabled {
65 if let Some(ref sess) = session {
66 unsafe {
69 config.set_session(sess)?;
70 }
71 }
72 }
73
74 config.set_status_type(boring::ssl::StatusType::OCSP)?;
76
77 let ssl_ptr = config.as_ptr();
79
80 fn build_alps_payload(
86 settings: &crate::profile::SettingsFrame,
87 extra: &[(u16, u32)],
88 ) -> Vec<u8> {
89 let entry_count = 4 + extra.len();
90 let mut payload = Vec::with_capacity(entry_count * 6);
91 payload.extend_from_slice(&1u16.to_be_bytes());
93 payload.extend_from_slice(&settings.header_table_size.to_be_bytes());
94 payload.extend_from_slice(&2u16.to_be_bytes());
95 payload.extend_from_slice(&(settings.enable_push as u32).to_be_bytes());
96 payload.extend_from_slice(&4u16.to_be_bytes());
97 payload.extend_from_slice(&settings.initial_window_size.to_be_bytes());
98 payload.extend_from_slice(&6u16.to_be_bytes());
99 payload.extend_from_slice(&settings.max_header_list_size.to_be_bytes());
100 for &(id, value) in extra {
102 payload.extend_from_slice(&id.to_be_bytes());
103 payload.extend_from_slice(&value.to_be_bytes());
104 }
105 payload
106 }
107
108 unsafe {
113 if profile.tls.enable_ech_grease {
114 boring_sys::SSL_set_enable_ech_grease(ssl_ptr, 1);
115 }
116 if profile.tls.alps_enabled {
117 let alps_data =
118 build_alps_payload(&profile.h2.settings, profile.tls.alps_extra_settings);
119
120 let alps_res = boring_sys::SSL_add_application_settings(
121 ssl_ptr,
122 b"h2".as_ptr(),
123 2,
124 alps_data.as_ptr(),
125 alps_data.len(),
126 );
127 if alps_res != 1 {
128 return Err(crate::error::Error::Connect(std::io::Error::other(
129 "failed to inject ALPS settings",
130 )));
131 }
132 }
133 }
134
135 let tls_stream = tokio_boring::connect(config, host, tcp)
137 .await
138 .map_err(|e| {
139 tracing::error!("TLS handshake failed: {:?}", e);
140 e
141 })?;
142
143 let negotiated_session = if profile.tls.session_ticket_enabled {
145 tls_stream.ssl().session().map(|s| s.to_owned())
146 } else {
147 None
148 };
149
150 let mut h2_builder = http2::client::Builder::new();
152 configure_builder(&mut h2_builder, &profile.h2);
153
154 let (h2, connection) = h2_builder.handshake(tls_stream).await?;
155
156 tokio::spawn(async move {
159 if let Err(e) = connection.await {
160 tracing::error!("HTTP/2 connection driver failed: {:?}", e);
161 }
162 });
163
164 Ok(QuikConnection {
165 h2,
166 profile: profile.clone(),
167 session: negotiated_session,
168 })
169}
170
171impl QuikConnection {
172 pub async fn send(
174 &mut self,
175 request: http::Request<()>,
176 body: Option<Bytes>,
177 ) -> Result<Response> {
178 let url_str = request.uri().to_string();
179 if let Some(data) = body {
180 let (response_future, mut send_stream) = self.h2.send_request(request, false)?;
181 send_stream.send_data(data, true)?;
182 let response = response_future.await?;
183 let (parts, body_stream) = response.into_parts();
184 Ok(Response::new(
185 parts.status,
186 parts.headers,
187 ResponseBody::Http2(body_stream),
188 url_str,
189 ))
190 } else {
191 let (response_future, _) = self.h2.send_request(request, true)?;
192 let response = response_future.await?;
193 let (parts, body_stream) = response.into_parts();
194 Ok(Response::new(
195 parts.status,
196 parts.headers,
197 ResponseBody::Http2(body_stream),
198 url_str,
199 ))
200 }
201 }
202}