1use async_trait::async_trait;
39use bytes::Bytes;
40use futures::future::BoxFuture;
41use futures::future::FutureExt;
42use http::{header, version::Version, Method};
43use log::{debug, error, trace, warn};
44use once_cell::sync::Lazy;
45use pingora_http::{RequestHeader, ResponseHeader};
46use std::fmt::Debug;
47use std::future::{poll_fn, Future};
48use std::str;
49use std::sync::{
50 atomic::{AtomicBool, AtomicU64, AtomicU8, AtomicUsize, Ordering},
51 Arc,
52};
53use std::task::Poll;
54use std::time::Duration;
55use tokio::sync::{mpsc, Notify};
56use tokio::time;
57
58use pingora_cache::NoCacheReason;
59use pingora_core::apps::{
60 HttpPersistentSettings, HttpServerApp, HttpServerOptions, ReusedHttpStream,
61};
62use pingora_core::connectors::http::custom;
63use pingora_core::connectors::{http::Connector, ConnectorOptions};
64use pingora_core::modules::http::compression::ResponseCompressionBuilder;
65use pingora_core::modules::http::{HttpModuleCtx, HttpModules};
66use pingora_core::protocols::http::client::HttpSession as ClientSession;
67use pingora_core::protocols::http::custom::CustomMessageWrite;
68use pingora_core::protocols::http::subrequest::server::SubrequestHandle;
69use pingora_core::protocols::http::v1::client::HttpSession as HttpSessionV1;
70use pingora_core::protocols::http::v2::server::H2Options;
71use pingora_core::protocols::http::HttpTask;
72use pingora_core::protocols::http::ServerSession as HttpSession;
73use pingora_core::protocols::http::SERVER_NAME;
74use pingora_core::protocols::Stream;
75use pingora_core::protocols::{Digest, UniqueID};
76use pingora_core::server::configuration::ServerConf;
77use pingora_core::server::{RuntimeOpts, ShutdownWatch};
78use pingora_core::upstreams::peer::{HttpPeer, Peer};
79use pingora_error::{Error, ErrorSource, ErrorType::*, OrErr, Result};
80
81const TASK_BUFFER_SIZE: usize = 4;
82
83const MAX_SHUTDOWN_NOTIFY_SHARDS: usize = 256;
85
86type DownstreamCustomMessageReader =
87 Box<dyn futures::Stream<Item = Result<Bytes>> + Unpin + Send + Sync + 'static>;
88
89mod proxy_cache;
90mod proxy_common;
91mod proxy_custom;
92mod proxy_h1;
93mod proxy_h2;
94mod proxy_purge;
95mod proxy_trait;
96pub mod subrequest;
97
98use subrequest::{BodyMode, Ctx as SubrequestCtx};
99
100pub use proxy_cache::range_filter::{range_header_filter, MultiRangeInfo, RangeType};
101pub use proxy_purge::PurgeStatus;
102pub use proxy_trait::{FailToProxy, ProxyHttp, ProxyWarnLogContext};
103
104pub mod prelude {
105 pub use crate::{http_proxy, http_proxy_service, ProxyHttp, ProxyWarnLogContext, Session};
106}
107
108pub type ProcessCustomSession<SV, C> = Arc<
109 dyn Fn(Arc<HttpProxy<SV, C>>, Stream, &ShutdownWatch) -> BoxFuture<'static, Option<Stream>>
110 + Send
111 + Sync
112 + Unpin
113 + 'static,
114>;
115
116struct ShardedNotify {
125 shards: Box<[NotifyShard]>,
126}
127
128#[repr(align(128))]
139struct NotifyShard(Notify);
140
141impl ShardedNotify {
142 fn new(worker_threads: usize) -> Self {
145 let shards = worker_threads
146 .max(1)
147 .checked_next_power_of_two()
148 .unwrap_or(MAX_SHUTDOWN_NOTIFY_SHARDS)
149 .min(MAX_SHUTDOWN_NOTIFY_SHARDS);
150 ShardedNotify {
151 shards: (0..shards).map(|_| NotifyShard(Notify::new())).collect(),
152 }
153 }
154
155 fn local(&self) -> &Notify {
160 static NEXT_THREAD_ID: AtomicUsize = AtomicUsize::new(0);
161 thread_local! {
162 static THREAD_ID: usize = NEXT_THREAD_ID.fetch_add(1, Ordering::Relaxed);
163 }
164 let id = THREAD_ID.with(|id| *id);
165 &self.shards[id & (self.shards.len() - 1)].0
167 }
168
169 fn notify_waiters(&self) {
172 for shard in self.shards.iter() {
173 shard.0.notify_waiters();
174 }
175 }
176}
177
178pub struct HttpProxy<SV, C = ()>
182where
183 C: custom::Connector, {
185 inner: SV, client_upstream: Connector<C>,
187 shutdown: ShardedNotify,
188 shutdown_flag: Arc<AtomicBool>,
189 pub server_options: Option<HttpServerOptions>,
190 pub h2_options: Option<H2Options>,
191 pub downstream_modules: HttpModules,
192 #[cfg(feature = "upstream_modules")]
193 pub upstream_modules: HttpModules,
194 max_retries: usize,
195 process_custom_session: Option<ProcessCustomSession<SV, C>>,
196}
197
198impl<SV> HttpProxy<SV, ()> {
199 pub fn new(inner: SV, conf: Arc<ServerConf>) -> Self {
220 HttpProxy {
221 inner,
222 client_upstream: Connector::new(Some(ConnectorOptions::from_server_conf(&conf))),
223 shutdown: ShardedNotify::new(conf.threads),
224 shutdown_flag: Arc::new(AtomicBool::new(false)),
225 server_options: None,
226 h2_options: None,
227 downstream_modules: HttpModules::new(),
228 #[cfg(feature = "upstream_modules")]
229 upstream_modules: HttpModules::new(),
230 max_retries: conf.max_retries,
231 process_custom_session: None,
232 }
233 }
234}
235
236impl<SV, C> HttpProxy<SV, C>
237where
238 C: custom::Connector,
239{
240 fn new_custom(
241 inner: SV,
242 conf: Arc<ServerConf>,
243 connector: C,
244 on_custom: Option<ProcessCustomSession<SV, C>>,
245 server_options: Option<HttpServerOptions>,
246 client_options: Option<ConnectorOptions>,
247 ) -> Self
248 where
249 SV: ProxyHttp + Send + Sync + 'static,
250 SV::CTX: Send + Sync,
251 {
252 let client_options =
253 client_options.unwrap_or_else(|| ConnectorOptions::from_server_conf(&conf));
254 let client_upstream = Connector::new_custom(Some(client_options), connector);
255
256 HttpProxy {
257 inner,
258 client_upstream,
259 shutdown: ShardedNotify::new(conf.threads),
260 shutdown_flag: Arc::new(AtomicBool::new(false)),
261 server_options,
262 downstream_modules: HttpModules::new(),
263 #[cfg(feature = "upstream_modules")]
264 upstream_modules: HttpModules::new(),
265 max_retries: conf.max_retries,
266 process_custom_session: on_custom,
267 h2_options: None,
268 }
269 }
270
271 pub fn unexpected_data_connection_count(&self) -> u64 {
274 self.client_upstream.unexpected_data_connection_count()
275 }
276
277 pub fn unexpected_data_connection_counter(&self) -> Arc<AtomicU64> {
279 self.client_upstream.unexpected_data_connection_counter()
280 }
281
282 pub fn handle_init_modules(&mut self)
291 where
292 SV: ProxyHttp,
293 {
294 self.inner
295 .init_downstream_modules(&mut self.downstream_modules);
296 #[cfg(feature = "upstream_modules")]
297 self.inner.init_upstream_modules(&mut self.upstream_modules);
298 }
299
300 async fn await_shutdown(&self) {
307 let notified = self.shutdown.local().notified();
308 tokio::pin!(notified);
309
310 poll_fn(|context| {
311 if notified.as_mut().poll(context).is_ready()
312 || self.shutdown_flag.load(Ordering::Acquire)
313 {
314 Poll::Ready(())
315 } else {
316 Poll::Pending
317 }
318 })
319 .await;
320 }
321
322 async fn handle_new_request(
323 &self,
324 mut downstream_session: Box<HttpSession>,
325 ) -> Option<Box<HttpSession>>
326 where
327 SV: ProxyHttp + Send + Sync,
328 SV::CTX: Send + Sync,
329 {
330 let res = tokio::select! {
333 biased; res = downstream_session.read_request() => { res }
335 _ = self.await_shutdown() => {
336 return None;
338 }
339 };
340 match res {
341 Ok(true) => {
342 debug!("Successfully get a new request");
344 }
345 Ok(false) => {
346 return None; }
348 Err(mut e) => {
349 e.as_down();
350 if matches!(e.etype, InvalidHTTPHeader) {
351 debug!(
352 "Fail to proxy: {e}, downstream session type: {}",
353 downstream_session.session_type()
354 );
355 downstream_session
356 .respond_error(400)
357 .await
358 .unwrap_or_else(|e| {
359 error!("failed to send error response to downstream: {e}");
360 });
361 } else {
362 error!(
364 "Fail to proxy: {e}, downstream session type: {}",
365 downstream_session.session_type()
366 );
367 }
368 downstream_session.shutdown().await;
369 return None;
370 }
371 }
372 trace!(
373 "Request header: {:?}",
374 downstream_session.req_header().as_ref()
375 );
376 if !self
383 .server_options
384 .as_ref()
385 .is_some_and(|opts| opts.allow_connect_method_proxying)
386 && downstream_session.req_header().method == Method::CONNECT
387 {
388 downstream_session
389 .respond_error(405)
390 .await
391 .unwrap_or_else(|e| {
392 error!("failed to send error response to downstream: {e}");
393 });
394 downstream_session.shutdown().await;
395 return None;
396 }
397 Some(downstream_session)
398 }
399
400 async fn proxy_to_upstream(
402 &self,
403 session: &mut Session,
404 ctx: &mut SV::CTX,
405 ) -> (bool, Option<Box<Error>>)
406 where
407 SV: ProxyHttp + Send + Sync,
408 SV::CTX: Send + Sync,
409 {
410 let peer = match self.inner.upstream_peer(session, ctx).await {
411 Ok(p) => p,
412 Err(e) => return (false, Some(e)),
413 };
414
415 let client_session = self.client_upstream.get_http_session(&*peer).await;
416 match client_session {
417 Ok((client_session, client_reused)) => {
418 let (server_reused, error) = match client_session {
419 ClientSession::H1(mut h1) => {
420 let (server_reused, client_reuse, error) = self
421 .proxy_to_h1_upstream(session, &mut h1, client_reused, &peer, ctx)
422 .await;
423 if client_reuse {
424 let session = ClientSession::H1(h1);
425 self.client_upstream
426 .release_http_session(session, &*peer, peer.idle_timeout())
427 .await;
428 }
429 (server_reused, error)
430 }
431 ClientSession::H2(mut h2) => {
432 let (server_reused, mut error) = self
433 .proxy_to_h2_upstream(session, &mut h2, client_reused, &peer, ctx)
434 .await;
435 let session = ClientSession::H2(h2);
436 self.client_upstream
437 .release_http_session(session, &*peer, peer.idle_timeout())
438 .await;
439
440 if let Some(e) = error.as_mut() {
441 if matches!(e.etype, H2Downgrade | InvalidH2) {
444 if peer
445 .get_alpn()
446 .is_none_or(|alpn| alpn.get_min_http_version() == 1)
447 {
448 self.client_upstream.prefer_h1(&*peer);
451 } else {
452 e.retry = false.into();
454 }
455 }
456 }
457
458 (server_reused, error)
459 }
460 ClientSession::Custom(mut c) => {
461 let (server_reused, error) = self
462 .proxy_to_custom_upstream(session, &mut c, client_reused, &peer, ctx)
463 .await;
464 let session = ClientSession::Custom(c);
465 self.client_upstream
466 .release_http_session(session, &*peer, peer.idle_timeout())
467 .await;
468 (server_reused, error)
469 }
470 };
471 (
472 server_reused,
473 error.map(|e| {
474 self.inner
475 .error_while_proxy(&peer, session, e, ctx, client_reused)
476 }),
477 )
478 }
479 Err(mut e) => {
480 e.as_up();
481 let new_err = self.inner.fail_to_connect(session, &peer, ctx, e);
482 (false, Some(new_err.into_up()))
483 }
484 }
485 }
486
487 async fn upstream_filter(
488 &self,
489 session: &mut Session,
490 task: &mut HttpTask,
491 ctx: &mut SV::CTX,
492 ) -> Result<Option<Duration>>
493 where
494 SV: ProxyHttp + Send + Sync,
495 SV::CTX: Send + Sync,
496 {
497 let duration = match task {
498 HttpTask::Header(header, _eos) => {
499 self.inner
500 .upstream_response_filter(session, header, ctx)
501 .await?;
502 None
503 }
504 HttpTask::Body(data, eos) | HttpTask::UpgradedBody(data, eos) => self
505 .inner
506 .upstream_response_body_filter(session, data, *eos, ctx)?,
507 HttpTask::Trailer(Some(trailers)) => {
508 self.inner
509 .upstream_response_trailer_filter(session, trailers, ctx)?;
510 None
511 }
512 _ => {
513 None
515 }
516 };
517
518 Ok(duration)
519 }
520
521 async fn finish(
522 &self,
523 mut session: Session,
524 ctx: &mut SV::CTX,
525 reuse: bool,
526 error: Option<Box<Error>>,
527 ) -> Option<ReusedHttpStream>
528 where
529 SV: ProxyHttp + Send + Sync,
530 SV::CTX: Send + Sync,
531 {
532 self.inner
533 .logging(&mut session, error.as_deref(), ctx)
534 .await;
535
536 if let Some(e) = error {
537 session.downstream_session.on_proxy_failure(e);
538 }
539
540 if reuse {
541 let mut persistent_settings = HttpPersistentSettings::for_session(&session);
543 if let Some(uc) = self.inner.persist_connection_context(&session, ctx) {
544 persistent_settings.set_user_context(uc);
545 }
546 session
547 .downstream_session
548 .finish()
549 .await
550 .ok()
551 .flatten()
552 .map(|s| ReusedHttpStream::from_reusable_stream(s, persistent_settings))
553 } else {
554 None
555 }
556 }
557
558 fn cleanup_sub_req(&self, session: &mut Session) {
559 if let Some(ctx) = session.subrequest_ctx.as_mut() {
560 ctx.release_write_lock();
561 }
562 }
563}
564
565use pingora_cache::HttpCache;
566use pingora_core::protocols::http::compression::ResponseCompressionCtx;
567
568pub struct Session {
573 pub downstream_session: Box<HttpSession>,
575 pub cache: HttpCache,
577 pub upstream_compression: ResponseCompressionCtx,
579 pub ignore_downstream_range: bool,
581 pub upstream_headers_mutated_for_cache: bool,
583 h1_upgrade_request_status: H1UpgradeRequestStatus,
585 pub subrequest_ctx: Option<Box<SubrequestCtx>>,
587 pub subrequest_spawner: Option<SubrequestSpawner>,
589 pub downstream_modules_ctx: HttpModuleCtx,
591 #[cfg(feature = "upstream_modules")]
594 pub upstream_modules_ctx: HttpModuleCtx,
595 upstream_body_bytes_received: usize,
598 upstream_body_bytes_sent: Option<usize>,
604 downstream_task_seen_upgraded: bool,
606 upstream_write_pending_time: Duration,
608 shutdown_flag: Arc<AtomicBool>,
610}
611
612impl Session {
613 fn new(
614 downstream_session: impl Into<Box<HttpSession>>,
615 downstream_modules: &HttpModules,
616 #[cfg(feature = "upstream_modules")] upstream_modules: &HttpModules,
617 shutdown_flag: Arc<AtomicBool>,
618 ) -> Self {
619 Session {
620 downstream_session: downstream_session.into(),
621 cache: HttpCache::new(),
622 upstream_compression: ResponseCompressionCtx::new(0, false, false),
624 ignore_downstream_range: false,
625 upstream_headers_mutated_for_cache: false,
626 h1_upgrade_request_status: H1UpgradeRequestStatus::default(),
627 subrequest_ctx: None,
628 subrequest_spawner: None, downstream_modules_ctx: downstream_modules.build_ctx(),
630 #[cfg(feature = "upstream_modules")]
631 upstream_modules_ctx: upstream_modules.build_ctx(),
632 upstream_body_bytes_received: 0,
633 upstream_body_bytes_sent: None,
634 downstream_task_seen_upgraded: false,
635 upstream_write_pending_time: Duration::ZERO,
636 shutdown_flag,
637 }
638 }
639
640 pub fn new_h1(stream: Stream) -> Self {
645 let modules = HttpModules::new();
646 Self::new(
647 Box::new(HttpSession::new_http1(stream)),
648 &modules,
649 #[cfg(feature = "upstream_modules")]
650 &HttpModules::new(),
651 Arc::new(AtomicBool::new(false)),
652 )
653 }
654
655 pub fn new_h1_with_modules(stream: Stream, downstream_modules: &HttpModules) -> Self {
660 Self::new(
661 Box::new(HttpSession::new_http1(stream)),
662 downstream_modules,
663 #[cfg(feature = "upstream_modules")]
664 &HttpModules::new(),
665 Arc::new(AtomicBool::new(false)),
666 )
667 }
668
669 #[cfg(feature = "upstream_modules")]
676 pub async fn upstream_modules_filter_task(&mut self, t: &mut HttpTask) -> Result<()> {
677 match t {
678 HttpTask::Header(header, eos) => {
679 self.upstream_modules_ctx
680 .response_header_filter(header, *eos)
681 .await?;
682 }
683 HttpTask::Body(body, eos) | HttpTask::UpgradedBody(body, eos) => {
684 self.upstream_modules_ctx.response_body_filter(body, *eos)?;
685 }
686 HttpTask::Trailer(trailers) => {
687 if let Some(buf) = self
688 .upstream_modules_ctx
689 .response_trailer_filter(trailers)?
690 {
691 *t = HttpTask::Body(Some(buf), true);
692 }
693 }
694 HttpTask::Done => {
695 if let Some(buf) = self.upstream_modules_ctx.response_done_filter()? {
696 *t = HttpTask::Body(Some(buf), true);
697 }
698 }
699 HttpTask::Failed(_) => {}
700 }
701 Ok(())
702 }
703
704 pub fn as_downstream_mut(&mut self) -> &mut HttpSession {
705 &mut self.downstream_session
706 }
707
708 pub fn as_downstream(&self) -> &HttpSession {
709 &self.downstream_session
710 }
711
712 pub async fn respond_error(&mut self, error: u16) -> Result<()> {
714 self.as_downstream_mut().respond_error(error).await
715 }
716
717 pub async fn respond_error_with_body(&mut self, error: u16, body: Bytes) -> Result<()> {
719 self.as_downstream_mut()
720 .respond_error_with_body(error, body)
721 .await
722 }
723
724 pub async fn write_response_header(
729 &mut self,
730 mut resp: Box<ResponseHeader>,
731 end_of_stream: bool,
732 ) -> Result<()> {
733 self.downstream_modules_ctx
734 .response_header_filter(&mut resp, end_of_stream)
735 .await?;
736 self.downstream_session.write_response_header(resp).await
737 }
738
739 pub async fn write_response_header_ref(
741 &mut self,
742 resp: &ResponseHeader,
743 end_of_stream: bool,
744 ) -> Result<(), Box<Error>> {
745 self.write_response_header(Box::new(resp.clone()), end_of_stream)
746 .await
747 }
748
749 pub async fn write_response_body(
754 &mut self,
755 mut body: Option<Bytes>,
756 end_of_stream: bool,
757 ) -> Result<()> {
758 self.downstream_modules_ctx
759 .response_body_filter(&mut body, end_of_stream)?;
760
761 if body.is_none() && !end_of_stream {
762 return Ok(());
763 }
764
765 let data = body.unwrap_or_default();
766 self.downstream_session
767 .write_response_body(data, end_of_stream)
768 .await
769 }
770
771 async fn downstream_response_task_filter(
775 &mut self,
776 task: &mut HttpTask,
777 seen_upgraded: &mut bool,
778 ) -> Result<()> {
779 match task {
780 HttpTask::Header(resp, end) => {
781 if *seen_upgraded {
782 return reject_unexpected_task_after_h1_upgrade(self, "header", *seen_upgraded);
783 }
784 self.downstream_modules_ctx
785 .response_header_filter(resp, *end)
786 .await?;
787 reject_mismatched_h1_upgrade_101(self, resp, "downstream_module_header_filter")
788 .map_err(|e| e.into_in())?;
789 if resp.status == http::StatusCode::SWITCHING_PROTOCOLS
790 && self.downstream_session.is_upgrade(resp) == Some(true)
791 {
792 *seen_upgraded = true;
793 }
794 }
795 HttpTask::Body(data, end) => {
796 if *seen_upgraded {
797 return reject_unexpected_task_after_h1_upgrade(self, "body", *seen_upgraded);
798 }
799 self.downstream_modules_ctx
800 .response_body_filter(data, *end)?;
801 }
802 HttpTask::UpgradedBody(data, end) => {
803 if !*seen_upgraded {
804 return reject_unexpected_upgraded_body_before_h1_upgrade(self, *seen_upgraded);
805 }
806 self.downstream_modules_ctx
807 .response_body_filter(data, *end)?;
808 }
809 HttpTask::Trailer(trailers) => {
810 if *seen_upgraded {
811 return reject_unexpected_task_after_h1_upgrade(
812 self,
813 "trailer",
814 *seen_upgraded,
815 );
816 }
817 if let Some(buf) = self
818 .downstream_modules_ctx
819 .response_trailer_filter(trailers)?
820 {
821 *task = HttpTask::Body(Some(buf), true);
828 }
829 }
830 HttpTask::Done => {
831 if let Some(buf) = self.downstream_modules_ctx.response_done_filter()? {
841 *task = if *seen_upgraded {
842 HttpTask::UpgradedBody(Some(buf), true)
843 } else {
844 HttpTask::Body(Some(buf), true)
845 };
846 }
847 }
848 _ => { }
849 }
850 Ok(())
851 }
852
853 pub async fn send_downstream_proxy_task(&mut self, mut task: HttpTask) -> Result<()> {
863 let mut seen_upgraded = self.downstream_task_seen_upgraded || self.was_upgraded();
864 self.downstream_response_task_filter(&mut task, &mut seen_upgraded)
865 .await?;
866 self.downstream_task_seen_upgraded = seen_upgraded;
867 self.downstream_session.send_downstream_proxy_task(task);
868 Ok(())
869 }
870
871 pub fn set_proxy_tasks_enabled(&mut self, enabled: bool) {
877 self.downstream_session.set_proxy_tasks_enabled(enabled);
878 }
879
880 pub fn has_pending_downstream_tasks(&self) -> bool {
884 self.downstream_session.supports_proxy_task_api()
885 && self.downstream_session.has_pending_downstream_proxy_tasks()
886 }
887
888 pub async fn write_downstream_proxy_tasks(&mut self) -> Result<bool> {
892 if self.downstream_session.supports_proxy_task_api() {
893 self.downstream_session.write_downstream_proxy_tasks().await
894 } else {
895 Ok(false)
896 }
897 }
898
899 pub async fn write_response_tasks(&mut self, mut tasks: Vec<HttpTask>) -> Result<bool> {
900 let mut seen_upgraded = self.downstream_task_seen_upgraded || self.was_upgraded();
901 for task in tasks.iter_mut() {
902 self.downstream_response_task_filter(task, &mut seen_upgraded)
903 .await?;
904 }
905 self.downstream_task_seen_upgraded = seen_upgraded;
906 self.downstream_session.response_duplex_vec(tasks).await
907 }
908
909 pub fn mark_upstream_headers_mutated_for_cache(&mut self) {
912 self.upstream_headers_mutated_for_cache = true;
913 }
914
915 pub fn upstream_headers_mutated_for_cache(&self) -> bool {
917 self.upstream_headers_mutated_for_cache
918 }
919
920 fn set_upstream_h1_upgrade_request_status(&mut self, upstream_is_upgrade_req: bool) {
921 self.h1_upgrade_request_status = H1UpgradeRequestStatus {
922 upstream: Some(upstream_is_upgrade_req),
923 };
924 }
925
926 fn h1_upgrade_request_snapshot(&self) -> H1UpgradeRequestSnapshot {
927 H1UpgradeRequestSnapshot {
928 downstream: self.downstream_session.is_upgrade_req(),
929 upstream: self.h1_upgrade_request_status.upstream,
930 }
931 }
932
933 pub fn upstream_body_bytes_received(&self) -> usize {
935 self.upstream_body_bytes_received
936 }
937
938 pub(crate) fn set_upstream_body_bytes_received(&mut self, n: usize) {
940 self.upstream_body_bytes_received = n;
941 }
942
943 pub fn upstream_body_bytes_sent(&self) -> Option<usize> {
948 self.upstream_body_bytes_sent
949 }
950
951 pub(crate) fn set_upstream_body_bytes_sent(&mut self, n: usize) {
954 self.upstream_body_bytes_sent = Some(n);
955 }
956
957 pub fn upstream_write_pending_time(&self) -> Duration {
959 self.upstream_write_pending_time
960 }
961
962 pub(crate) fn set_upstream_write_pending_time(&mut self, d: Duration) {
964 self.upstream_write_pending_time = d;
965 }
966
967 pub fn is_process_shutting_down(&self) -> bool {
969 self.shutdown_flag.load(Ordering::Acquire)
970 }
971
972 pub fn downstream_custom_message(&mut self) -> Result<Option<DownstreamCustomMessageReader>> {
973 if let Some(custom_session) = self.downstream_session.as_custom_mut() {
974 custom_session
975 .take_custom_message_reader()
976 .map(Some)
977 .ok_or(Error::explain(
978 ReadError,
979 "can't extract custom reader from downstream",
980 ))
981 } else {
982 Ok(None)
983 }
984 }
985
986 fn take_downstream_custom_message_reader(
987 &mut self,
988 downstream_custom_message_writer: &mut Option<Box<dyn CustomMessageWrite>>,
989 ) -> Result<Option<DownstreamCustomMessageReader>> {
990 if downstream_custom_message_writer.is_none() {
991 return Ok(None);
992 }
993
994 let Some(custom_session) = self.downstream_session.as_custom_mut() else {
995 return Ok(None);
996 };
997
998 let Some(reader) = custom_session.take_custom_message_reader() else {
999 if let Some(writer) = downstream_custom_message_writer.take() {
1000 custom_session.restore_custom_message_writer(writer)?;
1001 }
1002 return Err(Error::explain(
1003 ReadError,
1004 "can't extract custom reader from downstream",
1005 ));
1006 };
1007
1008 Ok(Some(reader))
1009 }
1010}
1011
1012#[derive(Clone, Copy, Debug, Default)]
1013struct H1UpgradeRequestStatus {
1014 upstream: Option<bool>,
1015}
1016
1017#[derive(Clone, Copy, Debug)]
1018struct H1UpgradeRequestSnapshot {
1019 downstream: bool,
1020 upstream: Option<bool>,
1021}
1022
1023impl H1UpgradeRequestSnapshot {
1024 fn mismatch(self) -> bool {
1025 matches!(self.upstream, Some(upstream) if self.downstream != upstream)
1028 }
1029}
1030
1031fn reject_mismatched_h1_upgrade_101(
1038 session: &Session,
1039 header: &ResponseHeader,
1040 stage: &'static str,
1041) -> Result<()> {
1042 if header.status != http::StatusCode::SWITCHING_PROTOCOLS {
1043 return Ok(());
1044 }
1045
1046 let status = session.h1_upgrade_request_snapshot();
1047 if status.mismatch() {
1048 return Error::e_explain(
1049 InvalidHTTPHeader,
1050 format!(
1051 "received 101 response with mismatched upstream/downstream upgrade status: stage={stage}, downstream_upgrade_req={}, upstream_upgrade_req={:?}, downstream_was_upgraded={}, downstream_task_seen_upgraded={}, response_version={:?}, response_upgrade_header_present={}, response_connection_header_present={}",
1052 status.downstream,
1053 status.upstream,
1054 session.was_upgraded(),
1055 session.downstream_task_seen_upgraded,
1056 header.version,
1057 header.headers.get(http::header::UPGRADE).is_some(),
1058 header.headers.get(http::header::CONNECTION).is_some(),
1059 ),
1060 );
1061 }
1062 Ok(())
1063}
1064
1065fn reject_unexpected_task_after_h1_upgrade(
1066 session: &Session,
1067 task: &'static str,
1068 task_filter_seen_upgraded: bool,
1069) -> Result<()> {
1070 let status = session.h1_upgrade_request_snapshot();
1071 Error::e_explain(
1072 InvalidHTTPHeader,
1073 format!(
1074 "received {task} task after downstream 101 upgrade: downstream_upgrade_req={}, upstream_upgrade_req={:?}, downstream_was_upgraded={}, downstream_task_seen_upgraded={}, task_filter_seen_upgraded={}",
1075 status.downstream,
1076 status.upstream,
1077 session.was_upgraded(),
1078 session.downstream_task_seen_upgraded,
1079 task_filter_seen_upgraded
1080 ),
1081 )
1082 .map_err(|e| e.into_in())
1083}
1084
1085fn reject_unexpected_upgraded_body_before_h1_upgrade(
1086 session: &Session,
1087 task_filter_seen_upgraded: bool,
1088) -> Result<()> {
1089 let status = session.h1_upgrade_request_snapshot();
1090 Error::e_explain(
1091 InvalidHTTPHeader,
1092 format!(
1093 "received upgraded body task before downstream 101 upgrade: downstream_upgrade_req={}, upstream_upgrade_req={:?}, downstream_was_upgraded={}, downstream_task_seen_upgraded={}, task_filter_seen_upgraded={}",
1094 status.downstream,
1095 status.upstream,
1096 session.was_upgraded(),
1097 session.downstream_task_seen_upgraded,
1098 task_filter_seen_upgraded
1099 ),
1100 )
1101 .map_err(|e| e.into_in())
1102}
1103
1104impl AsRef<HttpSession> for Session {
1105 fn as_ref(&self) -> &HttpSession {
1106 &self.downstream_session
1107 }
1108}
1109
1110impl AsMut<HttpSession> for Session {
1111 fn as_mut(&mut self) -> &mut HttpSession {
1112 &mut self.downstream_session
1113 }
1114}
1115
1116use std::ops::{Deref, DerefMut};
1117
1118impl Deref for Session {
1119 type Target = HttpSession;
1120
1121 fn deref(&self) -> &Self::Target {
1122 &self.downstream_session
1123 }
1124}
1125
1126impl DerefMut for Session {
1127 fn deref_mut(&mut self) -> &mut Self::Target {
1128 &mut self.downstream_session
1129 }
1130}
1131
1132static BAD_GATEWAY: Lazy<ResponseHeader> = Lazy::new(|| {
1134 let mut resp = ResponseHeader::build(http::StatusCode::BAD_GATEWAY, Some(3)).unwrap();
1135 resp.insert_header(header::SERVER, &SERVER_NAME[..])
1136 .unwrap();
1137 resp.insert_header(header::CONTENT_LENGTH, 0).unwrap();
1138 resp.insert_header(header::CACHE_CONTROL, "private, no-store")
1139 .unwrap();
1140
1141 resp
1142});
1143
1144impl<SV, C> HttpProxy<SV, C>
1145where
1146 C: custom::Connector,
1147{
1148 async fn process_request(
1149 self: &Arc<Self>,
1150 mut session: Session,
1151 mut ctx: <SV as ProxyHttp>::CTX,
1152 ) -> Option<ReusedHttpStream>
1153 where
1154 SV: ProxyHttp + Send + Sync + 'static,
1155 <SV as ProxyHttp>::CTX: Send + Sync,
1156 {
1157 if let Err(e) = self
1158 .inner
1159 .early_request_filter(&mut session, &mut ctx)
1160 .await
1161 {
1162 return self
1163 .handle_error(session, &mut ctx, e, "Fail to early filter request:")
1164 .await;
1165 }
1166
1167 if self.inner.allow_spawning_subrequest(&session, &ctx) {
1168 session.subrequest_spawner = Some(SubrequestSpawner::new(self.clone()));
1169 }
1170
1171 let req = session.downstream_session.req_header_mut();
1172
1173 if let Err(e) = session
1175 .downstream_modules_ctx
1176 .request_header_filter(req)
1177 .await
1178 {
1179 return self
1180 .handle_error(
1181 session,
1182 &mut ctx,
1183 e,
1184 "Failed in downstream modules request filter:",
1185 )
1186 .await;
1187 }
1188
1189 match self.inner.request_filter(&mut session, &mut ctx).await {
1190 Ok(response_sent) => {
1191 if response_sent {
1192 self.inner.logging(&mut session, None, &mut ctx).await;
1194 self.cleanup_sub_req(&mut session);
1195 let mut persistent_settings = HttpPersistentSettings::for_session(&session);
1196 if let Some(uc) = self.inner.persist_connection_context(&session, &ctx) {
1197 persistent_settings.set_user_context(uc);
1198 }
1199 return session
1200 .downstream_session
1201 .finish()
1202 .await
1203 .ok()
1204 .flatten()
1205 .map(|s| ReusedHttpStream::from_reusable_stream(s, persistent_settings));
1206 }
1207 }
1209 Err(e) => {
1210 return self
1211 .handle_error(session, &mut ctx, e, "Fail to filter request:")
1212 .await;
1213 }
1214 }
1215
1216 if let Some((reuse, err)) = self.proxy_cache(&mut session, &mut ctx).await {
1217 return self.finish(session, &mut ctx, reuse, err).await;
1219 }
1220 self.cleanup_sub_req(&mut session);
1224
1225 match self
1227 .inner
1228 .proxy_upstream_filter(&mut session, &mut ctx)
1229 .await
1230 {
1231 Ok(proxy_to_upstream) => {
1232 if !proxy_to_upstream {
1233 if session.cache.enabled() {
1236 session.cache.disable(NoCacheReason::DeclinedToUpstream);
1238 }
1239 if session.response_written().is_none() {
1240 match session.write_response_header_ref(&BAD_GATEWAY, true).await {
1241 Ok(()) => {}
1242 Err(e) => {
1243 return self
1244 .handle_error(
1245 session,
1246 &mut ctx,
1247 e,
1248 "Error responding with Bad Gateway:",
1249 )
1250 .await;
1251 }
1252 }
1253 }
1254
1255 return self.finish(session, &mut ctx, true, None).await;
1256 }
1257 }
1259 Err(e) => {
1260 if session.cache.enabled() {
1261 session.cache.disable(NoCacheReason::InternalError);
1262 }
1263
1264 return self
1265 .handle_error(
1266 session,
1267 &mut ctx,
1268 e,
1269 "Error deciding if we should proxy to upstream:",
1270 )
1271 .await;
1272 }
1273 }
1274
1275 let mut retries: usize = 0;
1276
1277 let mut server_reuse = false;
1278 let mut proxy_error: Option<Box<Error>> = None;
1279
1280 while retries < self.max_retries {
1281 retries += 1;
1282
1283 let (reuse, e) = self.proxy_to_upstream(&mut session, &mut ctx).await;
1284 server_reuse = reuse;
1285
1286 match e {
1287 Some(error) => {
1288 let retry = error.retry();
1289 if retry
1291 && !self.inner.suppress_proxy_warn_log(
1292 &session,
1293 &ctx,
1294 &error,
1295 ProxyWarnLogContext::UpstreamRetry,
1296 )
1297 {
1298 warn!(
1299 "Fail to proxy: {}, tries: {}, retry: {}, {}",
1300 error,
1301 retries,
1302 retry,
1303 self.inner.request_summary(&session, &ctx)
1304 );
1305 }
1306 proxy_error = Some(error);
1307 if !retry {
1308 break;
1309 }
1310 }
1311 None => {
1312 proxy_error = None;
1313 break;
1314 }
1315 };
1316 }
1317
1318 #[allow(clippy::unnecessary_unwrap)]
1322 let serve_stale_result = if proxy_error.is_some() && session.cache.can_serve_stale_error() {
1323 self.handle_stale_if_error(&mut session, &mut ctx, proxy_error.as_ref().unwrap())
1324 .await
1325 } else {
1326 None
1327 };
1328
1329 let final_error = if let Some((reuse, stale_cache_error)) = serve_stale_result {
1330 server_reuse = server_reuse && reuse;
1332 stale_cache_error
1333 } else {
1334 proxy_error
1335 };
1336
1337 if let Some(e) = final_error.as_ref() {
1338 if session.cache.enabled() {
1340 let reason = if *e.esource() == ErrorSource::Upstream {
1341 NoCacheReason::UpstreamError
1342 } else {
1343 NoCacheReason::InternalError
1344 };
1345 session.cache.disable(reason);
1346 }
1347 let res = self.inner.fail_to_proxy(&mut session, e, &mut ctx).await;
1348
1349 if !self.inner.suppress_error_log(&session, &ctx, e) {
1351 error!(
1352 "Fail to proxy: {}, status: {}, tries: {}, retry: {}, {}",
1353 e,
1354 res.error_code,
1355 retries,
1356 false, self.inner.request_summary(&session, &ctx),
1358 );
1359 }
1360 }
1361
1362 self.finish(session, &mut ctx, server_reuse, final_error)
1364 .await
1365 }
1366
1367 async fn handle_error(
1368 &self,
1369 mut session: Session,
1370 ctx: &mut <SV as ProxyHttp>::CTX,
1371 e: Box<Error>,
1372 context: &str,
1373 ) -> Option<ReusedHttpStream>
1374 where
1375 SV: ProxyHttp + Send + Sync + 'static,
1376 <SV as ProxyHttp>::CTX: Send + Sync,
1377 {
1378 let res = self.inner.fail_to_proxy(&mut session, &e, ctx).await;
1379 if !self.inner.suppress_error_log(&session, ctx, &e) {
1380 error!(
1381 "{context} {}, status: {}, {}",
1382 e,
1383 res.error_code,
1384 self.inner.request_summary(&session, ctx)
1385 );
1386 }
1387 self.inner.logging(&mut session, Some(&e), ctx).await;
1388 self.cleanup_sub_req(&mut session);
1389
1390 session.downstream_session.on_proxy_failure(e);
1391
1392 if res.can_reuse_downstream {
1393 let mut persistent_settings = HttpPersistentSettings::for_session(&session);
1394 if let Some(uc) = self.inner.persist_connection_context(&session, ctx) {
1395 persistent_settings.set_user_context(uc);
1396 }
1397 session
1398 .downstream_session
1399 .finish()
1400 .await
1401 .ok()
1402 .flatten()
1403 .map(|s| ReusedHttpStream::from_reusable_stream(s, persistent_settings))
1404 } else {
1405 None
1406 }
1407 }
1408}
1409
1410#[async_trait]
1420pub trait Subrequest {
1421 async fn process_subrequest(
1422 self: Arc<Self>,
1423 session: Box<HttpSession>,
1424 sub_req_ctx: Box<SubrequestCtx>,
1425 );
1426}
1427
1428#[async_trait]
1429impl<SV, C> Subrequest for HttpProxy<SV, C>
1430where
1431 SV: ProxyHttp + Send + Sync + 'static,
1432 <SV as ProxyHttp>::CTX: Send + Sync,
1433 C: custom::Connector,
1434{
1435 async fn process_subrequest(
1436 self: Arc<Self>,
1437 session: Box<HttpSession>,
1438 sub_req_ctx: Box<SubrequestCtx>,
1439 ) {
1440 debug!("starting subrequest");
1441
1442 let mut session = match self.handle_new_request(session).await {
1443 Some(downstream_session) => Session::new(
1444 downstream_session,
1445 &self.downstream_modules,
1446 #[cfg(feature = "upstream_modules")]
1447 &self.upstream_modules,
1448 self.shutdown_flag.clone(),
1449 ),
1450 None => return, };
1452
1453 session.set_keepalive(None);
1456
1457 session.subrequest_ctx.replace(sub_req_ctx);
1458 trace!("processing subrequest");
1459 let ctx = self.inner.new_ctx();
1460 self.process_request(session, ctx).await;
1461 trace!("subrequest done");
1462 }
1463}
1464
1465pub struct SubrequestSpawner {
1467 app: Arc<dyn Subrequest + Send + Sync>,
1468}
1469
1470pub struct PreparedSubrequest {
1472 app: Arc<dyn Subrequest + Send + Sync>,
1473 session: Box<HttpSession>,
1474 sub_req_ctx: Box<SubrequestCtx>,
1475}
1476
1477impl PreparedSubrequest {
1478 pub async fn run(self) {
1479 self.app
1480 .process_subrequest(self.session, self.sub_req_ctx)
1481 .await
1482 }
1483
1484 pub fn session(&self) -> &HttpSession {
1485 self.session.as_ref()
1486 }
1487
1488 pub fn session_mut(&mut self) -> &mut HttpSession {
1489 self.session.deref_mut()
1490 }
1491}
1492
1493impl SubrequestSpawner {
1494 pub fn new(app: Arc<dyn Subrequest + Send + Sync>) -> SubrequestSpawner {
1496 SubrequestSpawner { app }
1497 }
1498
1499 pub fn spawn_background_subrequest(
1502 &self,
1503 session: &HttpSession,
1504 ctx: SubrequestCtx,
1505 ) -> tokio::task::JoinHandle<()> {
1506 let new_app = self.app.clone(); let (mut session, handle) = subrequest::create_session(session);
1508 if ctx.body_mode() == BodyMode::NoBody {
1509 session
1510 .as_subrequest_mut()
1511 .expect("created subrequest session")
1512 .clear_request_body_headers();
1513 }
1514 let sub_req_ctx = Box::new(ctx);
1515 handle.drain_tasks();
1516 tokio::spawn(async move {
1517 new_app
1518 .process_subrequest(Box::new(session), sub_req_ctx)
1519 .await;
1520 })
1521 }
1522
1523 pub fn create_subrequest(
1529 &self,
1530 session: &HttpSession,
1531 ctx: SubrequestCtx,
1532 ) -> (PreparedSubrequest, SubrequestHandle) {
1533 let new_app = self.app.clone(); let (mut session, handle) = subrequest::create_session(session);
1535 if ctx.body_mode() == BodyMode::NoBody {
1536 session
1537 .as_subrequest_mut()
1538 .expect("created subrequest session")
1539 .clear_request_body_headers();
1540 }
1541 let sub_req_ctx = Box::new(ctx);
1542 (
1543 PreparedSubrequest {
1544 app: new_app,
1545 session: Box::new(session),
1546 sub_req_ctx,
1547 },
1548 handle,
1549 )
1550 }
1551}
1552
1553#[async_trait]
1554impl<SV, C> HttpServerApp for HttpProxy<SV, C>
1555where
1556 SV: ProxyHttp + Send + Sync + 'static,
1557 <SV as ProxyHttp>::CTX: Send + Sync,
1558 C: custom::Connector,
1559{
1560 async fn process_new_http(
1561 self: &Arc<Self>,
1562 mut session: HttpSession,
1563 shutdown: &ShutdownWatch,
1564 ) -> Option<ReusedHttpStream> {
1565 let prev_user_ctx = session.take_connection_user_context();
1567
1568 let session = Box::new(session);
1569
1570 let mut session = match self.handle_new_request(session).await {
1572 Some(downstream_session) => Session::new(
1573 downstream_session,
1574 &self.downstream_modules,
1575 #[cfg(feature = "upstream_modules")]
1576 &self.upstream_modules,
1577 self.shutdown_flag.clone(),
1578 ),
1579 None => return None, };
1581
1582 if *shutdown.borrow() {
1583 session.set_keepalive(None);
1585 }
1586
1587 let mut ctx = self.inner.new_ctx();
1588
1589 if let Some(prev_ctx) = prev_user_ctx {
1591 self.inner
1592 .on_connection_reuse(&mut session, &mut ctx, prev_ctx);
1593 }
1594
1595 self.process_request(session, ctx).await
1596 }
1597
1598 async fn http_cleanup(&self) {
1599 self.shutdown_flag.store(true, Ordering::Release);
1600 self.shutdown.notify_waiters();
1602 }
1603
1604 fn server_options(&self) -> Option<&HttpServerOptions> {
1605 self.server_options.as_ref()
1606 }
1607
1608 fn h2_options(&self) -> Option<H2Options> {
1609 self.h2_options.clone()
1610 }
1611 async fn process_custom_session(
1612 self: Arc<Self>,
1613 stream: Stream,
1614 shutdown: &ShutdownWatch,
1615 ) -> Option<Stream> {
1616 let app = self.clone();
1617
1618 let Some(process_custom_session) = app.process_custom_session.as_ref() else {
1619 warn!("custom was called on an empty on_custom");
1620 return None;
1621 };
1622
1623 process_custom_session(self.clone(), stream, shutdown).await
1624 }
1625
1626 }
1628
1629use pingora_core::services::listening::{RuntimeOptsOverride, Service};
1630
1631pub fn http_proxy<SV>(conf: &Arc<ServerConf>, inner: SV) -> HttpProxy<SV>
1662where
1663 SV: ProxyHttp,
1664{
1665 let mut proxy = HttpProxy::new(inner, conf.clone());
1666 proxy.handle_init_modules();
1667 proxy
1668}
1669
1670pub fn http_proxy_service<SV>(conf: &Arc<ServerConf>, inner: SV) -> Service<HttpProxy<SV, ()>>
1674where
1675 SV: ProxyHttp,
1676{
1677 http_proxy_service_with_name(conf, inner, "Pingora HTTP Proxy Service")
1678}
1679
1680pub fn http_proxy_service_with_name<SV>(
1684 conf: &Arc<ServerConf>,
1685 inner: SV,
1686 name: &str,
1687) -> Service<HttpProxy<SV, ()>>
1688where
1689 SV: ProxyHttp,
1690{
1691 let mut proxy = HttpProxy::new(inner, conf.clone());
1692 proxy.handle_init_modules();
1693 Service::new(name.to_string(), proxy)
1694}
1695
1696pub fn http_proxy_service_with_name_custom<SV, C>(
1700 conf: &Arc<ServerConf>,
1701 inner: SV,
1702 name: &str,
1703 connector: C,
1704 on_custom: ProcessCustomSession<SV, C>,
1705) -> Service<HttpProxy<SV, C>>
1706where
1707 SV: ProxyHttp + Send + Sync + 'static,
1708 SV::CTX: Send + Sync + 'static,
1709 C: custom::Connector,
1710{
1711 let mut proxy =
1712 HttpProxy::new_custom(inner, conf.clone(), connector, Some(on_custom), None, None);
1713 proxy.handle_init_modules();
1714
1715 Service::new(name.to_string(), proxy)
1716}
1717
1718pub struct ProxyServiceBuilder<SV, C>
1724where
1725 SV: ProxyHttp + Send + Sync + 'static,
1726 SV::CTX: Send + Sync + 'static,
1727 C: custom::Connector,
1728{
1729 conf: Arc<ServerConf>,
1730 inner: SV,
1731 name: String,
1732 connector: C,
1733 custom: Option<ProcessCustomSession<SV, C>>,
1734 server_options: Option<HttpServerOptions>,
1735 client_options: Option<ConnectorOptions>,
1736 runtime_opts_override: Option<RuntimeOptsOverride>,
1737}
1738
1739impl<SV> ProxyServiceBuilder<SV, ()>
1740where
1741 SV: ProxyHttp + Send + Sync + 'static,
1742 SV::CTX: Send + Sync + 'static,
1743{
1744 pub fn new(conf: &Arc<ServerConf>, inner: SV) -> Self {
1754 ProxyServiceBuilder {
1755 conf: conf.clone(),
1756 inner,
1757 name: "Pingora HTTP Proxy Service".into(),
1758 connector: (),
1759 custom: None,
1760 server_options: None,
1761 client_options: None,
1762 runtime_opts_override: None,
1763 }
1764 }
1765}
1766
1767impl<SV, C> ProxyServiceBuilder<SV, C>
1768where
1769 SV: ProxyHttp + Send + Sync + 'static,
1770 SV::CTX: Send + Sync + 'static,
1771 C: custom::Connector,
1772{
1773 pub fn name(mut self, name: impl AsRef<str>) -> Self {
1775 self.name = name.as_ref().to_owned();
1776 self
1777 }
1778
1779 pub fn custom<C2: custom::Connector>(
1788 self,
1789 connector: C2,
1790 on_custom: ProcessCustomSession<SV, C2>,
1791 ) -> ProxyServiceBuilder<SV, C2> {
1792 let Self {
1793 conf,
1794 inner,
1795 name,
1796 server_options,
1797 client_options,
1798 runtime_opts_override,
1799 ..
1800 } = self;
1801 ProxyServiceBuilder {
1802 conf,
1803 inner,
1804 name,
1805 connector,
1806 custom: Some(on_custom),
1807 server_options,
1808 client_options,
1809 runtime_opts_override,
1810 }
1811 }
1812
1813 pub fn client_options(mut self, options: ConnectorOptions) -> Self {
1817 self.client_options = Some(options);
1818 self
1819 }
1820
1821 pub fn server_options(mut self, options: HttpServerOptions) -> Self {
1825 self.server_options = Some(options);
1826 self
1827 }
1828
1829 pub fn runtime_opts_override<F>(mut self, override_fn: F) -> Self
1833 where
1834 F: Fn(&RuntimeOpts) -> Option<RuntimeOpts> + Send + Sync + 'static,
1835 {
1836 self.runtime_opts_override = Some(Arc::new(override_fn));
1837 self
1838 }
1839
1840 pub fn build(self) -> Service<HttpProxy<SV, C>> {
1847 let Self {
1848 conf,
1849 inner,
1850 name,
1851 connector,
1852 custom,
1853 server_options,
1854 client_options,
1855 runtime_opts_override,
1856 } = self;
1857
1858 let mut proxy = HttpProxy::new_custom(
1859 inner,
1860 conf,
1861 connector,
1862 custom,
1863 server_options,
1864 client_options,
1865 );
1866
1867 proxy.handle_init_modules();
1868 let mut service = Service::new(name, proxy);
1869 if let Some(runtime_opts_override) = runtime_opts_override {
1870 service.set_runtime_opts_override(runtime_opts_override);
1871 }
1872 service
1873 }
1874}
1875
1876#[cfg(test)]
1877mod tests {
1878 use super::*;
1879 use pingora_core::modules::http::{HttpModule, HttpModuleBuilder};
1880 use pingora_core::protocols::l4::stream::Stream as L4Stream;
1881 use pingora_core::protocols::l4::virt::{VirtualSockOpt, VirtualSocket, VirtualSocketStream};
1882 use pingora_error::RetryType;
1883 use std::pin::Pin;
1884 use std::sync::atomic::{AtomicBool, Ordering};
1885 use std::sync::Mutex;
1886 use std::task::{Context, Poll};
1887 use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
1888
1889 #[derive(Debug)]
1890 struct StaticVirtualSocket {
1891 read_buf: Vec<u8>,
1892 read_pos: usize,
1893 write_buf: Arc<Mutex<Vec<u8>>>,
1894 }
1895
1896 impl StaticVirtualSocket {
1897 fn new(read_buf: &[u8], write_buf: Arc<Mutex<Vec<u8>>>) -> Self {
1898 Self {
1899 read_buf: read_buf.to_vec(),
1900 read_pos: 0,
1901 write_buf,
1902 }
1903 }
1904 }
1905
1906 impl AsyncRead for StaticVirtualSocket {
1907 fn poll_read(
1908 mut self: Pin<&mut Self>,
1909 _cx: &mut Context<'_>,
1910 buf: &mut ReadBuf<'_>,
1911 ) -> Poll<std::io::Result<()>> {
1912 let remaining = self.read_buf.len() - self.read_pos;
1913 let to_read = remaining.min(buf.remaining());
1914 if to_read > 0 {
1915 buf.put_slice(&self.read_buf[self.read_pos..self.read_pos + to_read]);
1916 self.read_pos += to_read;
1917 }
1918 Poll::Ready(Ok(()))
1919 }
1920 }
1921
1922 impl AsyncWrite for StaticVirtualSocket {
1923 fn poll_write(
1924 self: Pin<&mut Self>,
1925 _cx: &mut Context<'_>,
1926 buf: &[u8],
1927 ) -> Poll<std::io::Result<usize>> {
1928 self.write_buf.lock().unwrap().extend_from_slice(buf);
1929 Poll::Ready(Ok(buf.len()))
1930 }
1931
1932 fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
1933 Poll::Ready(Ok(()))
1934 }
1935
1936 fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
1937 Poll::Ready(Ok(()))
1938 }
1939 }
1940
1941 impl VirtualSocket for StaticVirtualSocket {
1942 fn set_socket_option(&self, _opt: VirtualSockOpt) -> std::io::Result<()> {
1943 Ok(())
1944 }
1945 }
1946
1947 async fn new_request_session(request: &[u8], written: Arc<Mutex<Vec<u8>>>) -> Session {
1948 let socket = StaticVirtualSocket::new(request, written);
1949 let stream = L4Stream::from(VirtualSocketStream::new(Box::new(socket)));
1950 let mut session = Session::new_h1(Box::new(stream));
1951 session.read_request().await.unwrap();
1952 session
1953 }
1954
1955 async fn new_upgrade_request_session(written: Arc<Mutex<Vec<u8>>>) -> Session {
1956 new_request_session(
1957 b"GET / HTTP/1.1\r\nHost: example.com\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n\r\n",
1958 written,
1959 )
1960 .await
1961 }
1962
1963 struct DefaultRetryProxy;
1964
1965 #[async_trait]
1966 impl ProxyHttp for DefaultRetryProxy {
1967 type CTX = ();
1968
1969 fn new_ctx(&self) -> Self::CTX {}
1970
1971 async fn upstream_peer(
1972 &self,
1973 _session: &mut Session,
1974 _ctx: &mut Self::CTX,
1975 ) -> Result<Box<HttpPeer>> {
1976 unreachable!()
1977 }
1978 }
1979
1980 fn default_policy_would_retry_for_session(
1981 session: &mut Session,
1982 retry: RetryType,
1983 client_reused: bool,
1984 ) -> bool {
1985 let mut error = Error::new_up(ReadError);
1986 error.retry = retry;
1987
1988 DefaultRetryProxy
1989 .error_while_proxy(
1990 &HttpPeer::new("127.0.0.1:80", false, "".to_string()),
1991 session,
1992 error,
1993 &mut (),
1994 client_reused,
1995 )
1996 .retry()
1997 }
1998
1999 async fn default_policy_would_retry(
2000 request: &[u8],
2001 retry: RetryType,
2002 client_reused: bool,
2003 ) -> bool {
2004 let mut session = new_request_session(request, Arc::new(Mutex::new(Vec::new()))).await;
2005 default_policy_would_retry_for_session(&mut session, retry, client_reused)
2006 }
2007
2008 async fn buffered_put_session(body_len: usize) -> Session {
2009 let mut request =
2010 format!("PUT / HTTP/1.1\r\nHost: example.com\r\nContent-Length: {body_len}\r\n\r\n")
2011 .into_bytes();
2012 request.resize(request.len() + body_len, b'a');
2013
2014 let mut session = new_request_session(&request, Arc::new(Mutex::new(Vec::new()))).await;
2015 session.enable_retry_buffering();
2016 while session.read_request_body().await.unwrap().is_some() {}
2017 session
2018 }
2019
2020 #[tokio::test]
2021 async fn default_retry_policy_requires_an_idempotent_method() {
2022 let decided_retry = RetryType::Decided(true);
2023 assert!(
2024 default_policy_would_retry(
2025 b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n",
2026 decided_retry,
2027 false,
2028 )
2029 .await
2030 );
2031 assert!(
2032 default_policy_would_retry(
2033 b"PUT / HTTP/1.1\r\nHost: example.com\r\nContent-Length: 0\r\n\r\n",
2034 decided_retry,
2035 false,
2036 )
2037 .await
2038 );
2039 assert!(
2040 !default_policy_would_retry(
2041 b"POST / HTTP/1.1\r\nHost: example.com\r\nContent-Length: 0\r\n\r\n",
2042 decided_retry,
2043 false,
2044 )
2045 .await
2046 );
2047 assert!(
2048 !default_policy_would_retry(
2049 b"PATCH / HTTP/1.1\r\nHost: example.com\r\nContent-Length: 0\r\n\r\n",
2050 decided_retry,
2051 false,
2052 )
2053 .await
2054 );
2055 }
2056
2057 #[tokio::test]
2058 async fn default_retry_policy_resolves_reused_only() {
2059 let request = b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n";
2060
2061 assert!(default_policy_would_retry(request, RetryType::ReusedOnly, true).await);
2062 assert!(!default_policy_would_retry(request, RetryType::ReusedOnly, false).await);
2063 }
2064
2065 #[tokio::test]
2066 async fn default_retry_policy_requires_an_untruncated_body_buffer() {
2067 let mut complete = buffered_put_session(64 * 1024).await;
2068 assert!(!complete.retry_buffer_truncated());
2069 assert!(default_policy_would_retry_for_session(
2070 &mut complete,
2071 RetryType::Decided(true),
2072 false,
2073 ));
2074
2075 let mut truncated = buffered_put_session(64 * 1024 + 1).await;
2076 assert!(truncated.retry_buffer_truncated());
2077 assert!(!default_policy_would_retry_for_session(
2078 &mut truncated,
2079 RetryType::Decided(true),
2080 false,
2081 ));
2082 assert!(!default_policy_would_retry_for_session(
2083 &mut truncated,
2084 RetryType::ReusedOnly,
2085 true,
2086 ));
2087 }
2088
2089 fn upgrade_response_header() -> ResponseHeader {
2090 let mut header =
2091 ResponseHeader::build(http::StatusCode::SWITCHING_PROTOCOLS, Some(2)).unwrap();
2092 header
2093 .insert_header(http::header::UPGRADE, "websocket")
2094 .unwrap();
2095 header
2096 .insert_header(http::header::CONNECTION, "Upgrade")
2097 .unwrap();
2098 header
2099 }
2100
2101 struct SwitchTo101Module;
2102
2103 #[async_trait]
2104 impl HttpModule for SwitchTo101Module {
2105 async fn response_header_filter(
2106 &mut self,
2107 resp: &mut ResponseHeader,
2108 _end_of_stream: bool,
2109 ) -> Result<()> {
2110 resp.set_status(http::StatusCode::SWITCHING_PROTOCOLS)?;
2111 resp.set_version(Version::HTTP_11);
2112 Ok(())
2113 }
2114
2115 fn as_any(&self) -> &dyn std::any::Any {
2116 self
2117 }
2118
2119 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
2120 self
2121 }
2122 }
2123
2124 struct SwitchTo101ModuleBuilder;
2125
2126 impl HttpModuleBuilder for SwitchTo101ModuleBuilder {
2127 fn init(&self) -> pingora_core::modules::http::Module {
2128 Box::new(SwitchTo101Module)
2129 }
2130 }
2131
2132 struct DoneBytesModule {
2133 called: Arc<AtomicBool>,
2134 }
2135
2136 #[async_trait]
2137 impl HttpModule for DoneBytesModule {
2138 fn response_done_filter(&mut self) -> Result<Option<Bytes>> {
2139 self.called.store(true, Ordering::Release);
2140 Ok(Some(Bytes::from_static(b"hello")))
2141 }
2142
2143 fn as_any(&self) -> &dyn std::any::Any {
2144 self
2145 }
2146
2147 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
2148 self
2149 }
2150 }
2151
2152 struct DoneBytesModuleBuilder {
2153 called: Arc<AtomicBool>,
2154 }
2155
2156 impl HttpModuleBuilder for DoneBytesModuleBuilder {
2157 fn init(&self) -> pingora_core::modules::http::Module {
2158 Box::new(DoneBytesModule {
2159 called: self.called.clone(),
2160 })
2161 }
2162 }
2163
2164 struct DoneEmptyModule {
2165 called: Arc<AtomicBool>,
2166 }
2167
2168 impl HttpModule for DoneEmptyModule {
2169 fn response_done_filter(&mut self) -> Result<Option<Bytes>> {
2170 self.called.store(true, Ordering::Release);
2171 Ok(None)
2172 }
2173
2174 fn as_any(&self) -> &dyn std::any::Any {
2175 self
2176 }
2177
2178 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
2179 self
2180 }
2181 }
2182
2183 struct DoneEmptyModuleBuilder {
2184 called: Arc<AtomicBool>,
2185 }
2186
2187 impl HttpModuleBuilder for DoneEmptyModuleBuilder {
2188 fn init(&self) -> pingora_core::modules::http::Module {
2189 Box::new(DoneEmptyModule {
2190 called: self.called.clone(),
2191 })
2192 }
2193 }
2194
2195 fn assert_raw_upgrade_payload(written: &[u8]) {
2196 assert!(
2197 written.starts_with(b"HTTP/1.1 101 Switching Protocols\r\n"),
2198 "unexpected response: {:?}",
2199 String::from_utf8_lossy(written)
2200 );
2201 assert!(
2202 written.ends_with(b"\r\n\r\nhello"),
2203 "upgrade payload should be written as raw tunneled bytes: {:?}",
2204 String::from_utf8_lossy(written)
2205 );
2206 assert!(
2207 !written
2208 .windows(b"\r\n5\r\nhello".len())
2209 .any(|w| w == b"\r\n5\r\nhello"),
2210 "upgrade payload must not be chunk framed: {:?}",
2211 String::from_utf8_lossy(written)
2212 );
2213 }
2214
2215 #[tokio::test]
2216 async fn write_response_tasks_rejects_body_after_101() {
2217 let written = Arc::new(Mutex::new(Vec::new()));
2218 let mut session = new_upgrade_request_session(written.clone()).await;
2219
2220 let err = session
2221 .write_response_tasks(vec![
2222 HttpTask::Header(Box::new(upgrade_response_header()), false),
2223 HttpTask::Body(Some(Bytes::from_static(b"hello")), true),
2224 ])
2225 .await
2226 .unwrap_err();
2227
2228 assert_eq!(err.etype(), &InvalidHTTPHeader);
2229 assert_eq!(err.esource(), &ErrorSource::Internal);
2230 assert!(written.lock().unwrap().is_empty());
2231 }
2232
2233 #[tokio::test]
2234 async fn write_response_tasks_allows_upgraded_body_after_101() {
2235 let written = Arc::new(Mutex::new(Vec::new()));
2236 let mut session = new_upgrade_request_session(written.clone()).await;
2237
2238 let response_done = session
2239 .write_response_tasks(vec![
2240 HttpTask::Header(Box::new(upgrade_response_header()), false),
2241 HttpTask::UpgradedBody(Some(Bytes::from_static(b"hello")), true),
2242 ])
2243 .await
2244 .unwrap();
2245
2246 assert!(response_done);
2247 let written = written.lock().unwrap().clone();
2248 assert_raw_upgrade_payload(&written);
2249 }
2250
2251 #[tokio::test]
2252 async fn write_response_tasks_rejects_upgraded_body_before_101() {
2253 let written = Arc::new(Mutex::new(Vec::new()));
2254 let mut session = new_upgrade_request_session(written.clone()).await;
2255 session.set_upstream_h1_upgrade_request_status(true);
2256
2257 let err = session
2258 .write_response_tasks(vec![HttpTask::UpgradedBody(
2259 Some(Bytes::from_static(b"hello")),
2260 true,
2261 )])
2262 .await
2263 .unwrap_err();
2264
2265 assert_eq!(err.etype(), &InvalidHTTPHeader);
2266 assert_eq!(err.esource(), &ErrorSource::Internal);
2267 assert!(written.lock().unwrap().is_empty());
2268 }
2269
2270 #[tokio::test]
2271 async fn write_response_tasks_rejects_trailer_after_101() {
2272 let written = Arc::new(Mutex::new(Vec::new()));
2273 let mut session = new_upgrade_request_session(written.clone()).await;
2274
2275 let err = session
2276 .write_response_tasks(vec![
2277 HttpTask::Header(Box::new(upgrade_response_header()), false),
2278 HttpTask::Trailer(Some(Box::new(http::HeaderMap::new()))),
2279 ])
2280 .await
2281 .unwrap_err();
2282
2283 assert_eq!(err.etype(), &InvalidHTTPHeader);
2284 assert_eq!(err.esource(), &ErrorSource::Internal);
2285 assert!(written.lock().unwrap().is_empty());
2286 }
2287
2288 #[tokio::test]
2289 async fn write_response_tasks_runs_done_filter_after_101_as_upgraded_body() {
2290 let written = Arc::new(Mutex::new(Vec::new()));
2291 let called = Arc::new(AtomicBool::new(false));
2292 let socket = StaticVirtualSocket::new(
2293 b"GET / HTTP/1.1\r\nHost: example.com\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n\r\n",
2294 written.clone(),
2295 );
2296 let stream = L4Stream::from(VirtualSocketStream::new(Box::new(socket)));
2297 let mut modules = HttpModules::new();
2298 modules.add_module(Box::new(DoneBytesModuleBuilder {
2299 called: called.clone(),
2300 }));
2301
2302 let mut session = Session::new_h1_with_modules(Box::new(stream), &modules);
2303 session.read_request().await.unwrap();
2304
2305 let response_done = session
2306 .write_response_tasks(vec![
2307 HttpTask::Header(Box::new(upgrade_response_header()), false),
2308 HttpTask::Done,
2309 ])
2310 .await
2311 .unwrap();
2312
2313 assert!(response_done);
2314 assert!(called.load(Ordering::Acquire));
2315 let written = written.lock().unwrap().clone();
2316 assert_raw_upgrade_payload(&written);
2317 }
2318
2319 #[tokio::test]
2320 async fn write_response_tasks_allows_empty_done_after_101() {
2321 let written = Arc::new(Mutex::new(Vec::new()));
2322 let called = Arc::new(AtomicBool::new(false));
2323 let socket = StaticVirtualSocket::new(
2324 b"GET / HTTP/1.1\r\nHost: example.com\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n\r\n",
2325 written.clone(),
2326 );
2327 let stream = L4Stream::from(VirtualSocketStream::new(Box::new(socket)));
2328 let mut modules = HttpModules::new();
2329 modules.add_module(Box::new(DoneEmptyModuleBuilder {
2330 called: called.clone(),
2331 }));
2332
2333 let mut session = Session::new_h1_with_modules(Box::new(stream), &modules);
2334 session.read_request().await.unwrap();
2335
2336 let response_done = session
2337 .write_response_tasks(vec![
2338 HttpTask::Header(Box::new(upgrade_response_header()), false),
2339 HttpTask::Done,
2340 ])
2341 .await
2342 .unwrap();
2343
2344 assert!(response_done);
2345 assert!(called.load(Ordering::Acquire));
2346 let written = written.lock().unwrap().clone();
2347 assert!(
2348 written.starts_with(b"HTTP/1.1 101 Switching Protocols\r\n"),
2349 "unexpected response: {:?}",
2350 String::from_utf8_lossy(&written)
2351 );
2352 assert!(
2353 written.ends_with(b"\r\n\r\n"),
2354 "empty Done filter should only finish the upgraded response: {:?}",
2355 String::from_utf8_lossy(&written)
2356 );
2357 }
2358
2359 #[tokio::test]
2360 async fn write_response_tasks_rejects_module_created_101_with_upgrade_mismatch() {
2361 let written = Arc::new(Mutex::new(Vec::new()));
2362 let socket = StaticVirtualSocket::new(
2363 b"GET / HTTP/1.1\r\nHost: example.com\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n\r\n",
2364 written.clone(),
2365 );
2366 let stream = L4Stream::from(VirtualSocketStream::new(Box::new(socket)));
2367 let mut modules = HttpModules::new();
2368 modules.add_module(Box::new(SwitchTo101ModuleBuilder));
2369
2370 let mut session = Session::new_h1_with_modules(Box::new(stream), &modules);
2371 session.read_request().await.unwrap();
2372 session.h1_upgrade_request_status = H1UpgradeRequestStatus {
2373 upstream: Some(false),
2374 };
2375
2376 let err = session
2377 .write_response_tasks(vec![
2378 HttpTask::Header(
2379 Box::new(ResponseHeader::build(200, Some(0)).unwrap()),
2380 false,
2381 ),
2382 HttpTask::Body(Some(Bytes::from_static(b"hello")), true),
2383 ])
2384 .await
2385 .unwrap_err();
2386
2387 assert_eq!(err.etype(), &InvalidHTTPHeader);
2388 assert_eq!(err.esource(), &ErrorSource::Internal);
2389 assert!(written.lock().unwrap().is_empty());
2390 }
2391
2392 #[tokio::test]
2393 async fn write_response_tasks_rejects_module_created_101_before_body() {
2394 let written = Arc::new(Mutex::new(Vec::new()));
2395 let socket = StaticVirtualSocket::new(
2396 b"GET / HTTP/1.1\r\nHost: example.com\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n\r\n",
2397 written.clone(),
2398 );
2399 let stream = L4Stream::from(VirtualSocketStream::new(Box::new(socket)));
2400 let mut modules = HttpModules::new();
2401 modules.add_module(Box::new(SwitchTo101ModuleBuilder));
2402
2403 let mut session = Session::new_h1_with_modules(Box::new(stream), &modules);
2404 session.read_request().await.unwrap();
2405
2406 let err = session
2407 .write_response_tasks(vec![
2408 HttpTask::Header(
2409 Box::new(ResponseHeader::build(200, Some(0)).unwrap()),
2410 false,
2411 ),
2412 HttpTask::Body(Some(Bytes::from_static(b"hello")), true),
2413 ])
2414 .await
2415 .unwrap_err();
2416
2417 assert_eq!(err.etype(), &InvalidHTTPHeader);
2418 assert_eq!(err.esource(), &ErrorSource::Internal);
2419 assert!(written.lock().unwrap().is_empty());
2420 }
2421
2422 #[tokio::test]
2423 async fn send_downstream_proxy_task_rejects_body_after_101() {
2424 let written = Arc::new(Mutex::new(Vec::new()));
2425 let mut session = new_upgrade_request_session(written.clone()).await;
2426 session.set_proxy_tasks_enabled(true);
2427
2428 session
2429 .send_downstream_proxy_task(HttpTask::Header(
2430 Box::new(upgrade_response_header()),
2431 false,
2432 ))
2433 .await
2434 .unwrap();
2435 let err = session
2436 .send_downstream_proxy_task(HttpTask::Body(Some(Bytes::from_static(b"hello")), true))
2437 .await
2438 .unwrap_err();
2439
2440 assert_eq!(err.etype(), &InvalidHTTPHeader);
2441 assert_eq!(err.esource(), &ErrorSource::Internal);
2442 assert!(written.lock().unwrap().is_empty());
2443 }
2444
2445 #[tokio::test]
2446 async fn send_downstream_proxy_task_allows_upgraded_body_after_101() {
2447 let written = Arc::new(Mutex::new(Vec::new()));
2448 let mut session = new_upgrade_request_session(written.clone()).await;
2449 session.set_proxy_tasks_enabled(true);
2450
2451 session
2452 .send_downstream_proxy_task(HttpTask::Header(
2453 Box::new(upgrade_response_header()),
2454 false,
2455 ))
2456 .await
2457 .unwrap();
2458 session
2459 .send_downstream_proxy_task(HttpTask::UpgradedBody(
2460 Some(Bytes::from_static(b"hello")),
2461 true,
2462 ))
2463 .await
2464 .unwrap();
2465
2466 let response_done = session.write_downstream_proxy_tasks().await.unwrap();
2467
2468 assert!(response_done);
2469 let written = written.lock().unwrap().clone();
2470 assert_raw_upgrade_payload(&written);
2471 }
2472
2473 #[tokio::test]
2474 async fn send_downstream_proxy_task_rejects_upgraded_body_before_101() {
2475 let written = Arc::new(Mutex::new(Vec::new()));
2476 let mut session = new_upgrade_request_session(written.clone()).await;
2477 session.set_upstream_h1_upgrade_request_status(true);
2478 session.set_proxy_tasks_enabled(true);
2479
2480 let err = session
2481 .send_downstream_proxy_task(HttpTask::UpgradedBody(
2482 Some(Bytes::from_static(b"hello")),
2483 true,
2484 ))
2485 .await
2486 .unwrap_err();
2487
2488 assert_eq!(err.etype(), &InvalidHTTPHeader);
2489 assert_eq!(err.esource(), &ErrorSource::Internal);
2490 assert!(!session.has_pending_downstream_tasks());
2491 assert!(written.lock().unwrap().is_empty());
2492 }
2493
2494 #[tokio::test]
2495 async fn send_downstream_proxy_task_runs_done_filter_after_101_as_upgraded_body() {
2496 let written = Arc::new(Mutex::new(Vec::new()));
2497 let called = Arc::new(AtomicBool::new(false));
2498 let socket = StaticVirtualSocket::new(
2499 b"GET / HTTP/1.1\r\nHost: example.com\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n\r\n",
2500 written.clone(),
2501 );
2502 let stream = L4Stream::from(VirtualSocketStream::new(Box::new(socket)));
2503 let mut modules = HttpModules::new();
2504 modules.add_module(Box::new(DoneBytesModuleBuilder {
2505 called: called.clone(),
2506 }));
2507
2508 let mut session = Session::new_h1_with_modules(Box::new(stream), &modules);
2509 session.read_request().await.unwrap();
2510 session.set_proxy_tasks_enabled(true);
2511
2512 session
2513 .send_downstream_proxy_task(HttpTask::Header(
2514 Box::new(upgrade_response_header()),
2515 false,
2516 ))
2517 .await
2518 .unwrap();
2519 session
2520 .send_downstream_proxy_task(HttpTask::Done)
2521 .await
2522 .unwrap();
2523
2524 let response_done = session.write_downstream_proxy_tasks().await.unwrap();
2525
2526 assert!(response_done);
2527 assert!(called.load(Ordering::Acquire));
2528 let written = written.lock().unwrap().clone();
2529 assert_raw_upgrade_payload(&written);
2530 }
2531
2532 #[derive(Debug)]
2535 struct PendingVirtualSocket;
2536
2537 impl AsyncRead for PendingVirtualSocket {
2538 fn poll_read(
2539 self: Pin<&mut Self>,
2540 _cx: &mut Context<'_>,
2541 _buf: &mut ReadBuf<'_>,
2542 ) -> Poll<std::io::Result<()>> {
2543 Poll::Pending
2544 }
2545 }
2546
2547 impl AsyncWrite for PendingVirtualSocket {
2548 fn poll_write(
2549 self: Pin<&mut Self>,
2550 _cx: &mut Context<'_>,
2551 buf: &[u8],
2552 ) -> Poll<std::io::Result<usize>> {
2553 Poll::Ready(Ok(buf.len()))
2554 }
2555
2556 fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
2557 Poll::Ready(Ok(()))
2558 }
2559
2560 fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
2561 Poll::Ready(Ok(()))
2562 }
2563 }
2564
2565 impl VirtualSocket for PendingVirtualSocket {
2566 fn set_socket_option(&self, _opt: VirtualSockOpt) -> std::io::Result<()> {
2567 Ok(())
2568 }
2569 }
2570
2571 struct NoopProxy;
2572
2573 #[async_trait]
2574 impl ProxyHttp for NoopProxy {
2575 type CTX = ();
2576 fn new_ctx(&self) -> Self::CTX {}
2577 async fn upstream_peer(
2578 &self,
2579 _session: &mut Session,
2580 _ctx: &mut Self::CTX,
2581 ) -> Result<Box<HttpPeer>> {
2582 Err(Error::new(InternalError))
2583 }
2584 }
2585
2586 fn pending_session() -> Box<HttpSession> {
2587 let stream = L4Stream::from(VirtualSocketStream::new(Box::new(PendingVirtualSocket)));
2588 Box::new(HttpSession::new_http1(Box::new(stream)))
2589 }
2590
2591 #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
2592 async fn shutdown_wakes_parked_read_requests() {
2593 let conf = ServerConf {
2594 threads: 4,
2595 ..ServerConf::default()
2596 };
2597 let proxy = Arc::new(HttpProxy::new(NoopProxy, Arc::new(conf)));
2598 let handles: Vec<_> = (0..8)
2599 .map(|_| {
2600 let proxy = proxy.clone();
2601 tokio::spawn(async move { proxy.handle_new_request(pending_session()).await })
2602 })
2603 .collect();
2604 time::sleep(Duration::from_millis(50)).await;
2606 proxy.http_cleanup().await;
2607 for handle in handles {
2608 let session = time::timeout(Duration::from_secs(5), handle)
2609 .await
2610 .expect("shutdown did not wake the parked read")
2611 .unwrap();
2612 assert!(session.is_none());
2613 }
2614 }
2615
2616 #[tokio::test]
2617 async fn shutdown_before_read_request_parks_returns_immediately() {
2618 let proxy = Arc::new(HttpProxy::new(NoopProxy, Arc::new(ServerConf::default())));
2619 proxy.http_cleanup().await;
2620 let session = time::timeout(
2623 Duration::from_secs(5),
2624 proxy.handle_new_request(pending_session()),
2625 )
2626 .await
2627 .expect("read_request parked after shutdown");
2628 assert!(session.is_none());
2629 }
2630}