2121import static org .junit .jupiter .api .Assertions .*;
2222import static org .mockito .Mockito .*;
2323
24+ import org .slf4j .Logger ;
25+ import org .slf4j .LoggerFactory ;
2426/**
2527 * Unit tests for ProxyDatagramSocket IP address mapping and cache behavior.
2628 */
27- class ProxyDatagramSocketIPMappingTest {
29+ class ProxyDatagramSocketTest {
30+ private static final Logger LOG = LoggerFactory .getLogger (ProxyDatagramSocket .class );
2831
2932 private ProxyDatagramSocket socket ;
3033 private ProxyAddressCache mockCache ;
3134 private ProxyProtocolMetricsListener mockMetrics ;
35+
36+ private InetSocketAddress realClient ;
37+ private InetSocketAddress serviceAddress ;
38+ private InetSocketAddress backendAddress ;
3239 private int localPort ;
3340
3441 @ BeforeEach
3542 void setUp () throws Exception {
3643 mockCache = mock (ProxyAddressCache .class );
3744 mockMetrics = mock (ProxyProtocolMetricsListener .class );
3845
46+ realClient = new InetSocketAddress (InetAddress .getLoopbackAddress (), 12345 );
47+ serviceAddress = new InetSocketAddress (InetAddress .getLoopbackAddress (), 54321 );
3948 socket = new ProxyDatagramSocket (new InetSocketAddress (InetAddress .getLoopbackAddress (), 0 ), mockCache , mockMetrics , null );
4049 localPort = socket .getLocalPort ();
50+ backendAddress = new InetSocketAddress (InetAddress .getLoopbackAddress (), localPort );
4151 }
4252
4353 @ AfterEach
@@ -48,27 +58,24 @@ void tearDown() {
4858 }
4959
5060 @ Test
51- void receive_withValidProxyHeader_populatesCache () throws Exception {
61+ void receive_withValidProxyHeader () throws Exception {
5262 // Arrange
53- InetSocketAddress realClient = new InetSocketAddress ("10.1.2.3" , 12345 );
54- InetSocketAddress lbAddress = new InetSocketAddress ("127.0.0.1" , 54321 );
5563 byte [] payload = "test-data" .getBytes (StandardCharsets .UTF_8 );
5664
5765 var proxyHeader = new AwsProxyEncoderHelper ()
5866 .family (ProxyHeader .AddressFamily .AF_INET )
5967 .socket (ProxyHeader .TransportProtocol .DGRAM )
6068 .source (realClient )
61- .destination (new InetSocketAddress ( "127.0.0.1" , localPort ) )
69+ .destination (serviceAddress )
6270 .build ();
6371
6472 byte [] packet = new byte [proxyHeader .length + payload .length ];
6573 System .arraycopy (proxyHeader , 0 , packet , 0 , proxyHeader .length );
6674 System .arraycopy (payload , 0 , packet , proxyHeader .length , payload .length );
6775
6876 // Create a loopback socket to send from
69- try (java .net .DatagramSocket sender = new java .net .DatagramSocket (lbAddress )) {
70- sender .send (new DatagramPacket (packet , packet .length ,
71- new InetSocketAddress ("127.0.0.1" , localPort )));
77+ try (java .net .DatagramSocket sender = new java .net .DatagramSocket (serviceAddress )) {
78+ sender .send (new DatagramPacket (packet , packet .length , backendAddress ));
7279 }
7380
7481 // Act
@@ -82,8 +89,8 @@ void receive_withValidProxyHeader_populatesCache() throws Exception {
8289 verify (mockCache ).put (clientCaptor .capture (), lbCaptor .capture ());
8390
8491 assertEquals (realClient , clientCaptor .getValue ());
85- assertEquals (lbAddress .getAddress (), lbCaptor .getValue ().getAddress ());
86- assertEquals (lbAddress .getPort (), lbCaptor .getValue ().getPort ());
92+ assertEquals (serviceAddress .getAddress (), lbCaptor .getValue ().getAddress ());
93+ assertEquals (serviceAddress .getPort (), lbCaptor .getValue ().getPort ());
8794
8895 // Verify packet was modified to show real client address
8996 assertEquals (realClient , receivePacket .getSocketAddress ());
@@ -96,24 +103,21 @@ void receive_withValidProxyHeader_populatesCache() throws Exception {
96103 receivePacket .getOffset () + receivePacket .getLength ()));
97104
98105 verify (mockMetrics ).onHeaderParsed (any (ProxyHeader .class ));
99- verify (mockMetrics ).onTrustedProxy (lbAddress .getAddress ());
106+ verify (mockMetrics ).onTrustedProxy (serviceAddress .getAddress ());
100107 verify (mockMetrics , never ()).onUntrustedProxy (any ());
101108 verify (mockMetrics , never ()).onParseError (any ());
102109 verify (mockMetrics , never ()).onLocal (any ());
103110 }
104111
105112 @ Test
106113 void send_withCacheHit_usesLoadBalancerAddress () throws Exception {
107- // Arrange
108- InetSocketAddress realClient = new InetSocketAddress ("10.1.2.3" , 12345 );
109- InetSocketAddress lbAddress = new InetSocketAddress ("127.0.0.1" , 54321 );
110114 byte [] payload = "response" .getBytes (StandardCharsets .UTF_8 );
111115
112116 // Mock cache to return lb address
113- when (mockCache .get (realClient )).thenReturn (lbAddress );
117+ when (mockCache .get (realClient )).thenReturn (serviceAddress );
114118
115119 // Create a receiver to verify the packet destination
116- java .net .DatagramSocket receiver = new java .net .DatagramSocket (lbAddress );
120+ java .net .DatagramSocket receiver = new java .net .DatagramSocket (serviceAddress );
117121 receiver .setSoTimeout (1000 );
118122
119123 try {
@@ -142,37 +146,35 @@ void send_withCacheHit_usesLoadBalancerAddress() throws Exception {
142146 }
143147
144148 @ Test
145- void send_withCacheMiss_usesOriginalAddress () throws Exception {
149+ void send_withCacheMiss_dropsPacket () throws Exception {
146150 // Arrange
147- InetSocketAddress clientAddress = new InetSocketAddress ("127.0.0.1" , 55555 );
148151 byte [] payload = "response" .getBytes (StandardCharsets .UTF_8 );
149152
150153 // Mock cache to return null (cache miss)
151- when (mockCache .get (clientAddress )).thenReturn (null );
154+ when (mockCache .get (realClient )).thenReturn (null );
152155
153- // Create a receiver at the client address
154- java .net .DatagramSocket receiver = new java .net .DatagramSocket (clientAddress );
155- receiver .setSoTimeout (1000 );
156+ // Create a receiver at the client address to verify packet is NOT sent
157+ java .net .DatagramSocket receiver = new java .net .DatagramSocket (realClient );
158+ receiver .setSoTimeout (500 ); // Short timeout since we expect no packet
156159
157160 try {
158161 // Act - send to client address
159- DatagramPacket sendPacket = new DatagramPacket (payload , payload .length , clientAddress );
162+ DatagramPacket sendPacket = new DatagramPacket (payload , payload .length , realClient );
160163 socket .send (sendPacket );
161164
162- // Verify packet was sent to original address
165+ // Try to receive - should timeout since packet was dropped
163166 byte [] receiveBuf = new byte [2048 ];
164167 DatagramPacket receivePacket = new DatagramPacket (receiveBuf , receiveBuf .length );
165- receiver .receive (receivePacket );
166168
167- // Assert
168- assertArrayEquals ( payload ,
169- java . util . Arrays . copyOfRange ( receivePacket . getData (), 0 , receivePacket . getLength ()) );
169+ assertThrows ( java . net . SocketTimeoutException . class , () -> {
170+ receiver . receive ( receivePacket );
171+ }, "Expected packet to be dropped on cache miss" );
170172
171173 // Verify cache was queried
172- verify (mockCache ).get (clientAddress );
174+ verify (mockCache ).get (realClient );
173175
174176 // Verify metrics - cache miss
175- verify (mockMetrics ).onCacheMiss (clientAddress );
177+ verify (mockMetrics ).onCacheMiss (realClient );
176178 verify (mockMetrics , never ()).onCacheHit (any ());
177179 } finally {
178180 receiver .close ();
@@ -187,20 +189,24 @@ void receive_withLocalCommand_doesNotPopulateCache() throws Exception {
187189 .command (ProxyHeader .Command .LOCAL )
188190 .build ();
189191
192+ LOG .trace ("Payload:\n {}" , Utility .hexdump (payload , 0 , payload .length ));
193+ LOG .trace ("Proxy header:\n {}" , Utility .hexdump (proxyHeader , 0 , proxyHeader .length ));
194+
190195 byte [] packet = new byte [proxyHeader .length + payload .length ];
191196 System .arraycopy (proxyHeader , 0 , packet , 0 , proxyHeader .length );
192197 System .arraycopy (payload , 0 , packet , proxyHeader .length , payload .length );
193198
194199 try (java .net .DatagramSocket sender = new java .net .DatagramSocket ()) {
195- sender .send (new DatagramPacket (packet , packet .length ,
196- new InetSocketAddress ("127.0.0.1" , localPort )));
200+ sender .send (new DatagramPacket (packet , packet .length , backendAddress ));
197201 }
198202
199203 // Act
200204 byte [] receiveBuf = new byte [2048 ];
201205 DatagramPacket receivePacket = new DatagramPacket (receiveBuf , receiveBuf .length );
202206 socket .receive (receivePacket );
203207
208+ LOG .trace ("Received packet length={}, content:\n {}" , receivePacket .getLength (), Utility .hexdump (receivePacket .getData (), receivePacket .getOffset (), receivePacket .getLength ()));
209+
204210 // Assert - cache should NOT be populated for LOCAL commands
205211 verify (mockCache , never ()).put (any (), any ());
206212
@@ -218,17 +224,16 @@ void receive_withTcpProtocol_doesNotPopulateCache() throws Exception {
218224 byte [] proxyHeader = new AwsProxyEncoderHelper ()
219225 .family (ProxyHeader .AddressFamily .AF_INET )
220226 .socket (ProxyHeader .TransportProtocol .STREAM ) // TCP, not UDP
221- .source (new InetSocketAddress ( "10.1.2.3" , 12345 ) )
222- .destination (new InetSocketAddress ( "127.0.0.1" , localPort ) )
227+ .source (realClient )
228+ .destination (serviceAddress )
223229 .build ();
224230
225231 byte [] packet = new byte [proxyHeader .length + payload .length ];
226232 System .arraycopy (proxyHeader , 0 , packet , 0 , proxyHeader .length );
227233 System .arraycopy (payload , 0 , packet , proxyHeader .length , payload .length );
228234
229235 try (java .net .DatagramSocket sender = new java .net .DatagramSocket ()) {
230- sender .send (new DatagramPacket (packet , packet .length ,
231- new InetSocketAddress ("127.0.0.1" , localPort )));
236+ sender .send (new DatagramPacket (packet , packet .length , backendAddress ));
232237 }
233238
234239 // Act
@@ -247,14 +252,13 @@ void receive_withTcpProtocol_doesNotPopulateCache() throws Exception {
247252 @ Test
248253 void receive_withValidProxyHeader_callsMetricsOnHeaderParsed () throws Exception {
249254 // Arrange
250- InetSocketAddress realClient = new InetSocketAddress ("10.1.2.3" , 12345 );
251255 byte [] payload = "test" .getBytes (StandardCharsets .UTF_8 );
252256
253257 byte [] proxyHeader = new AwsProxyEncoderHelper ()
254258 .family (ProxyHeader .AddressFamily .AF_INET )
255259 .socket (ProxyHeader .TransportProtocol .DGRAM )
256260 .source (realClient )
257- .destination (new InetSocketAddress ( "127.0.0.1" , localPort ) )
261+ .destination (serviceAddress )
258262 .build ();
259263
260264 byte [] packet = new byte [proxyHeader .length + payload .length ];
@@ -263,8 +267,7 @@ void receive_withValidProxyHeader_callsMetricsOnHeaderParsed() throws Exception
263267
264268 // Send packet
265269 try (java .net .DatagramSocket sender = new java .net .DatagramSocket ()) {
266- sender .send (new DatagramPacket (packet , packet .length ,
267- new InetSocketAddress ("127.0.0.1" , localPort )));
270+ sender .send (new DatagramPacket (packet , packet .length , backendAddress ));
268271 }
269272
270273 // Act
@@ -288,8 +291,7 @@ void receive_withInvalidData_callsMetricsOnParseError() throws Exception {
288291 byte [] garbage = "not-a-proxy-header" .getBytes (StandardCharsets .UTF_8 );
289292
290293 try (java .net .DatagramSocket sender = new java .net .DatagramSocket ()) {
291- sender .send (new DatagramPacket (garbage , garbage .length ,
292- new InetSocketAddress ("127.0.0.1" , localPort )));
294+ sender .send (new DatagramPacket (garbage , garbage .length , backendAddress ));
293295 }
294296
295297 // Act
@@ -304,67 +306,5 @@ void receive_withInvalidData_callsMetricsOnParseError() throws Exception {
304306 assertEquals (garbage .length , receivePacket .getLength ());
305307 }
306308
307- @ Test
308- void send_withCacheHit_callsMetricsOnCacheHit () throws Exception {
309- // Arrange
310- InetSocketAddress realClient = new InetSocketAddress ("10.1.2.3" , 12345 );
311- InetSocketAddress lbAddress = new InetSocketAddress ("127.0.0.1" , 54321 );
312- byte [] payload = "response" .getBytes (StandardCharsets .UTF_8 );
313-
314- // Mock cache to return lb address
315- when (mockCache .get (realClient )).thenReturn (lbAddress );
316-
317- // Create a receiver to verify the packet destination
318- java .net .DatagramSocket receiver = new java .net .DatagramSocket (lbAddress );
319- receiver .setSoTimeout (1000 );
320-
321- try {
322- // Act - send to real client, should be redirected to LB
323- DatagramPacket sendPacket = new DatagramPacket (payload , payload .length , realClient );
324- socket .send (sendPacket );
325-
326- // Receive the packet (to avoid timeout)
327- byte [] receiveBuf = new byte [2048 ];
328- DatagramPacket receivePacket = new DatagramPacket (receiveBuf , receiveBuf .length );
329- receiver .receive (receivePacket );
330-
331- // Assert - onCacheHit should be called
332- verify (mockMetrics ).onCacheHit (realClient );
333- verify (mockMetrics , never ()).onCacheMiss (any ());
334- } finally {
335- receiver .close ();
336- }
337- }
338-
339- @ Test
340- void send_withCacheMiss_callsMetricsOnCacheMiss () throws Exception {
341- // Arrange
342- InetSocketAddress clientAddress = new InetSocketAddress ("127.0.0.1" , 55555 );
343- byte [] payload = "response" .getBytes (StandardCharsets .UTF_8 );
344-
345- // Mock cache to return null (cache miss)
346- when (mockCache .get (clientAddress )).thenReturn (null );
347-
348- // Create a receiver at the client address
349- java .net .DatagramSocket receiver = new java .net .DatagramSocket (clientAddress );
350- receiver .setSoTimeout (1000 );
351-
352- try {
353- // Act - send to client address
354- DatagramPacket sendPacket = new DatagramPacket (payload , payload .length , clientAddress );
355- socket .send (sendPacket );
356-
357- // Receive the packet (to avoid timeout)
358- byte [] receiveBuf = new byte [2048 ];
359- DatagramPacket receivePacket = new DatagramPacket (receiveBuf , receiveBuf .length );
360- receiver .receive (receivePacket );
361-
362- // Assert - onCacheMiss should be called
363- verify (mockMetrics ).onCacheMiss (clientAddress );
364- verify (mockMetrics , never ()).onCacheHit (any ());
365- } finally {
366- receiver .close ();
367- }
368- }
369309}
370310
0 commit comments