Refactor Jetty SameSiteSupplier cookie support to use a Handler

Update `JettyServletWebServerFactory` so that the `SimeSiteSupplier`
support is handled using a `Handler` rather than a `HttpStream.Wrapper`.

Closes gh-37809
pull/37813/head
Phillip Webb 1 year ago
parent 927ece3bfc
commit b3ddec7793

@ -33,7 +33,7 @@ import java.util.EventListener;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.Spliterator; import java.util.Spliterator;
import java.util.UUID; import java.util.UUID;
@ -52,8 +52,10 @@ import org.eclipse.jetty.ee10.webapp.AbstractConfiguration;
import org.eclipse.jetty.ee10.webapp.Configuration; import org.eclipse.jetty.ee10.webapp.Configuration;
import org.eclipse.jetty.ee10.webapp.WebAppContext; import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.ee10.webapp.WebInfConfiguration; import org.eclipse.jetty.ee10.webapp.WebInfConfiguration;
import org.eclipse.jetty.http.CookieCompliance;
import org.eclipse.jetty.http.HttpCookie; import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpFields.Mutable; import org.eclipse.jetty.http.HttpFields.Mutable;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.MimeTypes;
@ -68,7 +70,6 @@ import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.HttpCookieUtils; import org.eclipse.jetty.server.HttpCookieUtils;
import org.eclipse.jetty.server.HttpStream;
import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
@ -775,6 +776,8 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor
*/ */
private static class SuppliedSameSiteCookieHandlerWrapper extends Handler.Wrapper { private static class SuppliedSameSiteCookieHandlerWrapper extends Handler.Wrapper {
private static final SetCookieParser setCookieParser = SetCookieParser.newInstance();
private final List<CookieSameSiteSupplier> suppliers; private final List<CookieSameSiteSupplier> suppliers;
SuppliedSameSiteCookieHandlerWrapper(List<CookieSameSiteSupplier> suppliers) { SuppliedSameSiteCookieHandlerWrapper(List<CookieSameSiteSupplier> suppliers) {
@ -783,62 +786,74 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor
@Override @Override
public boolean handle(Request request, Response response, Callback callback) throws Exception { public boolean handle(Request request, Response response, Callback callback) throws Exception {
request.addHttpStreamWrapper((stream) -> new SameSiteCookieHttpStreamWrapper(stream, request)); SuppliedSameSiteCookieResponse wrappedResponse = new SuppliedSameSiteCookieResponse(request, response);
return super.handle(request, response, callback); return super.handle(request, wrappedResponse, callback);
} }
private final class SameSiteCookieHttpStreamWrapper extends HttpStream.Wrapper { private class SuppliedSameSiteCookieResponse extends Response.Wrapper {
private static final SetCookieParser setCookieParser = SetCookieParser.newInstance();
private final Request request; private HttpFields.Mutable wrappedHeaders;
private SameSiteCookieHttpStreamWrapper(HttpStream wrapped, Request request) { SuppliedSameSiteCookieResponse(Request request, Response wrapped) {
super(wrapped); super(request, wrapped);
this.request = request; this.wrappedHeaders = new SuppliedSameSiteCookieHeaders(
request.getConnectionMetaData().getHttpConfiguration().getResponseCookieCompliance(),
wrapped.getHeaders());
} }
@Override @Override
public void prepareResponse(Mutable headers) { public Mutable getHeaders() {
super.prepareResponse(headers); return this.wrappedHeaders;
ListIterator<HttpField> headerFields = headers.listIterator();
while (headerFields.hasNext()) {
HttpField updatedField = applySameSiteIfNecessary(headerFields.next());
if (updatedField != null) {
headerFields.set(updatedField);
}
} }
} }
private HttpField applySameSiteIfNecessary(HttpField headerField) { private class SuppliedSameSiteCookieHeaders extends HttpFields.Mutable.Wrapper {
if (headerField.getHeader() != HttpHeader.SET_COOKIE) {
return null; private final CookieCompliance compliance;
SuppliedSameSiteCookieHeaders(CookieCompliance compliance, HttpFields.Mutable fields) {
super(fields);
this.compliance = compliance;
} }
HttpCookie cookie = setCookieParser.parse(headerField.getValue());
if (cookie == null) { @Override
return null; public HttpField onAddField(HttpField field) {
return (field.getHeader() != HttpHeader.SET_COOKIE) ? field : onAddSetCookieField(field);
} }
SameSite sameSite = getSameSite(cookie);
private HttpField onAddSetCookieField(HttpField field) {
HttpCookie cookie = setCookieParser.parse(field.getValue());
SameSite sameSite = (cookie != null) ? getSameSite(cookie) : null;
if (sameSite == null) { if (sameSite == null) {
return null; return field;
}
HttpCookie updatedCookie = buildCookieWithUpdatedSameSite(cookie, sameSite);
return new HttpCookieUtils.SetCookieHttpField(updatedCookie, this.compliance);
} }
return new HttpCookieUtils.SetCookieHttpField(
HttpCookie.build(cookie) private HttpCookie buildCookieWithUpdatedSameSite(HttpCookie cookie, SameSite sameSite) {
return HttpCookie.build(cookie)
.sameSite(org.eclipse.jetty.http.HttpCookie.SameSite.from(sameSite.name())) .sameSite(org.eclipse.jetty.http.HttpCookie.SameSite.from(sameSite.name()))
.build(), .build();
this.request.getConnectionMetaData().getHttpConfiguration().getResponseCookieCompliance());
} }
private SameSite getSameSite(HttpCookie cookie) { private SameSite getSameSite(HttpCookie cookie) {
Cookie servletCookie = new Cookie(cookie.getName(), cookie.getValue()); return getSameSite(asServletCookie(cookie));
cookie.getAttributes().forEach(servletCookie::setAttribute);
for (CookieSameSiteSupplier supplier : SuppliedSameSiteCookieHandlerWrapper.this.suppliers) {
SameSite sameSite = supplier.getSameSite(servletCookie);
if (sameSite != null) {
return sameSite;
} }
private SameSite getSameSite(Cookie cookie) {
return SuppliedSameSiteCookieHandlerWrapper.this.suppliers.stream()
.map((supplier) -> supplier.getSameSite(cookie))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
} }
return null;
private Cookie asServletCookie(HttpCookie cookie) {
Cookie servletCookie = new Cookie(cookie.getName(), cookie.getValue());
cookie.getAttributes().forEach(servletCookie::setAttribute);
return servletCookie;
} }
} }

Loading…
Cancel
Save