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
14#[derive(Clone)]
23pub struct QuikConnection {
24 pub h2: SendRequest<Bytes>,
26 pub profile: ChromeProfile,
28}
29
30pub async fn connect(
43 host: &str,
44 port: u16,
45 addr: SocketAddr,
46 profile: &ChromeProfile,
47 proxy: Option<&Proxy>,
48) -> Result<QuikConnection> {
49 let tcp = if let Some(p) = proxy {
51 dial_proxy(p, host, port).await?
52 } else {
53 TcpStream::connect(addr).await?
54 };
55
56 let connector = build_connector(&profile.tls)?;
58 let mut config = connector.configure()?;
59
60 config.set_status_type(boring::ssl::StatusType::OCSP)?;
62
63 let ssl_ptr = config.as_ptr();
65
66 fn build_alps_payload(
72 settings: &crate::profile::SettingsFrame,
73 extra: &[(u16, u32)],
74 ) -> Vec<u8> {
75 let entry_count = 4 + extra.len();
76 let mut payload = Vec::with_capacity(entry_count * 6);
77 payload.extend_from_slice(&1u16.to_be_bytes());
79 payload.extend_from_slice(&settings.header_table_size.to_be_bytes());
80 payload.extend_from_slice(&2u16.to_be_bytes());
81 payload.extend_from_slice(&(settings.enable_push as u32).to_be_bytes());
82 payload.extend_from_slice(&4u16.to_be_bytes());
83 payload.extend_from_slice(&settings.initial_window_size.to_be_bytes());
84 payload.extend_from_slice(&6u16.to_be_bytes());
85 payload.extend_from_slice(&settings.max_header_list_size.to_be_bytes());
86 for &(id, value) in extra {
88 payload.extend_from_slice(&id.to_be_bytes());
89 payload.extend_from_slice(&value.to_be_bytes());
90 }
91 payload
92 }
93
94 unsafe {
99 if profile.tls.enable_ech_grease {
100 boring_sys::SSL_set_enable_ech_grease(ssl_ptr, 1);
101 }
102 if profile.tls.alps_enabled {
103 let alps_data =
104 build_alps_payload(&profile.h2.settings, profile.tls.alps_extra_settings);
105
106 let alps_res = boring_sys::SSL_add_application_settings(
107 ssl_ptr,
108 b"h2".as_ptr(),
109 2,
110 alps_data.as_ptr(),
111 alps_data.len(),
112 );
113 if alps_res != 1 {
114 return Err(crate::error::Error::Connect(std::io::Error::other(
115 "failed to inject ALPS settings",
116 )));
117 }
118 }
119 }
120
121 let tls_stream = tokio_boring::connect(config, host, tcp)
123 .await
124 .map_err(|e| {
125 tracing::error!("TLS handshake failed: {:?}", e);
126 e
127 })?;
128
129 let mut h2_builder = http2::client::Builder::new();
131 configure_builder(&mut h2_builder, &profile.h2);
132
133 let (h2, connection) = h2_builder.handshake(tls_stream).await?;
134
135 tokio::spawn(async move {
138 if let Err(e) = connection.await {
139 tracing::error!("HTTP/2 connection driver failed: {:?}", e);
140 }
141 });
142
143 Ok(QuikConnection {
144 h2,
145 profile: profile.clone(),
146 })
147}
148
149impl QuikConnection {
150 pub async fn send(
152 &mut self,
153 request: http::Request<()>,
154 body: Option<Bytes>,
155 ) -> Result<Response> {
156 let url_str = request.uri().to_string();
157 if let Some(data) = body {
158 let (response_future, mut send_stream) = self.h2.send_request(request, false)?;
159 send_stream.send_data(data, true)?;
160 let response = response_future.await?;
161 Ok(Response::new(response, url_str))
162 } else {
163 let (response_future, _) = self.h2.send_request(request, true)?;
164 let response = response_future.await?;
165 Ok(Response::new(response, url_str))
166 }
167 }
168}