1use rama_core::error::BoxErrorExt as _;
2use std::{fmt, marker::PhantomData, net::IpAddr};
3
4use crate::protocol::{v1, v2};
5use rama_core::{Layer, Service, bytes::Bytes, error::BoxError, extensions::ExtensionsRef, io::Io};
6use rama_net::{
7 client::{ConnectionError, ConnectionErrorKind, ConnectorService, EstablishedClientConnection},
8 forwarded::Forwarded,
9 stream::{Socket, SocketInfo},
10};
11use tokio::io::AsyncWriteExt;
12
13#[derive(Debug, Clone)]
19pub struct HaProxyLayer<P = protocol::Tcp, V = version::Two> {
20 version: V,
21 _phantom: PhantomData<fn(P)>,
22}
23
24impl HaProxyLayer {
25 #[must_use]
33 pub fn tcp() -> Self {
34 Self {
35 version: Default::default(),
36 _phantom: PhantomData,
37 }
38 }
39
40 pub fn v1(self) -> HaProxyLayer<protocol::Tcp, version::One> {
48 HaProxyLayer {
49 version: Default::default(),
50 _phantom: PhantomData,
51 }
52 }
53}
54
55impl HaProxyLayer<protocol::Udp> {
56 #[must_use]
63 pub fn udp() -> Self {
64 Self {
65 version: Default::default(),
66 _phantom: PhantomData,
67 }
68 }
69}
70
71impl<P> HaProxyLayer<P> {
72 rama_utils::macros::generate_set_and_with! {
73 pub fn payload(mut self, payload: impl Into<Bytes>) -> Self {
93 self.version.payload = Some(payload.into());
94 self
95 }
96 }
97
98 rama_utils::macros::generate_set_and_with! {
99 pub fn tlv(mut self, kind: v2::Type, value: impl Into<Bytes>) -> Self {
109 self.version.tlvs.push((kind, value.into()));
110 self
111 }
112 }
113
114 rama_utils::macros::generate_set_and_with! {
115 pub fn crc32c(mut self, enabled: bool) -> Self {
128 self.version.crc32c = enabled;
129 self
130 }
131 }
132}
133
134impl<S, P, V: Clone> Layer<S> for HaProxyLayer<P, V> {
135 type Service = HaProxyService<S, P, V>;
136
137 fn layer(&self, inner: S) -> Self::Service {
138 HaProxyService {
139 inner,
140 version: self.version.clone(),
141 _phantom: PhantomData,
142 }
143 }
144
145 fn into_layer(self, inner: S) -> Self::Service {
146 HaProxyService {
147 inner,
148 version: self.version,
149 _phantom: PhantomData,
150 }
151 }
152}
153
154pub struct HaProxyService<S, P = protocol::Tcp, V = version::Two> {
160 inner: S,
161 version: V,
162 _phantom: PhantomData<fn(P)>,
163}
164
165impl<S> HaProxyService<S> {
166 pub fn tcp(inner: S) -> Self {
174 Self {
175 inner,
176 version: Default::default(),
177 _phantom: PhantomData,
178 }
179 }
180
181 pub fn v1(self) -> HaProxyService<S, protocol::Tcp, version::One> {
189 HaProxyService {
190 inner: self.inner,
191 version: Default::default(),
192 _phantom: PhantomData,
193 }
194 }
195}
196
197impl<S> HaProxyService<S, protocol::Udp> {
198 pub fn udp(inner: S) -> Self {
205 Self {
206 inner,
207 version: Default::default(),
208 _phantom: PhantomData,
209 }
210 }
211}
212
213impl<S, P> HaProxyService<S, P> {
214 rama_utils::macros::generate_set_and_with! {
215 pub fn payload(mut self, payload: impl Into<Bytes>) -> Self {
221 self.version.payload = Some(payload.into());
222 self
223 }
224 }
225
226 rama_utils::macros::generate_set_and_with! {
227 pub fn tlv(mut self, kind: v2::Type, value: impl Into<Bytes>) -> Self {
230 self.version.tlvs.push((kind, value.into()));
231 self
232 }
233 }
234
235 rama_utils::macros::generate_set_and_with! {
236 pub fn crc32c(mut self, enabled: bool) -> Self {
239 self.version.crc32c = enabled;
240 self
241 }
242 }
243}
244
245impl<S: fmt::Debug, P, V: fmt::Debug> fmt::Debug for HaProxyService<S, P, V> {
246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 f.debug_struct("HaProxyService")
248 .field("inner", &self.inner)
249 .field("version", &self.version)
250 .field(
251 "_phantom",
252 &format_args!("{}", std::any::type_name::<fn(P)>()),
253 )
254 .finish()
255 }
256}
257
258impl<S: Clone, P, V: Clone> Clone for HaProxyService<S, P, V> {
259 fn clone(&self) -> Self {
260 Self {
261 inner: self.inner.clone(),
262 version: self.version.clone(),
263 _phantom: PhantomData,
264 }
265 }
266}
267
268impl<S, P, Input> Service<Input> for HaProxyService<S, P, version::One>
269where
270 S: ConnectorService<Input, Connection: Io + Socket + Unpin>,
271 P: Send + 'static,
272 Input: Send + ExtensionsRef + 'static,
273{
274 type Output = EstablishedClientConnection<S::Connection, Input>;
275 type Error = ConnectionError;
276
277 async fn serve(&self, input: Input) -> Result<Self::Output, Self::Error> {
278 let EstablishedClientConnection { input, mut conn } = self.inner.connect(input).await?;
279
280 let src = input
281 .extensions()
282 .clone_to_if_absent::<Forwarded>(conn.extensions())
283 .and_then(|f| f.client_socket_addr())
284 .or_else(|| {
285 input
286 .extensions()
287 .clone_to_if_absent::<SocketInfo>(conn.extensions())
288 .map(|info| info.peer_addr())
289 })
290 .ok_or_else(|| {
291 ConnectionError::local(
292 BoxError::from_static_str("PROXY client (v1): missing src socket address"),
293 ConnectionErrorKind::InvalidInput,
294 )
295 })?;
296
297 let peer_addr = conn.peer_addr().map_err(|error| {
298 ConnectionError::local(error, ConnectionErrorKind::Internal)
299 .context("PROXY client (v1): read peer address")
300 })?;
301 let addresses = match (src.ip_addr, peer_addr.ip_addr) {
302 (IpAddr::V4(src_ip), IpAddr::V4(dst_ip)) => {
303 v1::Addresses::new_tcp4(src_ip, dst_ip, src.port, peer_addr.port)
304 }
305 (IpAddr::V6(src_ip), IpAddr::V6(dst_ip)) => {
306 v1::Addresses::new_tcp6(src_ip, dst_ip, src.port, peer_addr.port)
307 }
308 (_, _) => {
309 return Err(ConnectionError::local(
310 BoxError::from_static_str(
311 "PROXY client (v1): IP version mismatch between src and dest",
312 ),
313 ConnectionErrorKind::InvalidInput,
314 ));
315 }
316 };
317
318 conn.write_all(addresses.to_string().as_bytes())
319 .await
320 .map_err(|error| {
321 ConnectionError::application(error, ConnectionErrorKind::Protocol)
322 .context("PROXY client (v1): write addresses")
323 })?;
324
325 Ok(EstablishedClientConnection { input, conn })
326 }
327}
328
329impl<S, P, Input> Service<Input> for HaProxyService<S, P, version::Two>
330where
331 S: ConnectorService<Input, Connection: Io + Socket + Unpin>,
332 P: protocol::Protocol + Send + 'static,
333 Input: Send + ExtensionsRef + 'static,
334{
335 type Output = EstablishedClientConnection<S::Connection, Input>;
336 type Error = ConnectionError;
337
338 async fn serve(&self, input: Input) -> Result<Self::Output, Self::Error> {
339 let EstablishedClientConnection { input, mut conn } = self.inner.connect(input).await?;
340
341 let src = {
342 input
343 .extensions()
344 .clone_to_if_absent::<Forwarded>(conn.extensions())
345 .and_then(|f| f.client_socket_addr())
346 .or_else(|| {
347 input
348 .extensions()
349 .clone_to_if_absent::<SocketInfo>(conn.extensions())
350 .map(|info| info.peer_addr())
351 })
352 .ok_or_else(|| {
353 ConnectionError::local(
354 BoxError::from_static_str("PROXY client (v2): missing src socket address"),
355 ConnectionErrorKind::InvalidInput,
356 )
357 })?
358 };
359
360 let peer_addr = conn.peer_addr().map_err(|error| {
361 ConnectionError::local(error, ConnectionErrorKind::Internal)
362 .context("PROXY client (v2): read peer address")
363 })?;
364 let builder = match (src.ip_addr, peer_addr.ip_addr) {
365 (IpAddr::V4(src_ip), IpAddr::V4(dst_ip)) => v2::Builder::with_addresses(
366 v2::Version::Two | v2::Command::Proxy,
367 P::v2_protocol(),
368 v2::IPv4::new(src_ip, dst_ip, src.port, peer_addr.port),
369 ),
370 (IpAddr::V6(src_ip), IpAddr::V6(dst_ip)) => v2::Builder::with_addresses(
371 v2::Version::Two | v2::Command::Proxy,
372 P::v2_protocol(),
373 v2::IPv6::new(src_ip, dst_ip, src.port, peer_addr.port),
374 ),
375 (_, _) => {
376 return Err(ConnectionError::local(
377 BoxError::from_static_str(
378 "PROXY client (v2): IP version mismatch between src and dest",
379 ),
380 ConnectionErrorKind::InvalidInput,
381 ));
382 }
383 };
384
385 if self.version.crc32c && self.version.payload.is_some() {
386 return Err(ConnectionError::local(
391 BoxError::from_static_str(
392 "PROXY client (v2): `payload` and `crc32c` cannot be combined; \
393 use typed TLVs instead",
394 ),
395 ConnectionErrorKind::InvalidInput,
396 ));
397 }
398
399 if self
405 .version
406 .tlvs
407 .iter()
408 .any(|(kind, _)| *kind == v2::Type::CRC32C)
409 {
410 return Err(ConnectionError::local(
411 BoxError::from_static_str(
412 "PROXY client (v2): manual `with_tlv(Type::CRC32C, ...)` is not supported; \
413 use `with_crc32c(true)` instead",
414 ),
415 ConnectionErrorKind::InvalidInput,
416 ));
417 }
418
419 let mut builder = builder;
420 for (kind, value) in &self.version.tlvs {
421 builder = builder.write_tlv(*kind, value.as_ref()).map_err(|error| {
422 ConnectionError::local(error, ConnectionErrorKind::InvalidInput)
423 .context("PROXY client (v2): write TLV")
424 })?;
425 }
426 if let Some(payload) = self.version.payload.as_deref() {
427 builder = builder.write_payload(payload).map_err(|error| {
428 ConnectionError::local(error, ConnectionErrorKind::InvalidInput)
429 .context("PROXY client (v2): write custom binary payload to header")
430 })?;
431 }
432 if self.version.crc32c {
433 builder = builder.with_crc32c(true);
436 }
437
438 let header = builder.build().map_err(|error| {
439 ConnectionError::local(error, ConnectionErrorKind::InvalidInput)
440 .context("PROXY client (v2): encode header")
441 })?;
442 conn.write_all(&header[..]).await.map_err(|error| {
443 ConnectionError::application(error, ConnectionErrorKind::Protocol)
444 .context("PROXY client (v2): write header")
445 })?;
446
447 Ok(EstablishedClientConnection { input, conn })
448 }
449}
450
451pub mod version {
452 use super::*;
455
456 #[derive(Debug, Clone, Default)]
457 #[non_exhaustive]
461 pub struct One;
462
463 #[derive(Debug, Clone, Default)]
464 pub struct Two {
468 pub(crate) payload: Option<Bytes>,
469 pub(crate) tlvs: Vec<(crate::protocol::v2::Type, Bytes)>,
470 pub(crate) crc32c: bool,
471 }
472}
473
474pub mod protocol {
475 use crate::protocol::v2;
478
479 #[derive(Debug, Clone)]
480 pub struct Tcp;
484
485 #[derive(Debug, Clone)]
486 pub struct Udp;
490
491 pub(super) trait Protocol {
492 fn v2_protocol() -> v2::Protocol;
494 }
495
496 impl Protocol for Tcp {
497 fn v2_protocol() -> v2::Protocol {
498 v2::Protocol::Stream
499 }
500 }
501
502 impl Protocol for Udp {
503 fn v2_protocol() -> v2::Protocol {
504 v2::Protocol::Datagram
505 }
506 }
507}
508
509#[cfg(test)]
510mod tests {
511 use super::*;
512 use rama_core::{
513 Layer, ServiceInput, extensions::Extensions, extensions::ExtensionsRef, service::service_fn,
514 };
515 use rama_net::{
516 address::SocketAddress,
517 forwarded::{ForwardedElement, NodeId},
518 };
519 use std::{convert::Infallible, pin::Pin};
520 use tokio::io::{AsyncRead, AsyncWrite};
521 use tokio_test::io::{Builder, Mock};
522
523 struct SocketConnection {
524 conn: Mock,
525 extensions: Extensions,
526 socket: SocketAddress,
527 }
528
529 impl ExtensionsRef for SocketConnection {
530 fn extensions(&self) -> &Extensions {
531 &self.extensions
532 }
533 }
534
535 impl Socket for SocketConnection {
536 fn local_addr(&self) -> std::io::Result<SocketAddress> {
537 Ok(self.socket)
538 }
539
540 fn peer_addr(&self) -> std::io::Result<SocketAddress> {
541 Ok(self.socket)
542 }
543 }
544
545 #[warn(clippy::missing_trait_methods)]
546 impl AsyncWrite for SocketConnection {
547 fn poll_write(
548 mut self: std::pin::Pin<&mut Self>,
549 cx: &mut std::task::Context<'_>,
550 buf: &[u8],
551 ) -> std::task::Poll<Result<usize, std::io::Error>> {
552 Pin::new(&mut self.conn).poll_write(cx, buf)
553 }
554
555 fn poll_flush(
556 mut self: std::pin::Pin<&mut Self>,
557 cx: &mut std::task::Context<'_>,
558 ) -> std::task::Poll<Result<(), std::io::Error>> {
559 Pin::new(&mut self.conn).poll_flush(cx)
560 }
561
562 fn poll_shutdown(
563 mut self: std::pin::Pin<&mut Self>,
564 cx: &mut std::task::Context<'_>,
565 ) -> std::task::Poll<Result<(), std::io::Error>> {
566 Pin::new(&mut self.conn).poll_shutdown(cx)
567 }
568
569 fn is_write_vectored(&self) -> bool {
570 self.conn.is_write_vectored()
571 }
572
573 fn poll_write_vectored(
574 mut self: Pin<&mut Self>,
575 cx: &mut std::task::Context<'_>,
576 bufs: &[std::io::IoSlice<'_>],
577 ) -> std::task::Poll<Result<usize, std::io::Error>> {
578 Pin::new(&mut self.conn).poll_write_vectored(cx, bufs)
579 }
580 }
581
582 #[warn(clippy::missing_trait_methods)]
583 impl AsyncRead for SocketConnection {
584 fn poll_read(
585 mut self: Pin<&mut Self>,
586 cx: &mut std::task::Context<'_>,
587 buf: &mut tokio::io::ReadBuf<'_>,
588 ) -> std::task::Poll<std::io::Result<()>> {
589 Pin::new(&mut self.conn).poll_read(cx, buf)
590 }
591 }
592
593 #[tokio::test]
594 async fn test_v1_tcp() {
595 for (expected_line, ext, target_addr) in [
596 (
597 "PROXY TCP4 127.0.1.2 192.168.1.101 80 443\r\n",
598 {
599 let ext = Extensions::new();
600 ext.insert(SocketInfo::new(None, "127.0.1.2:80".parse().unwrap()));
601 ext
602 },
603 "192.168.1.101:443",
604 ),
605 (
606 "PROXY TCP4 127.0.1.2 192.168.1.101 80 443\r\n",
607 {
608 let ext = Extensions::new();
609 ext.insert(SocketInfo::new(
610 None,
611 "[1234:5678:90ab:cdef:fedc:ba09:8765:4321]:443"
612 .parse()
613 .unwrap(),
614 ));
615 ext.insert(Forwarded::new(ForwardedElement::new_forwarded_for(
616 NodeId::try_from("127.0.1.2:80").unwrap(),
617 )));
618 ext
619 },
620 "192.168.1.101:443",
621 ),
622 (
623 "PROXY TCP6 1234:5678:90ab:cdef:fedc:ba09:8765:4321 4321:8765:ba09:fedc:cdef:90ab:5678:1234 443 65535\r\n",
624 {
625 let ext = Extensions::new();
626 ext.insert(SocketInfo::new(
627 None,
628 "[1234:5678:90ab:cdef:fedc:ba09:8765:4321]:443"
629 .parse()
630 .unwrap(),
631 ));
632 ext
633 },
634 "[4321:8765:ba09:fedc:cdef:90ab:5678:1234]:65535",
635 ),
636 (
637 "PROXY TCP6 1234:5678:90ab:cdef:fedc:ba09:8765:4321 4321:8765:ba09:fedc:cdef:90ab:5678:1234 443 65535\r\n",
638 {
639 let ext = Extensions::new();
640 ext.insert(SocketInfo::new(None, "127.0.1.2:80".parse().unwrap()));
641 ext.insert(Forwarded::new(ForwardedElement::new_forwarded_for(
642 NodeId::try_from("[1234:5678:90ab:cdef:fedc:ba09:8765:4321]:443").unwrap(),
643 )));
644 ext
645 },
646 "[4321:8765:ba09:fedc:cdef:90ab:5678:1234]:65535",
647 ),
648 ] {
649 let svc =
650 HaProxyLayer::tcp()
651 .v1()
652 .layer(service_fn(async move |input: ServiceInput<()>| {
653 Ok::<_, Infallible>(EstablishedClientConnection {
654 input,
655 conn: SocketConnection {
656 socket: target_addr.parse().unwrap(),
657 conn: Builder::new().write(expected_line.as_bytes()).build(),
658 extensions: Extensions::new(),
659 },
660 })
661 }));
662
663 let input = ServiceInput::new(());
664 input.extensions().extend(&ext);
665 svc.serve(input).await.unwrap();
666 }
667 }
668
669 #[tokio::test]
670 async fn test_v1_tcp_ip_version_mismatch() {
671 for (ext, target_addr) in [
672 (
673 {
674 let ext = Extensions::new();
675 ext.insert(SocketInfo::new(
676 None,
677 "[1234:5678:90ab:cdef:fedc:ba09:8765:4321]:80"
678 .parse()
679 .unwrap(),
680 ));
681 ext
682 },
683 "192.168.1.101:443",
684 ),
685 (
686 {
687 let ext = Extensions::new();
688 ext.insert(SocketInfo::new(None, "127.0.1.2:80".parse().unwrap()));
689 ext.insert(Forwarded::new(ForwardedElement::new_forwarded_for(
690 NodeId::try_from("[1234:5678:90ab:cdef:fedc:ba09:8765:4321]:80").unwrap(),
691 )));
692 ext
693 },
694 "192.168.1.101:443",
695 ),
696 (
697 {
698 let ext = Extensions::new();
699 ext.insert(SocketInfo::new(None, "127.0.1.2:80".parse().unwrap()));
700 ext
701 },
702 "[4321:8765:ba09:fedc:cdef:90ab:5678:1234]:65535",
703 ),
704 (
705 {
706 let ext = Extensions::new();
707 ext.insert(SocketInfo::new(
708 None,
709 "[1234:5678:90ab:cdef:fedc:ba09:8765:4321]:80"
710 .parse()
711 .unwrap(),
712 ));
713 ext.insert(Forwarded::new(ForwardedElement::new_forwarded_for(
714 NodeId::try_from("127.0.1.2:80").unwrap(),
715 )));
716 ext
717 },
718 "[4321:8765:ba09:fedc:cdef:90ab:5678:1234]:65535",
719 ),
720 ] {
721 let svc =
722 HaProxyLayer::tcp()
723 .v1()
724 .layer(service_fn(async move |input: ServiceInput<()>| {
725 Ok::<_, Infallible>(EstablishedClientConnection {
726 input,
727 conn: SocketConnection {
728 socket: target_addr.parse().unwrap(),
729 conn: Builder::new().build(),
730 extensions: Extensions::new(),
731 },
732 })
733 }));
734
735 let input = ServiceInput::new(());
736 input.extensions().extend(&ext);
737 assert!(svc.serve(input).await.is_err());
738 }
739 }
740
741 #[tokio::test]
742 async fn test_v1_tcp_missing_src() {
743 for target_addr in [
744 "192.168.1.101:443",
745 "[1234:5678:90ab:cdef:fedc:ba09:8765:4321]:443",
746 ] {
747 let svc = HaProxyLayer::tcp()
748 .v1()
749 .layer(service_fn(async move |input| {
750 Ok::<_, Infallible>(EstablishedClientConnection {
751 input,
752 conn: SocketConnection {
753 socket: target_addr.parse().unwrap(),
754 conn: Builder::new().build(),
755 extensions: Extensions::new(),
756 },
757 })
758 }));
759 assert!(svc.serve(ServiceInput::new(())).await.is_err());
760 }
761 }
762
763 #[tokio::test]
764 async fn test_v2_tcp4() {
765 for ext in [
766 {
767 let ext = Extensions::new();
768 ext.insert(SocketInfo::new(None, "127.0.0.1:80".parse().unwrap()));
769 ext
770 },
771 {
772 let ext = Extensions::new();
773 ext.insert(SocketInfo::new(
774 None,
775 "[1234:5678:90ab:cdef:fedc:ba09:8765:4321]:443"
776 .parse()
777 .unwrap(),
778 ));
779 ext.insert(Forwarded::new(ForwardedElement::new_forwarded_for(
780 NodeId::try_from("127.0.0.1:80").unwrap(),
781 )));
782 ext
783 },
784 ] {
785 let svc = HaProxyLayer::tcp().with_payload(vec![42]).layer(service_fn(
786 async move |input: ServiceInput<()>| {
787 Ok::<_, Infallible>(EstablishedClientConnection {
788 input,
789 conn: SocketConnection {
790 socket: "192.168.1.1:443".parse().unwrap(),
791 extensions: Extensions::new(),
792 conn: Builder::new()
793 .write(&[
794 b'\r', b'\n', b'\r', b'\n', b'\0', b'\r', b'\n', b'Q', b'U',
795 b'I', b'T', b'\n', 0x21, 0x11, 0, 13, 127, 0, 0, 1, 192, 168,
796 1, 1, 0, 80, 1, 187, 42,
797 ])
798 .build(),
799 },
800 })
801 },
802 ));
803
804 let input = ServiceInput::new(());
805 input.extensions().extend(&ext);
806 svc.serve(input).await.unwrap();
807 }
808 }
809
810 #[tokio::test]
811 async fn test_v2_udp4() {
812 for ext in [
813 {
814 let ext = Extensions::new();
815 ext.insert(SocketInfo::new(None, "127.0.0.1:80".parse().unwrap()));
816 ext
817 },
818 {
819 let ext = Extensions::new();
820 ext.insert(SocketInfo::new(
821 None,
822 "[1234:5678:90ab:cdef:fedc:ba09:8765:4321]:443"
823 .parse()
824 .unwrap(),
825 ));
826 ext.insert(Forwarded::new(ForwardedElement::new_forwarded_for(
827 NodeId::try_from("127.0.0.1:80").unwrap(),
828 )));
829 ext
830 },
831 ] {
832 let svc = HaProxyLayer::udp().with_payload(vec![42]).layer(service_fn(
833 async move |input: ServiceInput<()>| {
834 Ok::<_, Infallible>(EstablishedClientConnection {
835 input,
836 conn: SocketConnection {
837 socket: "192.168.1.1:443".parse().unwrap(),
838 extensions: Extensions::new(),
839 conn: Builder::new()
840 .write(&[
841 b'\r', b'\n', b'\r', b'\n', b'\0', b'\r', b'\n', b'Q', b'U',
842 b'I', b'T', b'\n', 0x21, 0x12, 0, 13, 127, 0, 0, 1, 192, 168,
843 1, 1, 0, 80, 1, 187, 42,
844 ])
845 .build(),
846 },
847 })
848 },
849 ));
850
851 let input = ServiceInput::new(());
852 input.extensions().extend(&ext);
853 svc.serve(input).await.unwrap();
854 }
855 }
856
857 #[tokio::test]
858 async fn test_v2_tcp6() {
859 for ext in [
860 {
861 let ext = Extensions::new();
862 ext.insert(SocketInfo::new(
863 None,
864 "[1234:5678:90ab:cdef:fedc:ba09:8765:4321]:80"
865 .parse()
866 .unwrap(),
867 ));
868 ext
869 },
870 {
871 let ext = Extensions::new();
872 ext.insert(SocketInfo::new(None, "127.0.0.1:80".parse().unwrap()));
873 ext.insert(Forwarded::new(ForwardedElement::new_forwarded_for(
874 NodeId::try_from("[1234:5678:90ab:cdef:fedc:ba09:8765:4321]:80").unwrap(),
875 )));
876 ext
877 },
878 ] {
879 let svc = HaProxyLayer::tcp().with_payload(vec![42]).layer(service_fn(
880 async move |input: ServiceInput<()>| {
881 Ok::<_, Infallible>(EstablishedClientConnection {
882 input,
883 conn: SocketConnection {
884 socket: "[4321:8765:ba09:fedc:cdef:90ab:5678:1234]:443"
885 .parse()
886 .unwrap(),
887 extensions: Extensions::new(),
888 conn: Builder::new()
889 .write(&[
890 b'\r', b'\n', b'\r', b'\n', b'\0', b'\r', b'\n', b'Q', b'U',
891 b'I', b'T', b'\n', 0x21, 0x21, 0, 37, 0x12, 0x34, 0x56, 0x78,
892 0x90, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65,
893 0x43, 0x21, 0x43, 0x21, 0x87, 0x65, 0xba, 0x09, 0xfe, 0xdc,
894 0xcd, 0xef, 0x90, 0xab, 0x56, 0x78, 0x12, 0x34, 0, 80, 1, 187,
895 42,
896 ])
897 .build(),
898 },
899 })
900 },
901 ));
902
903 let input = ServiceInput::new(());
904 input.extensions().extend(&ext);
905 svc.serve(input).await.unwrap();
906 }
907 }
908
909 #[tokio::test]
910 async fn test_v2_udp6() {
911 for ext in [
912 {
913 let ext = Extensions::new();
914 ext.insert(SocketInfo::new(
915 None,
916 "[1234:5678:90ab:cdef:fedc:ba09:8765:4321]:80"
917 .parse()
918 .unwrap(),
919 ));
920 ext
921 },
922 {
923 let ext = Extensions::new();
924 ext.insert(SocketInfo::new(None, "127.0.0.1:80".parse().unwrap()));
925 ext.insert(Forwarded::new(ForwardedElement::new_forwarded_for(
926 NodeId::try_from("[1234:5678:90ab:cdef:fedc:ba09:8765:4321]:80").unwrap(),
927 )));
928 ext
929 },
930 ] {
931 let svc = HaProxyLayer::udp().with_payload(vec![42]).layer(service_fn(
932 async move |input: ServiceInput<()>| {
933 Ok::<_, Infallible>(EstablishedClientConnection {
934 input,
935 conn: SocketConnection {
936 socket: "[4321:8765:ba09:fedc:cdef:90ab:5678:1234]:443"
937 .parse()
938 .unwrap(),
939 extensions: Extensions::new(),
940 conn: Builder::new()
941 .write(&[
942 b'\r', b'\n', b'\r', b'\n', b'\0', b'\r', b'\n', b'Q', b'U',
943 b'I', b'T', b'\n', 0x21, 0x22, 0, 37, 0x12, 0x34, 0x56, 0x78,
944 0x90, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65,
945 0x43, 0x21, 0x43, 0x21, 0x87, 0x65, 0xba, 0x09, 0xfe, 0xdc,
946 0xcd, 0xef, 0x90, 0xab, 0x56, 0x78, 0x12, 0x34, 0, 80, 1, 187,
947 42,
948 ])
949 .build(),
950 },
951 })
952 },
953 ));
954
955 let input = ServiceInput::new(());
956 input.extensions().extend(&ext);
957 svc.serve(input).await.unwrap();
958 }
959 }
960
961 #[tokio::test]
962 async fn test_v2_ip_version_mismatch() {
963 for (ext, target_addr) in [
964 (
965 {
966 let ext = Extensions::new();
967 ext.insert(SocketInfo::new(
968 None,
969 "[1234:5678:90ab:cdef:fedc:ba09:8765:4321]:80"
970 .parse()
971 .unwrap(),
972 ));
973 ext
974 },
975 "192.168.1.101:443",
976 ),
977 (
978 {
979 let ext = Extensions::new();
980 ext.insert(SocketInfo::new(None, "127.0.1.2:80".parse().unwrap()));
981 ext.insert(Forwarded::new(ForwardedElement::new_forwarded_for(
982 NodeId::try_from("[1234:5678:90ab:cdef:fedc:ba09:8765:4321]:80").unwrap(),
983 )));
984 ext
985 },
986 "192.168.1.101:443",
987 ),
988 (
989 {
990 let ext = Extensions::new();
991 ext.insert(SocketInfo::new(None, "127.0.1.2:80".parse().unwrap()));
992 ext
993 },
994 "[4321:8765:ba09:fedc:cdef:90ab:5678:1234]:65535",
995 ),
996 (
997 {
998 let ext = Extensions::new();
999 ext.insert(SocketInfo::new(
1000 None,
1001 "[1234:5678:90ab:cdef:fedc:ba09:8765:4321]:80"
1002 .parse()
1003 .unwrap(),
1004 ));
1005 ext.insert(Forwarded::new(ForwardedElement::new_forwarded_for(
1006 NodeId::try_from("127.0.1.2:80").unwrap(),
1007 )));
1008 ext
1009 },
1010 "[4321:8765:ba09:fedc:cdef:90ab:5678:1234]:65535",
1011 ),
1012 ] {
1013 let svc = HaProxyLayer::tcp().layer(service_fn(async move |input: ServiceInput<()>| {
1016 Ok::<_, Infallible>(EstablishedClientConnection {
1017 input,
1018 conn: SocketConnection {
1019 socket: target_addr.parse().unwrap(),
1020 extensions: Extensions::new(),
1021 conn: Builder::new().build(),
1022 },
1023 })
1024 }));
1025
1026 let input = ServiceInput::new(());
1027 input.extensions().extend(&ext);
1028 assert!(svc.serve(input).await.is_err());
1029
1030 let svc = HaProxyLayer::udp().layer(service_fn(async move |input: ServiceInput<()>| {
1033 Ok::<_, Infallible>(EstablishedClientConnection {
1034 input,
1035 conn: SocketConnection {
1036 socket: target_addr.parse().unwrap(),
1037 extensions: Extensions::new(),
1038 conn: Builder::new().build(),
1039 },
1040 })
1041 }));
1042
1043 let input = ServiceInput::new(());
1044 input.extensions().extend(&ext);
1045 assert!(svc.serve(input).await.is_err());
1046 }
1047 }
1048
1049 #[tokio::test]
1050 async fn test_v2_missing_src() {
1051 for target_addr in [
1052 "192.168.1.101:443",
1053 "[1234:5678:90ab:cdef:fedc:ba09:8765:4321]:443",
1054 ] {
1055 let svc = HaProxyLayer::tcp().layer(service_fn(async move |input| {
1058 Ok::<_, Infallible>(EstablishedClientConnection {
1059 input,
1060 conn: SocketConnection {
1061 socket: target_addr.parse().unwrap(),
1062 extensions: Extensions::new(),
1063 conn: Builder::new().build(),
1064 },
1065 })
1066 }));
1067 assert!(svc.serve(ServiceInput::new(())).await.is_err());
1068
1069 let svc = HaProxyLayer::udp().layer(service_fn(async move |input| {
1072 Ok::<_, Infallible>(EstablishedClientConnection {
1073 input,
1074 conn: SocketConnection {
1075 socket: target_addr.parse().unwrap(),
1076 extensions: Extensions::new(),
1077 conn: Builder::new().build(),
1078 },
1079 })
1080 }));
1081 assert!(svc.serve(ServiceInput::new(())).await.is_err());
1082 }
1083 }
1084}