@@ -36,11 +36,7 @@ void setUp() throws Exception {
3636 mockCache = mock (ProxyAddressCache .class );
3737 mockMetrics = mock (ProxyProtocolMetricsListener .class );
3838
39- socket = new ProxyDatagramSocket (new InetSocketAddress (InetAddress .getLoopbackAddress (), 0 ))
40- .setCache (mockCache )
41- .setMetrics (mockMetrics )
42- .setTrustedProxy (addr -> true ); // Trust all for these tests
43-
39+ socket = new ProxyDatagramSocket (new InetSocketAddress (InetAddress .getLoopbackAddress (), 0 ), mockCache , mockMetrics , null );
4440 localPort = socket .getLocalPort ();
4541 }
4642
@@ -58,8 +54,8 @@ void receive_withValidProxyHeader_populatesCache() throws Exception {
5854 InetSocketAddress lbAddress = new InetSocketAddress ("127.0.0.1" , 54321 );
5955 byte [] payload = "test-data" .getBytes (StandardCharsets .UTF_8 );
6056
61- byte [] proxyHeader = new ProxyProtocolV2Encoder ()
62- .family (ProxyHeader .AddressFamily .INET4 )
57+ var proxyHeader = new AwsProxyEncoderHelper ()
58+ .family (ProxyHeader .AddressFamily .AF_INET )
6359 .socket (ProxyHeader .TransportProtocol .DGRAM )
6460 .source (realClient )
6561 .destination (new InetSocketAddress ("127.0.0.1" , localPort ))
@@ -177,17 +173,13 @@ void send_withCacheMiss_usesOriginalAddress() throws Exception {
177173 }
178174 }
179175
180- @ Test
181- void receive_withUntrustedProxy_skipsProcessing () throws Exception {
182- // Arrange - configure to reject all sources
183- socket .setTrustedProxy (addr -> false );
184176
185- byte [] payload = "test" . getBytes ( StandardCharsets . UTF_8 );
186- byte [] proxyHeader = new ProxyProtocolV2Encoder ()
187- . family ( ProxyHeader . AddressFamily . INET4 )
188- . socket ( ProxyHeader . TransportProtocol . DGRAM )
189- . source ( new InetSocketAddress ( "10.1.2.3" , 12345 ) )
190- .destination ( new InetSocketAddress ( "127.0.0.1" , localPort ) )
177+ @ Test
178+ void receive_withLocalCommand_doesNotPopulateCache () throws Exception {
179+ // Arrange - create LOCAL command (not proxied )
180+ byte [] payload = "local" . getBytes ( StandardCharsets . UTF_8 );
181+ byte [] proxyHeader = new AwsProxyEncoderHelper ( )
182+ .command ( ProxyHeader . Command . LOCAL )
191183 .build ();
192184
193185 byte [] packet = new byte [proxyHeader .length + payload .length ];
@@ -204,20 +196,25 @@ void receive_withUntrustedProxy_skipsProcessing() throws Exception {
204196 DatagramPacket receivePacket = new DatagramPacket (receiveBuf , receiveBuf .length );
205197 socket .receive (receivePacket );
206198
207- // Assert - packet should be delivered unchanged, no parsing
208- verify (mockMetrics , never ()).onHeaderParsed (any ());
199+ // Assert - cache should NOT be populated for LOCAL commands
209200 verify (mockCache , never ()).put (any (), any ());
210201
211- // Packet length should include proxy header (not stripped)
212- assertEquals (packet .length , receivePacket .getLength ());
202+ // But metrics should still be called
203+ verify (mockMetrics ).onHeaderParsed (any ());
204+
205+ // Payload should be stripped of header
206+ assertEquals (payload .length , receivePacket .getLength ());
213207 }
214208
215209 @ Test
216- void receive_withLocalCommand_doesNotPopulateCache () throws Exception {
217- // Arrange - create LOCAL command (not proxied)
218- byte [] payload = "local " .getBytes (StandardCharsets .UTF_8 );
210+ void receive_withTcpProtocol_doesNotPopulateCache () throws Exception {
211+ // Arrange - create header with TCP (not DGRAM) protocol
212+ byte [] payload = "tcp " .getBytes (StandardCharsets .UTF_8 );
219213 byte [] proxyHeader = new ProxyProtocolV2Encoder ()
220- .command (ProxyHeader .Command .LOCAL )
214+ .family (ProxyHeader .AddressFamily .INET4 )
215+ .socket (ProxyHeader .TransportProtocol .STREAM ) // TCP, not UDP
216+ .source (new InetSocketAddress ("10.1.2.3" , 12345 ))
217+ .destination (new InetSocketAddress ("127.0.0.1" , localPort ))
221218 .build ();
222219
223220 byte [] packet = new byte [proxyHeader .length + payload .length ];
@@ -234,31 +231,32 @@ void receive_withLocalCommand_doesNotPopulateCache() throws Exception {
234231 DatagramPacket receivePacket = new DatagramPacket (receiveBuf , receiveBuf .length );
235232 socket .receive (receivePacket );
236233
237- // Assert - cache should NOT be populated for LOCAL commands
234+ // Assert - cache should NOT be populated for non-DGRAM protocols
238235 verify (mockCache , never ()).put (any (), any ());
239236
240- // But metrics should still be called
237+ // Metrics should still be called
241238 verify (mockMetrics ).onHeaderParsed (any ());
242-
243- // Payload should be stripped of header
244- assertEquals (payload .length , receivePacket .getLength ());
245239 }
246240
241+
247242 @ Test
248- void receive_withTcpProtocol_doesNotPopulateCache () throws Exception {
249- // Arrange - create header with TCP (not DGRAM) protocol
250- byte [] payload = "tcp" .getBytes (StandardCharsets .UTF_8 );
251- byte [] proxyHeader = new ProxyProtocolV2Encoder ()
243+ void receive_withValidProxyHeader_callsMetricsOnHeaderParsed () throws Exception {
244+ // Arrange
245+ InetSocketAddress realClient = new InetSocketAddress ("10.1.2.3" , 12345 );
246+ byte [] payload = "test" .getBytes (StandardCharsets .UTF_8 );
247+
248+ byte [] proxyHeader = new AwsProxyEncoderHelper ()
252249 .family (ProxyHeader .AddressFamily .INET4 )
253- .socket (ProxyHeader .TransportProtocol .STREAM ) // TCP, not UDP
254- .source (new InetSocketAddress ( "10.1.2.3" , 12345 ) )
250+ .socket (ProxyHeader .TransportProtocol .DGRAM )
251+ .source (realClient )
255252 .destination (new InetSocketAddress ("127.0.0.1" , localPort ))
256253 .build ();
257254
258255 byte [] packet = new byte [proxyHeader .length + payload .length ];
259256 System .arraycopy (proxyHeader , 0 , packet , 0 , proxyHeader .length );
260257 System .arraycopy (payload , 0 , packet , proxyHeader .length , payload .length );
261258
259+ // Send packet
262260 try (java .net .DatagramSocket sender = new java .net .DatagramSocket ()) {
263261 sender .send (new DatagramPacket (packet , packet .length ,
264262 new InetSocketAddress ("127.0.0.1" , localPort )));
@@ -269,11 +267,99 @@ void receive_withTcpProtocol_doesNotPopulateCache() throws Exception {
269267 DatagramPacket receivePacket = new DatagramPacket (receiveBuf , receiveBuf .length );
270268 socket .receive (receivePacket );
271269
272- // Assert - cache should NOT be populated for non-DGRAM protocols
273- verify (mockCache , never ()).put (any (), any ());
270+ // Assert - onHeaderParsed should be called
271+ ArgumentCaptor <ProxyHeader > headerCaptor = ArgumentCaptor .forClass (ProxyHeader .class );
272+ verify (mockMetrics ).onHeaderParsed (headerCaptor .capture ());
274273
275- // Metrics should still be called
276- verify (mockMetrics ).onHeaderParsed (any ());
274+ ProxyHeader capturedHeader = headerCaptor .getValue ();
275+ assertNotNull (capturedHeader );
276+ assertEquals (ProxyHeader .TransportProtocol .DGRAM , capturedHeader .getProtocol ());
277+ assertEquals (realClient , capturedHeader .getSourceAddress ());
278+ }
279+
280+ @ Test
281+ void receive_withInvalidData_callsMetricsOnParseError () throws Exception {
282+ // Arrange - send garbage data
283+ byte [] garbage = "not-a-proxy-header" .getBytes (StandardCharsets .UTF_8 );
284+
285+ try (java .net .DatagramSocket sender = new java .net .DatagramSocket ()) {
286+ sender .send (new DatagramPacket (garbage , garbage .length ,
287+ new InetSocketAddress ("127.0.0.1" , localPort )));
288+ }
289+
290+ // Act
291+ byte [] receiveBuf = new byte [2048 ];
292+ DatagramPacket receivePacket = new DatagramPacket (receiveBuf , receiveBuf .length );
293+ socket .receive (receivePacket );
294+
295+ // Assert - onParseError should be called
296+ verify (mockMetrics ).onParseError (any (Exception .class ));
297+
298+ // Original packet should be delivered unchanged
299+ assertEquals (garbage .length , receivePacket .getLength ());
300+ }
301+
302+ @ Test
303+ void send_withCacheHit_callsMetricsOnCacheHit () throws Exception {
304+ // Arrange
305+ InetSocketAddress realClient = new InetSocketAddress ("10.1.2.3" , 12345 );
306+ InetSocketAddress lbAddress = new InetSocketAddress ("127.0.0.1" , 54321 );
307+ byte [] payload = "response" .getBytes (StandardCharsets .UTF_8 );
308+
309+ // Mock cache to return lb address
310+ when (mockCache .get (realClient )).thenReturn (lbAddress );
311+
312+ // Create a receiver to verify the packet destination
313+ java .net .DatagramSocket receiver = new java .net .DatagramSocket (lbAddress );
314+ receiver .setSoTimeout (1000 );
315+
316+ try {
317+ // Act - send to real client, should be redirected to LB
318+ DatagramPacket sendPacket = new DatagramPacket (payload , payload .length , realClient );
319+ socket .send (sendPacket );
320+
321+ // Receive the packet (to avoid timeout)
322+ byte [] receiveBuf = new byte [2048 ];
323+ DatagramPacket receivePacket = new DatagramPacket (receiveBuf , receiveBuf .length );
324+ receiver .receive (receivePacket );
325+
326+ // Assert - onCacheHit should be called
327+ verify (mockMetrics ).onCacheHit (realClient );
328+ verify (mockMetrics , never ()).onCacheMiss (any ());
329+ } finally {
330+ receiver .close ();
331+ }
332+ }
333+
334+ @ Test
335+ void send_withCacheMiss_callsMetricsOnCacheMiss () throws Exception {
336+ // Arrange
337+ InetSocketAddress clientAddress = new InetSocketAddress ("127.0.0.1" , 55555 );
338+ byte [] payload = "response" .getBytes (StandardCharsets .UTF_8 );
339+
340+ // Mock cache to return null (cache miss)
341+ when (mockCache .get (clientAddress )).thenReturn (null );
342+
343+ // Create a receiver at the client address
344+ java .net .DatagramSocket receiver = new java .net .DatagramSocket (clientAddress );
345+ receiver .setSoTimeout (1000 );
346+
347+ try {
348+ // Act - send to client address
349+ DatagramPacket sendPacket = new DatagramPacket (payload , payload .length , clientAddress );
350+ socket .send (sendPacket );
351+
352+ // Receive the packet (to avoid timeout)
353+ byte [] receiveBuf = new byte [2048 ];
354+ DatagramPacket receivePacket = new DatagramPacket (receiveBuf , receiveBuf .length );
355+ receiver .receive (receivePacket );
356+
357+ // Assert - onCacheMiss should be called
358+ verify (mockMetrics ).onCacheMiss (clientAddress );
359+ verify (mockMetrics , never ()).onCacheHit (any ());
360+ } finally {
361+ receiver .close ();
362+ }
277363 }
278364}
279365
0 commit comments