Merged branch 'jetty-11.0.x' into 'jetty-12.0.x'.
Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
commit
bb3355be83
|
@ -13,7 +13,6 @@
|
|||
|
||||
package org.eclipse.jetty.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
|
@ -29,7 +28,9 @@ import org.eclipse.jetty.client.api.Request;
|
|||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -39,7 +40,7 @@ import org.slf4j.LoggerFactory;
|
|||
* <p>
|
||||
* Applications can disable redirection via {@link Request#followRedirects(boolean)}
|
||||
* and then rely on this class to perform the redirect in a simpler way, for example:
|
||||
* <pre>
|
||||
* <pre>{@code
|
||||
* HttpRedirector redirector = new HttpRedirector(httpClient);
|
||||
*
|
||||
* Request request = httpClient.newRequest("http://host/path").followRedirects(false);
|
||||
|
@ -54,7 +55,7 @@ import org.slf4j.LoggerFactory;
|
|||
* request = result.getRequest();
|
||||
* response = result.getResponse();
|
||||
* }
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*/
|
||||
public class HttpRedirector
|
||||
{
|
||||
|
@ -86,7 +87,11 @@ public class HttpRedirector
|
|||
{
|
||||
return switch (response.getStatus())
|
||||
{
|
||||
case 301, 302, 303, 307, 308 -> true;
|
||||
case HttpStatus.MOVED_PERMANENTLY_301,
|
||||
HttpStatus.MOVED_TEMPORARILY_302,
|
||||
HttpStatus.SEE_OTHER_303,
|
||||
HttpStatus.TEMPORARY_REDIRECT_307,
|
||||
HttpStatus.PERMANENT_REDIRECT_308 -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
@ -186,7 +191,7 @@ public class HttpRedirector
|
|||
int status = response.getStatus();
|
||||
switch (status)
|
||||
{
|
||||
case 301:
|
||||
case HttpStatus.MOVED_PERMANENTLY_301:
|
||||
{
|
||||
String method = request.getMethod();
|
||||
if (HttpMethod.GET.is(method) || HttpMethod.HEAD.is(method) || HttpMethod.PUT.is(method))
|
||||
|
@ -196,7 +201,7 @@ public class HttpRedirector
|
|||
fail(request, response, new HttpResponseException("HTTP protocol violation: received 301 for non GET/HEAD/POST/PUT request", response));
|
||||
return null;
|
||||
}
|
||||
case 302:
|
||||
case HttpStatus.MOVED_TEMPORARILY_302:
|
||||
{
|
||||
String method = request.getMethod();
|
||||
if (HttpMethod.HEAD.is(method) || HttpMethod.PUT.is(method))
|
||||
|
@ -204,7 +209,7 @@ public class HttpRedirector
|
|||
else
|
||||
return redirect(request, response, listener, newURI, HttpMethod.GET.asString());
|
||||
}
|
||||
case 303:
|
||||
case HttpStatus.SEE_OTHER_303:
|
||||
{
|
||||
String method = request.getMethod();
|
||||
if (HttpMethod.HEAD.is(method))
|
||||
|
@ -212,8 +217,8 @@ public class HttpRedirector
|
|||
else
|
||||
return redirect(request, response, listener, newURI, HttpMethod.GET.asString());
|
||||
}
|
||||
case 307:
|
||||
case 308:
|
||||
case HttpStatus.TEMPORARY_REDIRECT_307:
|
||||
case HttpStatus.PERMANENT_REDIRECT_308:
|
||||
{
|
||||
// Keep same method
|
||||
return redirect(request, response, listener, newURI, request.getMethod());
|
||||
|
@ -303,11 +308,32 @@ public class HttpRedirector
|
|||
{
|
||||
try
|
||||
{
|
||||
Request.Content body = httpRequest.getBody();
|
||||
if (body != null && !body.rewind())
|
||||
throw new IOException("Could not reproduce request content for " + httpRequest);
|
||||
Request redirect = client.copyRequest(httpRequest, location);
|
||||
|
||||
// Use the given method.
|
||||
redirect.method(method);
|
||||
|
||||
if (HttpMethod.GET.is(method))
|
||||
{
|
||||
redirect.body(null);
|
||||
redirect.headers(headers ->
|
||||
{
|
||||
headers.remove(HttpHeader.CONTENT_LENGTH);
|
||||
headers.remove(HttpHeader.CONTENT_TYPE);
|
||||
});
|
||||
}
|
||||
|
||||
Request.Content body = redirect.getBody();
|
||||
if (body != null && !body.rewind())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Could not redirect to {}, request body is not reproducible", location);
|
||||
HttpConversation conversation = httpRequest.getConversation();
|
||||
conversation.updateResponseListeners(null);
|
||||
notifier.forwardSuccessComplete(conversation.getResponseListeners(), httpRequest, response);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Adjust the timeout of the new request, taking into account the
|
||||
// timeout of the previous request and the time already elapsed.
|
||||
long timeoutNanoTime = httpRequest.getTimeoutNanoTime();
|
||||
|
@ -326,9 +352,6 @@ public class HttpRedirector
|
|||
}
|
||||
}
|
||||
|
||||
// Use given method
|
||||
redirect.method(method);
|
||||
|
||||
redirect.onRequestBegin(request ->
|
||||
{
|
||||
Throwable cause = httpRequest.getAbortCause();
|
||||
|
|
|
@ -30,6 +30,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.client.util.AsyncRequestContent;
|
||||
import org.eclipse.jetty.client.util.ByteBufferRequestContent;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
|
@ -164,6 +165,49 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
assertArrayEquals(data, response.getContent());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(ScenarioProvider.class)
|
||||
public void test303WithRequestContentNotReproducible(Scenario scenario) throws Exception
|
||||
{
|
||||
start(scenario, new RedirectHandler());
|
||||
|
||||
byte[] data = new byte[]{0, 1, 2, 3, 4, 5, 6, 7};
|
||||
AsyncRequestContent body = new AsyncRequestContent(ByteBuffer.wrap(data));
|
||||
body.close();
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scenario.getScheme())
|
||||
.method(HttpMethod.POST)
|
||||
.path("/303/localhost/done")
|
||||
.body(body)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
assertNotNull(response);
|
||||
assertEquals(200, response.getStatus());
|
||||
assertFalse(response.getHeaders().contains(HttpHeader.LOCATION));
|
||||
assertEquals(0, response.getContent().length);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(ScenarioProvider.class)
|
||||
public void test307WithRequestContentNotReproducible(Scenario scenario) throws Exception
|
||||
{
|
||||
start(scenario, new RedirectHandler());
|
||||
|
||||
byte[] data = new byte[]{0, 1, 2, 3, 4, 5, 6, 7};
|
||||
AsyncRequestContent body = new AsyncRequestContent(ByteBuffer.wrap(data));
|
||||
body.close();
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scenario.getScheme())
|
||||
.method(HttpMethod.POST)
|
||||
.path("/307/localhost/done")
|
||||
.body(body)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
assertNotNull(response);
|
||||
assertEquals(307, response.getStatus());
|
||||
assertTrue(response.getHeaders().contains(HttpHeader.LOCATION));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(ScenarioProvider.class)
|
||||
public void testMaxRedirections(Scenario scenario) throws Exception
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.eclipse.jetty.http2.api.Session;
|
|||
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
@ -30,10 +31,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||
public class ConnectTimeoutTest extends AbstractTest
|
||||
{
|
||||
@Test
|
||||
@Tag("external")
|
||||
public void testConnectTimeout() throws Exception
|
||||
{
|
||||
final String host = "10.255.255.1";
|
||||
final int port = 80;
|
||||
// Using IANA hosted example.com:81 to reliably produce a Connect Timeout.
|
||||
final String host = "example.com";
|
||||
final int port = 81;
|
||||
int connectTimeout = 1000;
|
||||
assumeConnectTimeout(host, port, connectTimeout);
|
||||
|
||||
|
|
|
@ -90,6 +90,14 @@ public class AbstractTest
|
|||
return List.copyOf(transports);
|
||||
}
|
||||
|
||||
public static Collection<Transport> transportsTCP()
|
||||
{
|
||||
Collection<Transport> transports = transports();
|
||||
transports.remove(Transport.H3);
|
||||
transports.remove(Transport.UNIX_DOMAIN);
|
||||
return List.copyOf(transports);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void dispose()
|
||||
{
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
package org.eclipse.jetty.test.client.transport;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
@ -21,6 +20,7 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
|
@ -33,11 +33,13 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
|||
public class HttpClientConnectTimeoutTest extends AbstractTest
|
||||
{
|
||||
@ParameterizedTest
|
||||
@MethodSource("transportsNoUnixDomain")
|
||||
@MethodSource("transportsTCP")
|
||||
@Tag("external")
|
||||
public void testConnectTimeout(Transport transport) throws Exception
|
||||
{
|
||||
String host = "10.255.255.1";
|
||||
int port = 80;
|
||||
// Using IANA hosted example.com:81 to reliably produce a Connect Timeout.
|
||||
String host = "example.com";
|
||||
int port = 81;
|
||||
int connectTimeout = 1000;
|
||||
assumeTrue(connectTimeout(host, port, connectTimeout));
|
||||
|
||||
|
@ -57,11 +59,13 @@ public class HttpClientConnectTimeoutTest extends AbstractTest
|
|||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("transportsNoUnixDomain")
|
||||
@MethodSource("transportsTCP")
|
||||
@Tag("external")
|
||||
public void testConnectTimeoutIsCancelledByShorterRequestTimeout(Transport transport) throws Exception
|
||||
{
|
||||
String host = "10.255.255.1";
|
||||
int port = 80;
|
||||
// Using IANA hosted example.com:81 to reliably produce a Connect Timeout.
|
||||
String host = "example.com";
|
||||
int port = 81;
|
||||
int connectTimeout = 2000;
|
||||
assumeTrue(connectTimeout(host, port, connectTimeout));
|
||||
|
||||
|
@ -84,11 +88,13 @@ public class HttpClientConnectTimeoutTest extends AbstractTest
|
|||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("transportsNoUnixDomain")
|
||||
@MethodSource("transportsTCP")
|
||||
@Tag("external")
|
||||
public void retryAfterConnectTimeout(Transport transport) throws Exception
|
||||
{
|
||||
String host = "10.255.255.1";
|
||||
int port = 80;
|
||||
// Using IANA hosted example.com:81 to reliably produce a Connect Timeout.
|
||||
String host = "example.com";
|
||||
int port = 81;
|
||||
int connectTimeout = 1000;
|
||||
assumeTrue(connectTimeout(host, port, connectTimeout));
|
||||
|
||||
|
@ -114,7 +120,7 @@ public class HttpClientConnectTimeoutTest extends AbstractTest
|
|||
assertNotNull(request.getAbortCause());
|
||||
}
|
||||
|
||||
private boolean connectTimeout(String host, int port, int connectTimeout) throws IOException
|
||||
private boolean connectTimeout(String host, int port, int connectTimeout)
|
||||
{
|
||||
try (Socket socket = new Socket())
|
||||
{
|
||||
|
|
|
@ -50,6 +50,7 @@ import org.hamcrest.MatcherAssert;
|
|||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.opentest4j.TestAbortedException;
|
||||
|
@ -268,7 +269,8 @@ public class HttpClientTimeoutTest extends AbstractTest
|
|||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("transportsNoUnixDomain")
|
||||
@MethodSource("transportsTCP")
|
||||
@Tag("external")
|
||||
public void testBlockingConnectTimeoutFailsRequest(Transport transport) throws Exception
|
||||
{
|
||||
// Failure to connect is based on InetSocket address failure, which Unix-Domain does not use.
|
||||
|
@ -276,7 +278,8 @@ public class HttpClientTimeoutTest extends AbstractTest
|
|||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("transportsNoUnixDomain")
|
||||
@MethodSource("transportsTCP")
|
||||
@Tag("external")
|
||||
public void testNonBlockingConnectTimeoutFailsRequest(Transport transport) throws Exception
|
||||
{
|
||||
// Failure to connect is based on InetSocket address failure, which Unix-Domain does not use.
|
||||
|
@ -285,8 +288,9 @@ public class HttpClientTimeoutTest extends AbstractTest
|
|||
|
||||
private void testConnectTimeoutFailsRequest(Transport transport, boolean blocking) throws Exception
|
||||
{
|
||||
String host = "10.255.255.1";
|
||||
int port = 80;
|
||||
// Using IANA hosted example.com:81 to reliably produce a Connect Timeout.
|
||||
final String host = "example.com";
|
||||
final int port = 81;
|
||||
int connectTimeout = 1000;
|
||||
assumeConnectTimeout(host, port, connectTimeout);
|
||||
|
||||
|
@ -308,14 +312,16 @@ public class HttpClientTimeoutTest extends AbstractTest
|
|||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("transports")
|
||||
@MethodSource("transportsTCP")
|
||||
@Tag("external")
|
||||
public void testConnectTimeoutIsCancelledByShorterRequestTimeout(Transport transport) throws Exception
|
||||
{
|
||||
// Failure to connect is based on InetSocket address failure, which Unix-Domain does not use.
|
||||
Assumptions.assumeTrue(transport != Transport.UNIX_DOMAIN);
|
||||
|
||||
String host = "10.255.255.1";
|
||||
int port = 80;
|
||||
// Using IANA hosted example.com:81 to reliably produce a Connect Timeout.
|
||||
String host = "example.com";
|
||||
int port = 81;
|
||||
int connectTimeout = 2000;
|
||||
assumeConnectTimeout(host, port, connectTimeout);
|
||||
|
||||
|
@ -339,14 +345,16 @@ public class HttpClientTimeoutTest extends AbstractTest
|
|||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("transports")
|
||||
@MethodSource("transportsTCP")
|
||||
@Tag("external")
|
||||
public void testRetryAfterConnectTimeout(Transport transport) throws Exception
|
||||
{
|
||||
// Failure to connect is based on InetSocket address failure, which Unix-Domain does not use.
|
||||
Assumptions.assumeTrue(transport != Transport.UNIX_DOMAIN);
|
||||
|
||||
final String host = "10.255.255.1";
|
||||
final int port = 80;
|
||||
// Using IANA hosted example.com:81 to reliably produce a Connect Timeout.
|
||||
String host = "example.com";
|
||||
int port = 81;
|
||||
int connectTimeout = 1000;
|
||||
assumeConnectTimeout(host, port, connectTimeout);
|
||||
|
||||
|
|
|
@ -223,9 +223,9 @@ public class HttpClientContinueTest extends AbstractTest
|
|||
}
|
||||
else
|
||||
{
|
||||
// Send 100-Continue and consume the content
|
||||
IO.copy(request.getInputStream(), new ByteArrayOutputStream());
|
||||
// Send a redirect
|
||||
// Send 100-Continue and consume the content.
|
||||
IO.copy(request.getInputStream(), OutputStream.nullOutputStream());
|
||||
// Send a redirect.
|
||||
response.sendRedirect("/done");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue