Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented servlet 6.1 redirect with content #11743

Merged
merged 15 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ public static boolean isSuccess(int code)
*/
public static boolean isRedirection(int code)
{
return ((300 <= code) && (code <= 399));
return ((300 <= code) && (code <= 399) && code != 304);
}

/**
Expand Down
1 change: 1 addition & 0 deletions jetty-core/jetty-server/src/main/config/etc/jetty.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
<Set name="requestCookieCompliance"><Call class="org.eclipse.jetty.http.CookieCompliance" name="from"><Arg><Property name="jetty.httpConfig.requestCookieCompliance" default="RFC6265"/></Arg></Call></Set>
<Set name="responseCookieCompliance"><Call class="org.eclipse.jetty.http.CookieCompliance" name="from"><Arg><Property name="jetty.httpConfig.responseCookieCompliance" default="RFC6265"/></Arg></Call></Set>
<Set name="relativeRedirectAllowed"><Property name="jetty.httpConfig.relativeRedirectAllowed" default="false"/></Set>
<Set name="generateRedirectBody" property="jetty.httpConfig.generateRedirectBody"/>
<Set name="useInputDirectByteBuffers" property="jetty.httpConfig.useInputDirectByteBuffers"/>
<Set name="useOutputDirectByteBuffers" property="jetty.httpConfig.useOutputDirectByteBuffers"/>
</New>
Expand Down
3 changes: 3 additions & 0 deletions jetty-core/jetty-server/src/main/config/modules/server.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ default boolean isSecure()

/**
* @return whether the functionality of pushing resources is supported
* @deprecated in favour of 103 Early Hints
*/
@Deprecated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Orthogonal change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes sorry. We have not been reviewing 12.1 changes unless necessary. I thought the redirect changes needed review, but some other servlet 6.1 changes got swept up.

default boolean isPushSupported()
{
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}

/**
Expand All @@ -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);
}

/**
* <p>Sends a {@code 302} HTTP redirect status code to the given location.</p>
*
* @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.isRedirection(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)
{
Expand All @@ -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 = """
<!DOCTYPE html>
<html lang="en">
<head><meta charset="ISO-8859-1"/><meta http-equiv="refresh" content="0; URL=%s/"/><title>Redirecting...</title></head>
<body><p>If you are not redirected, <a href="%s">click here</a>.</p></body>
</html>
""".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);
}

/**
Expand Down
Loading
Loading