Merged branch 'jetty-10.0.x' into 'jetty-10.0.x-3951-http2_demand'.
This commit is contained in:
commit
13e40c8b33
|
@ -0,0 +1,19 @@
|
|||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 365
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 30
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- Pinned
|
||||
- Security
|
||||
- Specification
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: Stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has been a
|
||||
full year without activit. It will be closed if no further activity occurs.
|
||||
Thank you for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: >
|
||||
This issue has been closed due to it having no activity.
|
|
@ -55,13 +55,13 @@ public class JDK9ClientALPNProcessor implements ALPNProcessor.Client
|
|||
ALPNClientConnection alpn = (ALPNClientConnection)connection;
|
||||
SSLParameters sslParameters = sslEngine.getSSLParameters();
|
||||
List<String> protocols = alpn.getProtocols();
|
||||
sslParameters.setApplicationProtocols(protocols.toArray(new String[protocols.size()]));
|
||||
sslParameters.setApplicationProtocols(protocols.toArray(new String[0]));
|
||||
sslEngine.setSSLParameters(sslParameters);
|
||||
((DecryptedEndPoint)connection.getEndPoint()).getSslConnection()
|
||||
.addHandshakeListener(new ALPNListener(alpn));
|
||||
}
|
||||
|
||||
private final class ALPNListener implements SslHandshakeListener
|
||||
private static final class ALPNListener implements SslHandshakeListener
|
||||
{
|
||||
private final ALPNClientConnection alpnConnection;
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ public class JDK9ServerALPNProcessor implements ALPNProcessor.Server, SslHandsha
|
|||
sslEngine.setHandshakeApplicationProtocolSelector(new ALPNCallback((ALPNServerConnection)connection));
|
||||
}
|
||||
|
||||
private final class ALPNCallback implements BiFunction<SSLEngine, List<String>, String>, SslHandshakeListener
|
||||
private static final class ALPNCallback implements BiFunction<SSLEngine, List<String>, String>, SslHandshakeListener
|
||||
{
|
||||
private final ALPNServerConnection alpnConnection;
|
||||
|
||||
|
@ -68,10 +68,19 @@ public class JDK9ServerALPNProcessor implements ALPNProcessor.Server, SslHandsha
|
|||
@Override
|
||||
public String apply(SSLEngine engine, List<String> protocols)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("apply {} {}", alpnConnection, protocols);
|
||||
alpnConnection.select(protocols);
|
||||
return alpnConnection.getProtocol();
|
||||
try
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("apply {} {}", alpnConnection, protocols);
|
||||
alpnConnection.select(protocols);
|
||||
return alpnConnection.getProtocol();
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
// Cannot negotiate the protocol, return null to have
|
||||
// JSSE send Alert.NO_APPLICATION_PROTOCOL to the client.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,15 +19,18 @@
|
|||
package org.eclipse.jetty.alpn.java.server;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
@ -40,12 +43,16 @@ import org.eclipse.jetty.server.Request;
|
|||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
|
||||
public class JDK9ALPNTest
|
||||
{
|
||||
|
@ -90,7 +97,7 @@ public class JDK9ALPNTest
|
|||
startServer(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
|
@ -132,7 +139,7 @@ public class JDK9ALPNTest
|
|||
startServer(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
|
@ -170,4 +177,57 @@ public class JDK9ALPNTest
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClientSupportingALPNCannotNegotiateProtocol() throws Exception
|
||||
{
|
||||
startServer(new AbstractHandler() {
|
||||
@Override
|
||||
public void handle(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
jettyRequest.setHandled(true);
|
||||
}
|
||||
});
|
||||
|
||||
SslContextFactory sslContextFactory = new SslContextFactory.Client(true);
|
||||
sslContextFactory.start();
|
||||
String host = "localhost";
|
||||
int port = connector.getLocalPort();
|
||||
try (SocketChannel client = SocketChannel.open(new InetSocketAddress(host, port)))
|
||||
{
|
||||
client.socket().setSoTimeout(5000);
|
||||
|
||||
SSLEngine sslEngine = sslContextFactory.newSSLEngine(host, port);
|
||||
sslEngine.setUseClientMode(true);
|
||||
SSLParameters sslParameters = sslEngine.getSSLParameters();
|
||||
sslParameters.setApplicationProtocols(new String[]{"unknown/1.0"});
|
||||
sslEngine.setSSLParameters(sslParameters);
|
||||
sslEngine.beginHandshake();
|
||||
assertSame(SSLEngineResult.HandshakeStatus.NEED_WRAP, sslEngine.getHandshakeStatus());
|
||||
|
||||
ByteBuffer sslBuffer = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize());
|
||||
|
||||
SSLEngineResult result = sslEngine.wrap(BufferUtil.EMPTY_BUFFER, sslBuffer);
|
||||
assertSame(SSLEngineResult.Status.OK, result.getStatus());
|
||||
|
||||
sslBuffer.flip();
|
||||
client.write(sslBuffer);
|
||||
|
||||
assertSame(SSLEngineResult.HandshakeStatus.NEED_UNWRAP, sslEngine.getHandshakeStatus());
|
||||
|
||||
sslBuffer.clear();
|
||||
int read = client.read(sslBuffer);
|
||||
assertThat(read, greaterThan(0));
|
||||
|
||||
sslBuffer.flip();
|
||||
// TLS frame layout: record_type, major_version, minor_version, hi_length, lo_length
|
||||
int recordTypeAlert = 21;
|
||||
assertEquals(recordTypeAlert, sslBuffer.get(0) & 0xFF);
|
||||
// Alert record layout: alert_level, alert_code
|
||||
int alertLevelFatal = 2;
|
||||
assertEquals(alertLevelFatal, sslBuffer.get(5) & 0xFF);
|
||||
int alertCodeNoApplicationProtocol = 120;
|
||||
assertEquals(alertCodeNoApplicationProtocol, sslBuffer.get(6) & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ public abstract class HttpReceiver
|
|||
}
|
||||
}
|
||||
|
||||
private long demand()
|
||||
protected long demand()
|
||||
{
|
||||
return demand(LongUnaryOperator.identity());
|
||||
}
|
||||
|
|
|
@ -552,6 +552,12 @@ public class HttpRequest implements Request
|
|||
{
|
||||
this.responseListeners.add(new Response.DemandedContentListener()
|
||||
{
|
||||
@Override
|
||||
public void onBeforeContent(Response response, LongConsumer demand)
|
||||
{
|
||||
listener.onBeforeContent(response, demand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContent(Response response, LongConsumer demand, ByteBuffer content, Callback callback)
|
||||
{
|
||||
|
|
|
@ -416,13 +416,13 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements IConne
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onHeaders(int request)
|
||||
public boolean onHeaders(int request)
|
||||
{
|
||||
HttpChannelOverFCGI channel = activeChannels.get(request);
|
||||
if (channel != null)
|
||||
channel.responseHeaders();
|
||||
else
|
||||
noChannel(request);
|
||||
return !channel.responseHeaders();
|
||||
noChannel(request);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -80,9 +80,9 @@ public class ClientParser extends Parser
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onHeaders(int request)
|
||||
public boolean onHeaders(int request)
|
||||
{
|
||||
listener.onHeaders(request);
|
||||
return listener.onHeaders(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -135,7 +135,11 @@ public abstract class Parser
|
|||
{
|
||||
public void onHeader(int request, HttpField field);
|
||||
|
||||
public void onHeaders(int request);
|
||||
/**
|
||||
* @param request the request id
|
||||
* @return true to signal to the parser to stop parsing, false to continue parsing
|
||||
*/
|
||||
public boolean onHeaders(int request);
|
||||
|
||||
/**
|
||||
* @param request the request id
|
||||
|
@ -158,8 +162,9 @@ public abstract class Parser
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onHeaders(int request)
|
||||
public boolean onHeaders(int request)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -82,7 +82,7 @@ public class ResponseContentParser extends StreamContentParser
|
|||
parsers.remove(request);
|
||||
}
|
||||
|
||||
private class ResponseParser implements HttpParser.ResponseHandler
|
||||
private static class ResponseParser implements HttpParser.ResponseHandler
|
||||
{
|
||||
private final HttpFields fields = new HttpFields();
|
||||
private ClientParser.Listener listener;
|
||||
|
@ -90,6 +90,7 @@ public class ResponseContentParser extends StreamContentParser
|
|||
private final FCGIHttpParser httpParser;
|
||||
private State state = State.HEADERS;
|
||||
private boolean seenResponseCode;
|
||||
private boolean stalled;
|
||||
|
||||
private ResponseParser(ClientParser.Listener listener, int request)
|
||||
{
|
||||
|
@ -111,7 +112,11 @@ public class ResponseContentParser extends StreamContentParser
|
|||
case HEADERS:
|
||||
{
|
||||
if (httpParser.parseNext(buffer))
|
||||
{
|
||||
state = State.CONTENT_MODE;
|
||||
if (stalled)
|
||||
return true;
|
||||
}
|
||||
remaining = buffer.remaining();
|
||||
break;
|
||||
}
|
||||
|
@ -233,16 +238,17 @@ public class ResponseContentParser extends StreamContentParser
|
|||
}
|
||||
}
|
||||
|
||||
private void notifyHeaders()
|
||||
private boolean notifyHeaders()
|
||||
{
|
||||
try
|
||||
{
|
||||
listener.onHeaders(request);
|
||||
return listener.onHeaders(request);
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Exception while invoking listener " + listener, x);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,8 +261,10 @@ public class ResponseContentParser extends StreamContentParser
|
|||
notifyBegin(200, "OK");
|
||||
notifyHeaders(fields);
|
||||
}
|
||||
notifyHeaders();
|
||||
// Return from HTTP parsing so that we can parse the content.
|
||||
// Remember whether we have demand.
|
||||
stalled = notifyHeaders();
|
||||
// Always return from HTTP parsing so that we
|
||||
// can parse the content with the FCGI parser.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -109,10 +109,11 @@ public class ClientGeneratorTest
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onHeaders(int request)
|
||||
public boolean onHeaders(int request)
|
||||
{
|
||||
assertEquals(id, request);
|
||||
params.set(params.get() * primes[4]);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -91,10 +91,11 @@ public class ClientParserTest
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onHeaders(int request)
|
||||
public boolean onHeaders(int request)
|
||||
{
|
||||
assertEquals(id, request);
|
||||
params.set(params.get() * primes[2]);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ public class ServerFCGIConnection extends AbstractConnection
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onHeaders(int request)
|
||||
public boolean onHeaders(int request)
|
||||
{
|
||||
HttpChannelOverFCGI channel = channels.get(request);
|
||||
if (LOG.isDebugEnabled())
|
||||
|
@ -154,6 +154,7 @@ public class ServerFCGIConnection extends AbstractConnection
|
|||
channel.onRequest();
|
||||
channel.dispatch();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,7 +24,7 @@ import java.util.concurrent.TimeUnit;
|
|||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
|
||||
// TODO consider replacing this with java.net.HttpCookie
|
||||
// TODO consider replacing this with java.net.HttpCookie (once it supports RFC6265)
|
||||
public class HttpCookie
|
||||
{
|
||||
private static final String __COOKIE_DELIM = "\",;\\ \t";
|
||||
|
@ -33,14 +33,14 @@ public class HttpCookie
|
|||
/**
|
||||
*If this string is found within the comment parsed with {@link #isHttpOnlyInComment(String)} the check will return true
|
||||
**/
|
||||
private static final String HTTP_ONLY_COMMENT = "__HTTP_ONLY__";
|
||||
public static final String HTTP_ONLY_COMMENT = "__HTTP_ONLY__";
|
||||
/**
|
||||
*These strings are used by {@link #getSameSiteFromComment(String)} to check for a SameSite specifier in the comment
|
||||
**/
|
||||
private static final String SAME_SITE_COMMENT = "__SAME_SITE_";
|
||||
private static final String SAME_SITE_NONE_COMMENT = SAME_SITE_COMMENT + "NONE__";
|
||||
private static final String SAME_SITE_LAX_COMMENT = SAME_SITE_COMMENT + "LAX__";
|
||||
private static final String SAME_SITE_STRICT_COMMENT = SAME_SITE_COMMENT + "STRICT__";
|
||||
public static final String SAME_SITE_NONE_COMMENT = SAME_SITE_COMMENT + "NONE__";
|
||||
public static final String SAME_SITE_LAX_COMMENT = SAME_SITE_COMMENT + "LAX__";
|
||||
public static final String SAME_SITE_STRICT_COMMENT = SAME_SITE_COMMENT + "STRICT__";
|
||||
|
||||
public enum SameSite
|
||||
{
|
||||
|
@ -428,17 +428,17 @@ public class HttpCookie
|
|||
{
|
||||
if (comment != null)
|
||||
{
|
||||
if (comment.contains(SAME_SITE_NONE_COMMENT))
|
||||
if (comment.contains(SAME_SITE_STRICT_COMMENT))
|
||||
{
|
||||
return SameSite.NONE;
|
||||
return SameSite.STRICT;
|
||||
}
|
||||
if (comment.contains(SAME_SITE_LAX_COMMENT))
|
||||
{
|
||||
return SameSite.LAX;
|
||||
}
|
||||
if (comment.contains(SAME_SITE_STRICT_COMMENT))
|
||||
if (comment.contains(SAME_SITE_NONE_COMMENT))
|
||||
{
|
||||
return SameSite.STRICT;
|
||||
return SameSite.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -462,6 +462,44 @@ public class HttpCookie
|
|||
return strippedComment.length() == 0 ? null : strippedComment;
|
||||
}
|
||||
|
||||
public static String getCommentWithAttributes(String comment, boolean httpOnly, SameSite sameSite)
|
||||
{
|
||||
if (comment == null && sameSite == null)
|
||||
return null;
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (StringUtil.isNotBlank(comment))
|
||||
{
|
||||
comment = getCommentWithoutAttributes(comment);
|
||||
if (StringUtil.isNotBlank(comment))
|
||||
builder.append(comment);
|
||||
}
|
||||
if (httpOnly)
|
||||
builder.append(HTTP_ONLY_COMMENT);
|
||||
|
||||
if (sameSite != null)
|
||||
{
|
||||
switch (sameSite)
|
||||
{
|
||||
case NONE:
|
||||
builder.append(SAME_SITE_NONE_COMMENT);
|
||||
break;
|
||||
case STRICT:
|
||||
builder.append(SAME_SITE_STRICT_COMMENT);
|
||||
break;
|
||||
case LAX:
|
||||
builder.append(SAME_SITE_LAX_COMMENT);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(sameSite.toString());
|
||||
}
|
||||
}
|
||||
|
||||
if (builder.length() == 0)
|
||||
return null;
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static class SetCookieHttpField extends HttpField
|
||||
{
|
||||
final HttpCookie _cookie;
|
||||
|
|
|
@ -18,17 +18,25 @@
|
|||
|
||||
package org.eclipse.jetty.http;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
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 org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
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.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class HttpCookieTest
|
||||
{
|
||||
|
@ -93,7 +101,6 @@ public class HttpCookieTest
|
|||
httpCookie = new HttpCookie("everything", "value", "domain", "path", 0, true, true, null, -1);
|
||||
assertEquals("everything=value; Path=path; Domain=domain; Expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Secure; HttpOnly", httpCookie.getRFC6265SetCookie());
|
||||
|
||||
|
||||
httpCookie = new HttpCookie("everything", "value", "domain", "path", 0, true, true, null, -1, HttpCookie.SameSite.NONE);
|
||||
assertEquals("everything=value; Path=path; Domain=domain; Expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Secure; HttpOnly; SameSite=None", httpCookie.getRFC6265SetCookie());
|
||||
|
||||
|
@ -102,8 +109,11 @@ public class HttpCookieTest
|
|||
|
||||
httpCookie = new HttpCookie("everything", "value", "domain", "path", 0, true, true, null, -1, HttpCookie.SameSite.STRICT);
|
||||
assertEquals("everything=value; Path=path; Domain=domain; Expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Secure; HttpOnly; SameSite=Strict", httpCookie.getRFC6265SetCookie());
|
||||
}
|
||||
|
||||
String[] badNameExamples = {
|
||||
public static Stream<String> rfc6265BadNameSource()
|
||||
{
|
||||
return Stream.of(
|
||||
"\"name\"",
|
||||
"name\t",
|
||||
"na me",
|
||||
|
@ -113,25 +123,32 @@ public class HttpCookieTest
|
|||
"{name}",
|
||||
"[name]",
|
||||
"\""
|
||||
};
|
||||
);
|
||||
}
|
||||
|
||||
for (String badNameExample : badNameExamples)
|
||||
{
|
||||
try
|
||||
@ParameterizedTest
|
||||
@MethodSource("rfc6265BadNameSource")
|
||||
public void testSetRFC6265CookieBadName(String badNameExample)
|
||||
{
|
||||
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
|
||||
() ->
|
||||
{
|
||||
httpCookie = new HttpCookie(badNameExample, "value", null, "/", 1, true, true, null, -1);
|
||||
HttpCookie httpCookie = new HttpCookie(badNameExample, "value", null, "/", 1, true, true, null, -1);
|
||||
httpCookie.getRFC6265SetCookie();
|
||||
fail(badNameExample);
|
||||
}
|
||||
catch (IllegalArgumentException ex)
|
||||
{
|
||||
// System.err.printf("%s: %s%n", ex.getClass().getSimpleName(), ex.getMessage());
|
||||
assertThat("Testing bad name: [" + badNameExample + "]", ex.getMessage(),
|
||||
allOf(containsString("RFC6265"), containsString("RFC2616")));
|
||||
}
|
||||
}
|
||||
});
|
||||
// make sure that exception mentions just how mad of a name it truly is
|
||||
assertThat("message", ex.getMessage(),
|
||||
allOf(
|
||||
// violation of Cookie spec
|
||||
containsString("RFC6265"),
|
||||
// violation of HTTP spec
|
||||
containsString("RFC2616")
|
||||
));
|
||||
}
|
||||
|
||||
String[] badValueExamples = {
|
||||
public static Stream<String> rfc6265BadValueSource()
|
||||
{
|
||||
return Stream.of(
|
||||
"va\tlue",
|
||||
"\t",
|
||||
"value\u0000",
|
||||
|
@ -143,39 +160,44 @@ public class HttpCookieTest
|
|||
"val\\ue",
|
||||
"val\"ue",
|
||||
"\""
|
||||
};
|
||||
);
|
||||
}
|
||||
|
||||
for (String badValueExample : badValueExamples)
|
||||
{
|
||||
try
|
||||
@ParameterizedTest
|
||||
@MethodSource("rfc6265BadValueSource")
|
||||
public void testSetRFC6265CookieBadValue(String badValueExample)
|
||||
{
|
||||
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
|
||||
() ->
|
||||
{
|
||||
httpCookie = new HttpCookie("name", badValueExample, null, "/", 1, true, true, null, -1);
|
||||
HttpCookie httpCookie = new HttpCookie("name", badValueExample, null, "/", 1, true, true, null, -1);
|
||||
httpCookie.getRFC6265SetCookie();
|
||||
fail();
|
||||
}
|
||||
catch (IllegalArgumentException ex)
|
||||
{
|
||||
// System.err.printf("%s: %s%n", ex.getClass().getSimpleName(), ex.getMessage());
|
||||
assertThat("Testing bad value [" + badValueExample + "]", ex.getMessage(), Matchers.containsString("RFC6265"));
|
||||
}
|
||||
}
|
||||
});
|
||||
assertThat("message", ex.getMessage(), containsString("RFC6265"));
|
||||
}
|
||||
|
||||
String[] goodNameExamples = {
|
||||
public static Stream<String> rfc6265GoodNameSource()
|
||||
{
|
||||
return Stream.of(
|
||||
"name",
|
||||
"n.a.m.e",
|
||||
"na-me",
|
||||
"+name",
|
||||
"na*me",
|
||||
"na$me",
|
||||
"#name"
|
||||
};
|
||||
"#name");
|
||||
}
|
||||
|
||||
for (String goodNameExample : goodNameExamples)
|
||||
{
|
||||
httpCookie = new HttpCookie(goodNameExample, "value", null, "/", 1, true, true, null, -1);
|
||||
// should not throw an exception
|
||||
}
|
||||
@ParameterizedTest
|
||||
@MethodSource("rfc6265GoodNameSource")
|
||||
public void testSetRFC6265CookieGoodName(String goodNameExample)
|
||||
{
|
||||
new HttpCookie(goodNameExample, "value", null, "/", 1, true, true, null, -1);
|
||||
// should not throw an exception
|
||||
}
|
||||
|
||||
public static Stream<String> rfc6265GoodValueSource()
|
||||
{
|
||||
String[] goodValueExamples = {
|
||||
"value",
|
||||
"",
|
||||
|
@ -185,37 +207,150 @@ public class HttpCookieTest
|
|||
"val/ue",
|
||||
"v.a.l.u.e"
|
||||
};
|
||||
return Stream.of(goodValueExamples);
|
||||
}
|
||||
|
||||
for (String goodValueExample : goodValueExamples)
|
||||
@ParameterizedTest
|
||||
@MethodSource("rfc6265GoodValueSource")
|
||||
public void testSetRFC6265CookieGoodValue(String goodValueExample)
|
||||
{
|
||||
new HttpCookie("name", goodValueExample, null, "/", 1, true, true, null, -1);
|
||||
// should not throw an exception
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"__HTTP_ONLY__",
|
||||
"__HTTP_ONLY__comment",
|
||||
"comment__HTTP_ONLY__"
|
||||
})
|
||||
public void testIsHttpOnlyInCommentTrue(String comment)
|
||||
{
|
||||
assertTrue(HttpCookie.isHttpOnlyInComment(comment), "Comment \"" + comment + "\"");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"comment",
|
||||
"",
|
||||
"__",
|
||||
"__HTTP__ONLY__",
|
||||
"__http_only__",
|
||||
"HTTP_ONLY",
|
||||
"__HTTP__comment__ONLY__"
|
||||
})
|
||||
public void testIsHttpOnlyInCommentFalse(String comment)
|
||||
{
|
||||
assertFalse(HttpCookie.isHttpOnlyInComment(comment), "Comment \"" + comment + "\"");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"__SAME_SITE_NONE__",
|
||||
"__SAME_SITE_NONE____SAME_SITE_NONE__"
|
||||
})
|
||||
public void testGetSameSiteFromCommentNONE(String comment)
|
||||
{
|
||||
assertEquals(HttpCookie.getSameSiteFromComment(comment), HttpCookie.SameSite.NONE, "Comment \"" + comment + "\"");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"__SAME_SITE_LAX__",
|
||||
"__SAME_SITE_LAX____SAME_SITE_NONE__",
|
||||
"__SAME_SITE_NONE____SAME_SITE_LAX__",
|
||||
"__SAME_SITE_LAX____SAME_SITE_NONE__"
|
||||
})
|
||||
public void testGetSameSiteFromCommentLAX(String comment)
|
||||
{
|
||||
assertEquals(HttpCookie.getSameSiteFromComment(comment), HttpCookie.SameSite.LAX, "Comment \"" + comment + "\"");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"__SAME_SITE_STRICT__",
|
||||
"__SAME_SITE_NONE____SAME_SITE_STRICT____SAME_SITE_LAX__",
|
||||
"__SAME_SITE_STRICT____SAME_SITE_LAX____SAME_SITE_NONE__",
|
||||
"__SAME_SITE_STRICT____SAME_SITE_STRICT__"
|
||||
})
|
||||
public void testGetSameSiteFromCommentSTRICT(String comment)
|
||||
{
|
||||
assertEquals(HttpCookie.getSameSiteFromComment(comment), HttpCookie.SameSite.STRICT, "Comment \"" + comment + "\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* A comment that does not have a declared SamesSite attribute defined
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"__HTTP_ONLY__",
|
||||
"comment",
|
||||
// not jetty attributes
|
||||
"SameSite=None",
|
||||
"SameSite=Lax",
|
||||
"SameSite=Strict",
|
||||
// incomplete jetty attributes
|
||||
"SAME_SITE_NONE",
|
||||
"SAME_SITE_LAX",
|
||||
"SAME_SITE_STRICT",
|
||||
})
|
||||
public void testGetSameSiteFromCommentUndefined(String comment)
|
||||
{
|
||||
assertNull(HttpCookie.getSameSiteFromComment(comment), "Comment \"" + comment + "\"");
|
||||
}
|
||||
|
||||
public static Stream<Arguments> getCommentWithoutAttributesSource()
|
||||
{
|
||||
return Stream.of(
|
||||
// normal - only attribute comment
|
||||
Arguments.of("__SAME_SITE_LAX__", null),
|
||||
// normal - no attribute comment
|
||||
Arguments.of("comment", "comment"),
|
||||
// mixed - attributes at end
|
||||
Arguments.of("comment__SAME_SITE_NONE__", "comment"),
|
||||
Arguments.of("comment__HTTP_ONLY____SAME_SITE_NONE__", "comment"),
|
||||
// mixed - attributes at start
|
||||
Arguments.of("__SAME_SITE_NONE__comment", "comment"),
|
||||
Arguments.of("__HTTP_ONLY____SAME_SITE_NONE__comment", "comment"),
|
||||
// mixed - attributes at start and end
|
||||
Arguments.of("__SAME_SITE_NONE__comment__HTTP_ONLY__", "comment"),
|
||||
Arguments.of("__HTTP_ONLY__comment__SAME_SITE_NONE__", "comment")
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("getCommentWithoutAttributesSource")
|
||||
public void testGetCommentWithoutAttributes(String rawComment, String expectedComment)
|
||||
{
|
||||
String actualComment = HttpCookie.getCommentWithoutAttributes(rawComment);
|
||||
if (expectedComment == null)
|
||||
{
|
||||
httpCookie = new HttpCookie("name", goodValueExample, null, "/", 1, true, true, null, -1);
|
||||
// should not throw an exception
|
||||
assertNull(actualComment);
|
||||
}
|
||||
else
|
||||
{
|
||||
assertEquals(actualComment, expectedComment);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetHttpOnlyFromComment()
|
||||
public void testGetCommentWithAttributes()
|
||||
{
|
||||
assertTrue(HttpCookie.isHttpOnlyInComment("__HTTP_ONLY__"));
|
||||
assertTrue(HttpCookie.isHttpOnlyInComment("__HTTP_ONLY__comment"));
|
||||
assertFalse(HttpCookie.isHttpOnlyInComment("comment"));
|
||||
}
|
||||
assertThat(HttpCookie.getCommentWithAttributes(null, false, null), nullValue());
|
||||
assertThat(HttpCookie.getCommentWithAttributes("", false, null), nullValue());
|
||||
assertThat(HttpCookie.getCommentWithAttributes("hello", false, null), is("hello"));
|
||||
|
||||
@Test
|
||||
public void testGetSameSiteFromComment()
|
||||
{
|
||||
assertEquals(HttpCookie.getSameSiteFromComment("__SAME_SITE_NONE__"), HttpCookie.SameSite.NONE);
|
||||
assertEquals(HttpCookie.getSameSiteFromComment("__SAME_SITE_LAX__"), HttpCookie.SameSite.LAX);
|
||||
assertEquals(HttpCookie.getSameSiteFromComment("__SAME_SITE_STRICT__"), HttpCookie.SameSite.STRICT);
|
||||
assertEquals(HttpCookie.getSameSiteFromComment("__SAME_SITE_NONE____SAME_SITE_STRICT__"), HttpCookie.SameSite.NONE);
|
||||
assertNull(HttpCookie.getSameSiteFromComment("comment"));
|
||||
}
|
||||
assertThat(HttpCookie.getCommentWithAttributes(null, true, HttpCookie.SameSite.STRICT),
|
||||
is("__HTTP_ONLY____SAME_SITE_STRICT__"));
|
||||
assertThat(HttpCookie.getCommentWithAttributes("", true, HttpCookie.SameSite.NONE),
|
||||
is("__HTTP_ONLY____SAME_SITE_NONE__"));
|
||||
assertThat(HttpCookie.getCommentWithAttributes("hello", true, HttpCookie.SameSite.LAX),
|
||||
is("hello__HTTP_ONLY____SAME_SITE_LAX__"));
|
||||
|
||||
@Test
|
||||
public void getCommentWithoutAttributes()
|
||||
{
|
||||
assertEquals(HttpCookie.getCommentWithoutAttributes("comment__SAME_SITE_NONE__"), "comment");
|
||||
assertEquals(HttpCookie.getCommentWithoutAttributes("comment__HTTP_ONLY____SAME_SITE_NONE__"), "comment");
|
||||
assertNull(HttpCookie.getCommentWithoutAttributes("__SAME_SITE_LAX__"));
|
||||
assertThat(HttpCookie.getCommentWithAttributes("__HTTP_ONLY____SAME_SITE_LAX__", false, null), nullValue());
|
||||
assertThat(HttpCookie.getCommentWithAttributes("__HTTP_ONLY____SAME_SITE_LAX__", true, HttpCookie.SameSite.NONE),
|
||||
is("__HTTP_ONLY____SAME_SITE_NONE__"));
|
||||
assertThat(HttpCookie.getCommentWithAttributes("__HTTP_ONLY____SAME_SITE_LAX__hello", true, HttpCookie.SameSite.LAX),
|
||||
is("hello__HTTP_ONLY____SAME_SITE_LAX__"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
package org.eclipse.jetty.http2.parser;
|
||||
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
|
||||
/**
|
||||
* Controls rate of events via {@link #onEvent(Object)}.
|
||||
*/
|
||||
|
@ -36,4 +38,19 @@ public interface RateControl
|
|||
* @return true IFF the rate is within limits
|
||||
*/
|
||||
public boolean onEvent(Object event);
|
||||
|
||||
/**
|
||||
* Factory to create RateControl instances.
|
||||
*/
|
||||
public interface Factory
|
||||
{
|
||||
/**
|
||||
* @return a new RateControl instance for the given EndPoint
|
||||
* @param endPoint the EndPoint for which the RateControl is created
|
||||
*/
|
||||
public default RateControl newRateControl(EndPoint endPoint)
|
||||
{
|
||||
return NO_RATE_CONTROL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ import java.util.Queue;
|
|||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
|
||||
/**
|
||||
* <p>An implementation of {@link RateControl} that limits the number of
|
||||
* events within a time period.</p>
|
||||
|
@ -48,6 +50,21 @@ public class WindowRateControl implements RateControl
|
|||
{
|
||||
this.maxEvents = maxEvents;
|
||||
this.window = window.toNanos();
|
||||
if (this.window == 0)
|
||||
throw new IllegalArgumentException("Invalid duration " + window);
|
||||
}
|
||||
|
||||
public int getEventsPerSecond()
|
||||
{
|
||||
try
|
||||
{
|
||||
long rate = maxEvents * 1_000_000_000L / window;
|
||||
return Math.toIntExact(rate);
|
||||
}
|
||||
catch (ArithmeticException x)
|
||||
{
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -67,4 +84,20 @@ public class WindowRateControl implements RateControl
|
|||
events.add(now + window);
|
||||
return size.incrementAndGet() <= maxEvents;
|
||||
}
|
||||
|
||||
public static class Factory implements RateControl.Factory
|
||||
{
|
||||
private final int maxEventRate;
|
||||
|
||||
public Factory(int maxEventRate)
|
||||
{
|
||||
this.maxEventRate = maxEventRate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RateControl newRateControl(EndPoint endPoint)
|
||||
{
|
||||
return WindowRateControl.fromEventsPerSecond(maxEventRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,12 +120,24 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements HTTP2Channel.
|
|||
if (frame.isEndStream() || informational)
|
||||
responseSuccess(exchange);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (frame.isEndStream())
|
||||
{
|
||||
// There is no demand to trigger response success, so add
|
||||
// a poison pill to trigger it when there will be demand.
|
||||
notifyContent(exchange, new DataFrame(stream.getId(), BufferUtil.EMPTY_BUFFER, true), Callback.NOOP);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else // Response trailers.
|
||||
{
|
||||
HttpFields trailers = metaData.getFields();
|
||||
trailers.forEach(httpResponse::trailer);
|
||||
// Previous DataFrames had endStream=false, so
|
||||
// add a poison pill to trigger response success
|
||||
// after all normal DataFrames have been consumed.
|
||||
notifyContent(exchange, new DataFrame(stream.getId(), BufferUtil.EMPTY_BUFFER, true), Callback.NOOP);
|
||||
}
|
||||
}
|
||||
|
@ -212,7 +224,7 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements HTTP2Channel.
|
|||
contentNotifier.offer(exchange, frame, callback);
|
||||
}
|
||||
|
||||
private static class ContentNotifier
|
||||
private class ContentNotifier
|
||||
{
|
||||
private final Queue<DataInfo> queue = new ArrayDeque<>();
|
||||
private final HttpReceiverOverHTTP2 receiver;
|
||||
|
@ -246,9 +258,25 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements HTTP2Channel.
|
|||
private void process(boolean resume)
|
||||
{
|
||||
// Allow only one thread at a time.
|
||||
if (active(resume))
|
||||
boolean busy = active(resume);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Resuming({}) processing({}) of content", resume, !busy);
|
||||
if (busy)
|
||||
return;
|
||||
|
||||
// Process only if there is demand.
|
||||
synchronized (this)
|
||||
{
|
||||
if (!resume && demand() <= 0)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Stalling processing, content available but no demand");
|
||||
active = false;
|
||||
stalled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (dataInfo != null)
|
||||
|
@ -265,7 +293,7 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements HTTP2Channel.
|
|||
{
|
||||
dataInfo = queue.poll();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Dequeued content {}", dataInfo);
|
||||
LOG.debug("Processing content {}", dataInfo);
|
||||
if (dataInfo == null)
|
||||
{
|
||||
active = false;
|
||||
|
@ -281,8 +309,12 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements HTTP2Channel.
|
|||
boolean proceed = receiver.responseContent(dataInfo.exchange, buffer, Callback.from(callback::succeeded, x -> fail(callback, x)));
|
||||
if (!proceed)
|
||||
{
|
||||
// Should stall, unless just resumed.
|
||||
if (stall())
|
||||
// The call to responseContent() said we should
|
||||
// stall, but another thread may have just resumed.
|
||||
boolean stall = stall();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Stalling({}) processing", stall);
|
||||
if (stall)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -299,27 +331,46 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements HTTP2Channel.
|
|||
{
|
||||
if (active)
|
||||
{
|
||||
// There is a thread in process(),
|
||||
// but it may be about to exit, so
|
||||
// remember "resume" to signal the
|
||||
// processing thread to continue.
|
||||
if (resume)
|
||||
this.resume = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// If there is no demand (i.e. stalled
|
||||
// and not resuming) then don't process.
|
||||
if (stalled && !resume)
|
||||
return true;
|
||||
|
||||
// Start processing.
|
||||
active = true;
|
||||
stalled = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when there is no demand, this method checks whether
|
||||
* the processing should really stop or it should continue.
|
||||
*
|
||||
* @return true to stop processing, false to continue processing
|
||||
*/
|
||||
private boolean stall()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (resume)
|
||||
{
|
||||
// There was no demand, but another thread
|
||||
// just demanded, continue processing.
|
||||
resume = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// There is no demand, stop processing.
|
||||
active = false;
|
||||
stalled = true;
|
||||
return true;
|
||||
|
@ -344,7 +395,7 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements HTTP2Channel.
|
|||
receiver.responseFailure(failure);
|
||||
}
|
||||
|
||||
private static class DataInfo
|
||||
private class DataInfo
|
||||
{
|
||||
private final HttpExchange exchange;
|
||||
private final DataFrame frame;
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- Configure an HTTP2 on the ssl connector. -->
|
||||
<!-- ============================================================= -->
|
||||
<Configure id="sslConnector" class="org.eclipse.jetty.server.ServerConnector">
|
||||
<Call name="addConnectionFactory">
|
||||
<Arg>
|
||||
|
@ -13,10 +10,10 @@
|
|||
<Set name="initialStreamRecvWindow" property="jetty.http2.initialStreamRecvWindow"/>
|
||||
<Set name="initialSessionRecvWindow" property="jetty.http2.initialSessionRecvWindow"/>
|
||||
<Set name="maxSettingsKeys"><Property name="jetty.http2.maxSettingsKeys" default="64"/></Set>
|
||||
<Set name="rateControl">
|
||||
<Call class="org.eclipse.jetty.http2.parser.WindowRateControl" name="fromEventsPerSecond">
|
||||
<Set name="rateControlFactory">
|
||||
<New class="org.eclipse.jetty.http2.parser.WindowRateControl$Factory">
|
||||
<Arg type="int"><Property name="jetty.http2.rateControl.maxEventsPerSecond" default="20"/></Arg>
|
||||
</Call>
|
||||
</New>
|
||||
</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- Configure an HTTP2 on the ssl connector. -->
|
||||
<!-- ============================================================= -->
|
||||
<Configure id="httpConnector" class="org.eclipse.jetty.server.ServerConnector">
|
||||
<Call name="addConnectionFactory">
|
||||
<Arg>
|
||||
|
@ -12,10 +9,10 @@
|
|||
<Set name="maxConcurrentStreams" property="jetty.http2c.maxConcurrentStreams"/>
|
||||
<Set name="initialStreamRecvWindow" property="jetty.http2c.initialStreamRecvWindow"/>
|
||||
<Set name="maxSettingsKeys"><Property name="jetty.http2.maxSettingsKeys" default="64"/></Set>
|
||||
<Set name="rateControl">
|
||||
<Call class="org.eclipse.jetty.http2.parser.WindowRateControl" name="fromEventsPerSecond">
|
||||
<Set name="rateControlFactory">
|
||||
<New class="org.eclipse.jetty.http2.parser.WindowRateControl$Factory">
|
||||
<Arg type="int"><Property name="jetty.http2.rateControl.maxEventsPerSecond" default="20"/></Arg>
|
||||
</Call>
|
||||
</New>
|
||||
</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
package org.eclipse.jetty.http2.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
@ -61,7 +60,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
|
|||
private int maxHeaderBlockFragment = 0;
|
||||
private int maxFrameLength = Frame.DEFAULT_MAX_LENGTH;
|
||||
private int maxSettingsKeys = SettingsFrame.DEFAULT_MAX_KEYS;
|
||||
private RateControl rateControl = new WindowRateControl(20, Duration.ofSeconds(1));
|
||||
private RateControl.Factory rateControlFactory = new WindowRateControl.Factory(20);
|
||||
private FlowControlStrategy.Factory flowControlStrategyFactory = () -> new BufferingFlowControlStrategy(0.5F);
|
||||
private long streamIdleTimeout;
|
||||
private boolean _useInputDirectByteBuffers;
|
||||
|
@ -186,14 +185,22 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
|
|||
this.maxSettingsKeys = maxSettingsKeys;
|
||||
}
|
||||
|
||||
public RateControl getRateControl()
|
||||
/**
|
||||
* @return the factory that creates RateControl objects
|
||||
*/
|
||||
public RateControl.Factory getRateControlFactory()
|
||||
{
|
||||
return rateControl;
|
||||
return rateControlFactory;
|
||||
}
|
||||
|
||||
public void setRateControl(RateControl rateControl)
|
||||
/**
|
||||
* <p>Sets the factory that creates a per-connection RateControl object.</p>
|
||||
*
|
||||
* @param rateControlFactory the factory that creates RateControl objects
|
||||
*/
|
||||
public void setRateControlFactory(RateControl.Factory rateControlFactory)
|
||||
{
|
||||
this.rateControl = rateControl;
|
||||
this.rateControlFactory = Objects.requireNonNull(rateControlFactory);
|
||||
}
|
||||
|
||||
public boolean isUseInputDirectByteBuffers()
|
||||
|
@ -253,7 +260,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
|
|||
session.setInitialSessionRecvWindow(getInitialSessionRecvWindow());
|
||||
session.setWriteThreshold(getHttpConfiguration().getOutputBufferSize());
|
||||
|
||||
ServerParser parser = newServerParser(connector, session, getRateControl());
|
||||
ServerParser parser = newServerParser(connector, session, getRateControlFactory().newRateControl(endPoint));
|
||||
parser.setMaxFrameLength(getMaxFrameLength());
|
||||
parser.setMaxSettingsKeys(getMaxSettingsKeys());
|
||||
|
||||
|
|
|
@ -251,8 +251,7 @@ public class OpenIdAuthenticator extends LoginAuthenticator
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Session ID should be cookie for OpenID authentication to work");
|
||||
|
||||
int redirectCode = (baseRequest.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER);
|
||||
baseResponse.sendRedirect(redirectCode, URIUtil.addPaths(request.getContextPath(), _errorPage));
|
||||
baseResponse.sendRedirect(getRedirectCode(baseRequest.getHttpVersion()), URIUtil.addPaths(request.getContextPath(), _errorPage));
|
||||
return Authentication.SEND_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -297,8 +296,7 @@ public class OpenIdAuthenticator extends LoginAuthenticator
|
|||
LOG.debug("authenticated {}->{}", openIdAuth, nuri);
|
||||
|
||||
response.setContentLength(0);
|
||||
int redirectCode = (baseRequest.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER);
|
||||
baseResponse.sendRedirect(redirectCode, nuri);
|
||||
baseResponse.sendRedirect(getRedirectCode(baseRequest.getHttpVersion()), nuri);
|
||||
return openIdAuth;
|
||||
}
|
||||
}
|
||||
|
@ -317,8 +315,7 @@ public class OpenIdAuthenticator extends LoginAuthenticator
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("auth failed {}", _errorPage);
|
||||
int redirectCode = (baseRequest.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER);
|
||||
baseResponse.sendRedirect(redirectCode, URIUtil.addPaths(request.getContextPath(), _errorPage));
|
||||
baseResponse.sendRedirect(getRedirectCode(baseRequest.getHttpVersion()), URIUtil.addPaths(request.getContextPath(), _errorPage));
|
||||
}
|
||||
|
||||
return Authentication.SEND_FAILURE;
|
||||
|
@ -408,8 +405,7 @@ public class OpenIdAuthenticator extends LoginAuthenticator
|
|||
String challengeUri = getChallengeUri(request);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("challenge {}->{}", session.getId(), challengeUri);
|
||||
int redirectCode = (baseRequest.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER);
|
||||
baseResponse.sendRedirect(redirectCode, challengeUri);
|
||||
baseResponse.sendRedirect(getRedirectCode(baseRequest.getHttpVersion()), challengeUri);
|
||||
|
||||
return Authentication.SEND_CONTINUE;
|
||||
}
|
||||
|
@ -437,6 +433,12 @@ public class OpenIdAuthenticator extends LoginAuthenticator
|
|||
return pathInContext != null && (pathInContext.equals(_errorPath));
|
||||
}
|
||||
|
||||
private static int getRedirectCode(HttpVersion httpVersion)
|
||||
{
|
||||
return (httpVersion.getVersion() < HttpVersion.HTTP_1_1.getVersion()
|
||||
? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER);
|
||||
}
|
||||
|
||||
private String getRedirectUri(HttpServletRequest request)
|
||||
{
|
||||
final StringBuffer redirectUri = new StringBuffer(128);
|
||||
|
|
|
@ -124,13 +124,14 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
};
|
||||
|
||||
public static final int DEFAULT_LISTENER_TYPE_INDEX = 1;
|
||||
|
||||
public static final int EXTENDED_LISTENER_TYPE_INDEX = 0;
|
||||
|
||||
private static final String UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER = "Unimplemented {} - use org.eclipse.jetty.servlet.ServletContextHandler";
|
||||
|
||||
private static final Logger LOG = Log.getLogger(ContextHandler.class);
|
||||
|
||||
private static final ThreadLocal<Context> __context = new ThreadLocal<Context>();
|
||||
private static final ThreadLocal<Context> __context = new ThreadLocal<>();
|
||||
|
||||
private static String __serverInfo = "jetty/" + Server.getVersion();
|
||||
|
||||
|
@ -207,7 +208,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
private final List<ContextScopeListener> _contextListeners = new CopyOnWriteArrayList<>();
|
||||
private final Set<EventListener> _durableListeners = new HashSet<>();
|
||||
private String[] _protectedTargets;
|
||||
private final CopyOnWriteArrayList<AliasCheck> _aliasChecks = new CopyOnWriteArrayList<ContextHandler.AliasCheck>();
|
||||
private final CopyOnWriteArrayList<AliasCheck> _aliasChecks = new CopyOnWriteArrayList<>();
|
||||
|
||||
public enum Availability
|
||||
{
|
||||
|
@ -241,7 +242,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
{
|
||||
_scontext = context == null ? new Context() : context;
|
||||
_attributes = new AttributesMap();
|
||||
_initParams = new HashMap<String, String>();
|
||||
_initParams = new HashMap<>();
|
||||
addAliasCheck(new ApproveNonExistentDirectoryAliases());
|
||||
if (File.separatorChar == '/')
|
||||
addAliasCheck(new AllowSymLinkAliasChecker());
|
||||
|
@ -350,7 +351,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
if (connectorIndex == 0)
|
||||
{
|
||||
if (connectorOnlyIndexes == null)
|
||||
connectorOnlyIndexes = new ArrayList<Integer>();
|
||||
connectorOnlyIndexes = new ArrayList<>();
|
||||
connectorOnlyIndexes.add(i);
|
||||
}
|
||||
}
|
||||
|
@ -408,7 +409,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
}
|
||||
else
|
||||
{
|
||||
Set<String> currentVirtualHosts = new HashSet<String>(Arrays.asList(getVirtualHosts()));
|
||||
Set<String> currentVirtualHosts = new HashSet<>(Arrays.asList(getVirtualHosts()));
|
||||
for (String vh : virtualHosts)
|
||||
{
|
||||
currentVirtualHosts.add(normalizeHostname(vh));
|
||||
|
@ -433,7 +434,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
if (virtualHosts == null || virtualHosts.length == 0 || _vhosts == null || _vhosts.length == 0)
|
||||
return; // do nothing
|
||||
|
||||
Set<String> existingVirtualHosts = new HashSet<String>(Arrays.asList(getVirtualHosts()));
|
||||
Set<String> existingVirtualHosts = new HashSet<>(Arrays.asList(getVirtualHosts()));
|
||||
for (String vh : virtualHosts)
|
||||
{
|
||||
existingVirtualHosts.remove(normalizeHostname(vh));
|
||||
|
@ -1101,7 +1102,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("scope {}|{}|{} @ {}", baseRequest.getContextPath(), baseRequest.getServletPath(), baseRequest.getPathInfo(), this);
|
||||
|
||||
Context oldContext = null;
|
||||
Context oldContext;
|
||||
String oldContextPath = null;
|
||||
String oldServletPath = null;
|
||||
String oldPathInfo = null;
|
||||
|
@ -1119,7 +1120,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
// check the target.
|
||||
if (DispatcherType.REQUEST.equals(dispatch) || DispatcherType.ASYNC.equals(dispatch))
|
||||
{
|
||||
if (_compactPath)
|
||||
if (isCompactPath())
|
||||
target = URIUtil.compactPath(target);
|
||||
if (!checkContext(target, baseRequest, response))
|
||||
return;
|
||||
|
@ -1165,7 +1166,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
if (_contextPath.length() == 1)
|
||||
baseRequest.setContextPath("");
|
||||
else
|
||||
baseRequest.setContextPath(_contextPathEncoded);
|
||||
baseRequest.setContextPath(getContextPathEncoded());
|
||||
baseRequest.setServletPath(null);
|
||||
baseRequest.setPathInfo(pathInfo);
|
||||
}
|
||||
|
@ -1741,7 +1742,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
public void addLocaleEncoding(String locale, String encoding)
|
||||
{
|
||||
if (_localeEncodingMap == null)
|
||||
_localeEncodingMap = new HashMap<String, String>();
|
||||
_localeEncodingMap = new HashMap<>();
|
||||
_localeEncodingMap.put(locale, encoding);
|
||||
}
|
||||
|
||||
|
@ -1821,7 +1822,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
LOG.debug("Aliased resource: " + resource + "~=" + resource.getAlias());
|
||||
|
||||
// alias checks
|
||||
for (Iterator<AliasCheck> i = _aliasChecks.iterator(); i.hasNext(); )
|
||||
for (Iterator<AliasCheck> i = getAliasChecks().iterator(); i.hasNext(); )
|
||||
{
|
||||
AliasCheck check = i.next();
|
||||
if (check.check(path, resource))
|
||||
|
@ -1887,7 +1888,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
String[] l = resource.list();
|
||||
if (l != null)
|
||||
{
|
||||
HashSet<String> set = new HashSet<String>();
|
||||
HashSet<String> set = new HashSet<>();
|
||||
for (int i = 0; i < l.length; i++)
|
||||
{
|
||||
set.add(path + l[i]);
|
||||
|
@ -1930,7 +1931,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
*/
|
||||
public void addAliasCheck(AliasCheck check)
|
||||
{
|
||||
_aliasChecks.add(check);
|
||||
getAliasChecks().add(check);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1946,8 +1947,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
*/
|
||||
public void setAliasChecks(List<AliasCheck> checks)
|
||||
{
|
||||
_aliasChecks.clear();
|
||||
_aliasChecks.addAll(checks);
|
||||
getAliasChecks().clear();
|
||||
getAliasChecks().addAll(checks);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1955,13 +1956,14 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
*/
|
||||
public void clearAliasChecks()
|
||||
{
|
||||
_aliasChecks.clear();
|
||||
getAliasChecks().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Context.
|
||||
* <p>
|
||||
* A partial implementation of {@link javax.servlet.ServletContext}. A complete implementation is provided by the derived {@link ContextHandler}.
|
||||
* A partial implementation of {@link javax.servlet.ServletContext}. A complete implementation is provided by the
|
||||
* derived {@link ContextHandler} implementations.
|
||||
* </p>
|
||||
*/
|
||||
public class Context extends StaticContext
|
||||
|
@ -1984,7 +1986,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
@Override
|
||||
public ServletContext getContext(String uripath)
|
||||
{
|
||||
List<ContextHandler> contexts = new ArrayList<ContextHandler>();
|
||||
List<ContextHandler> contexts = new ArrayList<>();
|
||||
Handler[] handlers = getServer().getChildHandlersByClass(ContextHandler.class);
|
||||
String matchedPath = null;
|
||||
|
||||
|
@ -2057,7 +2059,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
matchedPath = contextPath;
|
||||
}
|
||||
|
||||
if (matchedPath != null && matchedPath.equals(contextPath))
|
||||
if (matchedPath.equals(contextPath))
|
||||
contexts.add(ch);
|
||||
}
|
||||
}
|
||||
|
@ -2252,7 +2254,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
@Override
|
||||
public synchronized Enumeration<String> getAttributeNames()
|
||||
{
|
||||
HashSet<String> set = new HashSet<String>();
|
||||
HashSet<String> set = new HashSet<>();
|
||||
Enumeration<String> e = super.getAttributeNames();
|
||||
while (e.hasMoreElements())
|
||||
{
|
||||
|
@ -2399,19 +2401,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException
|
||||
{
|
||||
try
|
||||
{
|
||||
return createInstance(clazz);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ServletException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void checkListener(Class<? extends EventListener> listener) throws IllegalStateException
|
||||
{
|
||||
boolean ok = false;
|
||||
|
@ -2445,7 +2434,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
throw new UnsupportedOperationException();
|
||||
|
||||
// no security manager just return the classloader
|
||||
if (!_usingSecurityManager)
|
||||
if (!isUsingSecurityManager())
|
||||
{
|
||||
return _classLoader;
|
||||
}
|
||||
|
@ -2501,12 +2490,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
return _enabled;
|
||||
}
|
||||
|
||||
public <T> T createInstance(Class<T> clazz) throws Exception
|
||||
{
|
||||
T o = clazz.getDeclaredConstructor().newInstance();
|
||||
return o;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVirtualServerName()
|
||||
{
|
||||
|
@ -2517,15 +2500,16 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple implementation of ServletContext that is used when there is no
|
||||
* ContextHandler. This is also used as the base for all other ServletContext
|
||||
* implementations.
|
||||
*/
|
||||
public static class StaticContext extends AttributesMap implements ServletContext
|
||||
{
|
||||
private int _effectiveMajorVersion = SERVLET_MAJOR_VERSION;
|
||||
private int _effectiveMinorVersion = SERVLET_MINOR_VERSION;
|
||||
|
||||
public StaticContext()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletContext getContext(String uripath)
|
||||
{
|
||||
|
@ -2589,7 +2573,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
@Override
|
||||
public String getServerInfo()
|
||||
{
|
||||
return __serverInfo;
|
||||
return ContextHandler.getServerInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2716,20 +2700,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Filter> T createFilter(Class<T> c) throws ServletException
|
||||
{
|
||||
LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "createFilter(Class)");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Servlet> T createServlet(Class<T> c) throws ServletException
|
||||
{
|
||||
LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "createServlet(Class)");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
|
||||
{
|
||||
|
@ -2803,8 +2773,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addListener(Class)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException
|
||||
protected <T> T createInstance(Class<T> clazz) throws ServletException
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -2816,6 +2785,24 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException
|
||||
{
|
||||
return createInstance(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Servlet> T createServlet(Class<T> clazz) throws ServletException
|
||||
{
|
||||
return createInstance(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Filter> T createFilter(Class<T> clazz) throws ServletException
|
||||
{
|
||||
return createInstance(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader getClassLoader()
|
||||
{
|
||||
|
|
|
@ -522,6 +522,16 @@ public class SessionHandler extends ScopedHandler
|
|||
return _httpOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The sameSite setting for session cookies or null for no setting
|
||||
* @see HttpCookie#getSameSite()
|
||||
*/
|
||||
@ManagedAttribute("SameSite setting for session cookies")
|
||||
public HttpCookie.SameSite getSameSite()
|
||||
{
|
||||
return HttpCookie.getSameSiteFromComment(_sessionComment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>HttpSession</code> with the given session id
|
||||
*
|
||||
|
@ -642,30 +652,18 @@ public class SessionHandler extends ScopedHandler
|
|||
sessionPath = (StringUtil.isEmpty(sessionPath)) ? "/" : sessionPath;
|
||||
String id = getExtendedId(session);
|
||||
HttpCookie cookie = null;
|
||||
if (_sessionComment == null)
|
||||
{
|
||||
cookie = new HttpCookie(
|
||||
_cookieConfig.getName(),
|
||||
id,
|
||||
_cookieConfig.getDomain(),
|
||||
sessionPath,
|
||||
_cookieConfig.getMaxAge(),
|
||||
_cookieConfig.isHttpOnly(),
|
||||
_cookieConfig.isSecure() || (isSecureRequestOnly() && requestIsSecure));
|
||||
}
|
||||
else
|
||||
{
|
||||
cookie = new HttpCookie(
|
||||
_cookieConfig.getName(),
|
||||
id,
|
||||
_cookieConfig.getDomain(),
|
||||
sessionPath,
|
||||
_cookieConfig.getMaxAge(),
|
||||
_cookieConfig.isHttpOnly(),
|
||||
_cookieConfig.isSecure() || (isSecureRequestOnly() && requestIsSecure),
|
||||
_sessionComment,
|
||||
1);
|
||||
}
|
||||
|
||||
cookie = new HttpCookie(
|
||||
_cookieConfig.getName(),
|
||||
id,
|
||||
_cookieConfig.getDomain(),
|
||||
sessionPath,
|
||||
_cookieConfig.getMaxAge(),
|
||||
_cookieConfig.isHttpOnly(),
|
||||
_cookieConfig.isSecure() || (isSecureRequestOnly() && requestIsSecure),
|
||||
HttpCookie.getCommentWithoutAttributes(_cookieConfig.getComment()),
|
||||
0,
|
||||
HttpCookie.getSameSiteFromComment(_cookieConfig.getComment()));
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
@ -805,13 +803,28 @@ public class SessionHandler extends ScopedHandler
|
|||
}
|
||||
|
||||
/**
|
||||
* @param httpOnly The httpOnly to set.
|
||||
* Set if Session cookies should use HTTP Only
|
||||
* @param httpOnly True if cookies should be HttpOnly.
|
||||
* @see HttpCookie
|
||||
*/
|
||||
public void setHttpOnly(boolean httpOnly)
|
||||
{
|
||||
_httpOnly = httpOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Session cookie sameSite mode.
|
||||
* Currently this is encoded in the session comment until sameSite is supported by {@link SessionCookieConfig}
|
||||
* @param sameSite The sameSite setting for Session cookies (or null for no sameSite setting)
|
||||
*/
|
||||
public void setSameSite(HttpCookie.SameSite sameSite)
|
||||
{
|
||||
// Encode in comment whilst not supported by SessionConfig, so that it can be set/saved in
|
||||
// web.xml and quickstart.
|
||||
// Always pass false for httpOnly as it has it's own setter.
|
||||
_sessionComment = HttpCookie.getCommentWithAttributes(_sessionComment, false, sameSite);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param metaManager The metaManager used for cross context session management.
|
||||
*/
|
||||
|
@ -1345,6 +1358,8 @@ public class SessionHandler extends ScopedHandler
|
|||
* CookieConfig
|
||||
*
|
||||
* Implementation of the javax.servlet.SessionCookieConfig.
|
||||
* SameSite configuration can be achieved by using setComment
|
||||
* @see HttpCookie
|
||||
*/
|
||||
public final class CookieConfig implements SessionCookieConfig
|
||||
{
|
||||
|
|
|
@ -114,7 +114,7 @@ public class FilterHolder extends Holder<Filter>
|
|||
try
|
||||
{
|
||||
ServletContext context = getServletHandler().getServletContext();
|
||||
_filter = (context instanceof ServletContextHandler.Context)
|
||||
_filter = (context != null)
|
||||
? context.createFilter(getHeldClass())
|
||||
: getHeldClass().getDeclaredConstructor().newInstance();
|
||||
}
|
||||
|
@ -234,7 +234,7 @@ public class FilterHolder extends Holder<Filter>
|
|||
public Collection<String> getServletNameMappings()
|
||||
{
|
||||
FilterMapping[] mappings = getServletHandler().getFilterMappings();
|
||||
List<String> names = new ArrayList<String>();
|
||||
List<String> names = new ArrayList<>();
|
||||
for (FilterMapping mapping : mappings)
|
||||
{
|
||||
if (mapping.getFilterHolder() != FilterHolder.this)
|
||||
|
@ -250,7 +250,7 @@ public class FilterHolder extends Holder<Filter>
|
|||
public Collection<String> getUrlPatternMappings()
|
||||
{
|
||||
FilterMapping[] mappings = getServletHandler().getFilterMappings();
|
||||
List<String> patterns = new ArrayList<String>();
|
||||
List<String> patterns = new ArrayList<>();
|
||||
for (FilterMapping mapping : mappings)
|
||||
{
|
||||
if (mapping.getFilterHolder() != FilterHolder.this)
|
||||
|
|
|
@ -88,9 +88,9 @@ public class ListenerHolder extends BaseHolder<EventListener>
|
|||
//create an instance of the listener and decorate it
|
||||
try
|
||||
{
|
||||
ServletContext scontext = contextHandler.getServletContext();
|
||||
_listener = (scontext instanceof ServletContextHandler.Context)
|
||||
? scontext.createListener(getHeldClass())
|
||||
ServletContext context = contextHandler.getServletContext();
|
||||
_listener = (context != null)
|
||||
? context.createListener(getHeldClass())
|
||||
: getHeldClass().getDeclaredConstructor().newInstance();
|
||||
}
|
||||
catch (ServletException ex)
|
||||
|
|
|
@ -305,7 +305,6 @@ public class ServletContextHandler extends ContextHandler
|
|||
|
||||
if (handler.getHandler() != _servletHandler)
|
||||
doSetHandler(handler, _servletHandler);
|
||||
handler = _servletHandler;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -356,7 +355,7 @@ public class ServletContextHandler extends ContextHandler
|
|||
{
|
||||
try
|
||||
{
|
||||
return _defaultSecurityHandlerClass.getDeclaredConstructor().newInstance();
|
||||
return getDefaultSecurityHandlerClass().getDeclaredConstructor().newInstance();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -547,7 +546,7 @@ public class ServletContextHandler extends ContextHandler
|
|||
//Get a reference to the SecurityHandler, which must be ConstraintAware
|
||||
if (_securityHandler != null && _securityHandler instanceof ConstraintAware)
|
||||
{
|
||||
HashSet<String> union = new HashSet<String>();
|
||||
HashSet<String> union = new HashSet<>();
|
||||
Set<String> existing = ((ConstraintAware)_securityHandler).getRoles();
|
||||
if (existing != null)
|
||||
union.addAll(existing);
|
||||
|
@ -745,13 +744,13 @@ public class ServletContextHandler extends ContextHandler
|
|||
|
||||
public static class JspPropertyGroup implements JspPropertyGroupDescriptor
|
||||
{
|
||||
private List<String> _urlPatterns = new ArrayList<String>();
|
||||
private List<String> _urlPatterns = new ArrayList<>();
|
||||
private String _elIgnored;
|
||||
private String _pageEncoding;
|
||||
private String _scriptingInvalid;
|
||||
private String _isXml;
|
||||
private List<String> _includePreludes = new ArrayList<String>();
|
||||
private List<String> _includeCodas = new ArrayList<String>();
|
||||
private List<String> _includePreludes = new ArrayList<>();
|
||||
private List<String> _includeCodas = new ArrayList<>();
|
||||
private String _deferredSyntaxAllowedAsLiteral;
|
||||
private String _trimDirectiveWhitespaces;
|
||||
private String _defaultContentType;
|
||||
|
@ -764,7 +763,7 @@ public class ServletContextHandler extends ContextHandler
|
|||
@Override
|
||||
public Collection<String> getUrlPatterns()
|
||||
{
|
||||
return new ArrayList<String>(_urlPatterns); // spec says must be a copy
|
||||
return new ArrayList<>(_urlPatterns); // spec says must be a copy
|
||||
}
|
||||
|
||||
public void addUrlPattern(String s)
|
||||
|
@ -860,7 +859,7 @@ public class ServletContextHandler extends ContextHandler
|
|||
@Override
|
||||
public Collection<String> getIncludePreludes()
|
||||
{
|
||||
return new ArrayList<String>(_includePreludes); //must be a copy
|
||||
return new ArrayList<>(_includePreludes); //must be a copy
|
||||
}
|
||||
|
||||
public void addIncludePrelude(String prelude)
|
||||
|
@ -875,7 +874,7 @@ public class ServletContextHandler extends ContextHandler
|
|||
@Override
|
||||
public Collection<String> getIncludeCodas()
|
||||
{
|
||||
return new ArrayList<String>(_includeCodas); //must be a copy
|
||||
return new ArrayList<>(_includeCodas); //must be a copy
|
||||
}
|
||||
|
||||
public void addIncludeCoda(String coda)
|
||||
|
@ -932,24 +931,24 @@ public class ServletContextHandler extends ContextHandler
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("JspPropertyGroupDescriptor:");
|
||||
sb.append(" el-ignored=" + _elIgnored);
|
||||
sb.append(" is-xml=" + _isXml);
|
||||
sb.append(" page-encoding=" + _pageEncoding);
|
||||
sb.append(" scripting-invalid=" + _scriptingInvalid);
|
||||
sb.append(" deferred-syntax-allowed-as-literal=" + _deferredSyntaxAllowedAsLiteral);
|
||||
sb.append(" trim-directive-whitespaces" + _trimDirectiveWhitespaces);
|
||||
sb.append(" default-content-type=" + _defaultContentType);
|
||||
sb.append(" buffer=" + _buffer);
|
||||
sb.append(" error-on-undeclared-namespace=" + _errorOnUndeclaredNamespace);
|
||||
sb.append(" el-ignored=").append(_elIgnored);
|
||||
sb.append(" is-xml=").append(_isXml);
|
||||
sb.append(" page-encoding=").append(_pageEncoding);
|
||||
sb.append(" scripting-invalid=").append(_scriptingInvalid);
|
||||
sb.append(" deferred-syntax-allowed-as-literal=").append(_deferredSyntaxAllowedAsLiteral);
|
||||
sb.append(" trim-directive-whitespaces").append(_trimDirectiveWhitespaces);
|
||||
sb.append(" default-content-type=").append(_defaultContentType);
|
||||
sb.append(" buffer=").append(_buffer);
|
||||
sb.append(" error-on-undeclared-namespace=").append(_errorOnUndeclaredNamespace);
|
||||
for (String prelude : _includePreludes)
|
||||
{
|
||||
sb.append(" include-prelude=" + prelude);
|
||||
sb.append(" include-prelude=").append(prelude);
|
||||
}
|
||||
for (String coda : _includeCodas)
|
||||
{
|
||||
sb.append(" include-coda=" + coda);
|
||||
sb.append(" include-coda=").append(coda);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
@ -997,8 +996,8 @@ public class ServletContextHandler extends ContextHandler
|
|||
|
||||
public static class JspConfig implements JspConfigDescriptor
|
||||
{
|
||||
private List<TaglibDescriptor> _taglibs = new ArrayList<TaglibDescriptor>();
|
||||
private List<JspPropertyGroupDescriptor> _jspPropertyGroups = new ArrayList<JspPropertyGroupDescriptor>();
|
||||
private List<TaglibDescriptor> _taglibs = new ArrayList<>();
|
||||
private List<JspPropertyGroupDescriptor> _jspPropertyGroups = new ArrayList<>();
|
||||
|
||||
public JspConfig()
|
||||
{
|
||||
|
@ -1010,7 +1009,7 @@ public class ServletContextHandler extends ContextHandler
|
|||
@Override
|
||||
public Collection<TaglibDescriptor> getTaglibs()
|
||||
{
|
||||
return new ArrayList<TaglibDescriptor>(_taglibs);
|
||||
return new ArrayList<>(_taglibs);
|
||||
}
|
||||
|
||||
public void addTaglibDescriptor(TaglibDescriptor d)
|
||||
|
@ -1024,7 +1023,7 @@ public class ServletContextHandler extends ContextHandler
|
|||
@Override
|
||||
public Collection<JspPropertyGroupDescriptor> getJspPropertyGroups()
|
||||
{
|
||||
return new ArrayList<JspPropertyGroupDescriptor>(_jspPropertyGroups);
|
||||
return new ArrayList<>(_jspPropertyGroups);
|
||||
}
|
||||
|
||||
public void addJspPropertyGroup(JspPropertyGroupDescriptor g)
|
||||
|
@ -1035,15 +1034,15 @@ public class ServletContextHandler extends ContextHandler
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("JspConfigDescriptor: \n");
|
||||
for (TaglibDescriptor taglib : _taglibs)
|
||||
{
|
||||
sb.append(taglib + "\n");
|
||||
sb.append(taglib).append("\n");
|
||||
}
|
||||
for (JspPropertyGroupDescriptor jpg : _jspPropertyGroups)
|
||||
{
|
||||
sb.append(jpg + "\n");
|
||||
sb.append(jpg).append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
@ -1051,7 +1050,6 @@ public class ServletContextHandler extends ContextHandler
|
|||
|
||||
public class Context extends ContextHandler.Context
|
||||
{
|
||||
|
||||
/*
|
||||
* @see javax.servlet.ServletContext#getNamedDispatcher(java.lang.String)
|
||||
*/
|
||||
|
@ -1272,18 +1270,9 @@ public class ServletContextHandler extends ContextHandler
|
|||
}
|
||||
|
||||
@Override
|
||||
public <T extends Filter> T createFilter(Class<T> c) throws ServletException
|
||||
protected <T> T createInstance(Class<T> clazz) throws ServletException
|
||||
{
|
||||
try
|
||||
{
|
||||
T f = createInstance(c);
|
||||
f = _objFactory.decorate(f);
|
||||
return f;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ServletException(e);
|
||||
}
|
||||
return _objFactory.decorate(super.createInstance(clazz));
|
||||
}
|
||||
|
||||
public <T extends Filter> void destroyFilter(T f)
|
||||
|
@ -1291,21 +1280,6 @@ public class ServletContextHandler extends ContextHandler
|
|||
_objFactory.destroy(f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Servlet> T createServlet(Class<T> c) throws ServletException
|
||||
{
|
||||
try
|
||||
{
|
||||
T s = createInstance(c);
|
||||
s = _objFactory.decorate(s);
|
||||
return s;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ServletException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends Servlet> void destroyServlet(T s)
|
||||
{
|
||||
_objFactory.destroy(s);
|
||||
|
@ -1343,7 +1317,7 @@ public class ServletContextHandler extends ContextHandler
|
|||
if (!_enabled)
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
HashMap<String, FilterRegistration> registrations = new HashMap<String, FilterRegistration>();
|
||||
HashMap<String, FilterRegistration> registrations = new HashMap<>();
|
||||
ServletHandler handler = ServletContextHandler.this.getServletHandler();
|
||||
FilterHolder[] holders = handler.getFilters();
|
||||
if (holders != null)
|
||||
|
@ -1372,7 +1346,7 @@ public class ServletContextHandler extends ContextHandler
|
|||
if (!_enabled)
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
HashMap<String, ServletRegistration> registrations = new HashMap<String, ServletRegistration>();
|
||||
HashMap<String, ServletRegistration> registrations = new HashMap<>();
|
||||
ServletHandler handler = ServletContextHandler.this.getServletHandler();
|
||||
ServletHolder[] holders = handler.getServlets();
|
||||
if (holders != null)
|
||||
|
@ -1455,21 +1429,6 @@ public class ServletContextHandler extends ContextHandler
|
|||
super.addListener(listenerClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException
|
||||
{
|
||||
try
|
||||
{
|
||||
T l = createInstance(clazz);
|
||||
l = _objFactory.decorate(l);
|
||||
return l;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ServletException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JspConfigDescriptor getJspConfigDescriptor()
|
||||
{
|
||||
|
|
|
@ -258,7 +258,7 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
public synchronized void setUserRoleLink(String name, String link)
|
||||
{
|
||||
if (_roleMap == null)
|
||||
_roleMap = new HashMap<String, String>();
|
||||
_roleMap = new HashMap<>();
|
||||
_roleMap.put(name, link);
|
||||
}
|
||||
|
||||
|
@ -639,7 +639,7 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
}
|
||||
|
||||
/* ensure scratch dir */
|
||||
File scratch = null;
|
||||
File scratch;
|
||||
if (getInitParameter("scratchdir") == null)
|
||||
{
|
||||
File tmp = (File)getServletHandler().getServletContext().getAttribute(ServletContext.TEMPDIR);
|
||||
|
@ -848,8 +848,7 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
{
|
||||
Class<?> jspUtil = Loader.loadClass("org.apache.jasper.compiler.JspUtil");
|
||||
Method makeJavaPackage = jspUtil.getMethod("makeJavaPackage", String.class);
|
||||
String p = (String)makeJavaPackage.invoke(null, jsp.substring(0, i));
|
||||
return p;
|
||||
return (String)makeJavaPackage.invoke(null, jsp.substring(0, i));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -953,7 +952,7 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
if (!mapping.isDefault())
|
||||
{
|
||||
if (clash == null)
|
||||
clash = new HashSet<String>();
|
||||
clash = new HashSet<>();
|
||||
clash.add(pattern);
|
||||
}
|
||||
}
|
||||
|
@ -976,7 +975,7 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
public Collection<String> getMappings()
|
||||
{
|
||||
ServletMapping[] mappings = getServletHandler().getServletMappings();
|
||||
List<String> patterns = new ArrayList<String>();
|
||||
List<String> patterns = new ArrayList<>();
|
||||
if (mappings != null)
|
||||
{
|
||||
for (ServletMapping mapping : mappings)
|
||||
|
@ -1042,7 +1041,7 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
|
||||
private class SingleThreadedWrapper implements Servlet
|
||||
{
|
||||
Stack<Servlet> _stack = new Stack<Servlet>();
|
||||
Stack<Servlet> _stack = new Stack<>();
|
||||
|
||||
@Override
|
||||
public void destroy()
|
||||
|
@ -1154,7 +1153,7 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
try
|
||||
{
|
||||
ServletContext ctx = getServletHandler().getServletContext();
|
||||
if (ctx instanceof ServletContextHandler.Context)
|
||||
if (ctx != null)
|
||||
return ctx.createServlet(getHeldClass());
|
||||
return getHeldClass().getDeclaredConstructor().newInstance();
|
||||
|
||||
|
|
|
@ -318,7 +318,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
{
|
||||
super.setDisplayName(servletContextName);
|
||||
ClassLoader cl = getClassLoader();
|
||||
if (cl != null && cl instanceof WebAppClassLoader && servletContextName != null)
|
||||
if (cl instanceof WebAppClassLoader && servletContextName != null)
|
||||
((WebAppClassLoader)cl).setName(servletContextName);
|
||||
}
|
||||
|
||||
|
@ -344,7 +344,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
public void setResourceAlias(String alias, String uri)
|
||||
{
|
||||
if (_resourceAliases == null)
|
||||
_resourceAliases = new HashMap<String, String>(5);
|
||||
_resourceAliases = new HashMap<>(5);
|
||||
_resourceAliases.put(alias, uri);
|
||||
}
|
||||
|
||||
|
@ -398,7 +398,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
if (name == null)
|
||||
name = getContextPath();
|
||||
|
||||
if (classLoader != null && classLoader instanceof WebAppClassLoader && getDisplayName() != null)
|
||||
if (classLoader instanceof WebAppClassLoader && getDisplayName() != null)
|
||||
((WebAppClassLoader)classLoader).setName(name);
|
||||
}
|
||||
|
||||
|
@ -429,7 +429,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
}
|
||||
}
|
||||
|
||||
if (ioe != null && ioe instanceof MalformedURLException)
|
||||
if (ioe instanceof MalformedURLException)
|
||||
throw (MalformedURLException)ioe;
|
||||
|
||||
return resource;
|
||||
|
@ -537,7 +537,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
{
|
||||
_metadata.setAllowDuplicateFragmentNames(isAllowDuplicateFragmentNames());
|
||||
Boolean validate = (Boolean)getAttribute(MetaData.VALIDATE_XML);
|
||||
_metadata.setValidateXml((validate != null && validate.booleanValue()));
|
||||
_metadata.setValidateXml((validate != null && validate));
|
||||
preConfigure();
|
||||
super.doStart();
|
||||
postConfigure();
|
||||
|
@ -922,16 +922,18 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
{
|
||||
if (_war != null)
|
||||
{
|
||||
if (_war.indexOf("/webapps/") >= 0)
|
||||
name = _war.substring(_war.indexOf("/webapps/") + 8);
|
||||
int webapps = _war.indexOf("/webapps/");
|
||||
if (webapps >= 0)
|
||||
name = _war.substring(webapps + 8);
|
||||
else
|
||||
name = _war;
|
||||
}
|
||||
else if (getResourceBase() != null)
|
||||
{
|
||||
name = getResourceBase();
|
||||
if (name.indexOf("/webapps/") >= 0)
|
||||
name = name.substring(name.indexOf("/webapps/") + 8);
|
||||
int webapps = name.indexOf("/webapps/");
|
||||
if (webapps >= 0)
|
||||
name = name.substring(webapps + 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -966,7 +968,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
|
||||
public void setConfigurationClasses(List<String> configurations)
|
||||
{
|
||||
setConfigurationClasses(configurations.toArray(new String[configurations.size()]));
|
||||
setConfigurationClasses(configurations.toArray(new String[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1313,7 +1315,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
if (_ownClassLoader)
|
||||
{
|
||||
ClassLoader loader = getClassLoader();
|
||||
if (loader != null && loader instanceof URLClassLoader)
|
||||
if (loader instanceof URLClassLoader)
|
||||
((URLClassLoader)loader).close();
|
||||
setClassLoader(null);
|
||||
}
|
||||
|
@ -1326,7 +1328,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
@Override
|
||||
public Set<String> setServletSecurity(Dynamic registration, ServletSecurityElement servletSecurityElement)
|
||||
{
|
||||
Set<String> unchangedURLMappings = new HashSet<String>();
|
||||
Set<String> unchangedURLMappings = new HashSet<>();
|
||||
//From javadoc for ServletSecurityElement:
|
||||
/*
|
||||
If a URL pattern of this ServletRegistration is an exact target of a security-constraint that
|
||||
|
@ -1400,7 +1402,6 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
|
||||
public class Context extends ServletContextHandler.Context
|
||||
{
|
||||
|
||||
@Override
|
||||
public void checkListener(Class<? extends EventListener> listener) throws IllegalStateException
|
||||
{
|
||||
|
|
|
@ -269,6 +269,7 @@ public class HugeResourceTest
|
|||
multipart.addFilePart(name, filename, new PathContentProvider(inputFile), null);
|
||||
|
||||
URI destUri = server.getURI().resolve("/multipart");
|
||||
client.setIdleTimeout(90_000);
|
||||
Request request = client.newRequest(destUri).method(HttpMethod.POST).content(multipart);
|
||||
ContentResponse response = request.send();
|
||||
assertThat("HTTP Response Code", response.getStatus(), is(200));
|
||||
|
|
|
@ -26,6 +26,8 @@ import java.net.SocketTimeoutException;
|
|||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
@ -182,6 +184,27 @@ public class ClientConnectTest
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeRequest_PercentEncodedQuery() throws Exception
|
||||
{
|
||||
CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
|
||||
client.setIdleTimeout(Duration.ofSeconds(10));
|
||||
|
||||
URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/echo?name=%25foo"));
|
||||
ClientUpgradeRequest request = new ClientUpgradeRequest();
|
||||
request.setSubProtocols("echo");
|
||||
Future<Session> future = client.connect(cliSock, wsUri);
|
||||
|
||||
try (Session sess = future.get(30, TimeUnit.SECONDS))
|
||||
{
|
||||
assertThat("Connect.UpgradeRequest", sess.getUpgradeRequest(), notNullValue());
|
||||
Map<String, List<String>> paramMap = sess.getUpgradeRequest().getParameterMap();
|
||||
List<String> values = paramMap.get("name");
|
||||
assertThat("Params[name]", values.get(0), is("%foo"));
|
||||
assertThat("Connect.UpgradeResponse", sess.getUpgradeResponse(), notNullValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeWithAuthorizationHeader() throws Exception
|
||||
{
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.util.Objects;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jetty.util.MultiMap;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.UrlEncoded;
|
||||
import org.eclipse.jetty.websocket.core.ExtensionConfig;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketConstants;
|
||||
|
@ -50,14 +51,17 @@ public class Negotiated
|
|||
this.extensions = extensions;
|
||||
this.protocolVersion = protocolVersion;
|
||||
|
||||
String rawQuery = requestURI.getRawQuery();
|
||||
Map<String, List<String>> map;
|
||||
if (requestURI.getQuery() == null)
|
||||
if (StringUtil.isBlank(rawQuery))
|
||||
{
|
||||
map = Collections.emptyMap();
|
||||
}
|
||||
else
|
||||
{
|
||||
map = new HashMap<>();
|
||||
MultiMap<String> params = new MultiMap<>();
|
||||
UrlEncoded.decodeUtf8To(requestURI.getQuery(), params);
|
||||
UrlEncoded.decodeUtf8To(rawQuery, params);
|
||||
for (String p : params.keySet())
|
||||
{
|
||||
map.put(p, Collections.unmodifiableList(params.getValues(p)));
|
||||
|
|
|
@ -33,10 +33,12 @@ import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
|
|||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http2.client.HTTP2Client;
|
||||
import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
|
||||
import org.eclipse.jetty.io.ClientConnector;
|
||||
import org.eclipse.jetty.unixsocket.client.HttpClientTransportOverUnixSockets;
|
||||
import org.eclipse.jetty.unixsocket.server.UnixSocketConnector;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.DisabledOnJre;
|
||||
import org.junit.jupiter.api.condition.JRE;
|
||||
|
@ -211,6 +213,18 @@ public class DistributionTests extends AbstractDistributionTest
|
|||
@Test
|
||||
@DisabledOnJre(JRE.JAVA_8)
|
||||
public void testSimpleWebAppWithJSPOverH2C() throws Exception
|
||||
{
|
||||
testSimpleWebAppWithJSPOverHTTP2(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisabledOnJre(JRE.JAVA_8)
|
||||
public void testSimpleWebAppWithJSPOverH2() throws Exception
|
||||
{
|
||||
testSimpleWebAppWithJSPOverHTTP2(true);
|
||||
}
|
||||
|
||||
private void testSimpleWebAppWithJSPOverHTTP2(boolean ssl) throws Exception
|
||||
{
|
||||
String jettyVersion = System.getProperty("jettyVersion");
|
||||
DistributionTester distribution = DistributionTester.Builder.newInstance()
|
||||
|
@ -220,7 +234,7 @@ public class DistributionTests extends AbstractDistributionTest
|
|||
|
||||
String[] args1 = {
|
||||
"--create-startd",
|
||||
"--add-to-start=http2c,jsp,deploy"
|
||||
"--add-to-start=jsp,deploy," + (ssl ? "http2,test-keystore" : "http2c")
|
||||
};
|
||||
try (DistributionTester.Run run1 = distribution.start(args1))
|
||||
{
|
||||
|
@ -231,13 +245,16 @@ public class DistributionTests extends AbstractDistributionTest
|
|||
distribution.installWarFile(war, "test");
|
||||
|
||||
int port = distribution.freePort();
|
||||
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
|
||||
String portProp = ssl ? "jetty.ssl.port" : "jetty.http.port";
|
||||
try (DistributionTester.Run run2 = distribution.start(portProp + "=" + port))
|
||||
{
|
||||
assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
|
||||
|
||||
HTTP2Client h2Client = new HTTP2Client();
|
||||
ClientConnector connector = new ClientConnector();
|
||||
connector.setSslContextFactory(new SslContextFactory.Client(true));
|
||||
HTTP2Client h2Client = new HTTP2Client(connector);
|
||||
startHttpClient(() -> new HttpClient(new HttpClientTransportOverHTTP2(h2Client)));
|
||||
ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp");
|
||||
ContentResponse response = client.GET((ssl ? "https" : "http") + "://localhost:" + port + "/test/index.jsp");
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
assertThat(response.getContentAsString(), containsString("Hello"));
|
||||
assertThat(response.getContentAsString(), not(containsString("<%")));
|
||||
|
|
|
@ -414,4 +414,119 @@ public class HttpClientDemandTest extends AbstractTest<TransportScenario>
|
|||
assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
|
||||
assertArrayEquals(content, bytes);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(TransportProvider.class)
|
||||
public void testDelayedBeforeContentDemand(Transport transport) throws Exception
|
||||
{
|
||||
init(transport);
|
||||
|
||||
byte[] content = new byte[1024];
|
||||
new Random().nextBytes(content);
|
||||
scenario.start(new EmptyServerHandler()
|
||||
{
|
||||
@Override
|
||||
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
response.setContentLength(content.length);
|
||||
response.getOutputStream().write(content);
|
||||
}
|
||||
});
|
||||
|
||||
byte[] bytes = new byte[content.length];
|
||||
ByteBuffer received = ByteBuffer.wrap(bytes);
|
||||
AtomicReference<LongConsumer> beforeContentDemandRef = new AtomicReference<>();
|
||||
CountDownLatch beforeContentLatch = new CountDownLatch(1);
|
||||
CountDownLatch contentLatch = new CountDownLatch(1);
|
||||
CountDownLatch resultLatch = new CountDownLatch(1);
|
||||
scenario.client.newRequest(scenario.newURI())
|
||||
.onResponseContentDemanded(new Response.DemandedContentListener()
|
||||
{
|
||||
@Override
|
||||
public void onBeforeContent(Response response, LongConsumer demand)
|
||||
{
|
||||
// Do not demand now.
|
||||
beforeContentDemandRef.set(demand);
|
||||
beforeContentLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContent(Response response, LongConsumer demand, ByteBuffer buffer, Callback callback)
|
||||
{
|
||||
contentLatch.countDown();
|
||||
received.put(buffer);
|
||||
callback.succeeded();
|
||||
demand.accept(1);
|
||||
}
|
||||
})
|
||||
.send(result ->
|
||||
{
|
||||
assertTrue(result.isSucceeded());
|
||||
assertEquals(HttpStatus.OK_200, result.getResponse().getStatus());
|
||||
resultLatch.countDown();
|
||||
});
|
||||
|
||||
assertTrue(beforeContentLatch.await(5, TimeUnit.SECONDS));
|
||||
LongConsumer demand = beforeContentDemandRef.get();
|
||||
|
||||
// Content must not be notified until we demand.
|
||||
assertFalse(contentLatch.await(1, TimeUnit.SECONDS));
|
||||
|
||||
demand.accept(1);
|
||||
|
||||
assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
|
||||
assertArrayEquals(content, bytes);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(TransportProvider.class)
|
||||
public void testDelayedBeforeContentDemandWithNoResponseContent(Transport transport) throws Exception
|
||||
{
|
||||
init(transport);
|
||||
|
||||
scenario.start(new EmptyServerHandler());
|
||||
|
||||
AtomicReference<LongConsumer> beforeContentDemandRef = new AtomicReference<>();
|
||||
CountDownLatch beforeContentLatch = new CountDownLatch(1);
|
||||
CountDownLatch contentLatch = new CountDownLatch(1);
|
||||
CountDownLatch resultLatch = new CountDownLatch(1);
|
||||
scenario.client.newRequest(scenario.newURI())
|
||||
.onResponseContentDemanded(new Response.DemandedContentListener()
|
||||
{
|
||||
@Override
|
||||
public void onBeforeContent(Response response, LongConsumer demand)
|
||||
{
|
||||
// Do not demand now.
|
||||
beforeContentDemandRef.set(demand);
|
||||
beforeContentLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContent(Response response, LongConsumer demand, ByteBuffer buffer, Callback callback)
|
||||
{
|
||||
contentLatch.countDown();
|
||||
callback.succeeded();
|
||||
demand.accept(1);
|
||||
}
|
||||
})
|
||||
.send(result ->
|
||||
{
|
||||
assertTrue(result.isSucceeded());
|
||||
assertEquals(HttpStatus.OK_200, result.getResponse().getStatus());
|
||||
resultLatch.countDown();
|
||||
});
|
||||
|
||||
assertTrue(beforeContentLatch.await(5, TimeUnit.SECONDS));
|
||||
LongConsumer demand = beforeContentDemandRef.get();
|
||||
|
||||
// Content must not be notified until we demand.
|
||||
assertFalse(contentLatch.await(1, TimeUnit.SECONDS));
|
||||
|
||||
demand.accept(1);
|
||||
|
||||
// Content must not be notified as there is no content.
|
||||
assertFalse(contentLatch.await(1, TimeUnit.SECONDS));
|
||||
|
||||
assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue