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