1use std::{
2 cell::RefCell,
3 collections::{BTreeMap, HashMap, hash_map::Entry},
4 io::ErrorKind,
5 net::SocketAddr,
6 rc::{Rc, Weak},
7 str::from_utf8_unchecked,
8 time::{Duration, Instant},
9};
10
11use mio::{
12 Interest, Registry, Token,
13 net::{TcpListener as MioTcpListener, TcpStream},
14};
15use rusty_ulid::Ulid;
16use socket2::SockRef;
17use sozu_command::{
18 listener_key::ListenerKey,
19 logging::CachedTags,
20 proto::command::{
21 Cluster, ForwardedHeaders, HttpListenerConfig, ListenerType, RemoveListener,
22 RequestHttpFrontend, UpdateHttpListenerConfig, WorkerRequest, WorkerResponse,
23 request::RequestType,
24 },
25 ready::Ready,
26 response::HttpFrontend,
27 state::{
28 ClusterId, validate_forwarded_headers, validate_h2_flood_knobs_http,
29 validate_sozu_id_header,
30 },
31};
32
33use crate::metrics::names;
34use crate::{
35 AcceptError, FrontendFromRequestError, L7ListenerHandler, L7Proxy, ListenerError,
36 ListenerHandler, Protocol, ProxyConfiguration, ProxyError, ProxySession, SessionIsToBeClosed,
37 SessionMetrics, SessionResult, StateMachineBuilder, StateResult,
38 backends::BackendMap,
39 pool::Pool,
40 protocol::{
41 Pipe, SessionState,
42 h2c::{H2cPrefaceSniff, PrefaceVerdict},
43 http::{
44 answers::HttpAnswers,
45 parser::{Method, hostname_and_port},
46 },
47 mux::{self, Mux, MuxClear},
48 proxy_protocol::expect::ExpectProxyProtocol,
49 },
50 router::{RequestHeaders, RouteResult, Router},
51 server::{ListenToken, SessionManager},
52 socket::server_bind,
53 timer::TimeoutContainer,
54};
55
56#[derive(PartialEq, Eq)]
57pub enum SessionStatus {
58 Normal,
59 DefaultAnswer,
60}
61
62StateMachineBuilder! {
63 enum HttpStateMachine impl SessionState {
70 Expect(ExpectProxyProtocol<TcpStream>),
71 Sniff(H2cPrefaceSniff),
72 Mux(MuxClear),
73 WebSocket(Pipe<crate::socket::SessionTcpStream, HttpListener>),
74 }
75}
76
77macro_rules! log_module_context {
83 () => {{
84 let (open, reset, _, _, _) = sozu_command::logging::ansi_palette();
85 format!("{open}HTTP{reset}\t >>>", open = open, reset = reset)
86 }};
87}
88
89macro_rules! log_context {
95 ($self:expr) => {{
96 let (open, reset, grey, gray, white) = sozu_command::logging::ansi_palette();
97 format!(
98 "{open}HTTP{reset}\t{grey}Session{reset}({gray}frontend{reset}={white}{frontend}{reset})\t >>>",
99 open = open,
100 reset = reset,
101 grey = grey,
102 gray = gray,
103 white = white,
104 frontend = $self.frontend_token.0,
105 )
106 }};
107}
108
109pub struct HttpSession {
113 configured_backend_timeout: Duration,
114 configured_connect_timeout: Duration,
115 configured_frontend_timeout: Duration,
116 frontend_token: Token,
117 last_event: Instant,
118 listener: Rc<RefCell<HttpListener>>,
119 metrics: SessionMetrics,
120 pool: Weak<RefCell<Pool>>,
121 proxy: Rc<RefCell<HttpProxy>>,
122 state: HttpStateMachine,
123 has_been_closed: bool,
124 h2c_prior_knowledge: bool,
127}
128
129impl HttpSession {
130 #[allow(clippy::too_many_arguments)]
131 pub fn new(
132 configured_backend_timeout: Duration,
133 configured_connect_timeout: Duration,
134 configured_frontend_timeout: Duration,
135 configured_request_timeout: Duration,
136 expect_proxy: bool,
137 h2c_prior_knowledge: bool,
138 listener: Rc<RefCell<HttpListener>>,
139 pool: Weak<RefCell<Pool>>,
140 proxy: Rc<RefCell<HttpProxy>>,
141 backends: Rc<RefCell<BackendMap>>,
142 public_address: SocketAddr,
143 sock: TcpStream,
144 peer: SocketAddr,
145 token: Token,
146 wait_time: Duration,
147 ) -> Result<Self, AcceptError> {
148 let request_id = Ulid::generate();
149 let container_frontend_timeout = TimeoutContainer::new(configured_request_timeout, token);
150
151 let state = if expect_proxy {
152 trace!("{} starting in expect proxy state", log_module_context!());
153 gauge_add!(names::protocol::PROXY_EXPECT, 1);
154
155 HttpStateMachine::Expect(ExpectProxyProtocol::new(
156 container_frontend_timeout,
157 sock,
158 token,
159 request_id,
160 ))
161 } else if h2c_prior_knowledge {
162 trace!(
163 "{} starting in h2c preface sniff state",
164 log_module_context!()
165 );
166 gauge_add!(names::protocol::HTTP, 1);
169 HttpStateMachine::Sniff(H2cPrefaceSniff::new(
170 container_frontend_timeout,
171 sock,
172 token,
173 request_id,
174 Some(peer),
175 public_address,
176 Ready::EMPTY,
177 ))
178 } else {
179 gauge_add!(names::protocol::HTTP, 1);
180 let session_address = Some(peer);
184 let session_ulid = rusty_ulid::Ulid::generate();
185 let sock = crate::socket::SessionTcpStream::new(sock, session_ulid, session_address);
186
187 let frontend = mux::Connection::new_h1_server(
188 session_ulid,
189 sock,
190 container_frontend_timeout.duration(),
191 );
192 let router = mux::Router::new(configured_backend_timeout, configured_connect_timeout);
193 let mut context = mux::Context::new(
194 session_ulid,
195 pool.clone(),
196 listener.clone(),
197 session_address,
198 public_address,
199 );
200 context
201 .create_stream(request_id, 1 << 16)
202 .ok_or(AcceptError::BufferCapacityReached)?;
203 HttpStateMachine::Mux(Mux {
204 configured_frontend_timeout,
205 frontend_token: token,
206 frontend,
207 router,
208 context,
209 session_ulid,
210 timeouts: mux::MuxTimeouts::new(container_frontend_timeout),
214 backend_registry: mux::BackendRegistry::default(),
215 backends,
216 })
217 };
218
219 debug_assert_eq!(
226 state.marker() as u8,
227 if expect_proxy {
228 StateMarker::Expect as u8
229 } else if h2c_prior_knowledge {
230 StateMarker::Sniff as u8
231 } else {
232 StateMarker::Mux as u8
233 },
234 "constructed state must match the expect_proxy / h2c_prior_knowledge branch"
235 );
236 debug_assert!(
240 !state.failed(),
241 "a newly created session must not start in FailedUpgrade"
242 );
243
244 let metrics = SessionMetrics::new(Some(wait_time));
245 let session = HttpSession {
246 configured_backend_timeout,
247 configured_connect_timeout,
248 configured_frontend_timeout,
249 frontend_token: token,
250 has_been_closed: false,
251 h2c_prior_knowledge,
252 last_event: Instant::now(),
253 listener,
254 metrics,
255 pool,
256 proxy,
257 state,
258 };
259 debug_assert_eq!(
260 session.frontend_token, token,
261 "frontend token must be the slab token used for registration"
262 );
263 #[cfg(debug_assertions)]
264 session.check_invariants();
265 Ok(session)
266 }
267
268 #[cfg(debug_assertions)]
281 fn check_invariants(&self) {
282 let marker = self.state.marker();
283 debug_assert!(
284 matches!(
285 marker,
286 StateMarker::Expect
287 | StateMarker::Sniff
288 | StateMarker::Mux
289 | StateMarker::WebSocket
290 ),
291 "session marker must be a legal stage (Expect/Sniff/Mux/WebSocket), got {marker:?}"
292 );
293 debug_assert!(
295 !matches!(marker, StateMarker::Sniff) || self.h2c_prior_knowledge,
296 "a session without h2c_prior_knowledge must never sniff"
297 );
298 debug_assert!(
302 !self.state.failed() || self.has_been_closed,
303 "FailedUpgrade state must be reaped by close(), never left live"
304 );
305 }
306
307 pub fn upgrade(&mut self) -> SessionIsToBeClosed {
308 debug!("{} upgrade", log_context!(self));
309 let from_marker = self.state.marker();
315 let new_state = match self.state.take() {
316 HttpStateMachine::Mux(mux) => self.upgrade_mux(mux),
317 HttpStateMachine::Expect(expect) => self.upgrade_expect(expect),
318 HttpStateMachine::Sniff(sniff) => self.upgrade_sniff(sniff),
319 HttpStateMachine::WebSocket(ws) => self.upgrade_websocket(ws),
320 HttpStateMachine::FailedUpgrade(_) => {
321 error!(
325 "{} upgrade called on FailedUpgrade state; closing session",
326 log_context!(self)
327 );
328 None
329 }
330 };
331
332 match new_state {
333 Some(state) => {
334 debug_assert!(
339 matches!(
340 (from_marker, state.marker()),
341 (StateMarker::Expect, StateMarker::Mux)
342 | (StateMarker::Expect, StateMarker::Sniff)
343 | (StateMarker::Sniff, StateMarker::Mux)
344 | (StateMarker::Mux, StateMarker::WebSocket)
345 | (StateMarker::WebSocket, StateMarker::WebSocket)
346 ),
347 "illegal protocol-upgrade transition {from_marker:?} -> {:?}",
348 state.marker()
349 );
350 debug_assert!(
351 !state.failed(),
352 "a successful upgrade must not install a FailedUpgrade state"
353 );
354 self.state = state;
355 #[cfg(debug_assertions)]
356 self.check_invariants();
357 false
358 }
359 None => {
361 debug_assert!(
365 self.state.failed(),
366 "a failed upgrade must leave the session in FailedUpgrade"
367 );
368 true
369 }
370 }
371 }
372
373 fn upgrade_expect(
374 &mut self,
375 expect: ExpectProxyProtocol<TcpStream>,
376 ) -> Option<HttpStateMachine> {
377 debug!("{} switching to HTTP", log_context!(self));
378 match expect
379 .addresses
380 .as_ref()
381 .map(|add| (add.destination(), add.source()))
382 {
383 Some((Some(public_address), Some(session_address))) if self.h2c_prior_knowledge => {
384 let sniff = H2cPrefaceSniff::new(
388 expect.container_frontend_timeout,
389 expect.frontend,
390 self.frontend_token,
391 expect.request_id,
392 Some(session_address),
393 public_address,
394 expect.frontend_readiness.event,
395 );
396 debug_assert_eq!(
397 sniff.frontend_token, self.frontend_token,
398 "expect upgrade must preserve the frontend token"
399 );
400 debug_assert!(sniff.verdict.is_none(), "a fresh sniff has not decided yet");
401 gauge_add!(names::protocol::PROXY_EXPECT, -1);
404 gauge_add!(names::protocol::HTTP, 1);
405 Some(HttpStateMachine::Sniff(sniff))
406 }
407 Some((Some(public_address), Some(session_address))) => {
408 let session_ulid = rusty_ulid::Ulid::generate();
409 let frontend = mux::Connection::new_h1_server(
410 session_ulid,
411 crate::socket::SessionTcpStream::new(
412 expect.frontend,
413 session_ulid,
414 Some(session_address),
415 ),
416 expect.container_frontend_timeout.duration(),
417 );
418 let router = mux::Router::new(
419 self.configured_backend_timeout,
420 self.configured_connect_timeout,
421 );
422 let mut context = mux::Context::new(
423 session_ulid,
424 self.pool.clone(),
425 self.listener.clone(),
426 Some(session_address),
427 public_address,
428 );
429 if context.create_stream(expect.request_id, 1 << 16).is_none() {
430 error!(
431 "{} expect upgrade failed: could not create stream",
432 log_context!(self)
433 );
434 return None;
435 }
436 let mut mux = Mux {
437 configured_frontend_timeout: self.configured_frontend_timeout,
438 frontend_token: self.frontend_token,
439 frontend,
440 router,
441 context,
442 session_ulid,
443 timeouts: mux::MuxTimeouts::new(expect.container_frontend_timeout),
444 backend_registry: mux::BackendRegistry::default(),
445 backends: Rc::clone(&self.proxy.borrow().backends),
450 };
451 mux.frontend.readiness_mut().event = expect.frontend_readiness.event;
452
453 debug_assert_eq!(
457 mux.frontend_token, self.frontend_token,
458 "expect upgrade must preserve the frontend token"
459 );
460 debug_assert_eq!(
463 mux.context.streams.len(),
464 1,
465 "a freshly upgraded Mux owns exactly the request stream"
466 );
467
468 gauge_add!(names::protocol::PROXY_EXPECT, -1);
472 gauge_add!(names::protocol::HTTP, 1);
473 Some(HttpStateMachine::Mux(mux))
474 }
475 _ => {
476 debug!(
477 "{} expect upgrade failed: bad header {:?}",
478 log_context!(self),
479 expect.addresses
480 );
481 None
482 }
483 }
484 }
485
486 fn upgrade_sniff(&mut self, sniff: H2cPrefaceSniff) -> Option<HttpStateMachine> {
498 let verdict = sniff.verdict;
499 debug!(
500 "{} preface sniff decided {:?}, switching to the mux",
501 log_context!(self),
502 verdict
503 );
504 if !matches!(verdict, Some(PrefaceVerdict::H2 | PrefaceVerdict::Http1)) {
505 error!(
506 "{} upgrade_sniff: the sniff upgraded without a verdict ({:?}), closing",
507 log_context!(self),
508 verdict
509 );
510 return None;
511 }
512
513 let session_ulid = rusty_ulid::Ulid::generate();
514 let front_stream = crate::socket::SessionTcpStream::new(
515 sniff.frontend,
516 session_ulid,
517 sniff.session_address,
518 );
519 let router = mux::Router::new(
520 self.configured_backend_timeout,
521 self.configured_connect_timeout,
522 );
523 let mut context = mux::Context::new(
524 session_ulid,
525 self.pool.clone(),
526 self.listener.clone(),
527 sniff.session_address,
528 sniff.public_address,
529 );
530 let timeout_duration = sniff.container_frontend_timeout.duration();
531 let mut frontend = if verdict == Some(PrefaceVerdict::H2) {
532 let (flood_config, connection_config, stream_idle_timeout, graceful_shutdown_deadline) = {
533 let listener = self.listener.borrow();
534 (
535 listener.get_h2_flood_config(),
536 listener.get_h2_connection_config(),
537 listener.get_h2_stream_idle_timeout(),
538 listener.get_h2_graceful_shutdown_deadline(),
539 )
540 };
541 let Some(frontend) = mux::Connection::new_h2_server(
542 session_ulid,
543 front_stream,
544 &mut *context.buffers,
545 timeout_duration,
546 flood_config,
547 connection_config,
548 stream_idle_timeout,
549 graceful_shutdown_deadline,
550 ) else {
551 error!(
552 "{} h2c upgrade failed: could not allocate the H2 connection buffers",
553 log_context!(self)
554 );
555 return None;
556 };
557 frontend
558 } else {
559 if context.create_stream(sniff.request_id, 1 << 16).is_none() {
560 error!(
561 "{} h2c sniff upgrade failed: could not create stream",
562 log_context!(self)
563 );
564 return None;
565 }
566 mux::Connection::new_h1_server(session_ulid, front_stream, timeout_duration)
567 };
568 frontend
576 .readiness_mut()
577 .event
578 .insert(sniff.frontend_readiness.event | Ready::READABLE | Ready::WRITABLE);
579 debug_assert!(
580 frontend.readiness_mut().event.is_readable()
581 && frontend.readiness_mut().event.is_writable(),
582 "the upgraded mux frontend must be armed READABLE and WRITABLE"
583 );
584 debug_assert_eq!(
586 context.streams.len(),
587 usize::from(verdict == Some(PrefaceVerdict::Http1)),
588 "the H1 mux owns exactly its request stream, the H2 mux none yet"
589 );
590
591 Some(HttpStateMachine::Mux(Mux {
592 configured_frontend_timeout: self.configured_frontend_timeout,
593 frontend_token: self.frontend_token,
594 frontend,
595 router,
596 context,
597 session_ulid,
598 timeouts: mux::MuxTimeouts::new(sniff.container_frontend_timeout),
601 backend_registry: mux::BackendRegistry::default(),
602 backends: Rc::clone(&self.proxy.borrow().backends),
603 }))
604 }
605
606 fn upgrade_mux(&mut self, mut mux: MuxClear) -> Option<HttpStateMachine> {
607 debug!("{} mux switching to ws", log_context!(self));
608 let Some(stream) = mux.context.streams.pop() else {
609 error!(
610 "{} upgrade_mux: no stream attached to the mux session, closing",
611 log_context!(self)
612 );
613 return None;
614 };
615 let mut container_frontend_timeout = mux.timeouts.frontend;
623 let (frontend_readiness, frontend_socket) = match mux.frontend {
624 mux::Connection::H1(mux::ConnectionH1 {
625 readiness, socket, ..
626 }) => (readiness, socket),
627 mux::Connection::H2(_) => {
628 error!(
629 "{} only h1<->h1 connections can upgrade to websocket",
630 log_context!(self)
631 );
632 return None;
633 }
634 };
635
636 let mux::StreamState::Linked(back_token) = stream.state else {
637 error!(
638 "{} upgrading stream should be linked to a backend",
639 log_context!(self)
640 );
641 return None;
642 };
643 debug_assert!(
649 mux.router.backends.contains_key(&back_token),
650 "a Linked stream's back token must index a connected backend"
651 );
652 let backends_before = mux.router.backends.len();
653 let Some(backend) = mux.router.backends.remove(&back_token) else {
654 error!(
655 "{} upgrade_mux: backend for token {:?} is missing (already disconnected?), closing",
656 log_context!(self),
657 back_token
658 );
659 return None;
660 };
661 let mut container_backend_timeout = mux
662 .timeouts
663 .backends
664 .remove(&back_token)
665 .unwrap_or_else(|| TimeoutContainer::new_empty(mux.router.configured_backend_timeout));
666 let (cluster_id, backend_id, backend_readiness, backend_socket) = match backend {
667 mux::Connection::H1(mux::ConnectionH1 {
668 position: mux::Position::Client(cluster_id, backend, mux::BackendStatus::Connected),
669 readiness,
670 socket,
671 ..
672 }) => (cluster_id, backend, readiness, socket),
673 mux::Connection::H1(_) => {
674 error!(
675 "{} the backend disconnected just after upgrade, abort",
676 log_context!(self)
677 );
678 return None;
679 }
680 mux::Connection::H2(_) => {
681 error!(
682 "{} only h1<->h1 connections can upgrade to websocket",
683 log_context!(self)
684 );
685 return None;
686 }
687 };
688
689 let Some(backend) = mux.backend_registry.handle(&backend_id).cloned() else {
694 error!(
695 "{} upgrade_mux: backend {:?} names a slot this session never minted, closing",
696 log_context!(self),
697 backend_id.backend_id
698 );
699 return None;
700 };
701 let metric_incarnation = backend_id.metric_incarnation();
702 let backend_id = backend_id.backend_id.to_string();
703
704 debug_assert!(
707 !mux.router.backends.contains_key(&back_token),
708 "the upgraded backend must be evicted from the router map"
709 );
710 debug_assert_eq!(
711 mux.router.backends.len(),
712 backends_before - 1,
713 "removing the backend must drop the backend count by exactly one"
714 );
715
716 let ws_context = stream.context.websocket_context();
717
718 container_frontend_timeout.reset();
719 container_backend_timeout.reset();
720
721 let backend_socket = backend_socket.stream;
726 let mut pipe = Pipe::new(
727 stream.back.storage.buffer,
728 Some(backend_id),
729 Some(backend_socket),
730 Some(backend),
731 Some(container_backend_timeout),
732 Some(container_frontend_timeout),
733 Some(cluster_id),
734 stream.front.storage.buffer,
735 self.frontend_token,
736 frontend_socket,
737 self.listener.clone(),
738 Protocol::HTTP,
739 stream.context.session_id,
740 stream.context.id,
741 stream.context.session_address,
742 ws_context,
743 );
744 pipe.set_cluster_metrics_incarnation(metric_incarnation);
745
746 pipe.restore_readiness_events(frontend_readiness.event, backend_readiness.event);
747 pipe.set_back_token(back_token);
750 debug_assert_eq!(
751 pipe.back_token(),
752 vec![back_token],
753 "websocket pipe must carry exactly the upgraded backend token"
754 );
755
756 gauge_add!(names::protocol::HTTP, -1);
763 gauge_add!(names::protocol::WS, 1);
764 gauge_add!(names::websocket::ACTIVE_REQUESTS, 1);
765 Some(HttpStateMachine::WebSocket(pipe))
766 }
767
768 fn upgrade_websocket(
769 &self,
770 ws: Pipe<crate::socket::SessionTcpStream, HttpListener>,
771 ) -> Option<HttpStateMachine> {
772 error!(
774 "{} upgrade called on WS, this should not happen",
775 log_context!(self)
776 );
777 Some(HttpStateMachine::WebSocket(ws))
778 }
779}
780
781impl ProxySession for HttpSession {
782 fn close(&mut self) {
783 if self.has_been_closed {
784 return;
785 }
786 debug_assert!(
788 !self.has_been_closed,
789 "close past the guard must run on a not-yet-closed session"
790 );
791
792 trace!("{} closing HTTP session", log_context!(self));
793 self.metrics.service_stop();
794
795 match self.state.marker() {
797 StateMarker::Expect => gauge_add!(names::protocol::PROXY_EXPECT, -1),
798 StateMarker::Sniff | StateMarker::Mux => gauge_add!(names::protocol::HTTP, -1),
799 StateMarker::WebSocket => {
800 gauge_add!(names::protocol::WS, -1);
801 gauge_add!(names::websocket::ACTIVE_REQUESTS, -1);
802 }
803 }
804
805 if self.state.failed() {
806 match self.state.marker() {
807 StateMarker::Expect => incr!(names::http::UPGRADE_EXPECT_FAILED),
808 StateMarker::Sniff => incr!(names::http::UPGRADE_H2C_FAILED),
809 StateMarker::Mux => incr!(names::http::UPGRADE_MUX_FAILED),
810 StateMarker::WebSocket => incr!(names::http::UPGRADE_WS_FAILED),
811 }
812 self.state.close(self.proxy.clone(), &mut self.metrics);
816 self.proxy.borrow().remove_session(self.frontend_token);
817 self.has_been_closed = true;
818 debug_assert!(
819 self.has_been_closed,
820 "failed-upgrade close path must mark the session closed"
821 );
822 return;
823 }
824
825 self.state.cancel_timeouts();
826 self.state.close(self.proxy.clone(), &mut self.metrics);
828
829 let peer_closed =
832 matches!(&self.state, HttpStateMachine::Mux(mux) if mux.frontend_peer_closed());
833 let front_socket = self.state.front_socket();
834 if let Err(e) = mux::shutdown_write(front_socket, peer_closed) {
840 if e.kind() != ErrorKind::NotConnected {
842 error!(
843 "{} error shutting down front socket({:?}): {:?}",
844 log_context!(self),
845 front_socket,
846 e
847 )
848 }
849 }
850
851 let proxy = self.proxy.borrow();
860 proxy.remove_session(self.frontend_token);
861
862 self.has_been_closed = true;
863 debug_assert!(
864 self.has_been_closed,
865 "close must leave the session marked closed (idempotency latch)"
866 );
867 }
868
869 fn timeout(&mut self, token: Token) -> SessionIsToBeClosed {
870 let state_result = self.state.timeout(token, &mut self.metrics);
871 state_result == StateResult::CloseSession
872 }
873
874 fn needs_ready_pass(&self) -> bool {
875 matches!(&self.state, HttpStateMachine::Mux(mux) if mux.has_pending_backend_failure())
876 }
877
878 fn yielded(&mut self) -> bool {
879 match &mut self.state {
880 HttpStateMachine::Mux(mux) => mux.take_loop_budget_yield(),
881 _ => false,
882 }
883 }
884
885 fn protocol(&self) -> Protocol {
886 Protocol::HTTP
887 }
888
889 fn update_readiness(&mut self, token: Token, events: Ready) {
890 trace!(
891 "{} token {:?} got event {}",
892 log_context!(self),
893 token,
894 super::ready_to_string(events)
895 );
896 self.last_event = Instant::now();
897 self.metrics.wait_start();
898 self.state.update_readiness(token, events);
899 }
900
901 fn ready(&mut self, session: Rc<RefCell<dyn ProxySession>>) -> SessionIsToBeClosed {
902 self.metrics.service_start();
903
904 let session_result =
905 self.state
906 .ready(session.clone(), self.proxy.clone(), &mut self.metrics);
907
908 let to_be_closed = match session_result {
909 SessionResult::Close => true,
910 SessionResult::Continue => false,
911 SessionResult::Upgrade => match self.upgrade() {
912 false => self.ready(session),
913 true => true,
914 },
915 };
916
917 self.metrics.service_stop();
918 #[cfg(debug_assertions)]
923 if !to_be_closed {
924 self.check_invariants();
925 }
926 to_be_closed
927 }
928
929 fn shutting_down(&mut self) -> SessionIsToBeClosed {
930 self.state.shutting_down()
931 }
932
933 fn targets(&self, target: &crate::SessionTarget) -> bool {
934 self.state.targets(target)
935 }
936
937 fn last_event(&self) -> Instant {
938 self.last_event
939 }
940
941 fn print_session(&self) {
942 self.state.print_state("HTTP");
943 error!("{} Metrics: {:?}", log_context!(self), self.metrics);
944 }
945
946 fn frontend_token(&self) -> Token {
947 self.frontend_token
948 }
949}
950
951pub type Hostname = String;
952
953pub struct HttpListener {
981 active: bool,
982 address: SocketAddr,
983 answers: Rc<RefCell<HttpAnswers>>,
984 config: HttpListenerConfig,
985 fronts: Router,
986 listener: Option<MioTcpListener>,
987 parked_listener: Option<MioTcpListener>,
1001 nodelay_backlog: bool,
1015 tags: BTreeMap<String, CachedTags>,
1016 token: Token,
1017}
1018
1019impl ListenerHandler for HttpListener {
1020 fn get_addr(&self) -> &SocketAddr {
1021 &self.address
1022 }
1023
1024 fn get_tags(&self, key: &str) -> Option<&CachedTags> {
1025 self.tags.get(key)
1026 }
1027
1028 fn set_tags(&mut self, key: String, tags: Option<BTreeMap<String, String>>) {
1029 match tags {
1030 Some(tags) => self.tags.insert(key, CachedTags::new(tags)),
1031 None => self.tags.remove(&key),
1032 };
1033 }
1034
1035 fn protocol(&self) -> Protocol {
1036 Protocol::HTTP
1037 }
1038
1039 fn public_address(&self) -> SocketAddr {
1040 self.config
1041 .public_address
1042 .map(|addr| addr.into())
1043 .unwrap_or(self.address)
1044 }
1045}
1046
1047impl L7ListenerHandler for HttpListener {
1048 fn get_sticky_name(&self) -> &str {
1049 &self.config.sticky_name
1050 }
1051
1052 fn get_sozu_id_header(&self) -> &str {
1053 self.config
1054 .sozu_id_header
1055 .as_deref()
1056 .filter(|s| !s.is_empty())
1057 .unwrap_or("Sozu-Id")
1058 }
1059
1060 fn get_connect_timeout(&self) -> u32 {
1061 self.config.connect_timeout
1062 }
1063
1064 fn frontend_from_request(
1066 &self,
1067 host: &str,
1068 uri: &str,
1069 method: &Method,
1070 headers: &dyn RequestHeaders,
1071 ) -> Result<RouteResult, FrontendFromRequestError> {
1072 let start = Instant::now();
1073 let (remaining_input, (hostname, _)) = match hostname_and_port(host.as_bytes()) {
1074 Ok(tuple) => tuple,
1075 Err(parse_error) => {
1076 return Err(FrontendFromRequestError::HostParse {
1078 host: host.to_owned(),
1079 error: parse_error.to_string(),
1080 });
1081 }
1082 };
1083 if remaining_input != &b""[..] {
1084 return Err(FrontendFromRequestError::InvalidCharsAfterHost(
1085 host.to_owned(),
1086 ));
1087 }
1088
1089 let host = unsafe { from_utf8_unchecked(hostname) };
1104
1105 let route = self
1106 .fronts
1107 .lookup_with_headers(host, uri, method, headers)
1108 .map_err(|e| {
1109 incr!(names::http::FAILED_BACKEND_MATCHING);
1110 FrontendFromRequestError::NoClusterFound(e)
1111 })?;
1112
1113 let now = Instant::now();
1114
1115 if let Some(cluster) = route.cluster_id.as_deref() {
1116 time!(
1117 names::event_loop::FRONTEND_MATCHING_TIME,
1118 cluster,
1119 (now - start).as_millis()
1120 );
1121 }
1122
1123 Ok(route)
1124 }
1125
1126 fn get_answers(&self) -> &Rc<RefCell<HttpAnswers>> {
1127 &self.answers
1128 }
1129
1130 fn get_h2_flood_config(&self) -> crate::protocol::mux::H2FloodConfig {
1131 crate::protocol::mux::H2FloodConfig::from_optional(
1139 self.config.h2_max_rst_stream_per_window,
1140 self.config.h2_max_ping_per_window,
1141 self.config.h2_max_settings_per_window,
1142 self.config.h2_max_empty_data_per_window,
1143 self.config.h2_max_window_update_stream0_per_window,
1144 self.config.h2_max_continuation_frames,
1145 self.config.h2_max_glitch_count,
1146 self.config.h2_max_rst_stream_lifetime,
1147 self.config.h2_max_rst_stream_abusive_lifetime,
1148 self.config.h2_max_rst_stream_emitted_lifetime,
1149 self.config.h2_max_header_list_size,
1150 self.config.h2_max_header_table_size,
1151 self.config.h2_max_header_fields,
1152 self.config.h2_stream_refusal_percent,
1153 )
1154 }
1155
1156 fn get_h2_connection_config(&self) -> crate::protocol::mux::H2ConnectionConfig {
1157 crate::protocol::mux::H2ConnectionConfig::from_optional(
1158 self.config.h2_initial_connection_window,
1159 self.config.h2_max_concurrent_streams,
1160 self.config.h2_stream_shrink_ratio,
1161 )
1162 }
1163
1164 fn get_h2_stream_idle_timeout(&self) -> std::time::Duration {
1165 let seconds = self
1172 .config
1173 .h2_stream_idle_timeout_seconds
1174 .map(|s| u64::from(s.max(1)))
1175 .unwrap_or_else(|| u64::from(self.config.back_timeout).max(30));
1176 std::time::Duration::from_secs(seconds)
1177 }
1178
1179 fn get_h2_graceful_shutdown_deadline(&self) -> Option<std::time::Duration> {
1180 match self.config.h2_graceful_shutdown_deadline_seconds {
1181 None => Some(std::time::Duration::from_secs(5)),
1182 Some(0) => None,
1183 Some(s) => Some(std::time::Duration::from_secs(u64::from(s))),
1184 }
1185 }
1186
1187 fn get_elide_x_real_ip(&self) -> bool {
1188 self.config.elide_x_real_ip.unwrap_or(false)
1189 }
1190
1191 fn get_send_x_real_ip(&self) -> bool {
1192 self.config.send_x_real_ip.unwrap_or(false)
1193 }
1194
1195 fn get_forwarded_headers(&self) -> ForwardedHeaders {
1196 self.config.forwarded_headers()
1199 }
1200}
1201
1202pub struct HttpProxy {
1203 backends: Rc<RefCell<BackendMap>>,
1204 clusters: HashMap<ClusterId, Cluster>,
1205 listeners: HashMap<Token, Rc<RefCell<HttpListener>>>,
1206 pool: Rc<RefCell<Pool>>,
1207 registry: Registry,
1208 sessions: Rc<RefCell<SessionManager>>,
1209}
1210
1211impl HttpProxy {
1212 pub fn new(
1213 registry: Registry,
1214 sessions: Rc<RefCell<SessionManager>>,
1215 pool: Rc<RefCell<Pool>>,
1216 backends: Rc<RefCell<BackendMap>>,
1217 ) -> HttpProxy {
1218 HttpProxy {
1219 backends,
1220 clusters: HashMap::new(),
1221 listeners: HashMap::new(),
1222 pool,
1223 registry,
1224 sessions,
1225 }
1226 }
1227
1228 pub fn add_listener(
1229 &mut self,
1230 config: HttpListenerConfig,
1231 token: Token,
1232 ) -> Result<Token, ProxyError> {
1233 if self.listeners.contains_key(&token) {
1234 return Err(ProxyError::ListenerAlreadyPresent);
1235 }
1236 let mut http_listener =
1237 HttpListener::new(config, token).map_err(ProxyError::AddListener)?;
1238 if let Some(sibling) = self
1243 .listeners_at(http_listener.address)
1244 .ok()
1245 .and_then(|l| l.into_iter().next())
1246 {
1247 let sibling = sibling.borrow();
1248 http_listener.fronts = sibling.fronts.clone();
1249 http_listener.tags = sibling.tags.clone();
1250 }
1251 match self.listeners.entry(token) {
1252 Entry::Vacant(entry) => {
1253 entry.insert(Rc::new(RefCell::new(http_listener)));
1254 Ok(token)
1255 }
1256 _ => Err(ProxyError::ListenerAlreadyPresent),
1257 }
1258 }
1259
1260 pub(crate) fn add_listener_with_retained_frontends(
1269 &mut self,
1270 config: HttpListenerConfig,
1271 token: Token,
1272 retained_frontends: Vec<RequestHttpFrontend>,
1273 ) -> Result<Token, ProxyError> {
1274 let address = config.address.into();
1275 let had_sibling = self.listeners_at(address).is_ok();
1276 self.add_listener(config, token)?;
1277
1278 if !had_sibling {
1279 for frontend in retained_frontends {
1280 if let Err(error) = self.add_http_frontend(frontend) {
1281 self.listeners.remove(&token);
1282 return Err(error);
1283 }
1284 }
1285 }
1286
1287 Ok(token)
1288 }
1289
1290 pub fn get_listener(&self, token: &Token) -> Option<Rc<RefCell<HttpListener>>> {
1291 self.listeners.get(token).cloned()
1292 }
1293
1294 pub fn remove_listener(&mut self, remove: RemoveListener) -> Result<(), ProxyError> {
1295 let len = self.listeners.len();
1296 let remove_address = remove.listener_key();
1297 self.listeners
1298 .retain(|_, l| !l.borrow().is(&remove_address));
1299
1300 if !self.listeners.len() < len {
1301 info!(
1302 "{} no HTTP listener to remove at {}",
1303 log_module_context!(),
1304 remove_address
1305 );
1306 }
1307 Ok(())
1308 }
1309
1310 pub fn inherited_socket_fate(&self, addr: &ListenerKey) -> crate::InheritedSocketFate {
1318 use crate::InheritedSocketFate;
1319 match self
1320 .listeners
1321 .values()
1322 .find(|listener| listener.borrow().is(addr))
1323 {
1324 None => InheritedSocketFate::Unclaimed,
1325 Some(listener) if listener.borrow().active => InheritedSocketFate::Refused,
1326 Some(_) => InheritedSocketFate::Adopted,
1327 }
1328 }
1329
1330 pub fn activate_listener(
1331 &self,
1332 addr: &ListenerKey,
1333 tcp_listener: Option<MioTcpListener>,
1334 ) -> Result<Token, ProxyError> {
1335 let listener = self
1336 .listeners
1337 .values()
1338 .find(|listener| listener.borrow().is(addr))
1339 .ok_or(ProxyError::NoListenerFound(addr.clone()))?;
1340
1341 listener
1342 .borrow_mut()
1343 .activate(&self.registry, tcp_listener)
1344 .map_err(|listener_error| ProxyError::ListenerActivation {
1345 address: addr.address,
1346 listener_error,
1347 })
1348 }
1349
1350 pub fn listener_token(&self, address: &ListenerKey) -> Option<Token> {
1359 self.listeners
1360 .iter()
1361 .find(|(_, listener)| listener.borrow().is(address))
1362 .map(|(token, _)| *token)
1363 }
1364
1365 pub fn give_back_listeners(&mut self) -> Vec<(ListenerKey, MioTcpListener)> {
1366 self.listeners
1367 .values()
1368 .filter_map(|listener| {
1369 let mut owned = listener.borrow_mut();
1370 if let Some(listener) = owned.listener.take() {
1371 owned.active = false;
1374 return Some((owned.key(), listener));
1375 }
1376
1377 None
1378 })
1379 .collect()
1380 }
1381
1382 pub fn give_back_listener(
1383 &mut self,
1384 address: &ListenerKey,
1385 ) -> Result<(Token, MioTcpListener), ProxyError> {
1386 let listener = self
1387 .listeners
1388 .values()
1389 .find(|listener| listener.borrow().is(address))
1390 .ok_or(ProxyError::NoListenerFound(address.clone()))?;
1391
1392 let mut owned = listener.borrow_mut();
1393
1394 let taken_listener = owned
1395 .listener
1396 .take()
1397 .ok_or(ProxyError::UnactivatedListener)?;
1398
1399 owned.active = false;
1402
1403 Ok((owned.token, taken_listener))
1404 }
1405
1406 fn listeners_at(
1410 &self,
1411 address: SocketAddr,
1412 ) -> Result<Vec<Rc<RefCell<HttpListener>>>, ProxyError> {
1413 let mut listeners: Vec<_> = self
1414 .listeners
1415 .iter()
1416 .filter(|(_, listener)| listener.borrow().address == address)
1417 .map(|(token, listener)| (*token, listener.clone()))
1418 .collect();
1419 listeners.sort_by_key(|(token, _)| *token);
1421 let listeners: Vec<_> = listeners
1422 .into_iter()
1423 .map(|(_, listener)| listener)
1424 .collect();
1425 if listeners.is_empty() {
1426 return Err(ProxyError::NoListenerFound(address.into()));
1427 }
1428 Ok(listeners)
1429 }
1430
1431 pub fn update_listener(&mut self, patch: UpdateHttpListenerConfig) -> Result<(), ProxyError> {
1433 let address: std::net::SocketAddr = patch.address.into();
1434 let listener = self
1435 .listeners
1436 .values()
1437 .find(|l| l.borrow().address == address)
1438 .ok_or(ProxyError::NoListenerFound(address.into()))?;
1439 listener
1440 .borrow_mut()
1441 .update_config(&patch)
1442 .map_err(|listener_error| ProxyError::ListenerActivation {
1443 address,
1444 listener_error,
1445 })
1446 }
1447
1448 pub fn add_cluster(&mut self, mut cluster: Cluster) -> Result<(), ProxyError> {
1449 let mut overrides = cluster.answers.clone();
1452 if let Some(answer_503) = cluster.answer_503.take() {
1453 overrides.entry("503".to_owned()).or_insert(answer_503);
1454 }
1455 if !overrides.is_empty() {
1456 for listener in self.listeners.values() {
1457 listener
1458 .borrow()
1459 .answers
1460 .borrow_mut()
1461 .add_cluster_answers(&cluster.cluster_id, &overrides)
1462 .map_err(|(name, error)| {
1463 ProxyError::AddCluster(ListenerError::TemplateParse(name, error))
1464 })?;
1465 }
1466 }
1467 self.clusters
1468 .insert(cluster.cluster_id.as_str().into(), cluster);
1469 Ok(())
1470 }
1471
1472 pub fn remove_cluster(&mut self, cluster_id: &str) -> Result<(), ProxyError> {
1473 self.clusters.remove(cluster_id);
1474
1475 for listener in self.listeners.values() {
1476 listener
1477 .borrow()
1478 .answers
1479 .borrow_mut()
1480 .remove_cluster_answers(cluster_id);
1481 }
1482 Ok(())
1483 }
1484
1485 pub fn add_http_frontend(&mut self, front: RequestHttpFrontend) -> Result<(), ProxyError> {
1486 if front.hsts.is_some() {
1501 incr!(names::http::HSTS_SUPPRESSED_PLAINTEXT);
1502 return Err(ProxyError::HstsOnPlainHttp(front.address.into()));
1503 }
1504
1505 let front = front.clone().to_frontend().map_err(|request_error| {
1506 ProxyError::WrongInputFrontend {
1507 front: Box::new(front),
1508 error: request_error.to_string(),
1509 }
1510 })?;
1511
1512 for listener in self.listeners_at(front.address)? {
1513 let mut listener = listener.borrow_mut();
1514 let hostname = front.hostname.to_owned();
1515 let tags = front.tags.to_owned();
1516
1517 listener
1518 .add_http_front(front.clone())
1519 .map_err(ProxyError::AddFrontend)?;
1520 listener.set_tags(hostname, tags);
1521 }
1522 Ok(())
1523 }
1524
1525 pub fn remove_http_frontend(&mut self, front: RequestHttpFrontend) -> Result<(), ProxyError> {
1526 let front = front.clone().to_frontend().map_err(|request_error| {
1527 ProxyError::WrongInputFrontend {
1528 front: Box::new(front),
1529 error: request_error.to_string(),
1530 }
1531 })?;
1532
1533 let mut result = Ok(());
1537 for listener in self.listeners_at(front.address)? {
1538 let mut listener = listener.borrow_mut();
1539 let hostname = front.hostname.to_owned();
1540
1541 let removed = listener
1542 .remove_http_front(front.clone())
1543 .map_err(ProxyError::RemoveFrontend);
1544 if result.is_ok() {
1545 result = removed;
1546 }
1547
1548 if !listener.fronts.has_hostname(&hostname) {
1549 listener.set_tags(hostname, None);
1550 }
1551 }
1552 result
1553 }
1554
1555 pub fn soft_stop(&mut self) -> Result<(), ProxyError> {
1556 let listeners = std::mem::take(&mut self.listeners);
1557 let mut socket_errors = vec![];
1558 for l in listeners.values() {
1559 if let Some(mut sock) = l.borrow_mut().listener.take() {
1560 debug!("{} deregistering socket {:?}", log_module_context!(), sock);
1561 if let Err(e) = self.registry.deregister(&mut sock) {
1562 let error = format!("socket {sock:?}: {e:?}");
1563 socket_errors.push(error);
1564 }
1565 }
1566 }
1567
1568 if !socket_errors.is_empty() {
1569 return Err(ProxyError::SoftStop {
1570 proxy_protocol: "HTTP".to_string(),
1571 error: format!("Error deregistering listen sockets: {socket_errors:?}"),
1572 });
1573 }
1574
1575 Ok(())
1576 }
1577
1578 pub fn hard_stop(&mut self) -> Result<(), ProxyError> {
1579 let mut listeners = std::mem::take(&mut self.listeners);
1580 let mut socket_errors = vec![];
1581 for (_, l) in listeners.drain() {
1582 if let Some(mut sock) = l.borrow_mut().listener.take() {
1583 debug!("{} deregistering socket {:?}", log_module_context!(), sock);
1584 if let Err(e) = self.registry.deregister(&mut sock) {
1585 let error = format!("socket {sock:?}: {e:?}");
1586 socket_errors.push(error);
1587 }
1588 }
1589 }
1590
1591 if !socket_errors.is_empty() {
1592 return Err(ProxyError::HardStop {
1593 proxy_protocol: "HTTP".to_string(),
1594 error: format!("Error deregistering listen sockets: {socket_errors:?}"),
1595 });
1596 }
1597
1598 Ok(())
1599 }
1600}
1601
1602impl HttpListener {
1603 pub fn key(&self) -> ListenerKey {
1606 ListenerKey::new(self.address, self.config.interface.as_deref())
1607 }
1608
1609 pub fn is(&self, key: &ListenerKey) -> bool {
1611 key.matches(&self.address, self.config.interface.as_deref())
1612 }
1613
1614 pub fn new(config: HttpListenerConfig, token: Token) -> Result<HttpListener, ListenerError> {
1615 if let Some(v) = config.forwarded_headers {
1618 validate_forwarded_headers(v)?;
1619 }
1620 let answers = Self::build_answers(&config)?;
1621 Ok(HttpListener {
1622 active: false,
1623 address: config.address.into(),
1624 answers: Rc::new(RefCell::new(answers)),
1625 config,
1626 fronts: Router::new(),
1627 listener: None,
1628 parked_listener: None,
1629 nodelay_backlog: false,
1630 tags: BTreeMap::new(),
1631 token,
1632 })
1633 }
1634
1635 fn build_answers(config: &HttpListenerConfig) -> Result<HttpAnswers, ListenerError> {
1642 let mut answers_map = config.answers.clone();
1643 if let Some(ref legacy) = config.http_answers {
1644 crate::protocol::http::answers::merge_legacy_into_map(&mut answers_map, legacy);
1645 }
1646 HttpAnswers::new(&answers_map)
1647 .map_err(|(name, error)| ListenerError::TemplateParse(name, error))
1648 }
1649
1650 pub fn validate_config(config: &HttpListenerConfig) -> Result<(), ListenerError> {
1665 Self::build_answers(config)?;
1666 Ok(())
1667 }
1668
1669 pub fn activate(
1670 &mut self,
1671 registry: &Registry,
1672 tcp_listener: Option<MioTcpListener>,
1673 ) -> Result<Token, ListenerError> {
1674 if self.active {
1675 return Ok(self.token);
1676 }
1677 let address: SocketAddr = self.config.address.into();
1678
1679 let parked = self.parked_listener.take();
1685 let mut listener = match (tcp_listener, parked) {
1686 (Some(inherited), Some(parked)) => {
1687 warn!(
1695 "{} closing the socket parked on the HTTP listener for {}: an \
1696 inherited socket arrived for the same address and is the one being \
1697 activated",
1698 log_module_context!(),
1699 address
1700 );
1701 drop(parked);
1702 inherited
1703 }
1704 (Some(inherited), None) => inherited,
1705 (None, Some(parked)) => parked,
1706 (None, None) => server_bind(address, self.config.interface.as_deref()).map_err(
1707 |server_bind_error| ListenerError::Activation {
1708 address,
1709 error: server_bind_error.to_string(),
1710 },
1711 )?,
1712 };
1713
1714 if let Err(e) = SockRef::from(&listener).set_tcp_nodelay(true) {
1720 error!(
1721 "{} error setting nodelay on listen socket({:?}): {:?}",
1722 log_module_context!(),
1723 listener,
1724 e
1725 );
1726 }
1727
1728 let registration = registry
1729 .register(&mut listener, self.token, Interest::READABLE)
1730 .map_err(ListenerError::SocketRegistration);
1731 if let Err(error) = registration {
1732 self.parked_listener = Some(listener);
1742 return Err(error);
1743 }
1744
1745 self.listener = Some(listener);
1746 self.nodelay_backlog = true;
1747 self.active = true;
1748 Ok(self.token)
1749 }
1750
1751 pub fn update_config(&mut self, patch: &UpdateHttpListenerConfig) -> Result<(), ListenerError> {
1764 validate_h2_flood_knobs_http(patch)?;
1772 if let Some(ref hdr) = patch.sozu_id_header {
1773 validate_sozu_id_header(hdr)?;
1774 }
1775 if let Some(v) = patch.forwarded_headers {
1776 validate_forwarded_headers(v)?;
1777 }
1778 HttpAnswers::validate_patch_templates(patch.http_answers.as_ref(), &patch.answers)
1779 .map_err(|(name, error)| ListenerError::TemplateParse(name, error))?;
1780
1781 let mut config = self.config.clone();
1782
1783 if let Some(v) = patch.public_address {
1784 config.public_address = Some(v);
1785 }
1786 if let Some(v) = patch.expect_proxy {
1787 config.expect_proxy = v;
1788 }
1789 if let Some(ref v) = patch.sticky_name {
1790 config.sticky_name = v.to_owned();
1791 }
1792 if let Some(v) = patch.front_timeout {
1793 config.front_timeout = v;
1794 }
1795 if let Some(v) = patch.back_timeout {
1796 config.back_timeout = v;
1797 }
1798 if let Some(v) = patch.connect_timeout {
1799 config.connect_timeout = v;
1800 }
1801 if let Some(v) = patch.request_timeout {
1802 config.request_timeout = v;
1803 }
1804 if let Some(ref v) = patch.sozu_id_header {
1805 config.sozu_id_header = Some(v.to_owned());
1806 }
1807 if let Some(v) = patch.elide_x_real_ip {
1808 config.elide_x_real_ip = Some(v);
1809 }
1810 if let Some(v) = patch.send_x_real_ip {
1811 config.send_x_real_ip = Some(v);
1812 }
1813 if let Some(v) = patch.forwarded_headers {
1814 config.forwarded_headers = Some(v);
1815 }
1816 if let Some(v) = patch.h2c_prior_knowledge {
1819 config.h2c_prior_knowledge = Some(v);
1820 }
1821
1822 if let Some(v) = patch.h2_max_rst_stream_per_window {
1824 config.h2_max_rst_stream_per_window = Some(v);
1825 }
1826 if let Some(v) = patch.h2_max_ping_per_window {
1827 config.h2_max_ping_per_window = Some(v);
1828 }
1829 if let Some(v) = patch.h2_max_settings_per_window {
1830 config.h2_max_settings_per_window = Some(v);
1831 }
1832 if let Some(v) = patch.h2_max_empty_data_per_window {
1833 config.h2_max_empty_data_per_window = Some(v);
1834 }
1835 if let Some(v) = patch.h2_max_continuation_frames {
1836 config.h2_max_continuation_frames = Some(v);
1837 }
1838 if let Some(v) = patch.h2_max_glitch_count {
1839 config.h2_max_glitch_count = Some(v);
1840 }
1841 if let Some(v) = patch.h2_initial_connection_window {
1842 config.h2_initial_connection_window = Some(v);
1843 }
1844 if let Some(v) = patch.h2_max_concurrent_streams {
1845 config.h2_max_concurrent_streams = Some(v);
1846 }
1847 if let Some(v) = patch.h2_stream_shrink_ratio {
1848 config.h2_stream_shrink_ratio = Some(v);
1849 }
1850 if let Some(v) = patch.h2_max_rst_stream_lifetime {
1851 config.h2_max_rst_stream_lifetime = Some(v);
1852 }
1853 if let Some(v) = patch.h2_max_rst_stream_abusive_lifetime {
1854 config.h2_max_rst_stream_abusive_lifetime = Some(v);
1855 }
1856 if let Some(v) = patch.h2_max_rst_stream_emitted_lifetime {
1857 config.h2_max_rst_stream_emitted_lifetime = Some(v);
1858 }
1859 if let Some(v) = patch.h2_max_header_list_size {
1860 config.h2_max_header_list_size = Some(v);
1861 }
1862 if let Some(v) = patch.h2_max_header_table_size {
1863 config.h2_max_header_table_size = Some(v);
1864 }
1865 if let Some(v) = patch.h2_max_header_fields {
1866 config.h2_max_header_fields = Some(v);
1867 }
1868 if let Some(v) = patch.h2_stream_refusal_percent {
1869 config.h2_stream_refusal_percent = Some(v);
1870 }
1871 if let Some(v) = patch.h2_stream_idle_timeout_seconds {
1872 config.h2_stream_idle_timeout_seconds = Some(v);
1873 }
1874 if let Some(v) = patch.h2_graceful_shutdown_deadline_seconds {
1875 config.h2_graceful_shutdown_deadline_seconds = Some(v);
1876 }
1877 if let Some(v) = patch.h2_max_window_update_stream0_per_window {
1878 config.h2_max_window_update_stream0_per_window = Some(v);
1879 }
1880
1881 let answers_changed = patch.http_answers.is_some() || !patch.answers.is_empty();
1886 let new_answers = if answers_changed {
1887 if let Some(ref new_answers) = patch.http_answers {
1888 crate::sozu_command::state::merge_custom_http_answers(
1889 &mut config.http_answers,
1890 new_answers,
1891 );
1892 }
1893 for (code, body) in &patch.answers {
1894 if !body.is_empty() {
1895 config.answers.insert(code.clone(), body.clone());
1896 }
1897 }
1898
1899 let mut answers_map = config.answers.clone();
1900 if let Some(ref legacy) = config.http_answers {
1901 crate::protocol::http::answers::merge_legacy_into_map(&mut answers_map, legacy);
1902 }
1903 Some(
1904 HttpAnswers::new(&answers_map)
1905 .map_err(|(name, error)| ListenerError::TemplateParse(name, error))?,
1906 )
1907 } else {
1908 None
1909 };
1910
1911 self.config = config;
1913 if let Some(mut new_answers) = new_answers {
1914 new_answers.cluster_answers = self.answers.borrow().cluster_answers.clone();
1932 self.answers = Rc::new(RefCell::new(new_answers));
1933 }
1934
1935 Ok(())
1936 }
1937
1938 pub fn add_http_front(&mut self, http_front: HttpFrontend) -> Result<(), ListenerError> {
1939 self.fronts
1940 .add_http_front(&http_front)
1941 .map_err(ListenerError::AddFrontend)
1942 }
1943
1944 pub fn remove_http_front(&mut self, http_front: HttpFrontend) -> Result<(), ListenerError> {
1945 debug!(
1946 "{} removing http_front {:?}",
1947 log_module_context!(),
1948 http_front
1949 );
1950 self.fronts
1951 .remove_http_front(&http_front)
1952 .map_err(ListenerError::RemoveFrontend)
1953 }
1954
1955 fn accept(&mut self) -> Result<(TcpStream, SocketAddr), AcceptError> {
1956 if let Some(ref sock) = self.listener {
1957 match sock.accept() {
1958 Ok((frontend_sock, peer)) => {
1959 if self.nodelay_backlog
1960 && let Err(e) = frontend_sock.set_nodelay(true)
1961 {
1962 error!(
1963 "{} error setting nodelay on front socket({:?}): {:?}",
1964 log_module_context!(),
1965 frontend_sock,
1966 e
1967 );
1968 }
1969 Ok((frontend_sock, peer))
1970 }
1971 Err(e) if e.kind() == ErrorKind::WouldBlock => {
1972 self.nodelay_backlog = false;
1974 Err(AcceptError::WouldBlock)
1975 }
1976 Err(e) => {
1977 error!("{} accept() IO error: {:?}", log_module_context!(), e);
1978 Err(AcceptError::IoError)
1979 }
1980 }
1981 } else {
1982 error!(
1983 "{} cannot accept connections, no listening socket available",
1984 log_module_context!()
1985 );
1986 Err(AcceptError::IoError)
1987 }
1988 }
1989}
1990
1991impl ProxyConfiguration for HttpProxy {
1992 fn notify(&mut self, request: WorkerRequest) -> WorkerResponse {
1993 let request_id = request.id.clone();
1994
1995 let result = match request.content.request_type {
1996 Some(RequestType::AddCluster(cluster)) => {
1997 debug!(
1998 "{} {} add cluster {:?}",
1999 log_module_context!(),
2000 request.id,
2001 cluster
2002 );
2003 self.add_cluster(cluster)
2004 }
2005 Some(RequestType::RemoveCluster(cluster_id)) => {
2006 debug!(
2007 "{} {} remove cluster {:?}",
2008 log_module_context!(),
2009 request_id,
2010 cluster_id
2011 );
2012 self.remove_cluster(&cluster_id)
2013 }
2014 Some(
2015 RequestType::AddHttpFrontend(front)
2016 | RequestType::AddHttpFrontendWithSilentClose(front)
2017 | RequestType::AddHttpFrontendWithHeaderPredicates(front),
2018 ) => {
2019 debug!(
2020 "{} {} add front {:?}",
2021 log_module_context!(),
2022 request_id,
2023 front
2024 );
2025 self.add_http_frontend(front)
2026 }
2027 Some(
2028 RequestType::RemoveHttpFrontend(front)
2029 | RequestType::RemoveHttpFrontendWithHeaderPredicates(front),
2030 ) => {
2031 debug!(
2032 "{} {} remove front {:?}",
2033 log_module_context!(),
2034 request_id,
2035 front
2036 );
2037 self.remove_http_frontend(front)
2038 }
2039 Some(RequestType::RemoveListener(remove)) => {
2040 debug!(
2041 "{} removing HTTP listener at address {:?}",
2042 log_module_context!(),
2043 remove.address
2044 );
2045 self.remove_listener(remove)
2046 }
2047 Some(RequestType::SoftStop(_)) => {
2048 debug!(
2049 "{} {} processing soft shutdown",
2050 log_module_context!(),
2051 request_id
2052 );
2053 match self.soft_stop() {
2054 Ok(()) => {
2055 info!(
2056 "{} {} soft stop successful",
2057 log_module_context!(),
2058 request_id
2059 );
2060 return WorkerResponse::processing(request.id);
2061 }
2062 Err(e) => Err(e),
2063 }
2064 }
2065 Some(RequestType::HardStop(_)) => {
2066 debug!(
2067 "{} {} processing hard shutdown",
2068 log_module_context!(),
2069 request_id
2070 );
2071 match self.hard_stop() {
2072 Ok(()) => {
2073 info!(
2074 "{} {} hard stop successful",
2075 log_module_context!(),
2076 request_id
2077 );
2078 return WorkerResponse::processing(request.id);
2079 }
2080 Err(e) => Err(e),
2081 }
2082 }
2083 Some(RequestType::Status(_)) => {
2084 debug!("{} {} status", log_module_context!(), request_id);
2085 Ok(())
2086 }
2087 other_command => {
2088 debug!(
2089 "{} {} unsupported message for HTTP proxy, ignoring: {:?}",
2090 log_module_context!(),
2091 request.id,
2092 other_command
2093 );
2094 Err(ProxyError::UnsupportedMessage)
2095 }
2096 };
2097
2098 match result {
2099 Ok(()) => {
2100 debug!("{} {} successful", log_module_context!(), request_id);
2101 WorkerResponse::ok(request_id)
2102 }
2103 Err(proxy_error) => {
2104 debug!(
2105 "{} {} unsuccessful: {}",
2106 log_module_context!(),
2107 request_id,
2108 proxy_error
2109 );
2110 WorkerResponse::error(request_id, proxy_error)
2111 }
2112 }
2113 }
2114
2115 fn accept(&mut self, token: ListenToken) -> Result<(TcpStream, SocketAddr), AcceptError> {
2116 if let Some(listener) = self.listeners.get(&Token(token.0)) {
2117 listener.borrow_mut().accept()
2118 } else {
2119 Err(AcceptError::IoError)
2120 }
2121 }
2122
2123 fn create_session(
2124 &mut self,
2125 mut frontend_sock: TcpStream,
2126 peer: SocketAddr,
2127 listener_token: ListenToken,
2128 wait_time: Duration,
2129 proxy: Rc<RefCell<Self>>,
2130 ) -> Result<(), AcceptError> {
2131 let listener = self
2132 .listeners
2133 .get(&Token(listener_token.0))
2134 .cloned()
2135 .ok_or(AcceptError::IoError)?;
2136
2137 let mut session_manager = self.sessions.borrow_mut();
2141 let slab_len_before = session_manager.slab.len();
2142 let session_entry = session_manager.slab.vacant_entry();
2143 let session_token = Token(session_entry.key());
2144 debug_assert_eq!(
2149 session_token.0,
2150 session_entry.key(),
2151 "session token must equal the slab vacant-entry key"
2152 );
2153 let owned = listener.borrow();
2154
2155 if let Err(register_error) = self.registry.register(
2156 &mut frontend_sock,
2157 session_token,
2158 Interest::READABLE | Interest::WRITABLE,
2159 ) {
2160 error!(
2161 "{} error registering listen socket({:?}): {:?}",
2162 log_module_context!(),
2163 frontend_sock,
2164 register_error
2165 );
2166 return Err(AcceptError::RegisterError);
2167 }
2168
2169 let public_address: SocketAddr = match owned.config.public_address {
2170 Some(pub_addr) => pub_addr.into(),
2171 None => owned.config.address.into(),
2172 };
2173
2174 let session = HttpSession::new(
2175 Duration::from_secs(owned.config.back_timeout as u64),
2176 Duration::from_secs(owned.config.connect_timeout as u64),
2177 Duration::from_secs(owned.config.front_timeout as u64),
2178 Duration::from_secs(owned.config.request_timeout as u64),
2179 owned.config.expect_proxy,
2180 owned.config.h2c_prior_knowledge.unwrap_or(false),
2181 listener.clone(),
2182 Rc::downgrade(&self.pool),
2183 proxy,
2184 Rc::clone(&self.backends),
2187 public_address,
2188 frontend_sock,
2189 peer,
2190 session_token,
2191 wait_time,
2192 )?;
2193
2194 debug_assert_eq!(
2198 session.frontend_token, session_token,
2199 "session must own the frontend token it was created with"
2200 );
2201
2202 let session = Rc::new(RefCell::new(session));
2203 session_entry.insert(session);
2204 debug_assert_eq!(
2208 session_manager.slab.len(),
2209 slab_len_before + 1,
2210 "creating a session must occupy exactly one new slab slot"
2211 );
2212
2213 Ok(())
2214 }
2215}
2216
2217impl L7Proxy for HttpProxy {
2218 fn kind(&self) -> ListenerType {
2219 ListenerType::Http
2220 }
2221
2222 fn register_socket(
2223 &self,
2224 source: &mut TcpStream,
2225 token: Token,
2226 interest: Interest,
2227 ) -> Result<(), std::io::Error> {
2228 self.registry.register(source, token, interest)
2229 }
2230
2231 fn add_session(&self, session: Rc<RefCell<dyn ProxySession>>) -> Token {
2232 let mut session_manager = self.sessions.borrow_mut();
2233 let len_before = session_manager.slab.len();
2234 let entry = session_manager.slab.vacant_entry();
2235 let token = Token(entry.key());
2236 let _entry = entry.insert(session);
2237 debug_assert_eq!(
2240 session_manager.slab.len(),
2241 len_before + 1,
2242 "add_session must occupy exactly one new slab slot"
2243 );
2244 debug_assert!(
2245 session_manager.slab.contains(token.0),
2246 "the returned token must index the freshly inserted session"
2247 );
2248 token
2249 }
2250
2251 fn remove_session(&self, token: Token) -> bool {
2252 let mut sessions = self.sessions.borrow_mut();
2253 let was_present = sessions.slab.contains(token.0);
2254 let len_before = sessions.slab.len();
2255 sessions.untrack_all_cluster_ip(token);
2261 let removed = sessions.slab.try_remove(token.0).is_some();
2262 debug_assert_eq!(
2266 removed, was_present,
2267 "try_remove reports presence iff the slot was occupied"
2268 );
2269 debug_assert_eq!(
2270 sessions.slab.len(),
2271 len_before - removed as usize,
2272 "slab len drops by exactly one iff a session was removed"
2273 );
2274 debug_assert!(
2275 !sessions.slab.contains(token.0),
2276 "the slot must be free after remove_session"
2277 );
2278 removed
2279 }
2280
2281 fn clusters(&self) -> &HashMap<ClusterId, Cluster> {
2282 &self.clusters
2283 }
2284
2285 fn sessions(&self) -> Rc<RefCell<SessionManager>> {
2286 self.sessions.clone()
2287 }
2288}
2289
2290pub mod testing {
2291 use crate::testing::*;
2292
2293 pub fn start_http_worker(
2295 config: HttpListenerConfig,
2296 channel: ProxyChannel,
2297 max_buffers: usize,
2298 buffer_size: usize,
2299 ) -> anyhow::Result<()> {
2300 let address = config.address.into();
2301
2302 let ServerParts {
2303 event_loop,
2304 registry,
2305 sessions,
2306 pool,
2307 backends,
2308 client_scm_socket: _,
2309 server_scm_socket,
2310 server_config,
2311 } = prebuild_server(max_buffers, buffer_size, true)?;
2312
2313 let token = {
2314 let mut sessions = sessions.borrow_mut();
2315 let entry = sessions.slab.vacant_entry();
2316 let key = entry.key();
2317 let _ = entry.insert(Rc::new(RefCell::new(ListenSession {
2318 protocol: Protocol::HTTPListen,
2319 })));
2320 Token(key)
2321 };
2322
2323 let mut proxy = HttpProxy::new(registry, sessions.clone(), pool.clone(), backends.clone());
2324 proxy
2325 .add_listener(config, token)
2326 .with_context(|| "Failed at creating adding the listener")?;
2327 proxy
2328 .activate_listener(&address, None)
2329 .with_context(|| "Failed at creating activating the listener")?;
2330
2331 let mut server = Server::new(
2332 event_loop,
2333 channel,
2334 server_scm_socket,
2335 sessions,
2336 pool,
2337 backends,
2338 Some(proxy),
2339 None,
2340 None,
2341 server_config,
2342 None,
2343 false,
2344 )
2345 .with_context(|| "Failed at creating server")?;
2346
2347 debug!("{} starting event loop", log_module_context!());
2348 server.run();
2349 debug!("{} ending event loop", log_module_context!());
2350 Ok(())
2351 }
2352}
2353
2354#[cfg(test)]
2355mod tests {
2356 extern crate tiny_http;
2357
2358 use std::{
2359 io::{Read, Write},
2360 net::TcpStream,
2361 str,
2362 sync::{Arc, Barrier},
2363 thread,
2364 time::Duration,
2365 };
2366
2367 use sozu_command::proto::command::SocketAddress;
2368
2369 use super::{testing::start_http_worker, *};
2370 use crate::sozu_command::{
2371 channel::Channel,
2372 config::ListenerBuilder,
2373 proto::command::{
2374 CustomHttpAnswers, LoadBalancingParams, PathRule, RulePosition, SoftStop,
2375 WorkerRequest, request::RequestType,
2376 },
2377 response::{Backend, HttpFrontend},
2378 };
2379
2380 #[test]
2397 fn get_h2_flood_config_clamps_a_zero_threshold_that_bypassed_config_load() {
2398 let mut config = ListenerBuilder::new_http(SocketAddress::new_v4(127, 0, 0, 1, 8080))
2399 .to_http(None)
2400 .expect("could not create listener config");
2401 config.h2_max_rst_stream_per_window = Some(0);
2404 config.h2_max_glitch_count = Some(0);
2405
2406 let listener =
2407 HttpListener::new(config, Token(0)).expect("could not build the HTTP listener");
2408 let flood_config = listener.get_h2_flood_config();
2409
2410 assert!(
2411 flood_config.max_rst_stream_per_window() >= 1,
2412 "max_rst_stream_per_window must be clamped to >= 1, got {}",
2413 flood_config.max_rst_stream_per_window()
2414 );
2415 assert!(
2416 flood_config.max_glitch_count() >= 1,
2417 "max_glitch_count must be clamped to >= 1, got {}",
2418 flood_config.max_glitch_count()
2419 );
2420 }
2421
2422 #[test]
2431 fn get_h2_flood_config_maps_every_listener_knob_to_its_own_threshold() {
2432 let mut config = ListenerBuilder::new_http(SocketAddress::new_v4(127, 0, 0, 1, 8080))
2433 .to_http(None)
2434 .expect("could not create listener config");
2435 config.h2_max_rst_stream_per_window = Some(101);
2436 config.h2_max_ping_per_window = Some(102);
2437 config.h2_max_settings_per_window = Some(103);
2438 config.h2_max_empty_data_per_window = Some(104);
2439 config.h2_max_window_update_stream0_per_window = Some(105);
2440 config.h2_max_continuation_frames = Some(106);
2441 config.h2_max_glitch_count = Some(107);
2442 config.h2_max_rst_stream_lifetime = Some(108);
2443 config.h2_max_rst_stream_abusive_lifetime = Some(109);
2444 config.h2_max_rst_stream_emitted_lifetime = Some(110);
2445 config.h2_max_header_list_size = Some(111);
2446 config.h2_max_header_table_size = Some(112);
2447 config.h2_max_header_fields = Some(113);
2448 config.h2_stream_refusal_percent = Some(114);
2449
2450 let listener =
2451 HttpListener::new(config, Token(0)).expect("could not build the HTTP listener");
2452 let flood = listener.get_h2_flood_config();
2453
2454 assert_eq!(flood.max_rst_stream_per_window(), 101);
2455 assert_eq!(flood.max_ping_per_window(), 102);
2456 assert_eq!(flood.max_settings_per_window(), 103);
2457 assert_eq!(flood.max_empty_data_per_window(), 104);
2458 assert_eq!(flood.max_window_update_stream0_per_window(), 105);
2459 assert_eq!(flood.max_continuation_frames(), 106);
2460 assert_eq!(flood.max_glitch_count(), 107);
2461 assert_eq!(flood.max_rst_stream_lifetime(), 108);
2462 assert_eq!(flood.max_rst_stream_abusive_lifetime(), 109);
2463 assert_eq!(flood.max_rst_stream_emitted_lifetime(), 110);
2464 assert_eq!(flood.max_header_list_size(), 111);
2465 assert_eq!(flood.max_header_table_size(), 112);
2466 assert_eq!(flood.max_header_fields(), 113);
2467 assert_eq!(flood.stream_refusal_percent(), 114);
2468 }
2469
2470 #[test]
2471 fn round_trip() {
2472 setup_test_logger!();
2473 let front_port = crate::testing::provide_port();
2474 let backend_server = Arc::new(
2475 tiny_http::Server::http("127.0.0.1:0").expect("could not create tiny_http server"),
2476 );
2477 let backend_port = backend_server
2478 .server_addr()
2479 .to_ip()
2480 .expect("tiny_http server should bind to IP address")
2481 .port();
2482
2483 let barrier = Arc::new(Barrier::new(2));
2484
2485 let config = ListenerBuilder::new_http(SocketAddress::new_v4(127, 0, 0, 1, front_port))
2486 .to_http(None)
2487 .expect("could not create listener config");
2488
2489 let (mut command, channel) =
2490 Channel::generate(1000, 10000).expect("should create a channel");
2491
2492 thread::scope(|s| {
2493 let backend_handle = backend_server.clone();
2494 let barrier_clone = barrier.to_owned();
2495 s.spawn(move || {
2496 setup_test_logger!();
2497 start_server(&backend_handle, barrier_clone);
2498 });
2499 barrier.wait();
2500
2501 s.spawn(move || {
2502 setup_test_logger!();
2503 start_http_worker(config, channel, 10, 16384)
2504 .expect("could not start the http server");
2505 });
2506
2507 let front = RequestHttpFrontend {
2508 cluster_id: Some("cluster_1".to_owned()),
2509 address: SocketAddress::new_v4(127, 0, 0, 1, front_port),
2510 hostname: "localhost".to_owned(),
2511 path: PathRule::prefix("/".to_owned()),
2512 ..Default::default()
2513 };
2514 command
2516 .write_message(&WorkerRequest {
2517 id: "ID_CLUSTER".to_owned(),
2518 content: RequestType::AddCluster(Cluster {
2519 cluster_id: "cluster_1".to_owned(),
2520 ..Default::default()
2521 })
2522 .into(),
2523 })
2524 .expect("could not send AddCluster");
2525 command
2526 .write_message(&WorkerRequest {
2527 id: "ID_ABCD".to_owned(),
2528 content: RequestType::AddHttpFrontend(front).into(),
2529 })
2530 .expect("could not send AddHttpFrontend");
2531 let backend = Backend {
2532 cluster_id: "cluster_1".into(),
2533 backend_id: "cluster_1-0".to_owned(),
2534 address: SocketAddress::new_v4(127, 0, 0, 1, backend_port).into(),
2535 load_balancing_parameters: Some(LoadBalancingParams::default()),
2536 sticky_id: None,
2537 backup: None,
2538 unix_path: None,
2539 };
2540 command
2541 .write_message(&WorkerRequest {
2542 id: "ID_EFGH".to_owned(),
2543 content: RequestType::AddBackend(backend.to_add_backend()).into(),
2544 })
2545 .expect("could not send AddBackend");
2546
2547 println!("test received: {:?}", command.read_message());
2548 println!("test received: {:?}", command.read_message());
2549 println!("test received: {:?}", command.read_message());
2550
2551 let mut client =
2552 TcpStream::connect(("127.0.0.1", front_port)).expect("could not connect to sozu");
2553
2554 client
2555 .set_read_timeout(Some(Duration::new(1, 0)))
2556 .expect("could not set read timeout");
2557 let request = format!(
2558 "GET / HTTP/1.1\r\nHost: localhost:{front_port}\r\nConnection: Close\r\n\r\n"
2559 );
2560 let w = client.write(request.as_bytes());
2561 println!("http client write: {w:?}");
2562
2563 barrier.wait();
2564 let mut buffer = [0; 4096];
2565 let mut index = 0;
2566
2567 let expected_len = 191;
2570
2571 loop {
2572 assert!(index <= expected_len);
2573 if index == expected_len {
2574 break;
2575 }
2576
2577 let r = client.read(&mut buffer[index..]);
2578 println!("http client read: {r:?}");
2579 match r {
2580 Err(e) => panic!("client request should not fail. Error: {e:?}"),
2581 Ok(sz) => {
2582 index += sz;
2583 }
2584 }
2585 }
2586 println!(
2587 "Response: {}",
2588 str::from_utf8(&buffer[..index]).expect("could not make string from buffer")
2589 );
2590
2591 command
2593 .write_message(&WorkerRequest {
2594 id: "ID_STOP".to_owned(),
2595 content: RequestType::SoftStop(SoftStop {}).into(),
2596 })
2597 .expect("could not send SoftStop");
2598 backend_server.unblock();
2600 });
2601 }
2602
2603 #[test]
2604 fn keep_alive() {
2605 setup_test_logger!();
2606 let front_port = crate::testing::provide_port();
2607 let backend_server = Arc::new(
2608 tiny_http::Server::http("127.0.0.1:0").expect("could not create tiny_http server"),
2609 );
2610 let backend_port = backend_server
2611 .server_addr()
2612 .to_ip()
2613 .expect("tiny_http server should bind to IP address")
2614 .port();
2615
2616 let barrier = Arc::new(Barrier::new(2));
2617
2618 let config = ListenerBuilder::new_http(SocketAddress::new_v4(127, 0, 0, 1, front_port))
2619 .to_http(None)
2620 .expect("could not create listener config");
2621
2622 let (mut command, channel) =
2623 Channel::generate(1000, 10000).expect("should create a channel");
2624
2625 thread::scope(|s| {
2626 let backend_handle = backend_server.clone();
2627 let barrier_clone = barrier.to_owned();
2628 s.spawn(move || {
2629 setup_test_logger!();
2630 start_server(&backend_handle, barrier_clone);
2631 });
2632 barrier.wait();
2633
2634 s.spawn(move || {
2635 setup_test_logger!();
2636 start_http_worker(config, channel, 10, 16384)
2637 .expect("could not start the http server");
2638 });
2639
2640 let front = RequestHttpFrontend {
2641 address: SocketAddress::new_v4(127, 0, 0, 1, front_port),
2642 hostname: "localhost".to_owned(),
2643 path: PathRule::prefix("/".to_owned()),
2644 cluster_id: Some("cluster_1".to_owned()),
2645 ..Default::default()
2646 };
2647 command
2649 .write_message(&WorkerRequest {
2650 id: "ID_CLUSTER".to_owned(),
2651 content: RequestType::AddCluster(Cluster {
2652 cluster_id: "cluster_1".to_owned(),
2653 ..Default::default()
2654 })
2655 .into(),
2656 })
2657 .expect("could not send AddCluster");
2658 command
2659 .write_message(&WorkerRequest {
2660 id: "ID_ABCD".to_owned(),
2661 content: RequestType::AddHttpFrontend(front).into(),
2662 })
2663 .expect("could not send AddHttpFrontend");
2664 let backend = Backend {
2665 address: SocketAddress::new_v4(127, 0, 0, 1, backend_port).into(),
2666 backend_id: "cluster_1-0".to_owned(),
2667 backup: None,
2668 cluster_id: "cluster_1".into(),
2669 load_balancing_parameters: Some(LoadBalancingParams::default()),
2670 sticky_id: None,
2671 unix_path: None,
2672 };
2673 command
2674 .write_message(&WorkerRequest {
2675 id: "ID_EFGH".to_owned(),
2676 content: RequestType::AddBackend(backend.to_add_backend()).into(),
2677 })
2678 .expect("could not send AddBackend");
2679
2680 println!("test received: {:?}", command.read_message());
2681 println!("test received: {:?}", command.read_message());
2682 println!("test received: {:?}", command.read_message());
2683
2684 let mut client =
2685 TcpStream::connect(("127.0.0.1", front_port)).expect("could not connect to sozu");
2686 client
2687 .set_read_timeout(Some(Duration::new(5, 0)))
2688 .expect("could not set read timeout");
2689
2690 let expected_len = 191;
2693
2694 let request = format!("GET / HTTP/1.1\r\nHost: localhost:{front_port}\r\n\r\n");
2695 let w = client
2696 .write(request.as_bytes())
2697 .expect("could not write first request");
2698 println!("http client write: {w:?}");
2699 barrier.wait();
2700
2701 let mut buffer = [0; 4096];
2702 let mut index = 0;
2703
2704 loop {
2705 assert!(index <= expected_len);
2706 if index == expected_len {
2707 break;
2708 }
2709
2710 let r = client.read(&mut buffer[index..]);
2711 println!("http client read: {r:?}");
2712 match r {
2713 Err(e) => panic!("client request should not fail. Error: {e:?}"),
2714 Ok(sz) => {
2715 index += sz;
2716 }
2717 }
2718 }
2719
2720 println!(
2721 "Response: {}",
2722 str::from_utf8(&buffer[..index]).expect("could not make string from buffer")
2723 );
2724
2725 println!("first request ended, will send second one");
2726 let request2 = format!("GET / HTTP/1.1\r\nHost: localhost:{front_port}\r\n\r\n");
2727 let w2 = client.write(request2.as_bytes());
2728 println!("http client write: {w2:?}");
2729 barrier.wait();
2730
2731 let mut buffer2 = [0; 4096];
2732 let mut index = 0;
2733
2734 loop {
2735 assert!(index <= expected_len);
2736 if index == expected_len {
2737 break;
2738 }
2739
2740 let r2 = client.read(&mut buffer2[index..]);
2741 println!("http client read: {r2:?}");
2742 match r2 {
2743 Err(e) => panic!("client request should not fail. Error: {e:?}"),
2744 Ok(sz) => {
2745 index += sz;
2746 }
2747 }
2748 }
2749 println!(
2750 "Response: {}",
2751 str::from_utf8(&buffer2[..index]).expect("could not make string from buffer")
2752 );
2753
2754 command
2756 .write_message(&WorkerRequest {
2757 id: "ID_STOP".to_owned(),
2758 content: RequestType::SoftStop(SoftStop {}).into(),
2759 })
2760 .expect("could not send SoftStop");
2761 backend_server.unblock();
2763 });
2764 }
2765
2766 use self::tiny_http::Response;
2767
2768 fn start_server(server: &tiny_http::Server, barrier: Arc<Barrier>) {
2769 let addr = server.server_addr();
2770 info!("starting web server on {:?}", addr);
2771 barrier.wait();
2772
2773 for request in server.incoming_requests() {
2774 info!(
2775 "backend web server got request -> method: {:?}, url: {:?}, headers: {:?}",
2776 request.method(),
2777 request.url(),
2778 request.headers()
2779 );
2780
2781 let response = Response::from_string("hello world");
2782 request
2783 .respond(response)
2784 .expect("could not respond to request");
2785 info!("backend web server sent response");
2786 barrier.wait();
2787 info!("server session stopped");
2788 }
2789
2790 println!("server on {addr:?} closed");
2791 }
2792
2793 #[test]
2794 fn frontend_from_request_test() {
2795 let cluster_id1 = ClusterId::from("cluster_1");
2796 let cluster_id2 = ClusterId::from("cluster_2");
2797 let cluster_id3 = ClusterId::from("cluster_3");
2798 let uri1 = "/".to_owned();
2799 let uri2 = "/yolo".to_owned();
2800 let uri3 = "/yolo/swag".to_owned();
2801
2802 let mut fronts = Router::new();
2803 fronts
2804 .add_http_front(&HttpFrontend {
2805 address: "0.0.0.0:80".parse().unwrap(),
2806 hostname: "lolcatho.st".to_owned(),
2807 method: None,
2808 path: PathRule::prefix(uri1),
2809 position: RulePosition::Tree,
2810 cluster_id: Some(cluster_id1),
2811 tags: None,
2812 redirect: None,
2813 redirect_scheme: None,
2814 redirect_template: None,
2815 rewrite_host: None,
2816 rewrite_path: None,
2817 rewrite_port: None,
2818 required_auth: None,
2819 headers: Vec::new(),
2820 hsts: None,
2821 header_predicates: Vec::new(),
2822 })
2823 .expect("Could not add http frontend");
2824 fronts
2825 .add_http_front(&HttpFrontend {
2826 address: "0.0.0.0:80".parse().unwrap(),
2827 hostname: "lolcatho.st".to_owned(),
2828 method: None,
2829 path: PathRule::prefix(uri2),
2830 position: RulePosition::Tree,
2831 cluster_id: Some(cluster_id2),
2832 tags: None,
2833 redirect: None,
2834 redirect_scheme: None,
2835 redirect_template: None,
2836 rewrite_host: None,
2837 rewrite_path: None,
2838 rewrite_port: None,
2839 required_auth: None,
2840 headers: Vec::new(),
2841 hsts: None,
2842 header_predicates: Vec::new(),
2843 })
2844 .expect("Could not add http frontend");
2845 fronts
2846 .add_http_front(&HttpFrontend {
2847 address: "0.0.0.0:80".parse().unwrap(),
2848 hostname: "lolcatho.st".to_owned(),
2849 method: None,
2850 path: PathRule::prefix(uri3),
2851 position: RulePosition::Tree,
2852 cluster_id: Some(cluster_id3),
2853 tags: None,
2854 redirect: None,
2855 redirect_scheme: None,
2856 redirect_template: None,
2857 rewrite_host: None,
2858 rewrite_path: None,
2859 rewrite_port: None,
2860 required_auth: None,
2861 headers: Vec::new(),
2862 hsts: None,
2863 header_predicates: Vec::new(),
2864 })
2865 .expect("Could not add http frontend");
2866 fronts
2867 .add_http_front(&HttpFrontend {
2868 address: "0.0.0.0:80".parse().unwrap(),
2869 hostname: "other.domain".to_owned(),
2870 method: None,
2871 path: PathRule::prefix("/test".to_owned()),
2872 position: RulePosition::Tree,
2873 cluster_id: Some("cluster_1".into()),
2874 tags: None,
2875 redirect: None,
2876 redirect_scheme: None,
2877 redirect_template: None,
2878 rewrite_host: None,
2879 rewrite_path: None,
2880 rewrite_port: None,
2881 required_auth: None,
2882 headers: Vec::new(),
2883 hsts: None,
2884 header_predicates: Vec::new(),
2885 })
2886 .expect("Could not add http frontend");
2887
2888 let address = SocketAddress::new_v4(127, 0, 0, 1, 1030);
2889
2890 let default_config = ListenerBuilder::new_http(address)
2891 .to_http(None)
2892 .expect("Could not create default HTTP listener config");
2893
2894 let listener = HttpListener {
2895 parked_listener: None,
2896 nodelay_backlog: false,
2897 listener: None,
2898 address: address.into(),
2899 fronts,
2900 answers: Rc::new(RefCell::new(HttpAnswers::new(&BTreeMap::new()).unwrap())),
2901 config: default_config,
2902 token: Token(0),
2903 active: true,
2904 tags: BTreeMap::new(),
2905 };
2906
2907 let frontend1 = listener.frontend_from_request(
2908 "lolcatho.st",
2909 "/",
2910 &Method::Get,
2911 &crate::router::NoHeaders,
2912 );
2913 let frontend2 = listener.frontend_from_request(
2914 "lolcatho.st",
2915 "/test",
2916 &Method::Get,
2917 &crate::router::NoHeaders,
2918 );
2919 let frontend3 = listener.frontend_from_request(
2920 "lolcatho.st",
2921 "/yolo/test",
2922 &Method::Get,
2923 &crate::router::NoHeaders,
2924 );
2925 let frontend4 = listener.frontend_from_request(
2926 "lolcatho.st",
2927 "/yolo/swag",
2928 &Method::Get,
2929 &crate::router::NoHeaders,
2930 );
2931 let frontend5 =
2932 listener.frontend_from_request("domain", "/", &Method::Get, &crate::router::NoHeaders);
2933 assert_eq!(
2934 frontend1
2935 .expect("should find frontend")
2936 .cluster_id
2937 .as_deref(),
2938 Some("cluster_1")
2939 );
2940 assert_eq!(
2941 frontend2
2942 .expect("should find frontend")
2943 .cluster_id
2944 .as_deref(),
2945 Some("cluster_1")
2946 );
2947 assert_eq!(
2948 frontend3
2949 .expect("should find frontend")
2950 .cluster_id
2951 .as_deref(),
2952 Some("cluster_2")
2953 );
2954 assert_eq!(
2955 frontend4
2956 .expect("should find frontend")
2957 .cluster_id
2958 .as_deref(),
2959 Some("cluster_3")
2960 );
2961 assert!(frontend5.is_err());
2962 }
2963
2964 #[test]
2969 fn a_listener_with_an_unknown_forwarded_headers_value_is_refused() {
2970 let address = SocketAddress::new_v4(127, 0, 0, 1, 1041);
2971 let build = |forwarded_headers: Option<i32>| {
2972 let mut cfg = ListenerBuilder::new_http(address)
2973 .to_http(None)
2974 .expect("default HTTP listener config");
2975 cfg.forwarded_headers = forwarded_headers;
2976 HttpListener::new(cfg, Token(0))
2977 };
2978 assert!(build(Some(42)).is_err(), "an unknown mode must be refused");
2979 let listener = build(Some(ForwardedHeaders::Rfc7239 as i32)).expect("a known mode");
2980 assert_eq!(listener.get_forwarded_headers(), ForwardedHeaders::Rfc7239);
2981 }
2982
2983 #[test]
2984 fn h2_stream_idle_timeout_inherits_back_timeout() {
2985 let address = SocketAddress::new_v4(127, 0, 0, 1, 1040);
2986 let build = |back_timeout: u32, explicit: Option<u32>| -> HttpListener {
2987 let mut cfg = ListenerBuilder::new_http(address)
2988 .to_http(None)
2989 .expect("default HTTP listener config");
2990 cfg.back_timeout = back_timeout;
2991 cfg.h2_stream_idle_timeout_seconds = explicit;
2992 HttpListener::new(cfg, Token(0)).expect("build listener")
2993 };
2994
2995 assert_eq!(
2997 build(180, None).get_h2_stream_idle_timeout(),
2998 Duration::from_secs(180)
2999 );
3000
3001 assert_eq!(
3004 build(5, None).get_h2_stream_idle_timeout(),
3005 Duration::from_secs(30)
3006 );
3007
3008 assert_eq!(
3011 build(180, Some(10)).get_h2_stream_idle_timeout(),
3012 Duration::from_secs(10)
3013 );
3014 assert_eq!(
3015 build(5, Some(600)).get_h2_stream_idle_timeout(),
3016 Duration::from_secs(600)
3017 );
3018
3019 assert_eq!(
3021 build(180, Some(0)).get_h2_stream_idle_timeout(),
3022 Duration::from_secs(1)
3023 );
3024 }
3025
3026 #[test]
3047 fn a_listener_answers_reload_publishes_a_new_registry() {
3048 let address = SocketAddress::new_v4(127, 0, 0, 1, 1041);
3049 let config = ListenerBuilder::new_http(address)
3050 .to_http(None)
3051 .expect("default HTTP listener config");
3052 let mut listener = HttpListener::new(config, Token(0)).expect("build listener");
3053
3054 listener
3056 .answers
3057 .borrow_mut()
3058 .add_cluster_answers(
3059 "cluster_1",
3060 &BTreeMap::from([(
3061 "503".to_owned(),
3062 "HTTP/1.1 503 Service Unavailable\r\nContent-Length: 0\r\n\r\n".to_owned(),
3063 )]),
3064 )
3065 .expect("the cluster override must compile");
3066
3067 let captured = listener.get_answers().clone();
3069
3070 listener
3071 .update_config(&UpdateHttpListenerConfig {
3072 address,
3073 answers: BTreeMap::from([(
3074 "404".to_owned(),
3075 "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n".to_owned(),
3076 )]),
3077 ..Default::default()
3078 })
3079 .expect("the answers patch must apply");
3080
3081 assert!(
3082 !Rc::ptr_eq(&captured, listener.get_answers()),
3083 "a reload must publish a NEW answer registry: rewriting the captured \
3084 one reaches every request already in flight"
3085 );
3086 assert!(
3087 captured.borrow().cluster_answers.contains_key("cluster_1"),
3088 "the registry captured before the reload must keep its cluster \
3089 overrides: a request in flight would otherwise lose its custom \
3090 pages mid-response"
3091 );
3092 assert!(
3093 listener
3094 .get_answers()
3095 .borrow()
3096 .cluster_answers
3097 .contains_key("cluster_1"),
3098 "the published registry must carry the cluster overrides forward"
3099 );
3100 }
3101
3102 #[test]
3114 fn a_rejected_patch_leaves_the_listener_unchanged() {
3115 let address = SocketAddress::new_v4(127, 0, 0, 1, 1042);
3116 let config = ListenerBuilder::new_http(address)
3117 .to_http(None)
3118 .expect("default HTTP listener config");
3119 let mut listener = HttpListener::new(config, Token(0)).expect("build listener");
3120
3121 let valid_prefix = UpdateHttpListenerConfig {
3122 address,
3123 front_timeout: Some(99),
3124 sozu_id_header: Some("X-Edge-Id".to_owned()),
3125 forwarded_headers: Some(ForwardedHeaders::Rfc7239 as i32),
3126 h2_max_rst_stream_per_window: Some(7),
3127 ..Default::default()
3128 };
3129 let rejected = [
3130 UpdateHttpListenerConfig {
3131 answers: BTreeMap::from([(
3132 "404".to_owned(),
3133 "not a valid http response".to_owned(),
3134 )]),
3135 ..valid_prefix.clone()
3136 },
3137 UpdateHttpListenerConfig {
3138 http_answers: Some(CustomHttpAnswers {
3139 answer_503: Some("not a valid http response".to_owned()),
3140 ..Default::default()
3141 }),
3142 ..valid_prefix.clone()
3143 },
3144 ];
3145 for patch in rejected {
3146 let config_before = listener.config.clone();
3147 let answers_before = listener.get_answers().clone();
3148 let error = listener
3149 .update_config(&patch)
3150 .expect_err("an unparseable template must be refused");
3151 assert!(
3152 matches!(error, ListenerError::TemplateParse(..)),
3153 "expected TemplateParse, got {error:?}"
3154 );
3155 assert_eq!(
3156 listener.config, config_before,
3157 "a refused patch must leave the listener configuration unchanged"
3158 );
3159 assert!(
3160 Rc::ptr_eq(&answers_before, listener.get_answers()),
3161 "a refused patch must not publish a new answer registry"
3162 );
3163 }
3164
3165 listener
3166 .update_config(&valid_prefix)
3167 .expect("the valid prefix alone must apply");
3168 assert_eq!(listener.config.front_timeout, 99);
3169 assert_eq!(listener.config.sozu_id_header.as_deref(), Some("X-Edge-Id"));
3170 }
3171
3172 #[cfg(target_os = "linux")]
3183 #[test]
3184 fn closed_sessions_leave_their_sockets_to_close() {
3185 use std::os::fd::AsRawFd;
3186
3187 use crate::protocol::mux::test_support::epoll_watches;
3188
3189 let address = SocketAddress::new_v4(127, 0, 0, 1, crate::testing::provide_port());
3190 let parts = crate::testing::prebuild_server(4, 16_384, false)
3191 .expect("test server parts must build");
3192 let registry = parts
3193 .registry
3194 .try_clone()
3195 .expect("the test registry must clone");
3196 let sessions = parts.sessions.clone();
3197 let listener_token = {
3198 let mut sessions = sessions.borrow_mut();
3199 let entry = sessions.slab.vacant_entry();
3200 let key = entry.key();
3201 entry.insert(Rc::new(RefCell::new(crate::server::ListenSession {
3202 protocol: Protocol::HTTPListen,
3203 })));
3204 Token(key)
3205 };
3206 let mut proxy = HttpProxy::new(parts.registry, parts.sessions, parts.pool, parts.backends);
3207 proxy
3208 .add_listener(
3209 ListenerBuilder::new_http(address)
3210 .to_http(None)
3211 .expect("test listener config must build"),
3212 listener_token,
3213 )
3214 .expect("test listener must register");
3215 let proxy = Rc::new(RefCell::new(proxy));
3216
3217 let peer_listener =
3218 std::net::TcpListener::bind("127.0.0.1:0").expect("bind the test peer listener");
3219 let _client = TcpStream::connect(
3220 peer_listener
3221 .local_addr()
3222 .expect("the test peer listener has an address"),
3223 )
3224 .expect("connect the test client");
3225 let (accepted, peer) = peer_listener.accept().expect("accept the test client");
3226 accepted
3227 .set_nonblocking(true)
3228 .expect("the front socket must go non-blocking");
3229 let front = mio::net::TcpStream::from_std(accepted);
3230 let front_fd = front.as_raw_fd();
3231
3232 let session_token = sessions.borrow().slab.vacant_key();
3233 proxy
3234 .borrow_mut()
3235 .create_session(
3236 front,
3237 peer,
3238 ListenToken(listener_token.0),
3239 Duration::ZERO,
3240 proxy.clone(),
3241 )
3242 .expect("the session must be created");
3243 assert!(
3244 sessions.borrow().slab.contains(session_token),
3245 "the session takes the slot that was vacant"
3246 );
3247 assert!(
3248 epoll_watches(®istry, front_fd),
3249 "precondition: the front socket is in the epoll set"
3250 );
3251
3252 let session = sessions.borrow_mut().slab.remove(session_token);
3254 session.borrow_mut().close();
3255 assert!(
3256 epoll_watches(®istry, front_fd),
3257 "HttpSession::close must not deregister a front socket it is about \
3258 to close: the close removes it, the EPOLL_CTL_DEL is a wasted syscall"
3259 );
3260
3261 drop(session);
3262 assert!(
3263 !epoll_watches(®istry, front_fd),
3264 "dropping the session closes the front socket's only descriptor, \
3265 which must take it out of the epoll set"
3266 );
3267 drop(parts.event_loop);
3268 }
3269
3270 #[test]
3285 fn a_steady_state_accepted_socket_inherits_nodelay_and_its_peer_address() {
3286 let address = SocketAddress::new_v4(127, 0, 0, 1, crate::testing::provide_port());
3287 let parts = crate::testing::prebuild_server(4, 16_384, false)
3288 .expect("test server parts must build");
3289 let listener_token = {
3290 let mut sessions = parts.sessions.borrow_mut();
3291 let entry = sessions.slab.vacant_entry();
3292 let key = entry.key();
3293 entry.insert(Rc::new(RefCell::new(crate::server::ListenSession {
3294 protocol: Protocol::HTTPListen,
3295 })));
3296 Token(key)
3297 };
3298 let mut proxy = HttpProxy::new(parts.registry, parts.sessions, parts.pool, parts.backends);
3299 proxy
3300 .add_listener(
3301 ListenerBuilder::new_http(address)
3302 .to_http(None)
3303 .expect("test listener config must build"),
3304 listener_token,
3305 )
3306 .expect("test listener must register");
3307 proxy
3308 .activate_listener(&address.into(), None)
3309 .expect("test listener must activate");
3310
3311 assert!(
3313 matches!(
3314 proxy.accept(ListenToken(listener_token.0)),
3315 Err(AcceptError::WouldBlock)
3316 ),
3317 "precondition: nothing is queued on a freshly bound listener"
3318 );
3319
3320 let client =
3321 TcpStream::connect(SocketAddr::from(address)).expect("connect the test client");
3322 let (accepted, peer) = proxy
3323 .accept(ListenToken(listener_token.0))
3324 .expect("the completed connection must be accepted");
3325
3326 assert_eq!(
3327 socket2::SockRef::from(&accepted).tcp_nodelay().ok(),
3328 Some(true),
3329 "a socket accepted in steady state must inherit TCP_NODELAY from its listener"
3330 );
3331 assert_eq!(
3332 peer,
3333 client.local_addr().expect("the test client has an address"),
3334 "the peer address handed over must be the client's"
3335 );
3336 drop(parts.event_loop);
3337 }
3338}
3339
3340#[cfg(test)]
3341mod listener_sibling_tests {
3342 use sozu_command::{
3343 config::ListenerBuilder,
3344 proto::command::{HstsConfig, PathRule, RequestHttpFrontend, RulePosition, SocketAddress},
3345 };
3346
3347 use super::*;
3348 use crate::testing::{ServerParts, prebuild_server, provide_port};
3349
3350 fn proxy() -> HttpProxy {
3351 let ServerParts {
3352 registry,
3353 sessions,
3354 pool,
3355 backends,
3356 ..
3357 } = prebuild_server(16, 16384, false).expect("could not prebuild a test server");
3358 HttpProxy::new(registry, sessions, pool, backends)
3359 }
3360
3361 fn add(proxy: &mut HttpProxy, address: SocketAddress, interface: Option<&str>, token: usize) {
3362 let config = ListenerBuilder::new_http(address)
3363 .with_interface(interface)
3364 .to_http(None)
3365 .expect("could not build the listener config");
3366 proxy
3367 .add_listener(config, Token(token))
3368 .expect("could not add the listener");
3369 }
3370
3371 fn front(address: SocketAddress) -> RequestHttpFrontend {
3372 RequestHttpFrontend {
3373 cluster_id: Some("cluster-a".to_owned()),
3374 address,
3375 hostname: "example.com".to_owned(),
3376 path: PathRule::prefix("/".to_owned()),
3377 ..Default::default()
3378 }
3379 }
3380
3381 fn routes(proxy: &HttpProxy, token: usize) -> bool {
3382 proxy.listeners[&Token(token)]
3383 .borrow()
3384 .fronts
3385 .lookup("example.com", "/", &Method::Get)
3386 .is_ok()
3387 }
3388
3389 #[test]
3395 fn a_listener_added_next_to_a_sibling_takes_over_its_frontends() {
3396 let mut proxy = proxy();
3397 let address = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
3398 add(&mut proxy, address, None, 0);
3399 proxy
3400 .add_http_frontend(front(address))
3401 .expect("add the frontend");
3402 add(&mut proxy, address, Some("lo"), 1);
3403
3404 assert!(routes(&proxy, 0));
3405 assert!(
3406 routes(&proxy, 1),
3407 "the listener added after the frontend must route it too"
3408 );
3409
3410 proxy
3411 .remove_http_frontend(front(address))
3412 .expect("remove the frontend");
3413 assert!(!routes(&proxy, 0));
3414 assert!(!routes(&proxy, 1));
3415 }
3416
3417 #[test]
3418 fn retained_frontends_are_not_replayed_when_a_sibling_already_has_them() {
3419 let mut proxy = proxy();
3420 let address = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
3421 add(&mut proxy, address, None, 0);
3422 proxy
3423 .add_http_frontend(front(address))
3424 .expect("add the frontend");
3425 let sibling = ListenerBuilder::new_http(address)
3426 .with_interface(Some("lo"))
3427 .to_http(None)
3428 .expect("build sibling listener config");
3429
3430 proxy
3431 .add_listener_with_retained_frontends(sibling, Token(1), vec![front(address)])
3432 .expect("the sibling must clone routes without replaying duplicates");
3433
3434 assert!(routes(&proxy, 0));
3435 assert!(routes(&proxy, 1));
3436 }
3437
3438 #[test]
3439 fn failed_retained_frontend_replay_discards_only_the_new_listener() {
3440 let mut proxy = proxy();
3441 let kept_address = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
3442 let rejected_address = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
3443 add(&mut proxy, kept_address, None, 0);
3444 let rejected = ListenerBuilder::new_http(rejected_address)
3445 .to_http(None)
3446 .expect("build rejected listener config");
3447 let invalid_plaintext_hsts = RequestHttpFrontend {
3448 hsts: Some(HstsConfig {
3449 enabled: Some(true),
3450 ..Default::default()
3451 }),
3452 ..front(rejected_address)
3453 };
3454
3455 assert!(
3456 proxy
3457 .add_listener_with_retained_frontends(
3458 rejected,
3459 Token(1),
3460 vec![invalid_plaintext_hsts],
3461 )
3462 .is_err()
3463 );
3464 assert!(proxy.listeners.contains_key(&Token(0)));
3465 assert!(!proxy.listeners.contains_key(&Token(1)));
3466 }
3467
3468 #[test]
3472 fn removing_a_frontend_reaches_every_listener_and_reports_a_failure() {
3473 let mut proxy = proxy();
3474 let address = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
3475 add(&mut proxy, address, None, 0);
3476 add(&mut proxy, address, Some("lo"), 1);
3477 let pre = RequestHttpFrontend {
3480 position: RulePosition::Pre.into(),
3481 ..front(address)
3482 };
3483 let frontend = pre
3484 .clone()
3485 .to_frontend()
3486 .expect("the test frontend must convert");
3487 proxy.listeners[&Token(1)]
3488 .borrow_mut()
3489 .add_http_front(frontend)
3490 .expect("add the route to one listener");
3491
3492 assert!(
3493 proxy.remove_http_frontend(pre).is_err(),
3494 "the listener that lacked the route must report it"
3495 );
3496 assert!(
3497 !routes(&proxy, 1),
3498 "the failure on one listener must not spare its sibling's route"
3499 );
3500 }
3501}