+
diff --git a/jetty-core/jetty-server/src/main/config/modules/server.mod b/jetty-core/jetty-server/src/main/config/modules/server.mod
index 2163a0938b44..42166f77e001 100644
--- a/jetty-core/jetty-server/src/main/config/modules/server.mod
+++ b/jetty-core/jetty-server/src/main/config/modules/server.mod
@@ -65,6 +65,9 @@ etc/jetty.xml
## Relative Redirect Locations allowed
# jetty.httpConfig.relativeRedirectAllowed=true
+## Redirect body generated
+# jetty.httpConfig.generateRedirectBody=false
+
## Whether to use direct ByteBuffers for reading or writing
# jetty.httpConfig.useInputDirectByteBuffers=true
# jetty.httpConfig.useOutputDirectByteBuffers=true
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionMetaData.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionMetaData.java
index ef4503c5b7af..771f8c427f9d 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionMetaData.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionMetaData.java
@@ -52,7 +52,9 @@ default boolean isSecure()
/**
* @return whether the functionality of pushing resources is supported
+ * @deprecated in favour of 103 Early Hints
*/
+ @Deprecated(since = "12.0.1")
default boolean isPushSupported()
{
return false;
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java
index 69d9396470e2..c331c1a3f6ad 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java
@@ -84,6 +84,7 @@ public class HttpConfiguration implements Dumpable
private MultiPartCompliance _multiPartCompliance = MultiPartCompliance.RFC7578;
private boolean _notifyRemoteAsyncErrors = true;
private boolean _relativeRedirectAllowed = true;
+ private boolean _generateRedirectBody = false;
private HostPort _serverAuthority;
private SocketAddress _localAddress;
private int _maxUnconsumedRequestContentReads = 16;
@@ -158,6 +159,7 @@ public HttpConfiguration(HttpConfiguration config)
_complianceViolationListeners.addAll(config._complianceViolationListeners);
_notifyRemoteAsyncErrors = config._notifyRemoteAsyncErrors;
_relativeRedirectAllowed = config._relativeRedirectAllowed;
+ _generateRedirectBody = config._generateRedirectBody;
_uriCompliance = config._uriCompliance;
_serverAuthority = config._serverAuthority;
_localAddress = config._localAddress;
@@ -716,6 +718,23 @@ public boolean isRelativeRedirectAllowed()
return _relativeRedirectAllowed;
}
+ /**
+ * @param generate True if a redirection body will be generated if no response body is supplied.
+ */
+ public void setGenerateRedirectBody(boolean generate)
+ {
+ _generateRedirectBody = generate;
+ }
+
+ /**
+ * @return True if a redirection body will be generated if no response body is supplied.
+ */
+ @ManagedAttribute("Whether a redirection response body will be generated")
+ public boolean isGenerateRedirectBody()
+ {
+ return _generateRedirectBody;
+ }
+
/**
* Get the SocketAddress override to be reported as the local address of all connections
*
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
index 4e8b26a322ab..ec92395dabd5 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
@@ -15,6 +15,7 @@
import java.io.OutputStream;
import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
import java.util.ListIterator;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeoutException;
@@ -32,12 +33,14 @@
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.Trailers;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.io.QuietException;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.server.internal.HttpChannelState;
+import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
@@ -262,10 +265,7 @@ static void sendRedirect(Request request, Response response, Callback callback,
*/
static void sendRedirect(Request request, Response response, Callback callback, String location, boolean consumeAvailable)
{
- int code = HttpMethod.GET.is(request.getMethod()) || request.getConnectionMetaData().getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion()
- ? HttpStatus.MOVED_TEMPORARILY_302
- : HttpStatus.SEE_OTHER_303;
- sendRedirect(request, response, callback, code, location, consumeAvailable);
+ sendRedirect(request, response, callback, 0, location, consumeAvailable);
}
/**
@@ -283,23 +283,47 @@ static void sendRedirect(Request request, Response response, Callback callback,
*/
static void sendRedirect(Request request, Response response, Callback callback, int code, String location, boolean consumeAvailable)
{
- if (!HttpStatus.isRedirection(code))
+ sendRedirect(request, response, callback, code, location, consumeAvailable, null);
+ }
+
+ /**
+ * Sends a {@code 302} HTTP redirect status code to the given location.
+ *
+ * @param request the HTTP request
+ * @param response the HTTP response
+ * @param callback the callback to complete
+ * @param code the redirect HTTP status code, or 0 for a default
+ * @param location the redirect location as an absolute URI or encoded relative URI path.
+ * @param consumeAvailable whether to consumer the available request content
+ * @param content the content of the response, or null for a generated HTML message if {@link HttpConfiguration#isGenerateRedirectBody()} is {@code true}.
+ * @see #toRedirectURI(Request, String)
+ * @throws IllegalArgumentException if the status code is not a redirect, or the location is {@code null}
+ * @throws IllegalStateException if the response is already {@link #isCommitted() committed}
+ */
+ static void sendRedirect(Request request, Response response, Callback callback, int code, String location, boolean consumeAvailable, ByteBuffer content)
+ {
+ if (response.isCommitted())
{
- callback.failed(new IllegalArgumentException("Not a 3xx redirect code"));
+ callback.failed(new IllegalStateException("Committed"));
return;
}
- if (location == null)
+ if (code <= 0)
+ code = HttpMethod.GET.is(request.getMethod()) || request.getConnectionMetaData().getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion()
+ ? HttpStatus.MOVED_TEMPORARILY_302
+ : HttpStatus.SEE_OTHER_303;
+ if (!HttpStatus.isRedirectionWithLocation(code))
{
- callback.failed(new IllegalArgumentException("No location"));
+ callback.failed(new IllegalArgumentException("Not a 3xx redirect code"));
return;
}
- if (response.isCommitted())
+ if (location == null)
{
- callback.failed(new IllegalStateException("Committed"));
+ callback.failed(new IllegalArgumentException("No location"));
return;
}
+ location = toRedirectURI(request, location);
if (consumeAvailable)
{
@@ -317,9 +341,22 @@ static void sendRedirect(Request request, Response response, Callback callback,
}
}
- response.getHeaders().put(HttpHeader.LOCATION, toRedirectURI(request, location));
+ if (content == null && request.getConnectionMetaData().getHttpConfiguration().isGenerateRedirectBody())
+ {
+ response.getHeaders().put(MimeTypes.Type.TEXT_HTML_8859_1.getContentTypeField());
+ String body = """
+
+
+ Redirecting...
+ If you are not redirected, click here.
+
+ """.formatted(location, location);
+ content = BufferUtil.toBuffer(body, StandardCharsets.ISO_8859_1);
+ }
+
+ response.getHeaders().put(HttpHeader.LOCATION, location);
response.setStatus(code);
- response.write(true, null, callback);
+ response.write(true, content, callback);
}
/**
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java
index 1ced4688d603..e320b63ec3ee 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java
@@ -119,7 +119,7 @@ protected boolean shouldBuffer(Response response, boolean last)
return false;
int status = response.getStatus();
- if (HttpStatus.hasNoBody(status) || HttpStatus.isRedirection(status))
+ if (HttpStatus.hasNoBody(status) || HttpStatus.isRedirectionWithLocation(status))
return false;
String ct = response.getHeaders().get(HttpHeader.CONTENT_TYPE);
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/MovedContextHandler.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/MovedContextHandler.java
index 3953334cb143..419136bc74b1 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/MovedContextHandler.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/MovedContextHandler.java
@@ -56,7 +56,7 @@ public int getStatusCode()
*/
public void setStatusCode(int statusCode)
{
- if (!HttpStatus.isRedirection(statusCode))
+ if (!HttpStatus.isRedirectionWithLocation(statusCode))
throw new IllegalArgumentException("Invalid HTTP redirection status code: " + statusCode);
_statusCode = statusCode;
}
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java
index 10e9b4c3c062..d8b34c76ee36 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java
@@ -79,7 +79,7 @@ public SecuredRedirectHandler(Handler handler)
public SecuredRedirectHandler(Handler handler, int code)
{
super(handler);
- if (!HttpStatus.isRedirection(code))
+ if (!HttpStatus.isRedirectionWithLocation(code))
throw new IllegalArgumentException("Not a 3xx redirect code");
_redirectCode = code;
}
diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
index 15484e0409a9..7f1a91586ab8 100644
--- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
+++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
@@ -13,9 +13,13 @@
package org.eclipse.jetty.server;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
+import java.util.stream.Stream;
import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpField;
@@ -23,18 +27,29 @@
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.SetCookieParser;
import org.eclipse.jetty.io.Content;
+import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.component.LifeCycle;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.emptyString;
+import static org.hamcrest.Matchers.equalToIgnoringCase;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -163,6 +178,112 @@ public boolean handle(Request request, Response response, Callback callback)
assertThat(response.get("Test"), is("after reset"));
}
+ public static Stream redirects()
+ {
+ List cases = new ArrayList<>();
+
+ for (int code : new int[] {0, 307})
+ {
+ for (String location : new String[] {"somewhere/else", "/somewhere/else", "http://else/where" })
+ {
+ for (boolean relative : new boolean[] {true, false})
+ {
+ for (boolean generate : new boolean[] {true, false})
+ {
+ for (String content : new String[] {null, "alternative text" })
+ {
+ cases.add(Arguments.of(code, location, relative, generate, content));
+ }
+ }
+ }
+ }
+ }
+ return cases.stream();
+ }
+
+ @ParameterizedTest
+ @MethodSource("redirects")
+ public void testRedirect(int code, String location, boolean relative, boolean generate, String content) throws Exception
+ {
+ server.getConnectors()[0].getConnectionFactory(HttpConnectionFactory.class)
+ .getHttpConfiguration().setRelativeRedirectAllowed(relative);
+ server.getConnectors()[0].getConnectionFactory(HttpConnectionFactory.class)
+ .getHttpConfiguration().setGenerateRedirectBody(generate);
+
+ server.setHandler(new Handler.Abstract()
+ {
+ @Override
+ public boolean handle(Request request, Response response, Callback callback)
+ {
+ if (content == null)
+ {
+ Response.sendRedirect(request, response, callback, code, location, true);
+ }
+ else
+ {
+ response.getHeaders().put(MimeTypes.Type.TEXT_PLAIN_UTF_8.getContentTypeField());
+ Response.sendRedirect(request, response, callback, code, location, true, BufferUtil.toBuffer(content, StandardCharsets.UTF_8));
+ }
+
+ return true;
+ }
+ });
+ server.start();
+
+ HttpTester.Request request = new HttpTester.Request();
+ request.setMethod("GET");
+ request.setURI("/ctx/servlet/test");
+ request.setVersion(HttpVersion.HTTP_1_1);
+ request.setHeader("Connection", "close");
+ request.setHeader("Host", "test");
+
+ ByteBuffer responseBuffer = connector.getResponse(request.generate());
+ HttpTester.Response response = HttpTester.parseResponse(responseBuffer);
+
+ assertThat(response.getStatus(), is(code == 0 ? HttpStatus.FOUND_302 : code));
+
+ String destination = location;
+ if (relative)
+ {
+ if (!location.startsWith("/") && !location.startsWith("http:/"))
+ destination = "/ctx/servlet/" + location;
+ }
+ else
+ {
+ if (location.startsWith("/"))
+ destination = "http://test" + location;
+ else if (!location.startsWith("http:/"))
+ destination = "http://test/ctx/servlet/" + location;
+ }
+
+ HttpField to = response.getField(HttpHeader.LOCATION);
+ assertThat(to, notNullValue());
+ assertThat(to.getValue(), is(destination));
+
+ String actual = response.getContent();
+
+ if (content == null)
+ {
+ if (generate)
+ {
+ assertThat(response.get(HttpHeader.CONTENT_TYPE), containsString("text/html"));
+ assertThat(actual, containsString("If you are not redirected, click here".formatted(destination)));
+ assertThat(actual, not(containsString("oops")));
+ }
+ else
+ {
+ assertThat(response.get().get(HttpHeader.CONTENT_TYPE), nullValue());
+ assertThat(actual, emptyString());
+ }
+ }
+ else
+ {
+ assertThat(response.get().get(HttpHeader.CONTENT_TYPE), notNullValue());
+ assertThat(actual, not(containsString("oops")));
+ assertThat(actual, containsString(content));
+ }
+ }
+
@Test
public void testRedirectGET() throws Exception
{
@@ -199,6 +320,67 @@ public boolean handle(Request request, Response response, Callback callback)
assertThat(response.get(HttpHeader.LOCATION), is("http://hostname/somewhere/else"));
}
+ @Test
+ public void testRedirectGetWithContent() throws Exception
+ {
+ server.getConnectors()[0].getConnectionFactory(HttpConnectionFactory.class)
+ .getHttpConfiguration().setRelativeRedirectAllowed(false);
+ server.setHandler(new Handler.Abstract()
+ {
+ @Override
+ public boolean handle(Request request, Response response, Callback callback)
+ {
+ response.getHeaders().put(HttpHeader.CONTENT_TYPE, "exotic");
+ Response.sendRedirect(request, response, callback, 0, "/somewhere/else", false,
+ BufferUtil.toBuffer("something special"));
+ return true;
+ }
+ });
+ server.start();
+
+ String request = """
+ GET /path HTTP/1.0\r
+ Host: hostname\r
+ \r
+ """;
+ HttpTester.Response response = HttpTester.parseResponse(connector.getResponse(request));
+ assertEquals(HttpStatus.MOVED_TEMPORARILY_302, response.getStatus());
+ assertThat(response.get(HttpHeader.LOCATION), is("http://hostname/somewhere/else"));
+ assertThat(response.get(HttpHeader.CONTENT_TYPE), is("exotic"));
+ assertThat(response.getContent(), containsString("something special"));
+ }
+
+ @Test
+ public void testRedirectGetWithGeneratedContent() throws Exception
+ {
+ server.getConnectors()[0].getConnectionFactory(HttpConnectionFactory.class)
+ .getHttpConfiguration().setRelativeRedirectAllowed(true);
+ server.getConnectors()[0].getConnectionFactory(HttpConnectionFactory.class)
+ .getHttpConfiguration().setGenerateRedirectBody(true);
+
+ server.setHandler(new Handler.Abstract()
+ {
+ @Override
+ public boolean handle(Request request, Response response, Callback callback)
+ {
+ Response.sendRedirect(request, response, callback, 0, "/somewhere/else", false, null);
+ return true;
+ }
+ });
+ server.start();
+
+ String request = """
+ GET /path HTTP/1.0\r
+ Host: hostname\r
+ \r
+ """;
+ HttpTester.Response response = HttpTester.parseResponse(connector.getResponse(request));
+ assertEquals(HttpStatus.MOVED_TEMPORARILY_302, response.getStatus());
+ assertThat(response.get(HttpHeader.LOCATION), is("/somewhere/else"));
+ assertThat(response.get(HttpHeader.CONTENT_TYPE), equalToIgnoringCase(MimeTypes.Type.TEXT_HTML_8859_1.asString()));
+ assertThat(response.getContent(), containsString(""));
+ }
+
@Test
public void testEncodedRedirectGET() throws Exception
{
diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiResponse.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiResponse.java
index 16896fd65b07..ce35fd536a58 100644
--- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiResponse.java
+++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiResponse.java
@@ -23,6 +23,8 @@
import java.util.function.Supplier;
import jakarta.servlet.ServletOutputStream;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler.ServletRequestInfo;
@@ -621,4 +623,119 @@ public String toString()
return HttpCookie.toString(this);
}
}
+
+ /**
+ * Servlet API wrapper for cross context included responses.
+ * It prevents the headers or response code from being updated.
+ * @see jakarta.servlet.RequestDispatcher#include(ServletRequest, ServletResponse)
+ */
+ public static class CrossContextInclude extends ServletApiResponse
+ {
+ public CrossContextInclude(ServletContextResponse servletContextResponse)
+ {
+ super(servletContextResponse);
+ }
+
+ @Override
+ public void setCharacterEncoding(String charset)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void setLocale(Locale loc)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void setContentLength(int len)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void setContentLengthLong(long len)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void setContentType(String type)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void reset()
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void resetBuffer()
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void setDateHeader(String name, long date)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void addDateHeader(String name, long date)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void setHeader(String name, String value)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void addHeader(String name, String value)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void setIntHeader(String name, int value)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void addIntHeader(String name, int value)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void setStatus(int sc)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void sendError(int sc, String msg) throws IOException
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void sendError(int sc) throws IOException
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void sendRedirect(String location) throws IOException
+ {
+ // NOOP for include.
+ }
+ }
}
diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextRequest.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextRequest.java
index aa7a48fb89f1..481612154127 100644
--- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextRequest.java
+++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextRequest.java
@@ -130,8 +130,6 @@ protected ServletContextRequest(
_httpInput = _servletChannel.getHttpInput();
_decodedPathInContext = decodedPathInContext;
_sessionManager = sessionManager;
- _servletApiRequest = newServletApiRequest();
- _response = newServletContextResponse(response);
_attributes = new Attributes.Synthetic(request)
{
@Override
@@ -156,6 +154,8 @@ protected Set getSyntheticNameSet()
return ATTRIBUTES;
}
};
+ _servletApiRequest = newServletApiRequest();
+ _response = newServletContextResponse(response);
addIdleTimeoutListener(_servletChannel.getServletRequestState()::onIdleTimeout);
}
diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextResponse.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextResponse.java
index 2ff380037196..975bc2933602 100644
--- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextResponse.java
+++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextResponse.java
@@ -19,6 +19,7 @@
import java.util.Map;
import java.util.function.Supplier;
+import jakarta.servlet.DispatcherType;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.ServletResponseWrapper;
@@ -174,6 +175,9 @@ protected ServletContextRequest getServletContextRequest()
protected ServletApiResponse newServletApiResponse()
{
+ if (_servletChannel.getServletContextHandler().isCrossContextDispatchSupported() &&
+ DispatcherType.INCLUDE.toString().equals(getRequest().getContext().getCrossContextDispatchType(getRequest())))
+ return new ServletApiResponse.CrossContextInclude(this);
return new ServletApiResponse(this);
}
diff --git a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/Dispatcher.java b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/Dispatcher.java
index 66d6ca64eef4..f533f551479e 100644
--- a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/Dispatcher.java
+++ b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/Dispatcher.java
@@ -664,6 +664,24 @@ public void sendRedirect(String location) throws IOException
{
// NOOP for include.
}
+
+ @Override
+ public void sendRedirect(String location, boolean clearBuffer) throws IOException
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void sendRedirect(String location, int sc) throws IOException
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void sendRedirect(String location, int sc, boolean clearBuffer) throws IOException
+ {
+ // NOOP for include.
+ }
}
private class AsyncRequest extends ParameterRequestWrapper
diff --git a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/HttpOutput.java b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/HttpOutput.java
index 13128e9a4ee8..4ac51b2adbb8 100644
--- a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/HttpOutput.java
+++ b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/HttpOutput.java
@@ -319,6 +319,20 @@ public void softClose()
}
}
+ public ByteBuffer takeContentAndClose()
+ {
+ try (AutoLock l = _channelState.lock())
+ {
+ if (_state != State.OPEN)
+ throw new IllegalStateException(stateString());
+ // TODO avoid this copy.
+ ByteBuffer content = _aggregate != null && _aggregate.hasRemaining() ? BufferUtil.copy(_aggregate.getByteBuffer()) : BufferUtil.EMPTY_BUFFER;
+ _state = State.CLOSED;
+ releaseBuffer();
+ return content;
+ }
+ }
+
/**
* This method is invoked for the COMPLETE action handling in
* HttpChannel.handle. The callback passed typically will call completed
diff --git a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletApiResponse.java b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletApiResponse.java
index 2999d99ca37a..c5668fe30955 100644
--- a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletApiResponse.java
+++ b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletApiResponse.java
@@ -15,6 +15,7 @@
import java.io.IOException;
import java.io.PrintWriter;
+import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.EnumSet;
@@ -24,6 +25,8 @@
import java.util.function.Supplier;
import jakarta.servlet.ServletOutputStream;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.ee11.servlet.ServletContextHandler.ServletRequestInfo;
@@ -34,6 +37,7 @@
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.WriteThroughWriter;
+import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.session.ManagedSession;
@@ -174,9 +178,17 @@ public void sendRedirect(String location) throws IOException
}
@Override
- public void sendRedirect(String s, int i, boolean b) throws IOException
+ public void sendRedirect(String location, int code, boolean clear) throws IOException
{
- //TODO servlet6.1
+ if (clear)
+ {
+ sendRedirect(code, location);
+ }
+ else
+ {
+ ByteBuffer buffer = getServletChannel().getHttpOutput().takeContentAndClose();
+ sendRedirect(code, location, buffer);
+ }
}
/**
@@ -187,11 +199,25 @@ public void sendRedirect(String s, int i, boolean b) throws IOException
* @throws IOException if unable to send the redirect
*/
public void sendRedirect(int code, String location) throws IOException
+ {
+ resetBuffer();
+ sendRedirect(code, location, null);
+ }
+
+ /**
+ * Sends a response with one of the 300 series redirection codes.
+ *
+ * @param code the redirect status code
+ * @param location the location to send in {@code Location} headers
+ * @param content the content of the response, or null for a generated HTML message if {@link HttpConfiguration#isGenerateRedirectBody()} is {@code true}.
+ * @throws IOException if unable to send the redirect
+ */
+ private void sendRedirect(int code, String location, ByteBuffer content) throws IOException
{
resetBuffer();
try (Blocker.Callback callback = Blocker.callback())
{
- Response.sendRedirect(getServletRequestInfo().getRequest(), getResponse(), callback, code, location, false);
+ Response.sendRedirect(getServletRequestInfo().getRequest(), getResponse(), callback, code, location, false, content);
callback.block();
}
}
@@ -634,4 +660,137 @@ public String toString()
return HttpCookie.toString(this);
}
}
+
+ /**
+ * Servlet API wrapper used on the post-dispatch side of a cross-context include.
+ * It prevents the headers or response code from being updated.
+ * @see jakarta.servlet.RequestDispatcher#include(ServletRequest, ServletResponse)
+ */
+ public static class CrossContextInclude extends ServletApiResponse
+ {
+ public CrossContextInclude(ServletContextResponse servletContextResponse)
+ {
+ super(servletContextResponse);
+ }
+
+ @Override
+ public void setCharacterEncoding(String charset)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void setLocale(Locale loc)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void setContentLength(int len)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void setContentLengthLong(long len)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void setContentType(String type)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void reset()
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void resetBuffer()
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void setDateHeader(String name, long date)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void addDateHeader(String name, long date)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void setHeader(String name, String value)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void addHeader(String name, String value)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void setIntHeader(String name, int value)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void addIntHeader(String name, int value)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void setStatus(int sc)
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void sendError(int sc, String msg) throws IOException
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void sendError(int sc) throws IOException
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void sendRedirect(String location) throws IOException
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void sendRedirect(String location, boolean clearBuffer) throws IOException
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void sendRedirect(String location, int sc) throws IOException
+ {
+ // NOOP for include.
+ }
+
+ @Override
+ public void sendRedirect(String location, int sc, boolean clearBuffer) throws IOException
+ {
+ // NOOP for include.
+ }
+ }
}
diff --git a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletContextRequest.java b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletContextRequest.java
index 3cbee385abc2..047d975fb75a 100644
--- a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletContextRequest.java
+++ b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletContextRequest.java
@@ -130,8 +130,6 @@ protected ServletContextRequest(
_httpInput = _servletChannel.getHttpInput();
_decodedPathInContext = decodedPathInContext;
_sessionManager = sessionManager;
- _servletApiRequest = newServletApiRequest();
- _response = newServletContextResponse(response);
_attributes = new Attributes.Synthetic(request)
{
@Override
@@ -156,6 +154,8 @@ protected Set getSyntheticNameSet()
return ATTRIBUTES;
}
};
+ _servletApiRequest = newServletApiRequest();
+ _response = newServletContextResponse(response);
addIdleTimeoutListener(_servletChannel.getServletRequestState()::onIdleTimeout);
}
diff --git a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletContextResponse.java b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletContextResponse.java
index 5043bfd8c4e1..3c83c20304be 100644
--- a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletContextResponse.java
+++ b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletContextResponse.java
@@ -19,6 +19,7 @@
import java.util.Map;
import java.util.function.Supplier;
+import jakarta.servlet.DispatcherType;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.ServletResponseWrapper;
@@ -174,6 +175,9 @@ protected ServletContextRequest getServletContextRequest()
protected ServletApiResponse newServletApiResponse()
{
+ if (_servletChannel.getServletContextHandler().isCrossContextDispatchSupported() &&
+ DispatcherType.INCLUDE.toString().equals(getRequest().getContext().getCrossContextDispatchType(getRequest())))
+ return new ServletApiResponse.CrossContextInclude(this);
return new ServletApiResponse(this);
}
diff --git a/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/CrossContextDispatcherTest.java b/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/CrossContextDispatcherTest.java
index 2b506ebb33f7..87e48222d737 100644
--- a/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/CrossContextDispatcherTest.java
+++ b/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/CrossContextDispatcherTest.java
@@ -65,8 +65,6 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
@@ -81,7 +79,6 @@
public class CrossContextDispatcherTest
{
- private static final Logger LOG = LoggerFactory.getLogger(CrossContextDispatcherTest.class);
public static final String MULTIPART = "--AaB03x\r\n" +
"content-disposition: form-data; name=\"field1\"\r\n" +
"\r\n" +
@@ -99,10 +96,6 @@ public class CrossContextDispatcherTest
"Connection: close\r\n";
public static final String GET_INCLUDE = "GET /context/dispatch/?include=/reader HTTP/1.1\r\n";
- public static final String MULTIPART_INCLUDE_REQUEST = GET_INCLUDE +
- MULTIPART_HEADERS +
- "\r\n" +
- MULTIPART;
public static final String GET_FORWARD = "GET /context/dispatch/?forward=/reader HTTP/1.1\r\n";
diff --git a/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResponseTest.java b/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResponseTest.java
new file mode 100644
index 000000000000..23f9577b94bf
--- /dev/null
+++ b/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResponseTest.java
@@ -0,0 +1,245 @@
+//
+// ========================================================================
+// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License v. 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+// which is available at https://www.apache.org/licenses/LICENSE-2.0.
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.ee11.servlet;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.emptyString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+
+public class ResponseTest
+{
+ private final HttpConfiguration _httpConfiguration = new HttpConfiguration();
+ private Server _server;
+ private LocalConnector _connector;
+
+ public void startServer(ServletContextHandler contextHandler) throws Exception
+ {
+ _server = new Server();
+ _connector = new LocalConnector(_server, new HttpConnectionFactory(_httpConfiguration));
+ _server.addConnector(_connector);
+
+ _server.setHandler(contextHandler);
+ _server.start();
+ }
+
+ @AfterEach
+ public void stopServer()
+ {
+ LifeCycle.stop(_server);
+ }
+
+ @Test
+ public void testSimple() throws Exception
+ {
+ ServletContextHandler contextHandler = new ServletContextHandler();
+ contextHandler.setContextPath("/");
+ HttpServlet servlet = new HttpServlet()
+ {
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ response.setContentType("text/plain; charset=US-ASCII");
+ response.getWriter().println("Hello");
+ }
+ };
+
+ contextHandler.addServlet(servlet, "/servlet/*");
+ startServer(contextHandler);
+
+ HttpTester.Request request = new HttpTester.Request();
+ request.setMethod("GET");
+ request.setURI("/servlet/");
+ request.setVersion(HttpVersion.HTTP_1_1);
+ request.setHeader("Connection", "close");
+ request.setHeader("Host", "test");
+
+ ByteBuffer responseBuffer = _connector.getResponse(request.generate());
+ HttpTester.Response response = HttpTester.parseResponse(responseBuffer);
+
+ assertThat(response.getStatus(), is(200));
+ assertThat(response.get("Content-Type"), is("text/plain; charset=US-ASCII"));
+ assertThat(response.getContent(), containsString("Hello"));
+ }
+
+ public static Stream redirects()
+ {
+ List cases = new ArrayList<>();
+
+ for (int code : new int[] {0, 307})
+ {
+ for (String location : new String[] {"somewhere/else", "/somewhere/else", "http://else/where" })
+ {
+ for (boolean relative : new boolean[] {true, false})
+ {
+ for (boolean generate : new boolean[] {true, false})
+ {
+ for (String content : new String[] {null, "clear", "alternative text" })
+ {
+ cases.add(Arguments.of(code, location, relative, generate, content));
+ }
+ }
+ }
+ }
+ }
+ return cases.stream();
+ }
+
+ @ParameterizedTest
+ @MethodSource("redirects")
+ public void testRedirect(int code, String location, boolean relative, boolean generate, String content) throws Exception
+ {
+ _httpConfiguration.setRelativeRedirectAllowed(relative);
+ _httpConfiguration.setGenerateRedirectBody(generate);
+
+ ServletContextHandler contextHandler = new ServletContextHandler();
+ contextHandler.setContextPath("/ctx");
+ HttpServlet servlet = new HttpServlet()
+ {
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException
+ {
+ if (code > 0)
+ {
+ if (content == null)
+ {
+ response.getOutputStream().write("oops".getBytes(StandardCharsets.UTF_8));
+ response.sendRedirect(location, code);
+ }
+ else if ("clear".equals(content))
+ {
+ response.getOutputStream().write("oops".getBytes(StandardCharsets.UTF_8));
+ response.sendRedirect(location, code, true);
+ }
+ else
+ {
+ response.setContentType(MimeTypes.Type.TEXT_PLAIN_UTF_8.asString());
+ response.getOutputStream().write(content.getBytes(StandardCharsets.UTF_8));
+ response.sendRedirect(location, code, false);
+ }
+ }
+ else
+ {
+ if (content == null)
+ {
+ response.getOutputStream().write("oops".getBytes(StandardCharsets.UTF_8));
+ response.sendRedirect(location);
+ }
+ else if ("clear".equals(content))
+ {
+ response.getOutputStream().write("oops".getBytes(StandardCharsets.UTF_8));
+ response.sendRedirect(location, true);
+ }
+ else
+ {
+ response.setContentType(MimeTypes.Type.TEXT_PLAIN_UTF_8.asString());
+ response.getOutputStream().write(content.getBytes(StandardCharsets.UTF_8));
+ response.sendRedirect(location, false);
+ }
+ }
+ }
+ };
+
+ contextHandler.addServlet(servlet, "/servlet/*");
+ startServer(contextHandler);
+
+ HttpTester.Request request = new HttpTester.Request();
+ request.setMethod("GET");
+ request.setURI("/ctx/servlet/test");
+ request.setVersion(HttpVersion.HTTP_1_1);
+ request.setHeader("Connection", "close");
+ request.setHeader("Host", "test");
+
+ ByteBuffer responseBuffer = _connector.getResponse(request.generate());
+ HttpTester.Response response = HttpTester.parseResponse(responseBuffer);
+
+ assertThat(response.getStatus(), is(code == 0 ? HttpStatus.FOUND_302 : code));
+
+ String destination = location;
+ if (relative)
+ {
+ if (!location.startsWith("/") && !location.startsWith("http:/"))
+ destination = "/ctx/servlet/" + location;
+ }
+ else
+ {
+ if (location.startsWith("/"))
+ destination = "http://test" + location;
+ else if (!location.startsWith("http:/"))
+ destination = "http://test/ctx/servlet/" + location;
+ }
+
+ HttpField to = response.getField(HttpHeader.LOCATION);
+ assertThat(to, notNullValue());
+ assertThat(to.getValue(), is(destination));
+
+ String expected = content;
+ if ("clear".equals(expected))
+ expected = null;
+
+ String actual = response.getContent();
+
+ if (expected == null)
+ {
+ if (generate)
+ {
+ assertThat(response.get(HttpHeader.CONTENT_TYPE), containsString("text/html"));
+ assertThat(actual, containsString("If you are not redirected, click here".formatted(destination)));
+ assertThat(actual, not(containsString("oops")));
+ }
+ else
+ {
+ assertThat(response.get().get(HttpHeader.CONTENT_TYPE), nullValue());
+ assertThat(actual, emptyString());
+ }
+ }
+ else
+ {
+ assertThat(response.get().get(HttpHeader.CONTENT_TYPE), notNullValue());
+ assertThat(actual, not(containsString("oops")));
+ assertThat(actual, containsString(expected));
+ }
+ }
+}
diff --git a/jetty-ee11/pom.xml b/jetty-ee11/pom.xml
index 803d6fd09b33..a1bbce93c306 100644
--- a/jetty-ee11/pom.xml
+++ b/jetty-ee11/pom.xml
@@ -61,7 +61,7 @@
4.0.1
4.0.0-M1
- 11.0.0-SNAPSHOT
+ 11.0.0-M19
2.0.1
true
diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/BufferedResponseHandler.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/BufferedResponseHandler.java
index 61fa4cd6738f..f6a58b08f685 100644
--- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/BufferedResponseHandler.java
+++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/BufferedResponseHandler.java
@@ -111,7 +111,7 @@ protected boolean shouldBuffer(HttpChannel channel, boolean last)
Response response = channel.getResponse();
int status = response.getStatus();
- if (HttpStatus.hasNoBody(status) || HttpStatus.isRedirection(status))
+ if (HttpStatus.hasNoBody(status) || HttpStatus.isRedirectionWithLocation(status))
return false;
String ct = response.getContentType();
diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/Response.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/Response.java
index 10e33baa484e..ee95e158a633 100644
--- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/Response.java
+++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/Response.java
@@ -561,7 +561,7 @@ public void sendRedirect(int code, String location, boolean consumeAll) throws I
{
if (consumeAll)
getHttpChannel().ensureConsumeAllOrNotPersistent();
- if (!HttpStatus.isRedirection(code))
+ if (!HttpStatus.isRedirectionWithLocation(code))
throw new IllegalArgumentException("Not a 3xx redirect code");
if (!isMutable())
diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/SecuredRedirectHandler.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/SecuredRedirectHandler.java
index 668d0ef1f688..dc9ac789fc34 100644
--- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/SecuredRedirectHandler.java
+++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/SecuredRedirectHandler.java
@@ -52,7 +52,7 @@ public SecuredRedirectHandler()
*/
public SecuredRedirectHandler(final int code)
{
- if (!HttpStatus.isRedirection(code))
+ if (!HttpStatus.isRedirectionWithLocation(code))
throw new IllegalArgumentException("Not a 3xx redirect code");
_redirectCode = code;
}