1+ /*
2+ * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+ /*
27+ * @test
28+ * @bug 8304701
29+ * @summary Verifies that for a redirected request, the given HttpClient
30+ * will clear and start a new response timer instead of throwing
31+ * an HttpTimeoutException during the redirected request.
32+ * @library /test/lib /test/jdk/java/net/httpclient/lib
33+ * @build jdk.test.lib.net.SimpleSSLContext
34+ * @run testng/othervm -Djdk.httpclient.HttpClient.log=errors,trace -Djdk.internal.httpclient.debug=false RedirectTimeoutTest
35+ */
36+
37+ import jdk .httpclient .test .lib .common .HttpServerAdapters .HttpTestExchange ;
38+ import jdk .httpclient .test .lib .common .HttpServerAdapters .HttpTestHandler ;
39+ import jdk .httpclient .test .lib .common .HttpServerAdapters .HttpTestResponseHeaders ;
40+ import jdk .httpclient .test .lib .common .HttpServerAdapters .HttpTestServer ;
41+ import org .testng .TestException ;
42+ import org .testng .annotations .AfterTest ;
43+ import org .testng .annotations .BeforeTest ;
44+ import org .testng .annotations .DataProvider ;
45+ import org .testng .annotations .Test ;
46+
47+ import java .io .IOException ;
48+ import java .io .OutputStream ;
49+ import java .io .PrintStream ;
50+ import java .net .URI ;
51+ import java .net .http .HttpClient ;
52+ import java .net .http .HttpClient .Version ;
53+ import java .net .http .HttpRequest ;
54+ import java .net .http .HttpResponse ;
55+ import java .net .http .HttpTimeoutException ;
56+ import java .nio .charset .StandardCharsets ;
57+ import java .time .Duration ;
58+ import java .time .Instant ;
59+
60+ import static java .net .http .HttpClient .Redirect .ALWAYS ;
61+ import static java .net .http .HttpClient .Version .HTTP_1_1 ;
62+ import static java .net .http .HttpClient .Version .HTTP_2 ;
63+ import static jdk .test .lib .Utils .adjustTimeout ;
64+
65+ public class RedirectTimeoutTest {
66+
67+ static HttpTestServer h1TestServer , h2TestServer ;
68+ static URI h1Uri , h1RedirectUri , h2Uri , h2RedirectUri , h2WarmupUri , testRedirectURI ;
69+ private static final long TIMEOUT_MILLIS = 3000L ; // 3s
70+ private static final long SLEEP_TIME = 1500L ; // 1.5s
71+ public static final int ITERATIONS = 4 ;
72+ private static final PrintStream out = System .out ;
73+
74+ @ BeforeTest
75+ public void setup () throws IOException {
76+ h1TestServer = HttpTestServer .create (HTTP_1_1 );
77+ h2TestServer = HttpTestServer .create (HTTP_2 );
78+ h1Uri = URI .create ("http://" + h1TestServer .serverAuthority () + "/h1_test" );
79+ h1RedirectUri = URI .create ("http://" + h1TestServer .serverAuthority () + "/h1_redirect" );
80+ h2Uri = URI .create ("http://" + h2TestServer .serverAuthority () + "/h2_test" );
81+ h2RedirectUri = URI .create ("http://" + h2TestServer .serverAuthority () + "/h2_redirect" );
82+ h2WarmupUri = URI .create ("http://" + h2TestServer .serverAuthority () + "/h2_warmup" );
83+ h1TestServer .addHandler (new GetHandler (), "/h1_test" );
84+ h1TestServer .addHandler (new RedirectHandler (), "/h1_redirect" );
85+ h2TestServer .addHandler (new GetHandler (), "/h2_test" );
86+ h2TestServer .addHandler (new RedirectHandler (), "/h2_redirect" );
87+ h2TestServer .addHandler (new Http2Warmup (), "/h2_warmup" );
88+ h1TestServer .start ();
89+ h2TestServer .start ();
90+ }
91+
92+ @ AfterTest
93+ public void teardown () {
94+ h1TestServer .stop ();
95+ h2TestServer .stop ();
96+ }
97+
98+ @ DataProvider (name = "testData" )
99+ public Object [][] testData () {
100+ return new Object [][] {
101+ { HTTP_1_1 , h1Uri , h1RedirectUri },
102+ { HTTP_2 , h2Uri , h2RedirectUri }
103+ };
104+ }
105+
106+ @ Test (dataProvider = "testData" )
107+ public void test (Version version , URI uri , URI redirectURI ) throws InterruptedException {
108+ out .println ("Testing for " + version );
109+ testRedirectURI = redirectURI ;
110+ HttpClient .Builder clientBuilder = HttpClient .newBuilder ().followRedirects (ALWAYS );
111+ HttpRequest request = HttpRequest .newBuilder ().uri (uri )
112+ .GET ()
113+ .version (version )
114+ .timeout (Duration .ofMillis (adjustTimeout (TIMEOUT_MILLIS )))
115+ .build ();
116+
117+ try (HttpClient client = clientBuilder .build ()) {
118+ if (version .equals (HTTP_2 ))
119+ client .send (HttpRequest .newBuilder (h2WarmupUri ).HEAD ().build (), HttpResponse .BodyHandlers .discarding ());
120+ /*
121+ With TIMEOUT_MILLIS set to 1500ms and the server's RedirectHandler sleeping for 750ms before responding
122+ to each request, 4 iterations will take a guaranteed minimum time of 3000ms which will ensure that any
123+ uncancelled/uncleared timers will fire within the test window.
124+ */
125+ for (int i = 0 ; i < ITERATIONS ; i ++) {
126+ out .println (Instant .now () + ": Client: Sending request #" + (i + 1 ));
127+ client .send (request , HttpResponse .BodyHandlers .ofString ());
128+ out .println ("Request complete" );
129+ }
130+ } catch (IOException e ) {
131+ if (e .getClass () == HttpTimeoutException .class ) {
132+ e .printStackTrace (System .out );
133+ throw new TestException ("Timeout from original HttpRequest expired on redirect when it should have been cancelled." );
134+ } else {
135+ throw new RuntimeException (e );
136+ }
137+ }
138+ }
139+
140+ public static class Http2Warmup implements HttpTestHandler {
141+
142+ @ Override
143+ public void handle (HttpTestExchange t ) throws IOException {
144+ t .sendResponseHeaders (200 , 0 );
145+ }
146+ }
147+
148+ public static class GetHandler implements HttpTestHandler {
149+
150+ @ Override
151+ public void handle (HttpTestExchange exchange ) throws IOException {
152+ out .println (Instant .now () + ": Server: Get Handler Called" );
153+ HttpTestResponseHeaders responseHeaders = exchange .getResponseHeaders ();
154+ responseHeaders .addHeader ("Location" , testRedirectURI .toString ());
155+ exchange .sendResponseHeaders (302 , 0 );
156+ }
157+ }
158+
159+ public static class RedirectHandler implements HttpTestHandler {
160+
161+ @ Override
162+ public void handle (HttpTestExchange exchange ) throws IOException {
163+ out .println (Instant .now () + ": Server: Redirect Handler Called" );
164+ byte [] data = "Test" .getBytes (StandardCharsets .UTF_8 );
165+ try {
166+ Thread .sleep (adjustTimeout (SLEEP_TIME ));
167+ } catch (InterruptedException e ) {
168+ throw new RuntimeException (e );
169+ }
170+ exchange .sendResponseHeaders (200 , data .length );
171+ try (OutputStream os = exchange .getResponseBody ()) {
172+ os .write (data );
173+ }
174+ }
175+ }
176+ }
0 commit comments