1use super::decompress::decompress;
19use super::error::{Error, Result};
20use super::finish_with_error;
21use super::grpc::grpc_request;
22use super::net::{dns_resolve, parse_certificates, quic_connect, tcp_connect, tls_handshake};
23use super::proxy::{http_connect, socks5_connect, ProxyConfig, ProxyKind};
24use super::stats::{parse_server_timing, HttpStat, ALPN_HTTP3, FIRST_CHUNK_BYTES};
25use super::HttpRequest;
26use bytes::{Buf, Bytes, BytesMut};
27use futures::future;
28
29use http::Request;
30use http::Response;
31use http::Version;
32use http_body::{Body, Frame, SizeHint};
33use http_body_util::BodyExt;
34use hyper::body::Incoming;
35use hyper_util::rt::TokioExecutor;
36use hyper_util::rt::TokioIo;
37use std::pin::Pin;
38use std::sync::{Arc, Once, OnceLock};
39use std::task::{Context, Poll};
40use std::time::Duration;
41use std::time::Instant;
42use tokio::net::TcpStream;
43use tokio::sync::oneshot;
44use tokio::time::timeout;
45use tokio_rustls::client::TlsStream;
46
47pub(crate) struct TrackedBody {
55 data: Option<Bytes>,
56 done: Arc<OnceLock<Instant>>,
57}
58
59impl TrackedBody {
60 pub(crate) fn new(data: Bytes) -> (Self, Arc<OnceLock<Instant>>) {
61 let done = Arc::new(OnceLock::new());
62 (
63 Self {
64 data: Some(data),
65 done: done.clone(),
66 },
67 done,
68 )
69 }
70}
71
72impl Body for TrackedBody {
73 type Data = Bytes;
74 type Error = std::convert::Infallible;
75
76 fn poll_frame(
77 self: Pin<&mut Self>,
78 _cx: &mut Context<'_>,
79 ) -> Poll<Option<std::result::Result<Frame<Self::Data>, Self::Error>>> {
80 let this = self.get_mut();
81 if let Some(bytes) = this.data.take() {
82 Poll::Ready(Some(Ok(Frame::data(bytes))))
83 } else {
84 let _ = this.done.set(Instant::now());
85 Poll::Ready(None)
86 }
87 }
88
89 fn is_end_stream(&self) -> bool {
90 false
92 }
93
94 fn size_hint(&self) -> SizeHint {
95 match &self.data {
96 Some(b) => SizeHint::with_exact(b.len() as u64),
97 None => SizeHint::with_exact(0),
98 }
99 }
100}
101
102fn build_tracked_request(
104 req: &HttpRequest,
105 is_http1: bool,
106) -> Result<(Request<TrackedBody>, Arc<OnceLock<Instant>>)> {
107 let body = req.body.clone().unwrap_or_default();
108 let (tracked, done) = TrackedBody::new(body);
109 let request = req
110 .builder(is_http1)
111 .body(tracked)
112 .map_err(|e| Error::Http { source: e })?;
113 Ok((request, done))
114}
115
116fn record_send_split(
121 stat: &mut HttpStat,
122 send_start: Instant,
123 response_at: Instant,
124 done: &Arc<OnceLock<Instant>>,
125) {
126 match done.get().copied() {
127 Some(done_at) if done_at >= send_start && done_at <= response_at => {
128 stat.request_send = Some(done_at.duration_since(send_start));
129 stat.server_processing = Some(response_at.duration_since(done_at));
130 }
131 _ => {
132 stat.server_processing = Some(response_at.duration_since(send_start));
133 }
134 }
135}
136
137fn capture_server_timing(stat: &mut HttpStat, headers: &http::HeaderMap) {
139 let values: Vec<&str> = headers
140 .get_all("server-timing")
141 .iter()
142 .filter_map(|v| v.to_str().ok())
143 .collect();
144 if !values.is_empty() {
145 stat.server_timing = parse_server_timing(values.iter().copied());
146 }
147}
148
149async fn drain_body_with_split(
154 body: Incoming,
155 start: Instant,
156) -> std::result::Result<(Bytes, Option<Duration>), String> {
157 let mut body = body;
158 let mut buf = BytesMut::new();
159 let mut first_chunk_at: Option<Duration> = None;
160 while let Some(frame_res) = body.frame().await {
161 let frame = frame_res.map_err(|e| format!("Failed to read response body: {e}"))?;
162 if let Ok(data) = frame.into_data() {
163 buf.extend_from_slice(&data);
164 if first_chunk_at.is_none() && buf.len() >= FIRST_CHUNK_BYTES {
165 first_chunk_at = Some(start.elapsed());
166 }
167 }
168 }
169 Ok((buf.freeze(), first_chunk_at))
170}
171
172static INIT: Once = Once::new();
174
175fn ensure_crypto_provider() {
176 INIT.call_once(|| {
177 let _ = tokio_rustls::rustls::crypto::ring::default_provider().install_default();
178 });
179}
180
181async fn send_http1_request<S>(
183 req: Request<TrackedBody>,
184 done: Arc<OnceLock<Instant>>,
185 stream: S,
186 request_timeout: Option<Duration>,
187 tx: oneshot::Sender<String>,
188 stat: &mut HttpStat,
189) -> Result<Response<Incoming>>
190where
191 S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static,
192{
193 let (mut sender, conn) = timeout(
194 request_timeout.unwrap_or(Duration::from_secs(30)),
195 hyper::client::conn::http1::handshake(TokioIo::new(stream)),
196 )
197 .await
198 .map_err(|e| Error::Timeout { source: e })?
199 .map_err(|e| Error::Hyper { source: e })?;
200
201 tokio::spawn(async move {
203 if let Err(e) = conn.await {
204 let _ = tx.send(e.to_string());
205 }
206 });
207
208 let send_start = Instant::now();
209 let resp = sender
210 .send_request(req)
211 .await
212 .map_err(|e| Error::Hyper { source: e })?;
213 let response_at = Instant::now();
214 record_send_split(stat, send_start, response_at, &done);
215 Ok(resp)
216}
217
218async fn send_https2_request(
220 req: Request<TrackedBody>,
221 done: Arc<OnceLock<Instant>>,
222 tls_stream: TlsStream<TcpStream>,
223 request_timeout: Option<Duration>,
224 tx: oneshot::Sender<String>,
225 stat: &mut HttpStat,
226) -> Result<Response<Incoming>> {
227 let (mut sender, conn) = timeout(
228 request_timeout.unwrap_or(Duration::from_secs(30)),
229 hyper::client::conn::http2::handshake(TokioExecutor::new(), TokioIo::new(tls_stream)),
230 )
231 .await
232 .map_err(|e| Error::Timeout { source: e })?
233 .map_err(|e| Error::Hyper { source: e })?;
234
235 tokio::spawn(async move {
237 if let Err(e) = conn.await {
238 let _ = tx.send(e.to_string());
239 }
240 });
241
242 let mut req = req;
243 *req.version_mut() = hyper::Version::HTTP_2;
244 req.headers_mut().remove("Host");
246
247 let send_start = Instant::now();
248 let resp = sender
249 .send_request(req)
250 .await
251 .map_err(|e| Error::Hyper { source: e })?;
252 let response_at = Instant::now();
253 record_send_split(stat, send_start, response_at, &done);
254 Ok(resp)
255}
256
257async fn http3_request(http_req: HttpRequest) -> HttpStat {
259 let start = Instant::now();
260 let mut stat = HttpStat {
261 alpn: Some(ALPN_HTTP3.to_string()),
262 ..Default::default()
263 };
264
265 let dns_result = dns_resolve(&http_req, &mut stat).await;
267 let (addr, host) = match dns_result {
268 Ok(result) => result,
269 Err(e) => {
270 return finish_with_error(stat, e, start);
271 }
272 };
273
274 let (client_endpoint, conn) = match timeout(
276 http_req.quic_timeout.unwrap_or(Duration::from_secs(30)),
277 quic_connect(
278 host,
279 addr,
280 http_req.skip_verify,
281 http_req.client_cert.as_deref(),
282 http_req.client_key.as_deref(),
283 http_req.bind_addr,
284 &mut stat,
285 ),
286 )
287 .await
288 {
289 Ok(Ok(result)) => result,
290 Ok(Err(e)) => {
291 return finish_with_error(stat, e, start);
292 }
293 Err(e) => {
294 return finish_with_error(stat, e, start);
295 }
296 };
297
298 stat.tls = Some("tls 1.3".to_string()); stat.alpn = Some(ALPN_HTTP3.to_string()); if let Some(peer_identity) = conn.peer_identity() {
304 if let Ok(certs) = peer_identity.downcast::<Vec<rustls::pki_types::CertificateDer>>() {
305 if let Some(first_cert) = certs.first() {
307 if let Ok((_, cert)) = x509_parser::parse_x509_certificate(first_cert.as_ref()) {
308 let oid_str = match cert.signature_algorithm.algorithm.to_string().as_str() {
309 "1.2.840.113549.1.1.11" => "AES_256_GCM_SHA384".to_string(),
310 "1.2.840.113549.1.1.12" => "AES_128_GCM_SHA256".to_string(),
311 "1.2.840.113549.1.1.13" => "CHACHA20_POLY1305_SHA256".to_string(),
312 "1.2.840.10045.4.3.2" => "AES_256_GCM_SHA384".to_string(),
313 "1.2.840.10045.4.3.3" => "AES_128_GCM_SHA256".to_string(),
314 "1.2.840.10045.4.3.4" => "CHACHA20_POLY1305_SHA256".to_string(),
315 "1.3.101.112" => "AES_256_GCM_SHA384".to_string(),
316 "1.3.101.113" => "AES_128_GCM_SHA256".to_string(),
317 _ => format!("{:?}", cert.signature_algorithm.algorithm),
318 };
319 stat.cert_cipher = Some(oid_str);
320 }
321 }
322 parse_certificates(&certs, &mut stat);
323 }
324 }
325
326 let quinn_conn = h3_quinn::Connection::new(conn);
328
329 let (mut driver, mut send_request) = match timeout(
330 http_req.request_timeout.unwrap_or(Duration::from_secs(30)),
331 h3::client::new(quinn_conn),
332 )
333 .await
334 {
335 Ok(Ok(result)) => result,
336 Ok(Err(e)) => {
337 return finish_with_error(stat, e, start);
338 }
339 Err(e) => {
340 return finish_with_error(stat, e, start);
341 }
342 };
343
344 let mut req = match http_req.builder(false).body(()) {
346 Ok(req) => req,
347 Err(e) => {
348 return finish_with_error(stat, e, start);
349 }
350 };
351 *req.version_mut() = Version::HTTP_3;
352 stat.request_headers = req.headers().clone();
353 let body = http_req.body.unwrap_or_default();
354
355 let drive = async move {
357 Err::<(), h3::error::ConnectionError>(future::poll_fn(|cx| driver.poll_close(cx)).await)
358 };
359
360 let request = async move {
362 let mut sub_stat = HttpStat::default();
363
364 let request_send_start = Instant::now();
365 let mut stream = send_request.send_request(req).await?;
366 stream.send_data(body).await?;
367 stream.finish().await?;
369 sub_stat.request_send = Some(request_send_start.elapsed());
370
371 let server_processing_start = Instant::now();
372 let resp = stream.recv_response().await?;
373 sub_stat.server_processing = Some(server_processing_start.elapsed());
374
375 sub_stat.status = Some(resp.status());
376 sub_stat.headers = Some(resp.headers().clone());
377 capture_server_timing(&mut sub_stat, resp.headers());
378
379 let content_transfer_start = Instant::now();
382 let mut buf = BytesMut::new();
383 let mut first_chunk_at: Option<Duration> = None;
384 while let Some(chunk) = stream.recv_data().await? {
385 buf.extend(chunk.chunk());
386 if first_chunk_at.is_none() && buf.len() >= FIRST_CHUNK_BYTES {
387 first_chunk_at = Some(content_transfer_start.elapsed());
388 }
389 }
390 sub_stat.content_transfer = Some(content_transfer_start.elapsed());
391 sub_stat.wire_body_size = Some(buf.len());
392 sub_stat.time_to_first_100k = first_chunk_at;
393 sub_stat.body = Some(Bytes::from(buf));
394 Ok::<HttpStat, h3::error::StreamError>(sub_stat)
395 };
396
397 let (req_res, drive_res) = tokio::join!(request, drive);
399 match req_res {
400 Ok(sub_stat) => {
401 stat.request_send = sub_stat.request_send;
402 stat.server_processing = sub_stat.server_processing;
403 stat.content_transfer = sub_stat.content_transfer;
404 stat.status = sub_stat.status;
405 stat.headers = sub_stat.headers;
406 stat.body = sub_stat.body;
407 stat.wire_body_size = sub_stat.wire_body_size;
408 stat.time_to_first_100k = sub_stat.time_to_first_100k;
409 stat.server_timing = sub_stat.server_timing;
410 }
411 Err(err) => {
412 if !err.is_h3_no_error() {
413 stat.error = Some(err.to_string());
414 }
415 }
416 }
417 if let Err(err) = drive_res {
418 if !err.is_h3_no_error() {
419 stat.error = Some(err.to_string());
420 }
421 }
422
423 stat.total = Some(start.elapsed());
424 client_endpoint.close(0u32.into(), b"done");
426
427 stat
428}
429
430async fn tcp_via_proxy(
440 http_req: &HttpRequest,
441 stat: &mut HttpStat,
442) -> Result<(
443 TcpStream,
444 String,
445 bool,
446 Option<crate::tcp_info::TcpInfoProbe>,
447)> {
448 let uri = &http_req.uri;
449 let is_https = uri.scheme() == Some(&http::uri::Scheme::HTTPS);
450 let target_host = uri.host().unwrap_or_default().to_string();
451 let target_port = http_req.get_port();
452
453 if let Some(proxy) = http_req.proxy.as_deref().and_then(ProxyConfig::parse) {
454 let proxy_addr = format!("{}:{}", proxy.host, proxy.port);
455 let tcp_start = Instant::now();
456 let proxy_stream = timeout(
457 http_req.tcp_timeout.unwrap_or(Duration::from_secs(5)),
458 TcpStream::connect(&proxy_addr),
459 )
460 .await
461 .map_err(|e| Error::Timeout { source: e })?
462 .map_err(|e| Error::Io { source: e })?;
463
464 if let Ok(peer) = proxy_stream.peer_addr() {
465 stat.addr = Some(peer.to_string());
466 }
467
468 let (baseline, probe) = crate::tcp_info::TcpInfoProbe::capture(&proxy_stream);
471 stat.tcp_info_post_connect = baseline;
472
473 let is_http_forward = !is_https && matches!(proxy.kind, ProxyKind::Http);
475 let stream = if is_http_forward {
476 proxy_stream
477 } else {
478 match proxy.kind {
479 ProxyKind::Socks5 => {
480 socks5_connect(proxy_stream, &target_host, target_port).await?
481 }
482 ProxyKind::Http => http_connect(proxy_stream, &target_host, target_port).await?,
483 }
484 };
485 stat.tcp_connect = Some(tcp_start.elapsed());
486 Ok((stream, target_host, is_http_forward, probe))
487 } else {
488 let (addr, host) = dns_resolve(http_req, stat).await?;
489 let (stream, probe) =
490 tcp_connect(addr, http_req.tcp_timeout, http_req.bind_addr, stat).await?;
491 Ok((stream, host, false, probe))
492 }
493}
494
495async fn http1_2_request(mut http_req: HttpRequest) -> HttpStat {
496 let start = Instant::now();
497 let mut stat = HttpStat::default();
498
499 let is_https = http_req.uri.scheme() == Some(&http::uri::Scheme::HTTPS);
500
501 let (tcp_stream, host, is_http_forward, tcp_probe) =
503 match tcp_via_proxy(&http_req, &mut stat).await {
504 Ok(r) => r,
505 Err(e) => return finish_with_error(stat, e, start),
506 };
507
508 if is_http_forward {
510 http_req.use_absolute_uri = true;
511 }
512
513 let (tx, mut rx) = oneshot::channel();
515
516 let resp = if is_https {
518 let tls_result = tls_handshake(host.clone(), tcp_stream, &http_req, &mut stat).await;
520 let (tls_stream, is_http2) = match tls_result {
521 Ok(result) => result,
522 Err(e) => {
523 return finish_with_error(stat, e, start);
524 }
525 };
526
527 if is_http2 {
529 let (req, done) = match build_tracked_request(&http_req, false) {
530 Ok(r) => r,
531 Err(e) => {
532 return finish_with_error(stat, e, start);
533 }
534 };
535 stat.request_headers = req.headers().clone();
536 match send_https2_request(
537 req,
538 done,
539 tls_stream,
540 http_req.request_timeout,
541 tx,
542 &mut stat,
543 )
544 .await
545 {
546 Ok(resp) => resp,
547 Err(e) => {
548 return finish_with_error(stat, e, start);
549 }
550 }
551 } else {
552 let (req, done) = match build_tracked_request(&http_req, true) {
553 Ok(r) => r,
554 Err(e) => {
555 return finish_with_error(stat, e, start);
556 }
557 };
558 stat.request_headers = req.headers().clone();
559 match send_http1_request(
560 req,
561 done,
562 tls_stream,
563 http_req.request_timeout,
564 tx,
565 &mut stat,
566 )
567 .await
568 {
569 Ok(resp) => resp,
570 Err(e) => {
571 return finish_with_error(stat, e, start);
572 }
573 }
574 }
575 } else {
576 let (req, done) = match build_tracked_request(&http_req, true) {
577 Ok(r) => r,
578 Err(e) => {
579 return finish_with_error(stat, e, start);
580 }
581 };
582 stat.request_headers = req.headers().clone();
583 match send_http1_request(
585 req,
586 done,
587 tcp_stream,
588 http_req.request_timeout,
589 tx,
590 &mut stat,
591 )
592 .await
593 {
594 Ok(resp) => resp,
595 Err(e) => {
596 return finish_with_error(stat, e, start);
597 }
598 }
599 };
600
601 stat.status = Some(resp.status());
603 stat.headers = Some(resp.headers().clone());
604 capture_server_timing(&mut stat, resp.headers());
605
606 if let Ok(error) = rx.try_recv() {
608 stat.error = Some(error);
609 }
610 let content_transfer_start = Instant::now();
615 let drain_result = drain_body_with_split(resp.into_body(), content_transfer_start).await;
616 let (body_bytes, time_to_first_100k) = match drain_result {
617 Ok(p) => p,
618 Err(e) => return finish_with_error(stat, e, start),
619 };
620 stat.wire_body_size = Some(body_bytes.len());
621 stat.time_to_first_100k = time_to_first_100k;
622 stat.body = Some(body_bytes);
623 stat.content_transfer = Some(content_transfer_start.elapsed());
624
625 if let Some(probe) = &tcp_probe {
629 stat.tcp_info_final = probe.sample();
630 }
631
632 stat.total = Some(start.elapsed());
633 stat
634}
635
636pub async fn request(http_req: HttpRequest) -> HttpStat {
671 ensure_crypto_provider();
672 let schema = if let Some(schema) = http_req.uri.scheme() {
673 schema.to_string()
674 } else {
675 "".to_string()
676 };
677
678 let mut stat = if ["grpc", "grpcs"].contains(&schema.as_str()) {
680 grpc_request(http_req).await
681 } else if http_req.alpn_protocols.contains(&ALPN_HTTP3.to_string()) {
682 http3_request(http_req).await
683 } else {
684 http1_2_request(http_req).await
685 };
686 if let Some(body) = &stat.body {
687 stat.body_size = Some(body.len());
688 }
689 let encoding = if let Some(headers) = &stat.headers {
690 headers
691 .get("content-encoding")
692 .map(|v| v.to_str().unwrap_or_default())
693 .unwrap_or_default()
694 } else {
695 ""
696 };
697
698 if !encoding.is_empty() {
699 if let Some(body) = &stat.body {
700 match decompress(encoding, body) {
701 Ok(data) => {
702 stat.body = Some(data);
703 }
704 Err(e) => {
705 stat.error = Some(e.to_string());
706 }
707 }
708 }
709 }
710
711 stat
712}
713
714enum ConnectionSender {
717 Http1(hyper::client::conn::http1::SendRequest<TrackedBody>),
718 Http2(hyper::client::conn::http2::SendRequest<TrackedBody>),
719}
720
721pub struct HttpConnection {
723 sender: ConnectionSender,
724 is_http2: bool,
725 tcp_probe: Option<crate::tcp_info::TcpInfoProbe>,
729 last_tcp_info: Option<crate::TcpInfo>,
733}
734
735async fn establish_http1<S>(
736 stream: S,
737 handshake_timeout: Duration,
738 mut stat: HttpStat,
739 tcp_probe: Option<crate::tcp_info::TcpInfoProbe>,
740 start: Instant,
741) -> (HttpStat, Option<HttpConnection>)
742where
743 S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static,
744{
745 match timeout(
746 handshake_timeout,
747 hyper::client::conn::http1::handshake(TokioIo::new(stream)),
748 )
749 .await
750 {
751 Ok(Ok((sender, conn))) => {
752 tokio::spawn(async move {
753 let _ = conn.await;
754 });
755 stat.total = Some(start.elapsed());
756 let last_tcp_info = stat.tcp_info_post_connect.clone();
757 (
758 stat,
759 Some(HttpConnection {
760 sender: ConnectionSender::Http1(sender),
761 is_http2: false,
762 tcp_probe,
763 last_tcp_info,
764 }),
765 )
766 }
767 Ok(Err(e)) => (
768 finish_with_error(stat, Error::Hyper { source: e }, start),
769 None,
770 ),
771 Err(e) => (
772 finish_with_error(stat, Error::Timeout { source: e }, start),
773 None,
774 ),
775 }
776}
777
778async fn establish_http2<S>(
779 stream: S,
780 handshake_timeout: Duration,
781 mut stat: HttpStat,
782 tcp_probe: Option<crate::tcp_info::TcpInfoProbe>,
783 start: Instant,
784) -> (HttpStat, Option<HttpConnection>)
785where
786 S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static,
787{
788 match timeout(
789 handshake_timeout,
790 hyper::client::conn::http2::handshake(TokioExecutor::new(), TokioIo::new(stream)),
791 )
792 .await
793 {
794 Ok(Ok((sender, conn))) => {
795 tokio::spawn(async move {
796 let _ = conn.await;
797 });
798 stat.total = Some(start.elapsed());
799 let last_tcp_info = stat.tcp_info_post_connect.clone();
800 (
801 stat,
802 Some(HttpConnection {
803 sender: ConnectionSender::Http2(sender),
804 is_http2: true,
805 tcp_probe,
806 last_tcp_info,
807 }),
808 )
809 }
810 Ok(Err(e)) => (
811 finish_with_error(stat, Error::Hyper { source: e }, start),
812 None,
813 ),
814 Err(e) => (
815 finish_with_error(stat, Error::Timeout { source: e }, start),
816 None,
817 ),
818 }
819}
820
821pub async fn connect(http_req: &HttpRequest) -> (HttpStat, Option<HttpConnection>) {
826 ensure_crypto_provider();
827 let start = Instant::now();
828 let mut stat = HttpStat::default();
829
830 let is_https = http_req.uri.scheme() == Some(&http::uri::Scheme::HTTPS);
831
832 let (tcp_stream, host, _is_http_forward, tcp_probe) =
833 match tcp_via_proxy(http_req, &mut stat).await {
834 Ok(r) => r,
835 Err(e) => return (finish_with_error(stat, e, start), None),
836 };
837
838 let handshake_timeout = http_req.request_timeout.unwrap_or(Duration::from_secs(30));
839
840 if is_https {
841 let (tls_stream, is_h2) = match tls_handshake(host, tcp_stream, http_req, &mut stat).await {
842 Ok(r) => r,
843 Err(e) => return (finish_with_error(stat, e, start), None),
844 };
845
846 if is_h2 {
847 establish_http2(tls_stream, handshake_timeout, stat, tcp_probe, start).await
848 } else {
849 establish_http1(tls_stream, handshake_timeout, stat, tcp_probe, start).await
850 }
851 } else {
852 establish_http1(tcp_stream, handshake_timeout, stat, tcp_probe, start).await
853 }
854}
855
856impl HttpConnection {
857 pub async fn send(&mut self, http_req: &HttpRequest) -> HttpStat {
859 let start = Instant::now();
860 let mut stat = HttpStat {
865 tcp_info_post_connect: self.last_tcp_info.clone(),
866 ..HttpStat::default()
867 };
868
869 let is_http1 = !self.is_http2;
870 let (req, done) = match build_tracked_request(http_req, is_http1) {
871 Ok(r) => r,
872 Err(e) => return finish_with_error(stat, e, start),
873 };
874 stat.request_headers = req.headers().clone();
875
876 match &mut self.sender {
878 ConnectionSender::Http1(sender) => {
879 if let Err(e) = sender.ready().await {
880 return finish_with_error(stat, Error::Hyper { source: e }, start);
881 }
882 }
883 ConnectionSender::Http2(sender) => {
884 if let Err(e) = sender.ready().await {
885 return finish_with_error(stat, Error::Hyper { source: e }, start);
886 }
887 }
888 }
889
890 let send_start = Instant::now();
891 let resp = match &mut self.sender {
892 ConnectionSender::Http1(sender) => sender.send_request(req).await,
893 ConnectionSender::Http2(sender) => {
894 let mut req = req;
895 *req.version_mut() = Version::HTTP_2;
896 req.headers_mut().remove("Host");
897 sender.send_request(req).await
898 }
899 };
900
901 let resp = match resp {
902 Ok(resp) => resp,
903 Err(e) => return finish_with_error(stat, Error::Hyper { source: e }, start),
904 };
905 let response_at = Instant::now();
906 record_send_split(&mut stat, send_start, response_at, &done);
907 stat.status = Some(resp.status());
908 stat.headers = Some(resp.headers().clone());
909 capture_server_timing(&mut stat, resp.headers());
910
911 let content_transfer_start = Instant::now();
915 match drain_body_with_split(resp.into_body(), content_transfer_start).await {
916 Ok((body_bytes, first_100k)) => {
917 stat.wire_body_size = Some(body_bytes.len());
918 stat.time_to_first_100k = first_100k;
919 stat.body = Some(body_bytes);
920 stat.content_transfer = Some(content_transfer_start.elapsed());
921 }
922 Err(e) => {
923 return finish_with_error(stat, e, start);
924 }
925 }
926
927 if let Some(probe) = &self.tcp_probe {
931 let now = probe.sample();
932 stat.tcp_info_final = now.clone();
933 if now.is_some() {
934 self.last_tcp_info = now;
935 }
936 }
937
938 stat.total = Some(start.elapsed());
939
940 if let Some(body) = &stat.body {
942 stat.body_size = Some(body.len());
943 }
944 let encoding = stat
945 .headers
946 .as_ref()
947 .and_then(|h| h.get("content-encoding"))
948 .and_then(|v| v.to_str().ok())
949 .unwrap_or_default();
950 if !encoding.is_empty() {
951 if let Some(body) = &stat.body {
952 match decompress(encoding, body) {
953 Ok(data) => stat.body = Some(data),
954 Err(e) => stat.error = Some(e.to_string()),
955 }
956 }
957 }
958
959 stat
960 }
961}