sozu_lib/protocol/proxy_protocol/
send.rs1use std::{
9 cell::RefCell,
10 io::{ErrorKind, Write},
11 rc::Rc,
12};
13
14use mio::{Token, net::TcpStream};
15use rusty_ulid::Ulid;
16use sozu_command::logging::ansi_palette;
17
18use crate::metrics::names;
19use crate::{
20 BackendConnectionStatus, Protocol, Readiness, SessionMetrics, SessionResult,
21 pool::Checkout,
22 protocol::{
23 pipe::{Pipe, WebSocketContext},
24 proxy_protocol::header::{Command, HeaderV2, ProxyProtocolHeader},
25 },
26 socket::SocketHandler,
27 sozu_command::ready::Ready,
28 tcp::TcpListener,
29};
30
31#[allow(unused_macros)]
36macro_rules! log_module_context {
37 () => {{
38 let (open, reset, _, _, _) = ansi_palette();
39 format!("{open}PROXY-SEND{reset}\t >>>", open = open, reset = reset)
40 }};
41}
42
43macro_rules! log_context {
48 ($self:expr) => {{
49 let (open, reset, grey, gray, white) = ansi_palette();
50 format!(
51 "{open}PROXY-SEND{reset}\t{grey}Session{reset}({gray}frontend{reset}={white}{frontend}{reset}, {gray}backend{reset}={white}{backend}{reset}, {gray}front_readiness{reset}={white}{front_readiness}{reset}, {gray}back_readiness{reset}={white}{back_readiness}{reset})\t >>>",
52 open = open,
53 reset = reset,
54 grey = grey,
55 gray = gray,
56 white = white,
57 frontend = $self.frontend_token.0,
58 backend = $self.backend_token.map(|t| t.0.to_string()).unwrap_or_else(|| "<none>".to_string()),
59 front_readiness = $self.frontend_readiness,
60 back_readiness = $self.backend_readiness,
61 )
62 }};
63}
64
65pub struct SendProxyProtocol<Front: SocketHandler> {
66 cursor_header: usize,
67 pub backend_readiness: Readiness,
68 pub backend_token: Option<Token>,
69 pub backend: Option<TcpStream>,
70 pub frontend_readiness: Readiness,
71 pub frontend_token: Token,
72 pub frontend: Front,
73 pub header: Option<Vec<u8>>,
74 pub request_id: Ulid,
75}
76
77impl<Front: SocketHandler> SendProxyProtocol<Front> {
78 pub fn new(
84 frontend: Front,
85 frontend_token: Token,
86 request_id: Ulid,
87 backend: Option<TcpStream>,
88 ) -> Self {
89 SendProxyProtocol {
90 header: None,
91 frontend,
92 request_id,
93 backend,
94 frontend_token,
95 backend_token: None,
96 frontend_readiness: Readiness {
97 interest: Ready::HUP | Ready::ERROR,
98 event: Ready::EMPTY,
99 },
100 backend_readiness: Readiness {
101 interest: Ready::HUP | Ready::ERROR,
102 event: Ready::EMPTY,
103 },
104 cursor_header: 0,
105 }
106 }
107
108 pub fn back_writable(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
111 debug!(
112 "{} trying to write proxy protocol header",
113 log_context!(self)
114 );
115
116 if self.header.is_none()
118 && let Ok(local_addr) = self.front_socket().local_addr()
119 {
120 let Ok(frontend_addr) = self.front_socket().peer_addr() else {
121 return SessionResult::Close;
122 };
123 let v2 = HeaderV2::new(Command::Proxy, frontend_addr, local_addr);
124 let declared_len = v2.len();
125 let serialized = ProxyProtocolHeader::V2(v2).into_bytes();
126 debug_assert_eq!(
131 serialized.len(),
132 declared_len,
133 "serialized send header length must match HeaderV2::len()"
134 );
135 debug_assert!(
136 serialized.len() >= 16,
137 "a v2 send header is at least its 16-byte fixed prefix"
138 );
139 self.header = Some(serialized);
140 };
141
142 if let Some(ref mut socket) = self.backend
143 && let Some(ref mut header) = self.header
144 {
145 loop {
146 debug_assert!(
149 self.cursor_header <= header.len(),
150 "send cursor must stay within the serialized header"
151 );
152 let remaining = header.len() - self.cursor_header;
153 match socket.write(&header[self.cursor_header..]) {
154 Ok(sz) => {
155 debug_assert!(
156 sz <= remaining,
157 "socket.write cannot send more than the unsent header tail"
158 );
159 let cursor_before = self.cursor_header;
160 self.cursor_header += sz;
161 debug_assert_eq!(
164 self.cursor_header,
165 cursor_before + sz,
166 "send cursor advances by exactly the bytes written"
167 );
168 debug_assert!(
169 self.cursor_header <= header.len(),
170 "send cursor must not pass the header length"
171 );
172 count!(names::backend::BACK_BYTES_OUT, sz as i64);
173 metrics.backend_bout += sz;
174
175 if self.cursor_header == header.len() {
176 debug!("{} proxy protocol sent, upgrading", log_context!(self));
177 return SessionResult::Upgrade;
178 }
179 }
180 Err(e) => match e.kind() {
181 ErrorKind::WouldBlock => {
182 self.backend_readiness.event.remove(Ready::WRITABLE);
183 return SessionResult::Continue;
184 }
185 e => {
186 incr!(names::proxy_protocol::ERRORS);
187 debug!("{} write error: {:?}", log_context!(self), e);
188 return SessionResult::Close;
189 }
190 },
191 }
192 }
193 }
194
195 error!(
196 "{} started send proxy protocol with no header or backend socket",
197 log_context!(self)
198 );
199 SessionResult::Close
200 }
201
202 pub fn front_socket(&self) -> &TcpStream {
203 self.frontend.socket_ref()
204 }
205
206 pub fn front_socket_mut(&mut self) -> &mut TcpStream {
207 self.frontend.socket_mut()
208 }
209
210 pub fn back_socket(&self) -> Option<&TcpStream> {
211 self.backend.as_ref()
212 }
213
214 pub fn back_socket_mut(&mut self) -> Option<&mut TcpStream> {
215 self.backend.as_mut()
216 }
217
218 pub fn set_back_socket(&mut self, socket: TcpStream) {
219 self.backend = Some(socket);
220 }
221
222 pub fn back_token(&self) -> Option<Token> {
223 self.backend_token
224 }
225
226 pub fn set_back_token(&mut self, token: Token) {
227 self.backend_token = Some(token);
228 }
229
230 pub fn set_back_connected(&mut self, status: BackendConnectionStatus) {
231 if status == BackendConnectionStatus::Connected {
232 self.backend_readiness.interest.insert(Ready::WRITABLE);
233 }
234 }
235
236 pub fn into_pipe(
237 mut self,
238 front_buf: Checkout,
239 back_buf: Checkout,
240 listener: Rc<RefCell<TcpListener>>,
241 ) -> Pipe<Front, TcpListener> {
242 let backend_socket = self.backend.take().unwrap();
243 let addr = self.front_socket().peer_addr().ok();
244
245 let mut pipe = Pipe::new(
246 back_buf,
247 None,
248 Some(backend_socket),
249 None,
250 None,
251 None,
252 None,
253 front_buf,
254 self.frontend_token,
255 self.frontend,
256 listener,
257 Protocol::TCP,
258 self.request_id,
259 self.request_id,
260 addr,
261 WebSocketContext::Tcp,
262 );
263
264 pipe.frontend_readiness = self.frontend_readiness;
265 pipe.backend_readiness = self.backend_readiness;
266
267 pipe.frontend_readiness.interest.insert(Ready::READABLE);
268 pipe.backend_readiness.interest.insert(Ready::READABLE);
269
270 if let Some(back_token) = self.backend_token {
271 pipe.set_back_token(back_token);
272 }
273
274 pipe
275 }
276}
277
278#[cfg(test)]
279mod send_test {
280 use std::{
281 io::Read,
282 net::{SocketAddr, TcpListener as StdTcpListener, TcpStream as StdTcpStream},
283 os::unix::io::{FromRawFd, IntoRawFd},
284 sync::{Arc, Barrier},
285 thread::{self, JoinHandle},
286 };
287
288 use mio::net::{TcpListener, TcpStream};
289 use rusty_ulid::Ulid;
290
291 use super::{
292 super::parser::parse_v2_header, BackendConnectionStatus, ErrorKind, SendProxyProtocol,
293 SessionMetrics, SessionResult, Token,
294 };
295
296 #[test]
297 fn it_should_send_a_proxy_protocol_header_to_the_upstream_backend() {
298 setup_test_logger!();
299 let addr_client: SocketAddr = "127.0.0.1:6666".parse().expect("parse address error");
300 let addr_backend: SocketAddr = "127.0.0.1:2001".parse().expect("parse address error");
301 let barrier = Arc::new(Barrier::new(3));
302 let end_barrier = Arc::new(Barrier::new(2));
303
304 start_client(addr_client, barrier.clone(), end_barrier.clone());
305 let backend = start_backend(addr_backend, barrier.clone(), end_barrier);
306 start_middleware(addr_client, addr_backend, barrier);
307
308 backend
309 .join()
310 .expect("Couldn't join on the associated backend");
311 }
312
313 fn start_middleware(addr_client: SocketAddr, addr_backend: SocketAddr, barrier: Arc<Barrier>) {
316 let listener = TcpListener::bind(addr_client).expect("could not accept session connection");
317
318 let client_stream;
319 barrier.wait();
320
321 loop {
322 if let Ok((stream, _addr)) = listener.accept() {
323 client_stream = stream;
324 break;
325 }
326 }
327
328 let backend_stream =
330 StdTcpStream::connect(addr_backend).expect("could not connect to the backend");
331 let fd = backend_stream.into_raw_fd();
332 let backend_stream = unsafe { TcpStream::from_raw_fd(fd) };
336
337 let mut send_pp = SendProxyProtocol::new(
338 client_stream,
339 Token(0),
340 Ulid::generate(),
341 Some(backend_stream),
342 );
343 let mut session_metrics = SessionMetrics::new(None);
344
345 send_pp.set_back_connected(BackendConnectionStatus::Connected);
346
347 loop {
348 let result = send_pp.back_writable(&mut session_metrics);
349 if result == SessionResult::Upgrade {
350 break;
351 }
352
353 if result != SessionResult::Continue {
354 panic!("state machine error: result = {result:?}");
355 }
356 }
357 }
358
359 fn start_client(addr: SocketAddr, barrier: Arc<Barrier>, end_barrier: Arc<Barrier>) {
361 thread::spawn(move || {
362 barrier.wait();
363
364 let _stream = StdTcpStream::connect(addr).unwrap();
365
366 end_barrier.wait();
367 });
368 }
369
370 fn start_backend(
373 addr: SocketAddr,
374 barrier: Arc<Barrier>,
375 end_barrier: Arc<Barrier>,
376 ) -> JoinHandle<()> {
377 let listener = StdTcpListener::bind(addr).expect("could not start backend");
378
379 thread::spawn(move || {
380 barrier.wait();
381
382 let mut buf: [u8; 28] = [0; 28];
383 let (mut conn, _) = listener
384 .accept()
385 .expect("could not accept connection from light middleware");
386 println!("backend got a connection from the middleware");
387
388 let mut index = 0usize;
389 loop {
390 if index >= 28 {
391 break;
392 }
393
394 match conn.read(&mut buf[index..]) {
395 Err(e) => match e.kind() {
396 ErrorKind::WouldBlock => continue,
397 e => {
398 end_barrier.wait();
399 panic!("read error: {e:?}");
400 }
401 },
402 Ok(sz) => {
403 println!("backend read {sz} bytes");
404 index += sz;
405 }
406 }
407 }
408
409 match parse_v2_header(&buf) {
410 Ok((_, _)) => println!("complete header received"),
411 err => {
412 end_barrier.wait();
413 panic!("incorrect proxy protocol header received: {err:?}");
414 }
415 };
416
417 end_barrier.wait();
418 })
419 }
420}