11// SPDX-License-Identifier: BSD-3-Clause
22package org .xbill .DNS ;
33
4- import static org .junit .jupiter .api .Assertions .assertEquals ;
5- import static org .junit .jupiter .api .Assertions .assertTrue ;
6-
74import io .netty .handler .codec .http .HttpHeaderNames ;
85import io .vertx .core .Future ;
96import io .vertx .core .Vertx ;
2017import java .time .Duration ;
2118import java .util .Base64 ;
2219import java .util .Collections ;
20+ import java .util .concurrent .CompletionStage ;
2321import java .util .concurrent .TimeoutException ;
22+ import java .util .concurrent .atomic .AtomicBoolean ;
2423import java .util .concurrent .atomic .AtomicInteger ;
2524import org .junit .jupiter .api .BeforeEach ;
2625import org .junit .jupiter .api .Test ;
2726import org .junit .jupiter .api .extension .ExtendWith ;
2827import org .junit .jupiter .params .ParameterizedTest ;
2928import org .junit .jupiter .params .provider .ValueSource ;
3029
30+ import static org .junit .jupiter .api .Assertions .*;
31+
3132@ ExtendWith (VertxExtension .class )
3233class DohResolverTest {
3334 private DohResolver resolver ;
@@ -152,6 +153,88 @@ void initialRequestSlowResolve(Vertx vertx, VertxTestContext context) {
152153 });
153154 }
154155
156+
157+ @ Test
158+ void initialRequestGuardIfIdleConnectionTimeIsLargerThanSystemNanoTime (Vertx vertx , VertxTestContext context ) {
159+ if (isPreJava9 ()) {
160+ System .out .println ("Current JVM is PreJava9, no need to run such test." );
161+ context .completeNow ();
162+ return ;
163+ }
164+ resolver = new DohResolver ("http://localhost" ,
165+ 2 ,
166+ // so long idleConnectionTimeout
167+ // in order to hack the condition for checking initial request in org.xbill.DNS.DohResolver.checkInitialRequest
168+ Duration .ofNanos (System .nanoTime () + Duration .ofSeconds (100L ).toNanos ()));
169+ resolver .setTimeout (Duration .ofSeconds (1 ));
170+ // Just add a 100ms delay before responding to the 1st call
171+ // to simulate a 'concurrent doh request' for the 2nd call,
172+ // then let the fake dns server respond to the 2nd call ASAP.
173+ allRequestsUseTimeout = false ;
174+
175+ // idleConnectionTimeout = 2s, lastRequest = 0L
176+ // Ensure lastRequest + idleConnectionTimeout < System.nanoTime() (3s)
177+
178+ // Timeline:
179+ // |<-------- 100ms -------->|
180+ // ↑ ↑
181+ // 1st call sent response of 1st call
182+ // |20ms|<------ 80ms ------>|<------ few millis ------->|
183+ // ↑ wait until 1st call ↑ ↑
184+ // 2nd call begin 2nd call sent response of 2nd call
185+
186+ AtomicBoolean firstCallCompleted = new AtomicBoolean (false );
187+
188+ setupResolverWithServer (Duration .ofMillis (100L ),
189+ 200 ,
190+ 2 ,
191+ vertx ,
192+ context )
193+ .onSuccess (
194+ server -> {
195+ // First call
196+ CompletionStage <Message > firstCall = resolver .sendAsync (qm );
197+ // Ensure second call was made after first call.
198+ sleepNotThrown (20L );
199+ CompletionStage <Message > secondCall = resolver .sendAsync (qm );
200+
201+ Future .fromCompletionStage (firstCall )
202+ .onComplete (
203+ context .succeeding (
204+ result ->
205+ context .verify (
206+ () -> {
207+ assertEquals (Rcode .NOERROR , result .getHeader ().getRcode ());
208+ assertEquals (0 , result .getHeader ().getID ());
209+ assertEquals (queryName , result .getQuestion ().getName ());
210+ firstCallCompleted .set (true );
211+ })));
212+
213+ Future .fromCompletionStage (secondCall )
214+ .onComplete (
215+ context .succeeding (
216+ result ->
217+ context .verify (
218+ () -> {
219+ assertTrue (firstCallCompleted .get ());
220+ assertEquals (Rcode .NOERROR , result .getHeader ().getRcode ());
221+ assertEquals (0 , result .getHeader ().getID ());
222+ assertEquals (queryName , result .getQuestion ().getName ());
223+ // Complete context after the 2nd call was completed.
224+ context .completeNow ();
225+ })));
226+ }
227+ );
228+ }
229+
230+ private static void sleepNotThrown (long millis ) {
231+ try {
232+ Thread .sleep (millis );
233+ } catch (InterruptedException e ) {
234+ throw new RuntimeException (e );
235+ }
236+ }
237+
155238 @ Test
156239 void initialRequestTimeoutResolve (Vertx vertx , VertxTestContext context ) {
157240 resolver = new DohResolver ("http://localhost" , 2 , Duration .ofMinutes (2 ));
@@ -192,6 +275,10 @@ void initialRequestTimeoutResolve(Vertx vertx, VertxTestContext context) {
192275 });
193276 }
194277
278+ private static boolean isPreJava9 () {
279+ return System .getProperty ("java.version" ).startsWith ("1." );
280+ }
281+
195282 private Future <HttpServer > setupResolverWithServer (
196283 Duration responseDelay ,
197284 int statusCode ,
@@ -211,7 +298,7 @@ private Future<HttpServer> setupServer(
211298 VertxTestContext context ,
212299 Vertx vertx ) {
213300 HttpVersion version =
214- System . getProperty ( "java.version" ). startsWith ( "1." )
301+ isPreJava9 ( )
215302 ? HttpVersion .HTTP_1_1
216303 : HttpVersion .HTTP_2 ;
217304 AtomicInteger requestCount = new AtomicInteger (0 );
0 commit comments