1use std::collections::{HashMap, VecDeque};
8use std::net::SocketAddr;
9
10use bytes::{Bytes, BytesMut};
11use ringline::{ConnCtx, ParseResult};
12use ringline_h2::hpack::HeaderField;
13use ringline_h2::settings::Settings;
14use ringline_h2::{H2Connection, H2Event};
15
16use crate::error::HttpError;
17use crate::response::Response;
18
19struct PendingStream {
21 status: Option<u16>,
22 headers: Vec<(String, String)>,
23 body: BytesMut,
24 done: bool,
25 streaming: bool,
27 chunks: VecDeque<Bytes>,
29 content_encoding: Option<String>,
31}
32
33impl PendingStream {
34 fn new() -> Self {
35 Self {
36 status: None,
37 headers: Vec::new(),
38 body: BytesMut::new(),
39 done: false,
40 streaming: false,
41 chunks: VecDeque::new(),
42 content_encoding: None,
43 }
44 }
45
46 fn into_response(self) -> Result<Response, HttpError> {
47 let body = self.body.freeze();
48
49 #[cfg(any(feature = "gzip", feature = "zstd", feature = "brotli"))]
51 if let Some(ref encoding) = self.content_encoding {
52 let decompressed = crate::compress::decompress(encoding, &body)?;
53 return Ok(Response::new(
54 self.status.unwrap_or(0),
55 self.headers,
56 Bytes::from(decompressed),
57 ));
58 }
59
60 Ok(Response::new(self.status.unwrap_or(0), self.headers, body))
61 }
62}
63
64struct BlockedSend {
66 stream_id: u32,
67 data: Vec<u8>,
68 end_stream: bool,
69}
70
71pub struct H2AsyncConn {
76 conn: ConnCtx,
77 h2: H2Connection,
78 pending_streams: HashMap<u32, PendingStream>,
79 blocked_sends: VecDeque<BlockedSend>,
80 completed: VecDeque<(u32, Response)>,
82 settings_acked: bool,
83}
84
85impl H2AsyncConn {
86 pub async fn connect(addr: SocketAddr, host: &str) -> Result<Self, HttpError> {
91 let conn = ringline::connect_tls(addr, host)?.await?;
92 Self::from_conn(conn).await
93 }
94
95 pub async fn connect_with_timeout(
97 addr: SocketAddr,
98 host: &str,
99 timeout_ms: u64,
100 ) -> Result<Self, HttpError> {
101 let conn = ringline::connect_tls_with_timeout(addr, host, timeout_ms)?.await?;
102 Self::from_conn(conn).await
103 }
104
105 pub async fn from_conn(conn: ConnCtx) -> Result<Self, HttpError> {
109 let h2 = H2Connection::new(Settings::client_default());
110
111 let mut this = Self {
112 conn,
113 h2,
114 pending_streams: HashMap::new(),
115 blocked_sends: VecDeque::new(),
116 completed: VecDeque::new(),
117 settings_acked: false,
118 };
119
120 this.flush_pending_send()?;
122
123 while !this.settings_acked {
125 this.pump_once().await?;
126 }
127
128 Ok(this)
129 }
130
131 pub fn close(&self) {
133 self.conn.close();
134 }
135
136 pub fn conn(&self) -> ConnCtx {
137 self.conn
138 }
139
140 pub fn pending_count(&self) -> usize {
142 self.pending_streams.len()
143 }
144
145 pub async fn send_request(
149 &mut self,
150 method: &str,
151 path: &str,
152 host: &str,
153 extra_headers: &[(&str, &str)],
154 body: Option<&[u8]>,
155 ) -> Result<Response, HttpError> {
156 let stream_id = self.fire_request(method, path, host, extra_headers, body)?;
157 self.recv_stream(stream_id).await
158 }
159
160 pub fn fire_request(
167 &mut self,
168 method: &str,
169 path: &str,
170 host: &str,
171 extra_headers: &[(&str, &str)],
172 body: Option<&[u8]>,
173 ) -> Result<u32, HttpError> {
174 let has_body = body.is_some_and(|b| !b.is_empty());
175
176 let mut headers = vec![
177 HeaderField::new(b":method", method.as_bytes()),
178 HeaderField::new(b":path", path.as_bytes()),
179 HeaderField::new(b":scheme", b"https"),
180 HeaderField::new(b":authority", host.as_bytes()),
181 ];
182
183 let has_accept_encoding = extra_headers
184 .iter()
185 .any(|(name, _)| name.eq_ignore_ascii_case("accept-encoding"));
186
187 for (name, value) in extra_headers {
188 headers.push(HeaderField::new(name.as_bytes(), value.as_bytes()));
189 }
190
191 if !has_accept_encoding && let Some(ae) = crate::compress::accept_encoding_value() {
194 headers.push(HeaderField::new(b"accept-encoding", ae.as_bytes()));
195 }
196
197 let end_stream = !has_body;
198 let stream_id = self.h2.send_request(&headers, end_stream)?;
199
200 if let Some(data) = body
201 && !data.is_empty()
202 && let Err(e) = self.h2.send_data(stream_id, data, true)
203 {
204 if matches!(e, ringline_h2::H2Error::FlowControlError) {
205 self.blocked_sends.push_back(BlockedSend {
206 stream_id,
207 data: data.to_vec(),
208 end_stream: true,
209 });
210 } else {
211 return Err(HttpError::H2(e));
212 }
213 }
214
215 self.pending_streams.insert(stream_id, PendingStream::new());
216
217 self.flush_pending_send()?;
219
220 Ok(stream_id)
221 }
222
223 pub async fn recv(&mut self) -> Result<(u32, Response), HttpError> {
227 if let Some(completed) = self.completed.pop_front() {
229 return Ok(completed);
230 }
231
232 loop {
233 self.pump_once().await?;
234
235 if let Some(completed) = self.completed.pop_front() {
236 return Ok(completed);
237 }
238 }
239 }
240
241 pub async fn recv_stream(&mut self, stream_id: u32) -> Result<Response, HttpError> {
243 if let Some(idx) = self.completed.iter().position(|(sid, _)| *sid == stream_id) {
245 let (_, resp) = self.completed.remove(idx).unwrap();
246 return Ok(resp);
247 }
248
249 loop {
250 self.pump_once().await?;
251
252 if let Some(idx) = self.completed.iter().position(|(sid, _)| *sid == stream_id) {
254 let (_, resp) = self.completed.remove(idx).unwrap();
255 return Ok(resp);
256 }
257 }
258 }
259
260 async fn pump_once(&mut self) -> Result<(), HttpError> {
269 self.flush_pending_send()?;
271
272 let h2 = &mut self.h2;
274 let pending = &mut self.pending_streams;
275 let blocked = &mut self.blocked_sends;
276 let settings_acked = &mut self.settings_acked;
277
278 let n = self
279 .conn
280 .with_data(|data| {
281 if let Err(_e) = h2.recv(data) {
283 return ParseResult::Consumed(data.len());
284 }
285
286 while let Some(event) = h2.poll_event() {
288 match event {
289 H2Event::SettingsAcknowledged => {
290 *settings_acked = true;
291 }
292 H2Event::Response {
293 stream_id,
294 headers,
295 end_stream,
296 } => {
297 if let Some(ps) = pending.get_mut(&stream_id) {
298 for h in &headers {
300 if h.name == b":status" {
301 if let Ok(s) = std::str::from_utf8(&h.value) {
302 ps.status = s.parse().ok();
303 }
304 } else {
305 let name = String::from_utf8_lossy(&h.name).into_owned();
306 let value = String::from_utf8_lossy(&h.value).into_owned();
307 if name.eq_ignore_ascii_case("content-encoding") {
308 ps.content_encoding = Some(value.clone());
309 }
310 ps.headers.push((name, value));
311 }
312 }
313 if end_stream {
314 ps.done = true;
315 }
316 }
317 }
318 H2Event::Data {
319 stream_id,
320 data: payload,
321 end_stream,
322 } => {
323 if let Some(ps) = pending.get_mut(&stream_id) {
324 if ps.streaming {
325 ps.chunks.push_back(Bytes::from(payload));
326 } else {
327 ps.body.extend_from_slice(&payload);
328 }
329 if end_stream {
330 ps.done = true;
331 }
332 }
333 }
334 H2Event::Trailers { stream_id, .. } => {
335 if let Some(ps) = pending.get_mut(&stream_id) {
336 ps.done = true;
337 }
338 }
339 H2Event::StreamReset { stream_id, .. } => {
340 if let Some(ps) = pending.get_mut(&stream_id) {
341 ps.done = true;
342 }
343 }
344 H2Event::GoAway { .. } => {
345 for ps in pending.values_mut() {
347 ps.done = true;
348 }
349 }
350 H2Event::Error(_) => {}
351 H2Event::PingAcknowledged { .. } => {}
352 }
353 }
354
355 let mut retry = VecDeque::new();
357 std::mem::swap(blocked, &mut retry);
358 for bs in retry {
359 if let Err(_e) = h2.send_data(bs.stream_id, &bs.data, bs.end_stream) {
360 blocked.push_back(bs);
362 }
363 }
364
365 ParseResult::Consumed(data.len())
367 })
368 .await;
369
370 if n == 0 {
371 return Err(HttpError::ConnectionClosed);
372 }
373
374 let done_ids: Vec<u32> = self
376 .pending_streams
377 .iter()
378 .filter(|(_, ps)| ps.done && !ps.streaming)
379 .map(|(id, _)| *id)
380 .collect();
381 for id in done_ids {
382 if let Some(ps) = self.pending_streams.remove(&id) {
383 self.completed.push_back((id, ps.into_response()?));
384 }
385 }
386
387 self.flush_pending_send()?;
389
390 Ok(())
391 }
392
393 pub async fn send_request_streaming(
400 &mut self,
401 method: &str,
402 path: &str,
403 host: &str,
404 extra_headers: &[(&str, &str)],
405 body: Option<&[u8]>,
406 ) -> Result<H2StreamingResponse<'_>, HttpError> {
407 let stream_id = self.fire_request(method, path, host, extra_headers, body)?;
408
409 if let Some(ps) = self.pending_streams.get_mut(&stream_id) {
411 ps.streaming = true;
412 }
413
414 loop {
416 if let Some(ps) = self.pending_streams.get(&stream_id) {
417 if ps.status.is_some() {
418 break;
419 }
420 } else {
421 return Err(HttpError::Protocol("stream vanished".into()));
422 }
423
424 self.pump_once().await?;
425 }
426
427 Ok(H2StreamingResponse {
428 conn: self,
429 stream_id,
430 })
431 }
432
433 fn flush_pending_send(&mut self) -> Result<(), HttpError> {
435 let pending = self.h2.take_pending_send();
436 if !pending.is_empty() {
437 self.conn.send_nowait(&pending)?;
438 }
439 Ok(())
440 }
441}
442
443pub struct H2StreamingResponse<'a> {
449 conn: &'a mut H2AsyncConn,
450 stream_id: u32,
451}
452
453impl<'a> H2StreamingResponse<'a> {
454 pub fn status(&self) -> u16 {
456 self.conn
457 .pending_streams
458 .get(&self.stream_id)
459 .and_then(|ps| ps.status)
460 .unwrap_or(0)
461 }
462
463 pub fn headers(&self) -> &[(String, String)] {
465 self.conn
466 .pending_streams
467 .get(&self.stream_id)
468 .map(|ps| ps.headers.as_slice())
469 .unwrap_or(&[])
470 }
471
472 pub fn header(&self, name: &str) -> Option<&str> {
474 let lower = name.to_ascii_lowercase();
475 self.headers()
476 .iter()
477 .find(|(k, _)| k.to_ascii_lowercase() == lower)
478 .map(|(_, v)| v.as_str())
479 }
480
481 pub async fn next_chunk(&mut self) -> Result<Option<Bytes>, HttpError> {
483 loop {
484 if let Some(ps) = self.conn.pending_streams.get_mut(&self.stream_id) {
485 if let Some(chunk) = ps.chunks.pop_front() {
487 return Ok(Some(chunk));
488 }
489 if ps.done {
491 self.conn.pending_streams.remove(&self.stream_id);
492 return Ok(None);
493 }
494 } else {
495 return Ok(None);
496 }
497
498 self.conn.pump_once().await?;
500 }
501 }
502}
503
504impl Drop for H2StreamingResponse<'_> {
505 fn drop(&mut self) {
506 self.conn.pending_streams.remove(&self.stream_id);
507 }
508}