3939import java .io .UncheckedIOException ;
4040import java .net .Authenticator ;
4141import java .net .PasswordAuthentication ;
42+ import java .net .ProtocolException ;
4243import java .net .Proxy ;
4344import java .net .ProxySelector ;
4445import java .net .SocketAddress ;
6263import java .util .logging .Level ;
6364import java .util .logging .Logger ;
6465
65- import static java .net .http .HttpClient .Redirect .ALWAYS ;
66-
6766public class JdkHttpClient implements HttpClient {
6867 public static final Logger LOG = Logger .getLogger (JdkHttpClient .class .getName ());
6968 private final JdkHttpMessages messages ;
@@ -82,7 +81,7 @@ public class JdkHttpClient implements HttpClient {
8281
8382 java .net .http .HttpClient .Builder builder = java .net .http .HttpClient .newBuilder ()
8483 .connectTimeout (config .connectionTimeout ())
85- .followRedirects (ALWAYS )
84+ .followRedirects (java . net . http . HttpClient . Redirect . NEVER )
8685 .executor (executorService );
8786
8887 Credentials credentials = config .credentials ();
@@ -289,7 +288,42 @@ public HttpResponse execute(HttpRequest req) throws UncheckedIOException {
289288
290289 BodyHandler <byte []> byteHandler = BodyHandlers .ofByteArray ();
291290 try {
292- return messages .createResponse (client .send (messages .createRequest (req ), byteHandler ));
291+ URI rawUri = messages .getRawUri (req );
292+
293+ // We need a custom handling of redirects to:
294+ // - increase the maximum number of retries to 100
295+ // - avoid a downgrade of POST requests, see the javadoc of j.n.h.HttpClient.Redirect
296+ // - not run into https://bugs.openjdk.org/browse/JDK-8304701
297+ for (int i = 0 ; i < 100 ; i ++) {
298+ java .net .http .HttpRequest request = messages .createRequest (req , rawUri );
299+ java .net .http .HttpResponse <byte []> response = client .send (request , byteHandler );
300+
301+ switch (response .statusCode ()) {
302+ case 301 :
303+ case 302 :
304+ case 303 :
305+ case 307 :
306+ case 308 :
307+ URI location = rawUri .resolve (response .headers ()
308+ .firstValue ("location" )
309+ .orElseThrow (() -> new ProtocolException (
310+ "HTTP " + response .statusCode () + " without 'location' header set"
311+ )));
312+
313+ if ("https" .equalsIgnoreCase (rawUri .getScheme ()) && !"https" .equalsIgnoreCase (location .getScheme ()) ) {
314+ throw new SecurityException ("Downgrade from secure to insecure connection." );
315+ } else if ("wss" .equalsIgnoreCase (rawUri .getScheme ()) && !"wss" .equalsIgnoreCase (location .getScheme ()) ) {
316+ throw new SecurityException ("Downgrade from secure to insecure connection." );
317+ }
318+
319+ rawUri = location ;
320+ continue ;
321+ default :
322+ return messages .createResponse (response );
323+ }
324+ }
325+
326+ throw new ProtocolException ("Too many redirects: 101" );
293327 } catch (HttpTimeoutException e ) {
294328 throw new TimeoutException (e );
295329 } catch (IOException e ) {
0 commit comments