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 http::{
43 answers::HttpAnswers,
44 parser::{Method, hostname_and_port},
45 },
46 mux::{self, Mux, MuxClear},
47 proxy_protocol::expect::ExpectProxyProtocol,
48 },
49 router::{RequestHeaders, RouteResult, Router},
50 server::{ListenToken, SessionManager},
51 socket::server_bind,
52 timer::TimeoutContainer,
53};
54
55#[derive(PartialEq, Eq)]
56pub enum SessionStatus {
57 Normal,
58 DefaultAnswer,
59}
60
61StateMachineBuilder! {
62 enum HttpStateMachine impl SessionState {
68 Expect(ExpectProxyProtocol<TcpStream>),
69 Mux(MuxClear),
70 WebSocket(Pipe<crate::socket::SessionTcpStream, HttpListener>),
71 }
72}
73
74macro_rules! log_module_context {
80 () => {{
81 let (open, reset, _, _, _) = sozu_command::logging::ansi_palette();
82 format!("{open}HTTP{reset}\t >>>", open = open, reset = reset)
83 }};
84}
85
86macro_rules! log_context {
92 ($self:expr) => {{
93 let (open, reset, grey, gray, white) = sozu_command::logging::ansi_palette();
94 format!(
95 "{open}HTTP{reset}\t{grey}Session{reset}({gray}frontend{reset}={white}{frontend}{reset})\t >>>",
96 open = open,
97 reset = reset,
98 grey = grey,
99 gray = gray,
100 white = white,
101 frontend = $self.frontend_token.0,
102 )
103 }};
104}
105
106pub struct HttpSession {
110 configured_backend_timeout: Duration,
111 configured_connect_timeout: Duration,
112 configured_frontend_timeout: Duration,
113 frontend_token: Token,
114 last_event: Instant,
115 listener: Rc<RefCell<HttpListener>>,
116 metrics: SessionMetrics,
117 pool: Weak<RefCell<Pool>>,
118 proxy: Rc<RefCell<HttpProxy>>,
119 state: HttpStateMachine,
120 has_been_closed: bool,
121}
122
123impl HttpSession {
124 #[allow(clippy::too_many_arguments)]
125 pub fn new(
126 configured_backend_timeout: Duration,
127 configured_connect_timeout: Duration,
128 configured_frontend_timeout: Duration,
129 configured_request_timeout: Duration,
130 expect_proxy: bool,
131 listener: Rc<RefCell<HttpListener>>,
132 pool: Weak<RefCell<Pool>>,
133 proxy: Rc<RefCell<HttpProxy>>,
134 backends: Rc<RefCell<BackendMap>>,
135 public_address: SocketAddr,
136 sock: TcpStream,
137 peer: SocketAddr,
138 token: Token,
139 wait_time: Duration,
140 ) -> Result<Self, AcceptError> {
141 let request_id = Ulid::generate();
142 let container_frontend_timeout = TimeoutContainer::new(configured_request_timeout, token);
143
144 let state = if expect_proxy {
145 trace!("{} starting in expect proxy state", log_module_context!());
146 gauge_add!(names::protocol::PROXY_EXPECT, 1);
147
148 HttpStateMachine::Expect(ExpectProxyProtocol::new(
149 container_frontend_timeout,
150 sock,
151 token,
152 request_id,
153 ))
154 } else {
155 gauge_add!(names::protocol::HTTP, 1);
156 let session_address = Some(peer);
160 let session_ulid = rusty_ulid::Ulid::generate();
161 let sock = crate::socket::SessionTcpStream::new(sock, session_ulid, session_address);
162
163 let frontend = mux::Connection::new_h1_server(
164 session_ulid,
165 sock,
166 container_frontend_timeout.duration(),
167 );
168 let router = mux::Router::new(configured_backend_timeout, configured_connect_timeout);
169 let mut context = mux::Context::new(
170 session_ulid,
171 pool.clone(),
172 listener.clone(),
173 session_address,
174 public_address,
175 );
176 context
177 .create_stream(request_id, 1 << 16)
178 .ok_or(AcceptError::BufferCapacityReached)?;
179 HttpStateMachine::Mux(Mux {
180 configured_frontend_timeout,
181 frontend_token: token,
182 frontend,
183 router,
184 context,
185 session_ulid,
186 timeouts: mux::MuxTimeouts::new(container_frontend_timeout),
190 backend_registry: mux::BackendRegistry::default(),
191 backends,
192 })
193 };
194
195 debug_assert_eq!(
202 state.marker() as u8,
203 if expect_proxy {
204 StateMarker::Expect as u8
205 } else {
206 StateMarker::Mux as u8
207 },
208 "constructed state must match the expect_proxy branch"
209 );
210 debug_assert!(
214 !state.failed(),
215 "a newly created session must not start in FailedUpgrade"
216 );
217
218 let metrics = SessionMetrics::new(Some(wait_time));
219 let session = HttpSession {
220 configured_backend_timeout,
221 configured_connect_timeout,
222 configured_frontend_timeout,
223 frontend_token: token,
224 has_been_closed: false,
225 last_event: Instant::now(),
226 listener,
227 metrics,
228 pool,
229 proxy,
230 state,
231 };
232 debug_assert_eq!(
233 session.frontend_token, token,
234 "frontend token must be the slab token used for registration"
235 );
236 #[cfg(debug_assertions)]
237 session.check_invariants();
238 Ok(session)
239 }
240
241 #[cfg(debug_assertions)]
254 fn check_invariants(&self) {
255 let marker = self.state.marker();
256 debug_assert!(
257 matches!(
258 marker,
259 StateMarker::Expect | StateMarker::Mux | StateMarker::WebSocket
260 ),
261 "session marker must be a legal H1 stage (Expect/Mux/WebSocket), got {marker:?}"
262 );
263 debug_assert!(
267 !self.state.failed() || self.has_been_closed,
268 "FailedUpgrade state must be reaped by close(), never left live"
269 );
270 }
271
272 pub fn upgrade(&mut self) -> SessionIsToBeClosed {
273 debug!("{} upgrade", log_context!(self));
274 let from_marker = self.state.marker();
280 let new_state = match self.state.take() {
281 HttpStateMachine::Mux(mux) => self.upgrade_mux(mux),
282 HttpStateMachine::Expect(expect) => self.upgrade_expect(expect),
283 HttpStateMachine::WebSocket(ws) => self.upgrade_websocket(ws),
284 HttpStateMachine::FailedUpgrade(_) => {
285 error!(
289 "{} upgrade called on FailedUpgrade state; closing session",
290 log_context!(self)
291 );
292 None
293 }
294 };
295
296 match new_state {
297 Some(state) => {
298 debug_assert!(
302 matches!(
303 (from_marker, state.marker()),
304 (StateMarker::Expect, StateMarker::Mux)
305 | (StateMarker::Mux, StateMarker::WebSocket)
306 | (StateMarker::WebSocket, StateMarker::WebSocket)
307 ),
308 "illegal protocol-upgrade transition {from_marker:?} -> {:?}",
309 state.marker()
310 );
311 debug_assert!(
312 !state.failed(),
313 "a successful upgrade must not install a FailedUpgrade state"
314 );
315 self.state = state;
316 #[cfg(debug_assertions)]
317 self.check_invariants();
318 false
319 }
320 None => {
322 debug_assert!(
326 self.state.failed(),
327 "a failed upgrade must leave the session in FailedUpgrade"
328 );
329 true
330 }
331 }
332 }
333
334 fn upgrade_expect(
335 &mut self,
336 expect: ExpectProxyProtocol<TcpStream>,
337 ) -> Option<HttpStateMachine> {
338 debug!("{} switching to HTTP", log_context!(self));
339 match expect
340 .addresses
341 .as_ref()
342 .map(|add| (add.destination(), add.source()))
343 {
344 Some((Some(public_address), Some(session_address))) => {
345 let session_ulid = rusty_ulid::Ulid::generate();
346 let frontend = mux::Connection::new_h1_server(
347 session_ulid,
348 crate::socket::SessionTcpStream::new(
349 expect.frontend,
350 session_ulid,
351 Some(session_address),
352 ),
353 expect.container_frontend_timeout.duration(),
354 );
355 let router = mux::Router::new(
356 self.configured_backend_timeout,
357 self.configured_connect_timeout,
358 );
359 let mut context = mux::Context::new(
360 session_ulid,
361 self.pool.clone(),
362 self.listener.clone(),
363 Some(session_address),
364 public_address,
365 );
366 if context.create_stream(expect.request_id, 1 << 16).is_none() {
367 error!(
368 "{} expect upgrade failed: could not create stream",
369 log_context!(self)
370 );
371 return None;
372 }
373 let mut mux = Mux {
374 configured_frontend_timeout: self.configured_frontend_timeout,
375 frontend_token: self.frontend_token,
376 frontend,
377 router,
378 context,
379 session_ulid,
380 timeouts: mux::MuxTimeouts::new(expect.container_frontend_timeout),
381 backend_registry: mux::BackendRegistry::default(),
382 backends: Rc::clone(&self.proxy.borrow().backends),
387 };
388 mux.frontend.readiness_mut().event = expect.frontend_readiness.event;
389
390 debug_assert_eq!(
394 mux.frontend_token, self.frontend_token,
395 "expect upgrade must preserve the frontend token"
396 );
397 debug_assert_eq!(
400 mux.context.streams.len(),
401 1,
402 "a freshly upgraded Mux owns exactly the request stream"
403 );
404
405 gauge_add!(names::protocol::PROXY_EXPECT, -1);
409 gauge_add!(names::protocol::HTTP, 1);
410 Some(HttpStateMachine::Mux(mux))
411 }
412 _ => {
413 debug!(
414 "{} expect upgrade failed: bad header {:?}",
415 log_context!(self),
416 expect.addresses
417 );
418 None
419 }
420 }
421 }
422
423 fn upgrade_mux(&mut self, mut mux: MuxClear) -> Option<HttpStateMachine> {
424 debug!("{} mux switching to ws", log_context!(self));
425 let Some(stream) = mux.context.streams.pop() else {
426 error!(
427 "{} upgrade_mux: no stream attached to the mux session, closing",
428 log_context!(self)
429 );
430 return None;
431 };
432 let mut container_frontend_timeout = mux.timeouts.frontend;
440 let (frontend_readiness, frontend_socket) = match mux.frontend {
441 mux::Connection::H1(mux::ConnectionH1 {
442 readiness, socket, ..
443 }) => (readiness, socket),
444 mux::Connection::H2(_) => {
445 error!(
446 "{} only h1<->h1 connections can upgrade to websocket",
447 log_context!(self)
448 );
449 return None;
450 }
451 };
452
453 let mux::StreamState::Linked(back_token) = stream.state else {
454 error!(
455 "{} upgrading stream should be linked to a backend",
456 log_context!(self)
457 );
458 return None;
459 };
460 debug_assert!(
466 mux.router.backends.contains_key(&back_token),
467 "a Linked stream's back token must index a connected backend"
468 );
469 let backends_before = mux.router.backends.len();
470 let Some(backend) = mux.router.backends.remove(&back_token) else {
471 error!(
472 "{} upgrade_mux: backend for token {:?} is missing (already disconnected?), closing",
473 log_context!(self),
474 back_token
475 );
476 return None;
477 };
478 let mut container_backend_timeout = mux
479 .timeouts
480 .backends
481 .remove(&back_token)
482 .unwrap_or_else(|| TimeoutContainer::new_empty(mux.router.configured_backend_timeout));
483 let (cluster_id, backend_id, backend_readiness, backend_socket) = match backend {
484 mux::Connection::H1(mux::ConnectionH1 {
485 position: mux::Position::Client(cluster_id, backend, mux::BackendStatus::Connected),
486 readiness,
487 socket,
488 ..
489 }) => (cluster_id, backend, readiness, socket),
490 mux::Connection::H1(_) => {
491 error!(
492 "{} the backend disconnected just after upgrade, abort",
493 log_context!(self)
494 );
495 return None;
496 }
497 mux::Connection::H2(_) => {
498 error!(
499 "{} only h1<->h1 connections can upgrade to websocket",
500 log_context!(self)
501 );
502 return None;
503 }
504 };
505
506 let Some(backend) = mux.backend_registry.handle(&backend_id).cloned() else {
511 error!(
512 "{} upgrade_mux: backend {:?} names a slot this session never minted, closing",
513 log_context!(self),
514 backend_id.backend_id
515 );
516 return None;
517 };
518 let metric_incarnation = backend_id.metric_incarnation();
519 let backend_id = backend_id.backend_id.to_string();
520
521 debug_assert!(
524 !mux.router.backends.contains_key(&back_token),
525 "the upgraded backend must be evicted from the router map"
526 );
527 debug_assert_eq!(
528 mux.router.backends.len(),
529 backends_before - 1,
530 "removing the backend must drop the backend count by exactly one"
531 );
532
533 let ws_context = stream.context.websocket_context();
534
535 container_frontend_timeout.reset();
536 container_backend_timeout.reset();
537
538 let backend_socket = backend_socket.stream;
543 let mut pipe = Pipe::new(
544 stream.back.storage.buffer,
545 Some(backend_id),
546 Some(backend_socket),
547 Some(backend),
548 Some(container_backend_timeout),
549 Some(container_frontend_timeout),
550 Some(cluster_id),
551 stream.front.storage.buffer,
552 self.frontend_token,
553 frontend_socket,
554 self.listener.clone(),
555 Protocol::HTTP,
556 stream.context.session_id,
557 stream.context.id,
558 stream.context.session_address,
559 ws_context,
560 );
561 pipe.set_cluster_metrics_incarnation(metric_incarnation);
562
563 pipe.restore_readiness_events(frontend_readiness.event, backend_readiness.event);
564 pipe.set_back_token(back_token);
567 debug_assert_eq!(
568 pipe.back_token(),
569 vec![back_token],
570 "websocket pipe must carry exactly the upgraded backend token"
571 );
572
573 gauge_add!(names::protocol::HTTP, -1);
580 gauge_add!(names::protocol::WS, 1);
581 gauge_add!(names::websocket::ACTIVE_REQUESTS, 1);
582 Some(HttpStateMachine::WebSocket(pipe))
583 }
584
585 fn upgrade_websocket(
586 &self,
587 ws: Pipe<crate::socket::SessionTcpStream, HttpListener>,
588 ) -> Option<HttpStateMachine> {
589 error!(
591 "{} upgrade called on WS, this should not happen",
592 log_context!(self)
593 );
594 Some(HttpStateMachine::WebSocket(ws))
595 }
596}
597
598impl ProxySession for HttpSession {
599 fn close(&mut self) {
600 if self.has_been_closed {
601 return;
602 }
603 debug_assert!(
605 !self.has_been_closed,
606 "close past the guard must run on a not-yet-closed session"
607 );
608
609 trace!("{} closing HTTP session", log_context!(self));
610 self.metrics.service_stop();
611
612 match self.state.marker() {
614 StateMarker::Expect => gauge_add!(names::protocol::PROXY_EXPECT, -1),
615 StateMarker::Mux => gauge_add!(names::protocol::HTTP, -1),
616 StateMarker::WebSocket => {
617 gauge_add!(names::protocol::WS, -1);
618 gauge_add!(names::websocket::ACTIVE_REQUESTS, -1);
619 }
620 }
621
622 if self.state.failed() {
623 match self.state.marker() {
624 StateMarker::Expect => incr!(names::http::UPGRADE_EXPECT_FAILED),
625 StateMarker::Mux => incr!(names::http::UPGRADE_MUX_FAILED),
626 StateMarker::WebSocket => incr!(names::http::UPGRADE_WS_FAILED),
627 }
628 self.state.close(self.proxy.clone(), &mut self.metrics);
632 self.proxy.borrow().remove_session(self.frontend_token);
633 self.has_been_closed = true;
634 debug_assert!(
635 self.has_been_closed,
636 "failed-upgrade close path must mark the session closed"
637 );
638 return;
639 }
640
641 self.state.cancel_timeouts();
642 self.state.close(self.proxy.clone(), &mut self.metrics);
644
645 let peer_closed =
648 matches!(&self.state, HttpStateMachine::Mux(mux) if mux.frontend_peer_closed());
649 let front_socket = self.state.front_socket();
650 if let Err(e) = mux::shutdown_write(front_socket, peer_closed) {
656 if e.kind() != ErrorKind::NotConnected {
658 error!(
659 "{} error shutting down front socket({:?}): {:?}",
660 log_context!(self),
661 front_socket,
662 e
663 )
664 }
665 }
666
667 let proxy = self.proxy.borrow();
676 proxy.remove_session(self.frontend_token);
677
678 self.has_been_closed = true;
679 debug_assert!(
680 self.has_been_closed,
681 "close must leave the session marked closed (idempotency latch)"
682 );
683 }
684
685 fn timeout(&mut self, token: Token) -> SessionIsToBeClosed {
686 let state_result = self.state.timeout(token, &mut self.metrics);
687 state_result == StateResult::CloseSession
688 }
689
690 fn needs_ready_pass(&self) -> bool {
691 matches!(&self.state, HttpStateMachine::Mux(mux) if mux.has_pending_backend_failure())
692 }
693
694 fn protocol(&self) -> Protocol {
695 Protocol::HTTP
696 }
697
698 fn update_readiness(&mut self, token: Token, events: Ready) {
699 trace!(
700 "{} token {:?} got event {}",
701 log_context!(self),
702 token,
703 super::ready_to_string(events)
704 );
705 self.last_event = Instant::now();
706 self.metrics.wait_start();
707 self.state.update_readiness(token, events);
708 }
709
710 fn ready(&mut self, session: Rc<RefCell<dyn ProxySession>>) -> SessionIsToBeClosed {
711 self.metrics.service_start();
712
713 let session_result =
714 self.state
715 .ready(session.clone(), self.proxy.clone(), &mut self.metrics);
716
717 let to_be_closed = match session_result {
718 SessionResult::Close => true,
719 SessionResult::Continue => false,
720 SessionResult::Upgrade => match self.upgrade() {
721 false => self.ready(session),
722 true => true,
723 },
724 };
725
726 self.metrics.service_stop();
727 #[cfg(debug_assertions)]
732 if !to_be_closed {
733 self.check_invariants();
734 }
735 to_be_closed
736 }
737
738 fn shutting_down(&mut self) -> SessionIsToBeClosed {
739 self.state.shutting_down()
740 }
741
742 fn targets(&self, target: &crate::SessionTarget) -> bool {
743 self.state.targets(target)
744 }
745
746 fn last_event(&self) -> Instant {
747 self.last_event
748 }
749
750 fn print_session(&self) {
751 self.state.print_state("HTTP");
752 error!("{} Metrics: {:?}", log_context!(self), self.metrics);
753 }
754
755 fn frontend_token(&self) -> Token {
756 self.frontend_token
757 }
758}
759
760pub type Hostname = String;
761
762pub struct HttpListener {
784 active: bool,
785 address: SocketAddr,
786 answers: Rc<RefCell<HttpAnswers>>,
787 config: HttpListenerConfig,
788 fronts: Router,
789 listener: Option<MioTcpListener>,
790 parked_listener: Option<MioTcpListener>,
804 nodelay_backlog: bool,
818 tags: BTreeMap<String, CachedTags>,
819 token: Token,
820}
821
822impl ListenerHandler for HttpListener {
823 fn get_addr(&self) -> &SocketAddr {
824 &self.address
825 }
826
827 fn get_tags(&self, key: &str) -> Option<&CachedTags> {
828 self.tags.get(key)
829 }
830
831 fn set_tags(&mut self, key: String, tags: Option<BTreeMap<String, String>>) {
832 match tags {
833 Some(tags) => self.tags.insert(key, CachedTags::new(tags)),
834 None => self.tags.remove(&key),
835 };
836 }
837
838 fn protocol(&self) -> Protocol {
839 Protocol::HTTP
840 }
841
842 fn public_address(&self) -> SocketAddr {
843 self.config
844 .public_address
845 .map(|addr| addr.into())
846 .unwrap_or(self.address)
847 }
848}
849
850impl L7ListenerHandler for HttpListener {
851 fn get_sticky_name(&self) -> &str {
852 &self.config.sticky_name
853 }
854
855 fn get_sozu_id_header(&self) -> &str {
856 self.config
857 .sozu_id_header
858 .as_deref()
859 .filter(|s| !s.is_empty())
860 .unwrap_or("Sozu-Id")
861 }
862
863 fn get_connect_timeout(&self) -> u32 {
864 self.config.connect_timeout
865 }
866
867 fn frontend_from_request(
869 &self,
870 host: &str,
871 uri: &str,
872 method: &Method,
873 headers: &dyn RequestHeaders,
874 ) -> Result<RouteResult, FrontendFromRequestError> {
875 let start = Instant::now();
876 let (remaining_input, (hostname, _)) = match hostname_and_port(host.as_bytes()) {
877 Ok(tuple) => tuple,
878 Err(parse_error) => {
879 return Err(FrontendFromRequestError::HostParse {
881 host: host.to_owned(),
882 error: parse_error.to_string(),
883 });
884 }
885 };
886 if remaining_input != &b""[..] {
887 return Err(FrontendFromRequestError::InvalidCharsAfterHost(
888 host.to_owned(),
889 ));
890 }
891
892 let host = unsafe { from_utf8_unchecked(hostname) };
907
908 let route = self
909 .fronts
910 .lookup_with_headers(host, uri, method, headers)
911 .map_err(|e| {
912 incr!(names::http::FAILED_BACKEND_MATCHING);
913 FrontendFromRequestError::NoClusterFound(e)
914 })?;
915
916 let now = Instant::now();
917
918 if let Some(cluster) = route.cluster_id.as_deref() {
919 time!(
920 names::event_loop::FRONTEND_MATCHING_TIME,
921 cluster,
922 (now - start).as_millis()
923 );
924 }
925
926 Ok(route)
927 }
928
929 fn get_answers(&self) -> &Rc<RefCell<HttpAnswers>> {
930 &self.answers
931 }
932
933 fn get_h2_flood_config(&self) -> crate::protocol::mux::H2FloodConfig {
934 crate::protocol::mux::H2FloodConfig::from_optional(
942 self.config.h2_max_rst_stream_per_window,
943 self.config.h2_max_ping_per_window,
944 self.config.h2_max_settings_per_window,
945 self.config.h2_max_empty_data_per_window,
946 self.config.h2_max_window_update_stream0_per_window,
947 self.config.h2_max_continuation_frames,
948 self.config.h2_max_glitch_count,
949 self.config.h2_max_rst_stream_lifetime,
950 self.config.h2_max_rst_stream_abusive_lifetime,
951 self.config.h2_max_rst_stream_emitted_lifetime,
952 self.config.h2_max_header_list_size,
953 self.config.h2_max_header_table_size,
954 self.config.h2_max_header_fields,
955 self.config.h2_stream_refusal_percent,
956 )
957 }
958
959 fn get_h2_connection_config(&self) -> crate::protocol::mux::H2ConnectionConfig {
960 crate::protocol::mux::H2ConnectionConfig::from_optional(
961 self.config.h2_initial_connection_window,
962 self.config.h2_max_concurrent_streams,
963 self.config.h2_stream_shrink_ratio,
964 )
965 }
966
967 fn get_h2_stream_idle_timeout(&self) -> std::time::Duration {
968 let seconds = self
975 .config
976 .h2_stream_idle_timeout_seconds
977 .map(|s| u64::from(s.max(1)))
978 .unwrap_or_else(|| u64::from(self.config.back_timeout).max(30));
979 std::time::Duration::from_secs(seconds)
980 }
981
982 fn get_h2_graceful_shutdown_deadline(&self) -> Option<std::time::Duration> {
983 match self.config.h2_graceful_shutdown_deadline_seconds {
984 None => Some(std::time::Duration::from_secs(5)),
985 Some(0) => None,
986 Some(s) => Some(std::time::Duration::from_secs(u64::from(s))),
987 }
988 }
989
990 fn get_elide_x_real_ip(&self) -> bool {
991 self.config.elide_x_real_ip.unwrap_or(false)
992 }
993
994 fn get_send_x_real_ip(&self) -> bool {
995 self.config.send_x_real_ip.unwrap_or(false)
996 }
997
998 fn get_forwarded_headers(&self) -> ForwardedHeaders {
999 self.config.forwarded_headers()
1002 }
1003}
1004
1005pub struct HttpProxy {
1006 backends: Rc<RefCell<BackendMap>>,
1007 clusters: HashMap<ClusterId, Cluster>,
1008 listeners: HashMap<Token, Rc<RefCell<HttpListener>>>,
1009 pool: Rc<RefCell<Pool>>,
1010 registry: Registry,
1011 sessions: Rc<RefCell<SessionManager>>,
1012}
1013
1014impl HttpProxy {
1015 pub fn new(
1016 registry: Registry,
1017 sessions: Rc<RefCell<SessionManager>>,
1018 pool: Rc<RefCell<Pool>>,
1019 backends: Rc<RefCell<BackendMap>>,
1020 ) -> HttpProxy {
1021 HttpProxy {
1022 backends,
1023 clusters: HashMap::new(),
1024 listeners: HashMap::new(),
1025 pool,
1026 registry,
1027 sessions,
1028 }
1029 }
1030
1031 pub fn add_listener(
1032 &mut self,
1033 config: HttpListenerConfig,
1034 token: Token,
1035 ) -> Result<Token, ProxyError> {
1036 if self.listeners.contains_key(&token) {
1037 return Err(ProxyError::ListenerAlreadyPresent);
1038 }
1039 let mut http_listener =
1040 HttpListener::new(config, token).map_err(ProxyError::AddListener)?;
1041 if let Some(sibling) = self
1046 .listeners_at(http_listener.address)
1047 .ok()
1048 .and_then(|l| l.into_iter().next())
1049 {
1050 let sibling = sibling.borrow();
1051 http_listener.fronts = sibling.fronts.clone();
1052 http_listener.tags = sibling.tags.clone();
1053 }
1054 match self.listeners.entry(token) {
1055 Entry::Vacant(entry) => {
1056 entry.insert(Rc::new(RefCell::new(http_listener)));
1057 Ok(token)
1058 }
1059 _ => Err(ProxyError::ListenerAlreadyPresent),
1060 }
1061 }
1062
1063 pub(crate) fn add_listener_with_retained_frontends(
1072 &mut self,
1073 config: HttpListenerConfig,
1074 token: Token,
1075 retained_frontends: Vec<RequestHttpFrontend>,
1076 ) -> Result<Token, ProxyError> {
1077 let address = config.address.into();
1078 let had_sibling = self.listeners_at(address).is_ok();
1079 self.add_listener(config, token)?;
1080
1081 if !had_sibling {
1082 for frontend in retained_frontends {
1083 if let Err(error) = self.add_http_frontend(frontend) {
1084 self.listeners.remove(&token);
1085 return Err(error);
1086 }
1087 }
1088 }
1089
1090 Ok(token)
1091 }
1092
1093 pub fn get_listener(&self, token: &Token) -> Option<Rc<RefCell<HttpListener>>> {
1094 self.listeners.get(token).cloned()
1095 }
1096
1097 pub fn remove_listener(&mut self, remove: RemoveListener) -> Result<(), ProxyError> {
1098 let len = self.listeners.len();
1099 let remove_address = remove.listener_key();
1100 self.listeners
1101 .retain(|_, l| !l.borrow().is(&remove_address));
1102
1103 if !self.listeners.len() < len {
1104 info!(
1105 "{} no HTTP listener to remove at {}",
1106 log_module_context!(),
1107 remove_address
1108 );
1109 }
1110 Ok(())
1111 }
1112
1113 pub fn inherited_socket_fate(&self, addr: &ListenerKey) -> crate::InheritedSocketFate {
1121 use crate::InheritedSocketFate;
1122 match self
1123 .listeners
1124 .values()
1125 .find(|listener| listener.borrow().is(addr))
1126 {
1127 None => InheritedSocketFate::Unclaimed,
1128 Some(listener) if listener.borrow().active => InheritedSocketFate::Refused,
1129 Some(_) => InheritedSocketFate::Adopted,
1130 }
1131 }
1132
1133 pub fn activate_listener(
1134 &self,
1135 addr: &ListenerKey,
1136 tcp_listener: Option<MioTcpListener>,
1137 ) -> Result<Token, ProxyError> {
1138 let listener = self
1139 .listeners
1140 .values()
1141 .find(|listener| listener.borrow().is(addr))
1142 .ok_or(ProxyError::NoListenerFound(addr.clone()))?;
1143
1144 listener
1145 .borrow_mut()
1146 .activate(&self.registry, tcp_listener)
1147 .map_err(|listener_error| ProxyError::ListenerActivation {
1148 address: addr.address,
1149 listener_error,
1150 })
1151 }
1152
1153 pub fn listener_token(&self, address: &ListenerKey) -> Option<Token> {
1162 self.listeners
1163 .iter()
1164 .find(|(_, listener)| listener.borrow().is(address))
1165 .map(|(token, _)| *token)
1166 }
1167
1168 pub fn give_back_listeners(&mut self) -> Vec<(ListenerKey, MioTcpListener)> {
1169 self.listeners
1170 .values()
1171 .filter_map(|listener| {
1172 let mut owned = listener.borrow_mut();
1173 if let Some(listener) = owned.listener.take() {
1174 owned.active = false;
1177 return Some((owned.key(), listener));
1178 }
1179
1180 None
1181 })
1182 .collect()
1183 }
1184
1185 pub fn give_back_listener(
1186 &mut self,
1187 address: &ListenerKey,
1188 ) -> Result<(Token, MioTcpListener), ProxyError> {
1189 let listener = self
1190 .listeners
1191 .values()
1192 .find(|listener| listener.borrow().is(address))
1193 .ok_or(ProxyError::NoListenerFound(address.clone()))?;
1194
1195 let mut owned = listener.borrow_mut();
1196
1197 let taken_listener = owned
1198 .listener
1199 .take()
1200 .ok_or(ProxyError::UnactivatedListener)?;
1201
1202 owned.active = false;
1205
1206 Ok((owned.token, taken_listener))
1207 }
1208
1209 fn listeners_at(
1213 &self,
1214 address: SocketAddr,
1215 ) -> Result<Vec<Rc<RefCell<HttpListener>>>, ProxyError> {
1216 let mut listeners: Vec<_> = self
1217 .listeners
1218 .iter()
1219 .filter(|(_, listener)| listener.borrow().address == address)
1220 .map(|(token, listener)| (*token, listener.clone()))
1221 .collect();
1222 listeners.sort_by_key(|(token, _)| *token);
1224 let listeners: Vec<_> = listeners
1225 .into_iter()
1226 .map(|(_, listener)| listener)
1227 .collect();
1228 if listeners.is_empty() {
1229 return Err(ProxyError::NoListenerFound(address.into()));
1230 }
1231 Ok(listeners)
1232 }
1233
1234 pub fn update_listener(&mut self, patch: UpdateHttpListenerConfig) -> Result<(), ProxyError> {
1236 let address: std::net::SocketAddr = patch.address.into();
1237 let listener = self
1238 .listeners
1239 .values()
1240 .find(|l| l.borrow().address == address)
1241 .ok_or(ProxyError::NoListenerFound(address.into()))?;
1242 listener
1243 .borrow_mut()
1244 .update_config(&patch)
1245 .map_err(|listener_error| ProxyError::ListenerActivation {
1246 address,
1247 listener_error,
1248 })
1249 }
1250
1251 pub fn add_cluster(&mut self, mut cluster: Cluster) -> Result<(), ProxyError> {
1252 let mut overrides = cluster.answers.clone();
1255 if let Some(answer_503) = cluster.answer_503.take() {
1256 overrides.entry("503".to_owned()).or_insert(answer_503);
1257 }
1258 if !overrides.is_empty() {
1259 for listener in self.listeners.values() {
1260 listener
1261 .borrow()
1262 .answers
1263 .borrow_mut()
1264 .add_cluster_answers(&cluster.cluster_id, &overrides)
1265 .map_err(|(name, error)| {
1266 ProxyError::AddCluster(ListenerError::TemplateParse(name, error))
1267 })?;
1268 }
1269 }
1270 self.clusters
1271 .insert(cluster.cluster_id.as_str().into(), cluster);
1272 Ok(())
1273 }
1274
1275 pub fn remove_cluster(&mut self, cluster_id: &str) -> Result<(), ProxyError> {
1276 self.clusters.remove(cluster_id);
1277
1278 for listener in self.listeners.values() {
1279 listener
1280 .borrow()
1281 .answers
1282 .borrow_mut()
1283 .remove_cluster_answers(cluster_id);
1284 }
1285 Ok(())
1286 }
1287
1288 pub fn add_http_frontend(&mut self, front: RequestHttpFrontend) -> Result<(), ProxyError> {
1289 if front.hsts.is_some() {
1304 incr!(names::http::HSTS_SUPPRESSED_PLAINTEXT);
1305 return Err(ProxyError::HstsOnPlainHttp(front.address.into()));
1306 }
1307
1308 let front = front.clone().to_frontend().map_err(|request_error| {
1309 ProxyError::WrongInputFrontend {
1310 front: Box::new(front),
1311 error: request_error.to_string(),
1312 }
1313 })?;
1314
1315 for listener in self.listeners_at(front.address)? {
1316 let mut listener = listener.borrow_mut();
1317 let hostname = front.hostname.to_owned();
1318 let tags = front.tags.to_owned();
1319
1320 listener
1321 .add_http_front(front.clone())
1322 .map_err(ProxyError::AddFrontend)?;
1323 listener.set_tags(hostname, tags);
1324 }
1325 Ok(())
1326 }
1327
1328 pub fn remove_http_frontend(&mut self, front: RequestHttpFrontend) -> Result<(), ProxyError> {
1329 let front = front.clone().to_frontend().map_err(|request_error| {
1330 ProxyError::WrongInputFrontend {
1331 front: Box::new(front),
1332 error: request_error.to_string(),
1333 }
1334 })?;
1335
1336 let mut result = Ok(());
1340 for listener in self.listeners_at(front.address)? {
1341 let mut listener = listener.borrow_mut();
1342 let hostname = front.hostname.to_owned();
1343
1344 let removed = listener
1345 .remove_http_front(front.clone())
1346 .map_err(ProxyError::RemoveFrontend);
1347 if result.is_ok() {
1348 result = removed;
1349 }
1350
1351 if !listener.fronts.has_hostname(&hostname) {
1352 listener.set_tags(hostname, None);
1353 }
1354 }
1355 result
1356 }
1357
1358 pub fn soft_stop(&mut self) -> Result<(), ProxyError> {
1359 let listeners = std::mem::take(&mut self.listeners);
1360 let mut socket_errors = vec![];
1361 for l in listeners.values() {
1362 if let Some(mut sock) = l.borrow_mut().listener.take() {
1363 debug!("{} deregistering socket {:?}", log_module_context!(), sock);
1364 if let Err(e) = self.registry.deregister(&mut sock) {
1365 let error = format!("socket {sock:?}: {e:?}");
1366 socket_errors.push(error);
1367 }
1368 }
1369 }
1370
1371 if !socket_errors.is_empty() {
1372 return Err(ProxyError::SoftStop {
1373 proxy_protocol: "HTTP".to_string(),
1374 error: format!("Error deregistering listen sockets: {socket_errors:?}"),
1375 });
1376 }
1377
1378 Ok(())
1379 }
1380
1381 pub fn hard_stop(&mut self) -> Result<(), ProxyError> {
1382 let mut listeners = std::mem::take(&mut self.listeners);
1383 let mut socket_errors = vec![];
1384 for (_, l) in listeners.drain() {
1385 if let Some(mut sock) = l.borrow_mut().listener.take() {
1386 debug!("{} deregistering socket {:?}", log_module_context!(), sock);
1387 if let Err(e) = self.registry.deregister(&mut sock) {
1388 let error = format!("socket {sock:?}: {e:?}");
1389 socket_errors.push(error);
1390 }
1391 }
1392 }
1393
1394 if !socket_errors.is_empty() {
1395 return Err(ProxyError::HardStop {
1396 proxy_protocol: "HTTP".to_string(),
1397 error: format!("Error deregistering listen sockets: {socket_errors:?}"),
1398 });
1399 }
1400
1401 Ok(())
1402 }
1403}
1404
1405impl HttpListener {
1406 pub fn key(&self) -> ListenerKey {
1409 ListenerKey::new(self.address, self.config.interface.as_deref())
1410 }
1411
1412 pub fn is(&self, key: &ListenerKey) -> bool {
1414 key.matches(&self.address, self.config.interface.as_deref())
1415 }
1416
1417 pub fn new(config: HttpListenerConfig, token: Token) -> Result<HttpListener, ListenerError> {
1418 if let Some(v) = config.forwarded_headers {
1421 validate_forwarded_headers(v)?;
1422 }
1423 let answers = Self::build_answers(&config)?;
1424 Ok(HttpListener {
1425 active: false,
1426 address: config.address.into(),
1427 answers: Rc::new(RefCell::new(answers)),
1428 config,
1429 fronts: Router::new(),
1430 listener: None,
1431 parked_listener: None,
1432 nodelay_backlog: false,
1433 tags: BTreeMap::new(),
1434 token,
1435 })
1436 }
1437
1438 fn build_answers(config: &HttpListenerConfig) -> Result<HttpAnswers, ListenerError> {
1445 let mut answers_map = config.answers.clone();
1446 if let Some(ref legacy) = config.http_answers {
1447 crate::protocol::http::answers::merge_legacy_into_map(&mut answers_map, legacy);
1448 }
1449 HttpAnswers::new(&answers_map)
1450 .map_err(|(name, error)| ListenerError::TemplateParse(name, error))
1451 }
1452
1453 pub fn validate_config(config: &HttpListenerConfig) -> Result<(), ListenerError> {
1468 Self::build_answers(config)?;
1469 Ok(())
1470 }
1471
1472 pub fn activate(
1473 &mut self,
1474 registry: &Registry,
1475 tcp_listener: Option<MioTcpListener>,
1476 ) -> Result<Token, ListenerError> {
1477 if self.active {
1478 return Ok(self.token);
1479 }
1480 let address: SocketAddr = self.config.address.into();
1481
1482 let parked = self.parked_listener.take();
1488 let mut listener = match (tcp_listener, parked) {
1489 (Some(inherited), Some(parked)) => {
1490 warn!(
1498 "{} closing the socket parked on the HTTP listener for {}: an \
1499 inherited socket arrived for the same address and is the one being \
1500 activated",
1501 log_module_context!(),
1502 address
1503 );
1504 drop(parked);
1505 inherited
1506 }
1507 (Some(inherited), None) => inherited,
1508 (None, Some(parked)) => parked,
1509 (None, None) => server_bind(address, self.config.interface.as_deref()).map_err(
1510 |server_bind_error| ListenerError::Activation {
1511 address,
1512 error: server_bind_error.to_string(),
1513 },
1514 )?,
1515 };
1516
1517 if let Err(e) = SockRef::from(&listener).set_tcp_nodelay(true) {
1523 error!(
1524 "{} error setting nodelay on listen socket({:?}): {:?}",
1525 log_module_context!(),
1526 listener,
1527 e
1528 );
1529 }
1530
1531 let registration = registry
1532 .register(&mut listener, self.token, Interest::READABLE)
1533 .map_err(ListenerError::SocketRegistration);
1534 if let Err(error) = registration {
1535 self.parked_listener = Some(listener);
1545 return Err(error);
1546 }
1547
1548 self.listener = Some(listener);
1549 self.nodelay_backlog = true;
1550 self.active = true;
1551 Ok(self.token)
1552 }
1553
1554 pub fn update_config(&mut self, patch: &UpdateHttpListenerConfig) -> Result<(), ListenerError> {
1567 validate_h2_flood_knobs_http(patch)?;
1575 if let Some(ref hdr) = patch.sozu_id_header {
1576 validate_sozu_id_header(hdr)?;
1577 }
1578 if let Some(v) = patch.forwarded_headers {
1579 validate_forwarded_headers(v)?;
1580 }
1581 HttpAnswers::validate_patch_templates(patch.http_answers.as_ref(), &patch.answers)
1582 .map_err(|(name, error)| ListenerError::TemplateParse(name, error))?;
1583
1584 let mut config = self.config.clone();
1585
1586 if let Some(v) = patch.public_address {
1587 config.public_address = Some(v);
1588 }
1589 if let Some(v) = patch.expect_proxy {
1590 config.expect_proxy = v;
1591 }
1592 if let Some(ref v) = patch.sticky_name {
1593 config.sticky_name = v.to_owned();
1594 }
1595 if let Some(v) = patch.front_timeout {
1596 config.front_timeout = v;
1597 }
1598 if let Some(v) = patch.back_timeout {
1599 config.back_timeout = v;
1600 }
1601 if let Some(v) = patch.connect_timeout {
1602 config.connect_timeout = v;
1603 }
1604 if let Some(v) = patch.request_timeout {
1605 config.request_timeout = v;
1606 }
1607 if let Some(ref v) = patch.sozu_id_header {
1608 config.sozu_id_header = Some(v.to_owned());
1609 }
1610 if let Some(v) = patch.elide_x_real_ip {
1611 config.elide_x_real_ip = Some(v);
1612 }
1613 if let Some(v) = patch.send_x_real_ip {
1614 config.send_x_real_ip = Some(v);
1615 }
1616 if let Some(v) = patch.forwarded_headers {
1617 config.forwarded_headers = Some(v);
1618 }
1619
1620 if let Some(v) = patch.h2_max_rst_stream_per_window {
1622 config.h2_max_rst_stream_per_window = Some(v);
1623 }
1624 if let Some(v) = patch.h2_max_ping_per_window {
1625 config.h2_max_ping_per_window = Some(v);
1626 }
1627 if let Some(v) = patch.h2_max_settings_per_window {
1628 config.h2_max_settings_per_window = Some(v);
1629 }
1630 if let Some(v) = patch.h2_max_empty_data_per_window {
1631 config.h2_max_empty_data_per_window = Some(v);
1632 }
1633 if let Some(v) = patch.h2_max_continuation_frames {
1634 config.h2_max_continuation_frames = Some(v);
1635 }
1636 if let Some(v) = patch.h2_max_glitch_count {
1637 config.h2_max_glitch_count = Some(v);
1638 }
1639 if let Some(v) = patch.h2_initial_connection_window {
1640 config.h2_initial_connection_window = Some(v);
1641 }
1642 if let Some(v) = patch.h2_max_concurrent_streams {
1643 config.h2_max_concurrent_streams = Some(v);
1644 }
1645 if let Some(v) = patch.h2_stream_shrink_ratio {
1646 config.h2_stream_shrink_ratio = Some(v);
1647 }
1648 if let Some(v) = patch.h2_max_rst_stream_lifetime {
1649 config.h2_max_rst_stream_lifetime = Some(v);
1650 }
1651 if let Some(v) = patch.h2_max_rst_stream_abusive_lifetime {
1652 config.h2_max_rst_stream_abusive_lifetime = Some(v);
1653 }
1654 if let Some(v) = patch.h2_max_rst_stream_emitted_lifetime {
1655 config.h2_max_rst_stream_emitted_lifetime = Some(v);
1656 }
1657 if let Some(v) = patch.h2_max_header_list_size {
1658 config.h2_max_header_list_size = Some(v);
1659 }
1660 if let Some(v) = patch.h2_max_header_table_size {
1661 config.h2_max_header_table_size = Some(v);
1662 }
1663 if let Some(v) = patch.h2_max_header_fields {
1664 config.h2_max_header_fields = Some(v);
1665 }
1666 if let Some(v) = patch.h2_stream_refusal_percent {
1667 config.h2_stream_refusal_percent = Some(v);
1668 }
1669 if let Some(v) = patch.h2_stream_idle_timeout_seconds {
1670 config.h2_stream_idle_timeout_seconds = Some(v);
1671 }
1672 if let Some(v) = patch.h2_graceful_shutdown_deadline_seconds {
1673 config.h2_graceful_shutdown_deadline_seconds = Some(v);
1674 }
1675 if let Some(v) = patch.h2_max_window_update_stream0_per_window {
1676 config.h2_max_window_update_stream0_per_window = Some(v);
1677 }
1678
1679 let answers_changed = patch.http_answers.is_some() || !patch.answers.is_empty();
1684 let new_answers = if answers_changed {
1685 if let Some(ref new_answers) = patch.http_answers {
1686 crate::sozu_command::state::merge_custom_http_answers(
1687 &mut config.http_answers,
1688 new_answers,
1689 );
1690 }
1691 for (code, body) in &patch.answers {
1692 if !body.is_empty() {
1693 config.answers.insert(code.clone(), body.clone());
1694 }
1695 }
1696
1697 let mut answers_map = config.answers.clone();
1698 if let Some(ref legacy) = config.http_answers {
1699 crate::protocol::http::answers::merge_legacy_into_map(&mut answers_map, legacy);
1700 }
1701 Some(
1702 HttpAnswers::new(&answers_map)
1703 .map_err(|(name, error)| ListenerError::TemplateParse(name, error))?,
1704 )
1705 } else {
1706 None
1707 };
1708
1709 self.config = config;
1711 if let Some(mut new_answers) = new_answers {
1712 new_answers.cluster_answers = self.answers.borrow().cluster_answers.clone();
1730 self.answers = Rc::new(RefCell::new(new_answers));
1731 }
1732
1733 Ok(())
1734 }
1735
1736 pub fn add_http_front(&mut self, http_front: HttpFrontend) -> Result<(), ListenerError> {
1737 self.fronts
1738 .add_http_front(&http_front)
1739 .map_err(ListenerError::AddFrontend)
1740 }
1741
1742 pub fn remove_http_front(&mut self, http_front: HttpFrontend) -> Result<(), ListenerError> {
1743 debug!(
1744 "{} removing http_front {:?}",
1745 log_module_context!(),
1746 http_front
1747 );
1748 self.fronts
1749 .remove_http_front(&http_front)
1750 .map_err(ListenerError::RemoveFrontend)
1751 }
1752
1753 fn accept(&mut self) -> Result<(TcpStream, SocketAddr), AcceptError> {
1754 if let Some(ref sock) = self.listener {
1755 match sock.accept() {
1756 Ok((frontend_sock, peer)) => {
1757 if self.nodelay_backlog
1758 && let Err(e) = frontend_sock.set_nodelay(true)
1759 {
1760 error!(
1761 "{} error setting nodelay on front socket({:?}): {:?}",
1762 log_module_context!(),
1763 frontend_sock,
1764 e
1765 );
1766 }
1767 Ok((frontend_sock, peer))
1768 }
1769 Err(e) if e.kind() == ErrorKind::WouldBlock => {
1770 self.nodelay_backlog = false;
1772 Err(AcceptError::WouldBlock)
1773 }
1774 Err(e) => {
1775 error!("{} accept() IO error: {:?}", log_module_context!(), e);
1776 Err(AcceptError::IoError)
1777 }
1778 }
1779 } else {
1780 error!(
1781 "{} cannot accept connections, no listening socket available",
1782 log_module_context!()
1783 );
1784 Err(AcceptError::IoError)
1785 }
1786 }
1787}
1788
1789impl ProxyConfiguration for HttpProxy {
1790 fn notify(&mut self, request: WorkerRequest) -> WorkerResponse {
1791 let request_id = request.id.clone();
1792
1793 let result = match request.content.request_type {
1794 Some(RequestType::AddCluster(cluster)) => {
1795 debug!(
1796 "{} {} add cluster {:?}",
1797 log_module_context!(),
1798 request.id,
1799 cluster
1800 );
1801 self.add_cluster(cluster)
1802 }
1803 Some(RequestType::RemoveCluster(cluster_id)) => {
1804 debug!(
1805 "{} {} remove cluster {:?}",
1806 log_module_context!(),
1807 request_id,
1808 cluster_id
1809 );
1810 self.remove_cluster(&cluster_id)
1811 }
1812 Some(
1813 RequestType::AddHttpFrontend(front)
1814 | RequestType::AddHttpFrontendWithSilentClose(front)
1815 | RequestType::AddHttpFrontendWithHeaderPredicates(front),
1816 ) => {
1817 debug!(
1818 "{} {} add front {:?}",
1819 log_module_context!(),
1820 request_id,
1821 front
1822 );
1823 self.add_http_frontend(front)
1824 }
1825 Some(
1826 RequestType::RemoveHttpFrontend(front)
1827 | RequestType::RemoveHttpFrontendWithHeaderPredicates(front),
1828 ) => {
1829 debug!(
1830 "{} {} remove front {:?}",
1831 log_module_context!(),
1832 request_id,
1833 front
1834 );
1835 self.remove_http_frontend(front)
1836 }
1837 Some(RequestType::RemoveListener(remove)) => {
1838 debug!(
1839 "{} removing HTTP listener at address {:?}",
1840 log_module_context!(),
1841 remove.address
1842 );
1843 self.remove_listener(remove)
1844 }
1845 Some(RequestType::SoftStop(_)) => {
1846 debug!(
1847 "{} {} processing soft shutdown",
1848 log_module_context!(),
1849 request_id
1850 );
1851 match self.soft_stop() {
1852 Ok(()) => {
1853 info!(
1854 "{} {} soft stop successful",
1855 log_module_context!(),
1856 request_id
1857 );
1858 return WorkerResponse::processing(request.id);
1859 }
1860 Err(e) => Err(e),
1861 }
1862 }
1863 Some(RequestType::HardStop(_)) => {
1864 debug!(
1865 "{} {} processing hard shutdown",
1866 log_module_context!(),
1867 request_id
1868 );
1869 match self.hard_stop() {
1870 Ok(()) => {
1871 info!(
1872 "{} {} hard stop successful",
1873 log_module_context!(),
1874 request_id
1875 );
1876 return WorkerResponse::processing(request.id);
1877 }
1878 Err(e) => Err(e),
1879 }
1880 }
1881 Some(RequestType::Status(_)) => {
1882 debug!("{} {} status", log_module_context!(), request_id);
1883 Ok(())
1884 }
1885 other_command => {
1886 debug!(
1887 "{} {} unsupported message for HTTP proxy, ignoring: {:?}",
1888 log_module_context!(),
1889 request.id,
1890 other_command
1891 );
1892 Err(ProxyError::UnsupportedMessage)
1893 }
1894 };
1895
1896 match result {
1897 Ok(()) => {
1898 debug!("{} {} successful", log_module_context!(), request_id);
1899 WorkerResponse::ok(request_id)
1900 }
1901 Err(proxy_error) => {
1902 debug!(
1903 "{} {} unsuccessful: {}",
1904 log_module_context!(),
1905 request_id,
1906 proxy_error
1907 );
1908 WorkerResponse::error(request_id, proxy_error)
1909 }
1910 }
1911 }
1912
1913 fn accept(&mut self, token: ListenToken) -> Result<(TcpStream, SocketAddr), AcceptError> {
1914 if let Some(listener) = self.listeners.get(&Token(token.0)) {
1915 listener.borrow_mut().accept()
1916 } else {
1917 Err(AcceptError::IoError)
1918 }
1919 }
1920
1921 fn create_session(
1922 &mut self,
1923 mut frontend_sock: TcpStream,
1924 peer: SocketAddr,
1925 listener_token: ListenToken,
1926 wait_time: Duration,
1927 proxy: Rc<RefCell<Self>>,
1928 ) -> Result<(), AcceptError> {
1929 let listener = self
1930 .listeners
1931 .get(&Token(listener_token.0))
1932 .cloned()
1933 .ok_or(AcceptError::IoError)?;
1934
1935 let mut session_manager = self.sessions.borrow_mut();
1939 let slab_len_before = session_manager.slab.len();
1940 let session_entry = session_manager.slab.vacant_entry();
1941 let session_token = Token(session_entry.key());
1942 debug_assert_eq!(
1947 session_token.0,
1948 session_entry.key(),
1949 "session token must equal the slab vacant-entry key"
1950 );
1951 let owned = listener.borrow();
1952
1953 if let Err(register_error) = self.registry.register(
1954 &mut frontend_sock,
1955 session_token,
1956 Interest::READABLE | Interest::WRITABLE,
1957 ) {
1958 error!(
1959 "{} error registering listen socket({:?}): {:?}",
1960 log_module_context!(),
1961 frontend_sock,
1962 register_error
1963 );
1964 return Err(AcceptError::RegisterError);
1965 }
1966
1967 let public_address: SocketAddr = match owned.config.public_address {
1968 Some(pub_addr) => pub_addr.into(),
1969 None => owned.config.address.into(),
1970 };
1971
1972 let session = HttpSession::new(
1973 Duration::from_secs(owned.config.back_timeout as u64),
1974 Duration::from_secs(owned.config.connect_timeout as u64),
1975 Duration::from_secs(owned.config.front_timeout as u64),
1976 Duration::from_secs(owned.config.request_timeout as u64),
1977 owned.config.expect_proxy,
1978 listener.clone(),
1979 Rc::downgrade(&self.pool),
1980 proxy,
1981 Rc::clone(&self.backends),
1984 public_address,
1985 frontend_sock,
1986 peer,
1987 session_token,
1988 wait_time,
1989 )?;
1990
1991 debug_assert_eq!(
1995 session.frontend_token, session_token,
1996 "session must own the frontend token it was created with"
1997 );
1998
1999 let session = Rc::new(RefCell::new(session));
2000 session_entry.insert(session);
2001 debug_assert_eq!(
2005 session_manager.slab.len(),
2006 slab_len_before + 1,
2007 "creating a session must occupy exactly one new slab slot"
2008 );
2009
2010 Ok(())
2011 }
2012}
2013
2014impl L7Proxy for HttpProxy {
2015 fn kind(&self) -> ListenerType {
2016 ListenerType::Http
2017 }
2018
2019 fn register_socket(
2020 &self,
2021 source: &mut TcpStream,
2022 token: Token,
2023 interest: Interest,
2024 ) -> Result<(), std::io::Error> {
2025 self.registry.register(source, token, interest)
2026 }
2027
2028 fn add_session(&self, session: Rc<RefCell<dyn ProxySession>>) -> Token {
2029 let mut session_manager = self.sessions.borrow_mut();
2030 let len_before = session_manager.slab.len();
2031 let entry = session_manager.slab.vacant_entry();
2032 let token = Token(entry.key());
2033 let _entry = entry.insert(session);
2034 debug_assert_eq!(
2037 session_manager.slab.len(),
2038 len_before + 1,
2039 "add_session must occupy exactly one new slab slot"
2040 );
2041 debug_assert!(
2042 session_manager.slab.contains(token.0),
2043 "the returned token must index the freshly inserted session"
2044 );
2045 token
2046 }
2047
2048 fn remove_session(&self, token: Token) -> bool {
2049 let mut sessions = self.sessions.borrow_mut();
2050 let was_present = sessions.slab.contains(token.0);
2051 let len_before = sessions.slab.len();
2052 sessions.untrack_all_cluster_ip(token);
2058 let removed = sessions.slab.try_remove(token.0).is_some();
2059 debug_assert_eq!(
2063 removed, was_present,
2064 "try_remove reports presence iff the slot was occupied"
2065 );
2066 debug_assert_eq!(
2067 sessions.slab.len(),
2068 len_before - removed as usize,
2069 "slab len drops by exactly one iff a session was removed"
2070 );
2071 debug_assert!(
2072 !sessions.slab.contains(token.0),
2073 "the slot must be free after remove_session"
2074 );
2075 removed
2076 }
2077
2078 fn clusters(&self) -> &HashMap<ClusterId, Cluster> {
2079 &self.clusters
2080 }
2081
2082 fn sessions(&self) -> Rc<RefCell<SessionManager>> {
2083 self.sessions.clone()
2084 }
2085}
2086
2087pub mod testing {
2088 use crate::testing::*;
2089
2090 pub fn start_http_worker(
2092 config: HttpListenerConfig,
2093 channel: ProxyChannel,
2094 max_buffers: usize,
2095 buffer_size: usize,
2096 ) -> anyhow::Result<()> {
2097 let address = config.address.into();
2098
2099 let ServerParts {
2100 event_loop,
2101 registry,
2102 sessions,
2103 pool,
2104 backends,
2105 client_scm_socket: _,
2106 server_scm_socket,
2107 server_config,
2108 } = prebuild_server(max_buffers, buffer_size, true)?;
2109
2110 let token = {
2111 let mut sessions = sessions.borrow_mut();
2112 let entry = sessions.slab.vacant_entry();
2113 let key = entry.key();
2114 let _ = entry.insert(Rc::new(RefCell::new(ListenSession {
2115 protocol: Protocol::HTTPListen,
2116 })));
2117 Token(key)
2118 };
2119
2120 let mut proxy = HttpProxy::new(registry, sessions.clone(), pool.clone(), backends.clone());
2121 proxy
2122 .add_listener(config, token)
2123 .with_context(|| "Failed at creating adding the listener")?;
2124 proxy
2125 .activate_listener(&address, None)
2126 .with_context(|| "Failed at creating activating the listener")?;
2127
2128 let mut server = Server::new(
2129 event_loop,
2130 channel,
2131 server_scm_socket,
2132 sessions,
2133 pool,
2134 backends,
2135 Some(proxy),
2136 None,
2137 None,
2138 server_config,
2139 None,
2140 false,
2141 )
2142 .with_context(|| "Failed at creating server")?;
2143
2144 debug!("{} starting event loop", log_module_context!());
2145 server.run();
2146 debug!("{} ending event loop", log_module_context!());
2147 Ok(())
2148 }
2149}
2150
2151#[cfg(test)]
2152mod tests {
2153 extern crate tiny_http;
2154
2155 use std::{
2156 io::{Read, Write},
2157 net::TcpStream,
2158 str,
2159 sync::{Arc, Barrier},
2160 thread,
2161 time::Duration,
2162 };
2163
2164 use sozu_command::proto::command::SocketAddress;
2165
2166 use super::{testing::start_http_worker, *};
2167 use crate::sozu_command::{
2168 channel::Channel,
2169 config::ListenerBuilder,
2170 proto::command::{
2171 CustomHttpAnswers, LoadBalancingParams, PathRule, RulePosition, SoftStop,
2172 WorkerRequest, request::RequestType,
2173 },
2174 response::{Backend, HttpFrontend},
2175 };
2176
2177 #[test]
2194 fn get_h2_flood_config_clamps_a_zero_threshold_that_bypassed_config_load() {
2195 let mut config = ListenerBuilder::new_http(SocketAddress::new_v4(127, 0, 0, 1, 8080))
2196 .to_http(None)
2197 .expect("could not create listener config");
2198 config.h2_max_rst_stream_per_window = Some(0);
2201 config.h2_max_glitch_count = Some(0);
2202
2203 let listener =
2204 HttpListener::new(config, Token(0)).expect("could not build the HTTP listener");
2205 let flood_config = listener.get_h2_flood_config();
2206
2207 assert!(
2208 flood_config.max_rst_stream_per_window() >= 1,
2209 "max_rst_stream_per_window must be clamped to >= 1, got {}",
2210 flood_config.max_rst_stream_per_window()
2211 );
2212 assert!(
2213 flood_config.max_glitch_count() >= 1,
2214 "max_glitch_count must be clamped to >= 1, got {}",
2215 flood_config.max_glitch_count()
2216 );
2217 }
2218
2219 #[test]
2228 fn get_h2_flood_config_maps_every_listener_knob_to_its_own_threshold() {
2229 let mut config = ListenerBuilder::new_http(SocketAddress::new_v4(127, 0, 0, 1, 8080))
2230 .to_http(None)
2231 .expect("could not create listener config");
2232 config.h2_max_rst_stream_per_window = Some(101);
2233 config.h2_max_ping_per_window = Some(102);
2234 config.h2_max_settings_per_window = Some(103);
2235 config.h2_max_empty_data_per_window = Some(104);
2236 config.h2_max_window_update_stream0_per_window = Some(105);
2237 config.h2_max_continuation_frames = Some(106);
2238 config.h2_max_glitch_count = Some(107);
2239 config.h2_max_rst_stream_lifetime = Some(108);
2240 config.h2_max_rst_stream_abusive_lifetime = Some(109);
2241 config.h2_max_rst_stream_emitted_lifetime = Some(110);
2242 config.h2_max_header_list_size = Some(111);
2243 config.h2_max_header_table_size = Some(112);
2244 config.h2_max_header_fields = Some(113);
2245 config.h2_stream_refusal_percent = Some(114);
2246
2247 let listener =
2248 HttpListener::new(config, Token(0)).expect("could not build the HTTP listener");
2249 let flood = listener.get_h2_flood_config();
2250
2251 assert_eq!(flood.max_rst_stream_per_window(), 101);
2252 assert_eq!(flood.max_ping_per_window(), 102);
2253 assert_eq!(flood.max_settings_per_window(), 103);
2254 assert_eq!(flood.max_empty_data_per_window(), 104);
2255 assert_eq!(flood.max_window_update_stream0_per_window(), 105);
2256 assert_eq!(flood.max_continuation_frames(), 106);
2257 assert_eq!(flood.max_glitch_count(), 107);
2258 assert_eq!(flood.max_rst_stream_lifetime(), 108);
2259 assert_eq!(flood.max_rst_stream_abusive_lifetime(), 109);
2260 assert_eq!(flood.max_rst_stream_emitted_lifetime(), 110);
2261 assert_eq!(flood.max_header_list_size(), 111);
2262 assert_eq!(flood.max_header_table_size(), 112);
2263 assert_eq!(flood.max_header_fields(), 113);
2264 assert_eq!(flood.stream_refusal_percent(), 114);
2265 }
2266
2267 #[test]
2268 fn round_trip() {
2269 setup_test_logger!();
2270 let front_port = crate::testing::provide_port();
2271 let backend_server = Arc::new(
2272 tiny_http::Server::http("127.0.0.1:0").expect("could not create tiny_http server"),
2273 );
2274 let backend_port = backend_server
2275 .server_addr()
2276 .to_ip()
2277 .expect("tiny_http server should bind to IP address")
2278 .port();
2279
2280 let barrier = Arc::new(Barrier::new(2));
2281
2282 let config = ListenerBuilder::new_http(SocketAddress::new_v4(127, 0, 0, 1, front_port))
2283 .to_http(None)
2284 .expect("could not create listener config");
2285
2286 let (mut command, channel) =
2287 Channel::generate(1000, 10000).expect("should create a channel");
2288
2289 thread::scope(|s| {
2290 let backend_handle = backend_server.clone();
2291 let barrier_clone = barrier.to_owned();
2292 s.spawn(move || {
2293 setup_test_logger!();
2294 start_server(&backend_handle, barrier_clone);
2295 });
2296 barrier.wait();
2297
2298 s.spawn(move || {
2299 setup_test_logger!();
2300 start_http_worker(config, channel, 10, 16384)
2301 .expect("could not start the http server");
2302 });
2303
2304 let front = RequestHttpFrontend {
2305 cluster_id: Some("cluster_1".to_owned()),
2306 address: SocketAddress::new_v4(127, 0, 0, 1, front_port),
2307 hostname: "localhost".to_owned(),
2308 path: PathRule::prefix("/".to_owned()),
2309 ..Default::default()
2310 };
2311 command
2313 .write_message(&WorkerRequest {
2314 id: "ID_CLUSTER".to_owned(),
2315 content: RequestType::AddCluster(Cluster {
2316 cluster_id: "cluster_1".to_owned(),
2317 ..Default::default()
2318 })
2319 .into(),
2320 })
2321 .expect("could not send AddCluster");
2322 command
2323 .write_message(&WorkerRequest {
2324 id: "ID_ABCD".to_owned(),
2325 content: RequestType::AddHttpFrontend(front).into(),
2326 })
2327 .expect("could not send AddHttpFrontend");
2328 let backend = Backend {
2329 cluster_id: "cluster_1".into(),
2330 backend_id: "cluster_1-0".to_owned(),
2331 address: SocketAddress::new_v4(127, 0, 0, 1, backend_port).into(),
2332 load_balancing_parameters: Some(LoadBalancingParams::default()),
2333 sticky_id: None,
2334 backup: None,
2335 unix_path: None,
2336 };
2337 command
2338 .write_message(&WorkerRequest {
2339 id: "ID_EFGH".to_owned(),
2340 content: RequestType::AddBackend(backend.to_add_backend()).into(),
2341 })
2342 .expect("could not send AddBackend");
2343
2344 println!("test received: {:?}", command.read_message());
2345 println!("test received: {:?}", command.read_message());
2346 println!("test received: {:?}", command.read_message());
2347
2348 let mut client =
2349 TcpStream::connect(("127.0.0.1", front_port)).expect("could not connect to sozu");
2350
2351 client
2352 .set_read_timeout(Some(Duration::new(1, 0)))
2353 .expect("could not set read timeout");
2354 let request = format!(
2355 "GET / HTTP/1.1\r\nHost: localhost:{front_port}\r\nConnection: Close\r\n\r\n"
2356 );
2357 let w = client.write(request.as_bytes());
2358 println!("http client write: {w:?}");
2359
2360 barrier.wait();
2361 let mut buffer = [0; 4096];
2362 let mut index = 0;
2363
2364 let expected_len = 191;
2367
2368 loop {
2369 assert!(index <= expected_len);
2370 if index == expected_len {
2371 break;
2372 }
2373
2374 let r = client.read(&mut buffer[index..]);
2375 println!("http client read: {r:?}");
2376 match r {
2377 Err(e) => panic!("client request should not fail. Error: {e:?}"),
2378 Ok(sz) => {
2379 index += sz;
2380 }
2381 }
2382 }
2383 println!(
2384 "Response: {}",
2385 str::from_utf8(&buffer[..index]).expect("could not make string from buffer")
2386 );
2387
2388 command
2390 .write_message(&WorkerRequest {
2391 id: "ID_STOP".to_owned(),
2392 content: RequestType::SoftStop(SoftStop {}).into(),
2393 })
2394 .expect("could not send SoftStop");
2395 backend_server.unblock();
2397 });
2398 }
2399
2400 #[test]
2401 fn keep_alive() {
2402 setup_test_logger!();
2403 let front_port = crate::testing::provide_port();
2404 let backend_server = Arc::new(
2405 tiny_http::Server::http("127.0.0.1:0").expect("could not create tiny_http server"),
2406 );
2407 let backend_port = backend_server
2408 .server_addr()
2409 .to_ip()
2410 .expect("tiny_http server should bind to IP address")
2411 .port();
2412
2413 let barrier = Arc::new(Barrier::new(2));
2414
2415 let config = ListenerBuilder::new_http(SocketAddress::new_v4(127, 0, 0, 1, front_port))
2416 .to_http(None)
2417 .expect("could not create listener config");
2418
2419 let (mut command, channel) =
2420 Channel::generate(1000, 10000).expect("should create a channel");
2421
2422 thread::scope(|s| {
2423 let backend_handle = backend_server.clone();
2424 let barrier_clone = barrier.to_owned();
2425 s.spawn(move || {
2426 setup_test_logger!();
2427 start_server(&backend_handle, barrier_clone);
2428 });
2429 barrier.wait();
2430
2431 s.spawn(move || {
2432 setup_test_logger!();
2433 start_http_worker(config, channel, 10, 16384)
2434 .expect("could not start the http server");
2435 });
2436
2437 let front = RequestHttpFrontend {
2438 address: SocketAddress::new_v4(127, 0, 0, 1, front_port),
2439 hostname: "localhost".to_owned(),
2440 path: PathRule::prefix("/".to_owned()),
2441 cluster_id: Some("cluster_1".to_owned()),
2442 ..Default::default()
2443 };
2444 command
2446 .write_message(&WorkerRequest {
2447 id: "ID_CLUSTER".to_owned(),
2448 content: RequestType::AddCluster(Cluster {
2449 cluster_id: "cluster_1".to_owned(),
2450 ..Default::default()
2451 })
2452 .into(),
2453 })
2454 .expect("could not send AddCluster");
2455 command
2456 .write_message(&WorkerRequest {
2457 id: "ID_ABCD".to_owned(),
2458 content: RequestType::AddHttpFrontend(front).into(),
2459 })
2460 .expect("could not send AddHttpFrontend");
2461 let backend = Backend {
2462 address: SocketAddress::new_v4(127, 0, 0, 1, backend_port).into(),
2463 backend_id: "cluster_1-0".to_owned(),
2464 backup: None,
2465 cluster_id: "cluster_1".into(),
2466 load_balancing_parameters: Some(LoadBalancingParams::default()),
2467 sticky_id: None,
2468 unix_path: None,
2469 };
2470 command
2471 .write_message(&WorkerRequest {
2472 id: "ID_EFGH".to_owned(),
2473 content: RequestType::AddBackend(backend.to_add_backend()).into(),
2474 })
2475 .expect("could not send AddBackend");
2476
2477 println!("test received: {:?}", command.read_message());
2478 println!("test received: {:?}", command.read_message());
2479 println!("test received: {:?}", command.read_message());
2480
2481 let mut client =
2482 TcpStream::connect(("127.0.0.1", front_port)).expect("could not connect to sozu");
2483 client
2484 .set_read_timeout(Some(Duration::new(5, 0)))
2485 .expect("could not set read timeout");
2486
2487 let expected_len = 191;
2490
2491 let request = format!("GET / HTTP/1.1\r\nHost: localhost:{front_port}\r\n\r\n");
2492 let w = client
2493 .write(request.as_bytes())
2494 .expect("could not write first request");
2495 println!("http client write: {w:?}");
2496 barrier.wait();
2497
2498 let mut buffer = [0; 4096];
2499 let mut index = 0;
2500
2501 loop {
2502 assert!(index <= expected_len);
2503 if index == expected_len {
2504 break;
2505 }
2506
2507 let r = client.read(&mut buffer[index..]);
2508 println!("http client read: {r:?}");
2509 match r {
2510 Err(e) => panic!("client request should not fail. Error: {e:?}"),
2511 Ok(sz) => {
2512 index += sz;
2513 }
2514 }
2515 }
2516
2517 println!(
2518 "Response: {}",
2519 str::from_utf8(&buffer[..index]).expect("could not make string from buffer")
2520 );
2521
2522 println!("first request ended, will send second one");
2523 let request2 = format!("GET / HTTP/1.1\r\nHost: localhost:{front_port}\r\n\r\n");
2524 let w2 = client.write(request2.as_bytes());
2525 println!("http client write: {w2:?}");
2526 barrier.wait();
2527
2528 let mut buffer2 = [0; 4096];
2529 let mut index = 0;
2530
2531 loop {
2532 assert!(index <= expected_len);
2533 if index == expected_len {
2534 break;
2535 }
2536
2537 let r2 = client.read(&mut buffer2[index..]);
2538 println!("http client read: {r2:?}");
2539 match r2 {
2540 Err(e) => panic!("client request should not fail. Error: {e:?}"),
2541 Ok(sz) => {
2542 index += sz;
2543 }
2544 }
2545 }
2546 println!(
2547 "Response: {}",
2548 str::from_utf8(&buffer2[..index]).expect("could not make string from buffer")
2549 );
2550
2551 command
2553 .write_message(&WorkerRequest {
2554 id: "ID_STOP".to_owned(),
2555 content: RequestType::SoftStop(SoftStop {}).into(),
2556 })
2557 .expect("could not send SoftStop");
2558 backend_server.unblock();
2560 });
2561 }
2562
2563 use self::tiny_http::Response;
2564
2565 fn start_server(server: &tiny_http::Server, barrier: Arc<Barrier>) {
2566 let addr = server.server_addr();
2567 info!("starting web server on {:?}", addr);
2568 barrier.wait();
2569
2570 for request in server.incoming_requests() {
2571 info!(
2572 "backend web server got request -> method: {:?}, url: {:?}, headers: {:?}",
2573 request.method(),
2574 request.url(),
2575 request.headers()
2576 );
2577
2578 let response = Response::from_string("hello world");
2579 request
2580 .respond(response)
2581 .expect("could not respond to request");
2582 info!("backend web server sent response");
2583 barrier.wait();
2584 info!("server session stopped");
2585 }
2586
2587 println!("server on {addr:?} closed");
2588 }
2589
2590 #[test]
2591 fn frontend_from_request_test() {
2592 let cluster_id1 = ClusterId::from("cluster_1");
2593 let cluster_id2 = ClusterId::from("cluster_2");
2594 let cluster_id3 = ClusterId::from("cluster_3");
2595 let uri1 = "/".to_owned();
2596 let uri2 = "/yolo".to_owned();
2597 let uri3 = "/yolo/swag".to_owned();
2598
2599 let mut fronts = Router::new();
2600 fronts
2601 .add_http_front(&HttpFrontend {
2602 address: "0.0.0.0:80".parse().unwrap(),
2603 hostname: "lolcatho.st".to_owned(),
2604 method: None,
2605 path: PathRule::prefix(uri1),
2606 position: RulePosition::Tree,
2607 cluster_id: Some(cluster_id1),
2608 tags: None,
2609 redirect: None,
2610 redirect_scheme: None,
2611 redirect_template: None,
2612 rewrite_host: None,
2613 rewrite_path: None,
2614 rewrite_port: None,
2615 required_auth: None,
2616 headers: Vec::new(),
2617 hsts: None,
2618 header_predicates: Vec::new(),
2619 })
2620 .expect("Could not add http frontend");
2621 fronts
2622 .add_http_front(&HttpFrontend {
2623 address: "0.0.0.0:80".parse().unwrap(),
2624 hostname: "lolcatho.st".to_owned(),
2625 method: None,
2626 path: PathRule::prefix(uri2),
2627 position: RulePosition::Tree,
2628 cluster_id: Some(cluster_id2),
2629 tags: None,
2630 redirect: None,
2631 redirect_scheme: None,
2632 redirect_template: None,
2633 rewrite_host: None,
2634 rewrite_path: None,
2635 rewrite_port: None,
2636 required_auth: None,
2637 headers: Vec::new(),
2638 hsts: None,
2639 header_predicates: Vec::new(),
2640 })
2641 .expect("Could not add http frontend");
2642 fronts
2643 .add_http_front(&HttpFrontend {
2644 address: "0.0.0.0:80".parse().unwrap(),
2645 hostname: "lolcatho.st".to_owned(),
2646 method: None,
2647 path: PathRule::prefix(uri3),
2648 position: RulePosition::Tree,
2649 cluster_id: Some(cluster_id3),
2650 tags: None,
2651 redirect: None,
2652 redirect_scheme: None,
2653 redirect_template: None,
2654 rewrite_host: None,
2655 rewrite_path: None,
2656 rewrite_port: None,
2657 required_auth: None,
2658 headers: Vec::new(),
2659 hsts: None,
2660 header_predicates: Vec::new(),
2661 })
2662 .expect("Could not add http frontend");
2663 fronts
2664 .add_http_front(&HttpFrontend {
2665 address: "0.0.0.0:80".parse().unwrap(),
2666 hostname: "other.domain".to_owned(),
2667 method: None,
2668 path: PathRule::prefix("/test".to_owned()),
2669 position: RulePosition::Tree,
2670 cluster_id: Some("cluster_1".into()),
2671 tags: None,
2672 redirect: None,
2673 redirect_scheme: None,
2674 redirect_template: None,
2675 rewrite_host: None,
2676 rewrite_path: None,
2677 rewrite_port: None,
2678 required_auth: None,
2679 headers: Vec::new(),
2680 hsts: None,
2681 header_predicates: Vec::new(),
2682 })
2683 .expect("Could not add http frontend");
2684
2685 let address = SocketAddress::new_v4(127, 0, 0, 1, 1030);
2686
2687 let default_config = ListenerBuilder::new_http(address)
2688 .to_http(None)
2689 .expect("Could not create default HTTP listener config");
2690
2691 let listener = HttpListener {
2692 parked_listener: None,
2693 nodelay_backlog: false,
2694 listener: None,
2695 address: address.into(),
2696 fronts,
2697 answers: Rc::new(RefCell::new(HttpAnswers::new(&BTreeMap::new()).unwrap())),
2698 config: default_config,
2699 token: Token(0),
2700 active: true,
2701 tags: BTreeMap::new(),
2702 };
2703
2704 let frontend1 = listener.frontend_from_request(
2705 "lolcatho.st",
2706 "/",
2707 &Method::Get,
2708 &crate::router::NoHeaders,
2709 );
2710 let frontend2 = listener.frontend_from_request(
2711 "lolcatho.st",
2712 "/test",
2713 &Method::Get,
2714 &crate::router::NoHeaders,
2715 );
2716 let frontend3 = listener.frontend_from_request(
2717 "lolcatho.st",
2718 "/yolo/test",
2719 &Method::Get,
2720 &crate::router::NoHeaders,
2721 );
2722 let frontend4 = listener.frontend_from_request(
2723 "lolcatho.st",
2724 "/yolo/swag",
2725 &Method::Get,
2726 &crate::router::NoHeaders,
2727 );
2728 let frontend5 =
2729 listener.frontend_from_request("domain", "/", &Method::Get, &crate::router::NoHeaders);
2730 assert_eq!(
2731 frontend1
2732 .expect("should find frontend")
2733 .cluster_id
2734 .as_deref(),
2735 Some("cluster_1")
2736 );
2737 assert_eq!(
2738 frontend2
2739 .expect("should find frontend")
2740 .cluster_id
2741 .as_deref(),
2742 Some("cluster_1")
2743 );
2744 assert_eq!(
2745 frontend3
2746 .expect("should find frontend")
2747 .cluster_id
2748 .as_deref(),
2749 Some("cluster_2")
2750 );
2751 assert_eq!(
2752 frontend4
2753 .expect("should find frontend")
2754 .cluster_id
2755 .as_deref(),
2756 Some("cluster_3")
2757 );
2758 assert!(frontend5.is_err());
2759 }
2760
2761 #[test]
2766 fn a_listener_with_an_unknown_forwarded_headers_value_is_refused() {
2767 let address = SocketAddress::new_v4(127, 0, 0, 1, 1041);
2768 let build = |forwarded_headers: Option<i32>| {
2769 let mut cfg = ListenerBuilder::new_http(address)
2770 .to_http(None)
2771 .expect("default HTTP listener config");
2772 cfg.forwarded_headers = forwarded_headers;
2773 HttpListener::new(cfg, Token(0))
2774 };
2775 assert!(build(Some(42)).is_err(), "an unknown mode must be refused");
2776 let listener = build(Some(ForwardedHeaders::Rfc7239 as i32)).expect("a known mode");
2777 assert_eq!(listener.get_forwarded_headers(), ForwardedHeaders::Rfc7239);
2778 }
2779
2780 #[test]
2781 fn h2_stream_idle_timeout_inherits_back_timeout() {
2782 let address = SocketAddress::new_v4(127, 0, 0, 1, 1040);
2783 let build = |back_timeout: u32, explicit: Option<u32>| -> HttpListener {
2784 let mut cfg = ListenerBuilder::new_http(address)
2785 .to_http(None)
2786 .expect("default HTTP listener config");
2787 cfg.back_timeout = back_timeout;
2788 cfg.h2_stream_idle_timeout_seconds = explicit;
2789 HttpListener::new(cfg, Token(0)).expect("build listener")
2790 };
2791
2792 assert_eq!(
2794 build(180, None).get_h2_stream_idle_timeout(),
2795 Duration::from_secs(180)
2796 );
2797
2798 assert_eq!(
2801 build(5, None).get_h2_stream_idle_timeout(),
2802 Duration::from_secs(30)
2803 );
2804
2805 assert_eq!(
2808 build(180, Some(10)).get_h2_stream_idle_timeout(),
2809 Duration::from_secs(10)
2810 );
2811 assert_eq!(
2812 build(5, Some(600)).get_h2_stream_idle_timeout(),
2813 Duration::from_secs(600)
2814 );
2815
2816 assert_eq!(
2818 build(180, Some(0)).get_h2_stream_idle_timeout(),
2819 Duration::from_secs(1)
2820 );
2821 }
2822
2823 #[test]
2844 fn a_listener_answers_reload_publishes_a_new_registry() {
2845 let address = SocketAddress::new_v4(127, 0, 0, 1, 1041);
2846 let config = ListenerBuilder::new_http(address)
2847 .to_http(None)
2848 .expect("default HTTP listener config");
2849 let mut listener = HttpListener::new(config, Token(0)).expect("build listener");
2850
2851 listener
2853 .answers
2854 .borrow_mut()
2855 .add_cluster_answers(
2856 "cluster_1",
2857 &BTreeMap::from([(
2858 "503".to_owned(),
2859 "HTTP/1.1 503 Service Unavailable\r\nContent-Length: 0\r\n\r\n".to_owned(),
2860 )]),
2861 )
2862 .expect("the cluster override must compile");
2863
2864 let captured = listener.get_answers().clone();
2866
2867 listener
2868 .update_config(&UpdateHttpListenerConfig {
2869 address,
2870 answers: BTreeMap::from([(
2871 "404".to_owned(),
2872 "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n".to_owned(),
2873 )]),
2874 ..Default::default()
2875 })
2876 .expect("the answers patch must apply");
2877
2878 assert!(
2879 !Rc::ptr_eq(&captured, listener.get_answers()),
2880 "a reload must publish a NEW answer registry: rewriting the captured \
2881 one reaches every request already in flight"
2882 );
2883 assert!(
2884 captured.borrow().cluster_answers.contains_key("cluster_1"),
2885 "the registry captured before the reload must keep its cluster \
2886 overrides: a request in flight would otherwise lose its custom \
2887 pages mid-response"
2888 );
2889 assert!(
2890 listener
2891 .get_answers()
2892 .borrow()
2893 .cluster_answers
2894 .contains_key("cluster_1"),
2895 "the published registry must carry the cluster overrides forward"
2896 );
2897 }
2898
2899 #[test]
2911 fn a_rejected_patch_leaves_the_listener_unchanged() {
2912 let address = SocketAddress::new_v4(127, 0, 0, 1, 1042);
2913 let config = ListenerBuilder::new_http(address)
2914 .to_http(None)
2915 .expect("default HTTP listener config");
2916 let mut listener = HttpListener::new(config, Token(0)).expect("build listener");
2917
2918 let valid_prefix = UpdateHttpListenerConfig {
2919 address,
2920 front_timeout: Some(99),
2921 sozu_id_header: Some("X-Edge-Id".to_owned()),
2922 forwarded_headers: Some(ForwardedHeaders::Rfc7239 as i32),
2923 h2_max_rst_stream_per_window: Some(7),
2924 ..Default::default()
2925 };
2926 let rejected = [
2927 UpdateHttpListenerConfig {
2928 answers: BTreeMap::from([(
2929 "404".to_owned(),
2930 "not a valid http response".to_owned(),
2931 )]),
2932 ..valid_prefix.clone()
2933 },
2934 UpdateHttpListenerConfig {
2935 http_answers: Some(CustomHttpAnswers {
2936 answer_503: Some("not a valid http response".to_owned()),
2937 ..Default::default()
2938 }),
2939 ..valid_prefix.clone()
2940 },
2941 ];
2942 for patch in rejected {
2943 let config_before = listener.config.clone();
2944 let answers_before = listener.get_answers().clone();
2945 let error = listener
2946 .update_config(&patch)
2947 .expect_err("an unparseable template must be refused");
2948 assert!(
2949 matches!(error, ListenerError::TemplateParse(..)),
2950 "expected TemplateParse, got {error:?}"
2951 );
2952 assert_eq!(
2953 listener.config, config_before,
2954 "a refused patch must leave the listener configuration unchanged"
2955 );
2956 assert!(
2957 Rc::ptr_eq(&answers_before, listener.get_answers()),
2958 "a refused patch must not publish a new answer registry"
2959 );
2960 }
2961
2962 listener
2963 .update_config(&valid_prefix)
2964 .expect("the valid prefix alone must apply");
2965 assert_eq!(listener.config.front_timeout, 99);
2966 assert_eq!(listener.config.sozu_id_header.as_deref(), Some("X-Edge-Id"));
2967 }
2968
2969 #[cfg(target_os = "linux")]
2980 #[test]
2981 fn closed_sessions_leave_their_sockets_to_close() {
2982 use std::os::fd::AsRawFd;
2983
2984 use crate::protocol::mux::test_support::epoll_watches;
2985
2986 let address = SocketAddress::new_v4(127, 0, 0, 1, crate::testing::provide_port());
2987 let parts = crate::testing::prebuild_server(4, 16_384, false)
2988 .expect("test server parts must build");
2989 let registry = parts
2990 .registry
2991 .try_clone()
2992 .expect("the test registry must clone");
2993 let sessions = parts.sessions.clone();
2994 let listener_token = {
2995 let mut sessions = sessions.borrow_mut();
2996 let entry = sessions.slab.vacant_entry();
2997 let key = entry.key();
2998 entry.insert(Rc::new(RefCell::new(crate::server::ListenSession {
2999 protocol: Protocol::HTTPListen,
3000 })));
3001 Token(key)
3002 };
3003 let mut proxy = HttpProxy::new(parts.registry, parts.sessions, parts.pool, parts.backends);
3004 proxy
3005 .add_listener(
3006 ListenerBuilder::new_http(address)
3007 .to_http(None)
3008 .expect("test listener config must build"),
3009 listener_token,
3010 )
3011 .expect("test listener must register");
3012 let proxy = Rc::new(RefCell::new(proxy));
3013
3014 let peer_listener =
3015 std::net::TcpListener::bind("127.0.0.1:0").expect("bind the test peer listener");
3016 let _client = TcpStream::connect(
3017 peer_listener
3018 .local_addr()
3019 .expect("the test peer listener has an address"),
3020 )
3021 .expect("connect the test client");
3022 let (accepted, peer) = peer_listener.accept().expect("accept the test client");
3023 accepted
3024 .set_nonblocking(true)
3025 .expect("the front socket must go non-blocking");
3026 let front = mio::net::TcpStream::from_std(accepted);
3027 let front_fd = front.as_raw_fd();
3028
3029 let session_token = sessions.borrow().slab.vacant_key();
3030 proxy
3031 .borrow_mut()
3032 .create_session(
3033 front,
3034 peer,
3035 ListenToken(listener_token.0),
3036 Duration::ZERO,
3037 proxy.clone(),
3038 )
3039 .expect("the session must be created");
3040 assert!(
3041 sessions.borrow().slab.contains(session_token),
3042 "the session takes the slot that was vacant"
3043 );
3044 assert!(
3045 epoll_watches(®istry, front_fd),
3046 "precondition: the front socket is in the epoll set"
3047 );
3048
3049 let session = sessions.borrow_mut().slab.remove(session_token);
3051 session.borrow_mut().close();
3052 assert!(
3053 epoll_watches(®istry, front_fd),
3054 "HttpSession::close must not deregister a front socket it is about \
3055 to close: the close removes it, the EPOLL_CTL_DEL is a wasted syscall"
3056 );
3057
3058 drop(session);
3059 assert!(
3060 !epoll_watches(®istry, front_fd),
3061 "dropping the session closes the front socket's only descriptor, \
3062 which must take it out of the epoll set"
3063 );
3064 drop(parts.event_loop);
3065 }
3066
3067 #[test]
3082 fn a_steady_state_accepted_socket_inherits_nodelay_and_its_peer_address() {
3083 let address = SocketAddress::new_v4(127, 0, 0, 1, crate::testing::provide_port());
3084 let parts = crate::testing::prebuild_server(4, 16_384, false)
3085 .expect("test server parts must build");
3086 let listener_token = {
3087 let mut sessions = parts.sessions.borrow_mut();
3088 let entry = sessions.slab.vacant_entry();
3089 let key = entry.key();
3090 entry.insert(Rc::new(RefCell::new(crate::server::ListenSession {
3091 protocol: Protocol::HTTPListen,
3092 })));
3093 Token(key)
3094 };
3095 let mut proxy = HttpProxy::new(parts.registry, parts.sessions, parts.pool, parts.backends);
3096 proxy
3097 .add_listener(
3098 ListenerBuilder::new_http(address)
3099 .to_http(None)
3100 .expect("test listener config must build"),
3101 listener_token,
3102 )
3103 .expect("test listener must register");
3104 proxy
3105 .activate_listener(&address.into(), None)
3106 .expect("test listener must activate");
3107
3108 assert!(
3110 matches!(
3111 proxy.accept(ListenToken(listener_token.0)),
3112 Err(AcceptError::WouldBlock)
3113 ),
3114 "precondition: nothing is queued on a freshly bound listener"
3115 );
3116
3117 let client =
3118 TcpStream::connect(SocketAddr::from(address)).expect("connect the test client");
3119 let (accepted, peer) = proxy
3120 .accept(ListenToken(listener_token.0))
3121 .expect("the completed connection must be accepted");
3122
3123 assert_eq!(
3124 socket2::SockRef::from(&accepted).tcp_nodelay().ok(),
3125 Some(true),
3126 "a socket accepted in steady state must inherit TCP_NODELAY from its listener"
3127 );
3128 assert_eq!(
3129 peer,
3130 client.local_addr().expect("the test client has an address"),
3131 "the peer address handed over must be the client's"
3132 );
3133 drop(parts.event_loop);
3134 }
3135}
3136
3137#[cfg(test)]
3138mod listener_sibling_tests {
3139 use sozu_command::{
3140 config::ListenerBuilder,
3141 proto::command::{HstsConfig, PathRule, RequestHttpFrontend, RulePosition, SocketAddress},
3142 };
3143
3144 use super::*;
3145 use crate::testing::{ServerParts, prebuild_server, provide_port};
3146
3147 fn proxy() -> HttpProxy {
3148 let ServerParts {
3149 registry,
3150 sessions,
3151 pool,
3152 backends,
3153 ..
3154 } = prebuild_server(16, 16384, false).expect("could not prebuild a test server");
3155 HttpProxy::new(registry, sessions, pool, backends)
3156 }
3157
3158 fn add(proxy: &mut HttpProxy, address: SocketAddress, interface: Option<&str>, token: usize) {
3159 let config = ListenerBuilder::new_http(address)
3160 .with_interface(interface)
3161 .to_http(None)
3162 .expect("could not build the listener config");
3163 proxy
3164 .add_listener(config, Token(token))
3165 .expect("could not add the listener");
3166 }
3167
3168 fn front(address: SocketAddress) -> RequestHttpFrontend {
3169 RequestHttpFrontend {
3170 cluster_id: Some("cluster-a".to_owned()),
3171 address,
3172 hostname: "example.com".to_owned(),
3173 path: PathRule::prefix("/".to_owned()),
3174 ..Default::default()
3175 }
3176 }
3177
3178 fn routes(proxy: &HttpProxy, token: usize) -> bool {
3179 proxy.listeners[&Token(token)]
3180 .borrow()
3181 .fronts
3182 .lookup("example.com", "/", &Method::Get)
3183 .is_ok()
3184 }
3185
3186 #[test]
3192 fn a_listener_added_next_to_a_sibling_takes_over_its_frontends() {
3193 let mut proxy = proxy();
3194 let address = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
3195 add(&mut proxy, address, None, 0);
3196 proxy
3197 .add_http_frontend(front(address))
3198 .expect("add the frontend");
3199 add(&mut proxy, address, Some("lo"), 1);
3200
3201 assert!(routes(&proxy, 0));
3202 assert!(
3203 routes(&proxy, 1),
3204 "the listener added after the frontend must route it too"
3205 );
3206
3207 proxy
3208 .remove_http_frontend(front(address))
3209 .expect("remove the frontend");
3210 assert!(!routes(&proxy, 0));
3211 assert!(!routes(&proxy, 1));
3212 }
3213
3214 #[test]
3215 fn retained_frontends_are_not_replayed_when_a_sibling_already_has_them() {
3216 let mut proxy = proxy();
3217 let address = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
3218 add(&mut proxy, address, None, 0);
3219 proxy
3220 .add_http_frontend(front(address))
3221 .expect("add the frontend");
3222 let sibling = ListenerBuilder::new_http(address)
3223 .with_interface(Some("lo"))
3224 .to_http(None)
3225 .expect("build sibling listener config");
3226
3227 proxy
3228 .add_listener_with_retained_frontends(sibling, Token(1), vec![front(address)])
3229 .expect("the sibling must clone routes without replaying duplicates");
3230
3231 assert!(routes(&proxy, 0));
3232 assert!(routes(&proxy, 1));
3233 }
3234
3235 #[test]
3236 fn failed_retained_frontend_replay_discards_only_the_new_listener() {
3237 let mut proxy = proxy();
3238 let kept_address = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
3239 let rejected_address = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
3240 add(&mut proxy, kept_address, None, 0);
3241 let rejected = ListenerBuilder::new_http(rejected_address)
3242 .to_http(None)
3243 .expect("build rejected listener config");
3244 let invalid_plaintext_hsts = RequestHttpFrontend {
3245 hsts: Some(HstsConfig {
3246 enabled: Some(true),
3247 ..Default::default()
3248 }),
3249 ..front(rejected_address)
3250 };
3251
3252 assert!(
3253 proxy
3254 .add_listener_with_retained_frontends(
3255 rejected,
3256 Token(1),
3257 vec![invalid_plaintext_hsts],
3258 )
3259 .is_err()
3260 );
3261 assert!(proxy.listeners.contains_key(&Token(0)));
3262 assert!(!proxy.listeners.contains_key(&Token(1)));
3263 }
3264
3265 #[test]
3269 fn removing_a_frontend_reaches_every_listener_and_reports_a_failure() {
3270 let mut proxy = proxy();
3271 let address = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
3272 add(&mut proxy, address, None, 0);
3273 add(&mut proxy, address, Some("lo"), 1);
3274 let pre = RequestHttpFrontend {
3277 position: RulePosition::Pre.into(),
3278 ..front(address)
3279 };
3280 let frontend = pre
3281 .clone()
3282 .to_frontend()
3283 .expect("the test frontend must convert");
3284 proxy.listeners[&Token(1)]
3285 .borrow_mut()
3286 .add_http_front(frontend)
3287 .expect("add the route to one listener");
3288
3289 assert!(
3290 proxy.remove_http_frontend(pre).is_err(),
3291 "the listener that lacked the route must report it"
3292 );
3293 assert!(
3294 !routes(&proxy, 1),
3295 "the failure on one listener must not spare its sibling's route"
3296 );
3297 }
3298}