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