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 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 }
113 }
114
115 let tls_stream = tokio_boring::connect(config, host, tcp)
117 .await
118 .map_err(|e| {
119 tracing::error!("TLS handshake failed: {:?}", e);
120 e
121 })?;
122
123 let mut h2_builder = http2::client::Builder::new();
125 configure_builder(&mut h2_builder, &profile.h2);
126
127 let (h2, connection) = h2_builder.handshake(tls_stream).await?;
128
129 tokio::spawn(async move {
132 if let Err(e) = connection.await {
133 tracing::error!("HTTP/2 connection driver failed: {:?}", e);
134 }
135 });
136
137 Ok(QuikConnection {
138 h2,
139 profile: profile.clone(),
140 })
141}
142
143impl QuikConnection {
144 pub async fn send(
146 &mut self,
147 request: http::Request<()>,
148 body: Option<Bytes>,
149 ) -> Result<Response> {
150 let url_str = request.uri().to_string();
151 if let Some(data) = body {
152 let (response_future, mut send_stream) = self.h2.send_request(request, false)?;
153 send_stream.send_data(data, true)?;
154 let response = response_future.await?;
155 Ok(Response::new(response, url_str))
156 } else {
157 let (response_future, _) = self.h2.send_request(request, true)?;
158 let response = response_future.await?;
159 Ok(Response::new(response, url_str))
160 }
161 }
162}