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