Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-10.0.x-4919-WebSocketContainerStop
This commit is contained in:
commit
c94e82470d
12
VERSION.txt
12
VERSION.txt
|
@ -1,5 +1,7 @@
|
|||
jetty-10.0.0-SNAPSHOT
|
||||
|
||||
jetty-10.0.0.beta0 - 27 May 2020
|
||||
|
||||
jetty-9.4.30.v20200611 - 11 June 2020
|
||||
+ 4776 Incorrect path matching for WebSocket using PathMappings
|
||||
+ 4826 Upgrade to Apache Jasper 8.5.54
|
||||
|
@ -66,7 +68,7 @@ jetty-10.0.0.alpha1 - 26 November 2019
|
|||
+ 2709 current default for headerCacheSize is not large enough for many
|
||||
requests
|
||||
+ 2815 hpack fields are opaque octets
|
||||
+ 3040 Allow RFC6265 Cookies to include optional SameSite attribute.
|
||||
+ 3040 Allow RFC6265 Cookies to include optional SameSite attribute
|
||||
+ 3083 The ini-template for jetty.console-capture.dir does not match the
|
||||
default value
|
||||
+ 3106 Websocket connection stats and request stats
|
||||
|
@ -499,10 +501,10 @@ jetty-9.4.25.v20191220 - 20 December 2019
|
|||
SslContextFactory usage
|
||||
+ 4392 Suppress logging of QuietException in HttpChannelState.asyncError()
|
||||
+ 4402 NPE in JettyRunWarExplodedMojo
|
||||
+ 4411 Jetty server spins on incomplete request due to delayed dispatch
|
||||
until content
|
||||
+ 4415 GzipHandler invalid input zip size on large
|
||||
(over 2,147,483,647 bytes) request body content
|
||||
+ 4411 Jetty server spins on incomplete request due to delayed dispatch until
|
||||
content
|
||||
+ 4415 GzipHandler invalid input zip size on large (over 2,147,483,647 bytes)
|
||||
request body content
|
||||
+ 4421 HttpClient support for PROXY protocol
|
||||
+ 4427 Retried HttpClient Requests can result in duplicates cookies
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables use of the apache implementation of JSP
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables the apache version of JSTL
|
||||
|
|
|
@ -135,7 +135,7 @@
|
|||
<!-- ensure we don't have legacy comments that confuse javadoc tooling -->
|
||||
<module name="Regexp">
|
||||
<property name="id" value="LegacyMethodSeparators"/>
|
||||
<property name="format" value="/\*\s*[=*-]*\s*\*/"/>
|
||||
<property name="format" value="/\*\s*[=*-]+\s*\*/" />
|
||||
<property name="illegalPattern" value="true"/>
|
||||
<property name="ignoreComments" value="false"/>
|
||||
<property name="message" value="Legacy Method Separators"/>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Provides support for ALPN based on JDK 9+ APIs.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[depend]
|
||||
alpn-impl/alpn-11
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[depend]
|
||||
alpn-impl/alpn-11
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[depend]
|
||||
alpn-impl/alpn-11
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[depend]
|
||||
alpn-impl/alpn-11
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables the ALPN (Application Layer Protocol Negotiation) TLS extension.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables Annotation scanning for deployed webapplications.
|
||||
|
|
|
@ -941,7 +941,7 @@ public class AnnotationParser
|
|||
* @param name the class file name
|
||||
* @return whether the class file name is valid
|
||||
*/
|
||||
private boolean isValidClassFileName(String name)
|
||||
public boolean isValidClassFileName(String name)
|
||||
{
|
||||
//no name cannot be valid
|
||||
if (name == null || name.length() == 0)
|
||||
|
@ -983,7 +983,7 @@ public class AnnotationParser
|
|||
* @param path the class file path
|
||||
* @return whether the class file path is valid
|
||||
*/
|
||||
private boolean isValidClassFilePath(String path)
|
||||
public boolean isValidClassFilePath(String path)
|
||||
{
|
||||
//no path is not valid
|
||||
if (path == null || path.length() == 0)
|
||||
|
|
|
@ -54,23 +54,28 @@ public class TestSecurityAnnotationConversions
|
|||
{
|
||||
}
|
||||
|
||||
@ServletSecurity(value = @HttpConstraint(value = EmptyRoleSemantic.PERMIT, transportGuarantee = TransportGuarantee.CONFIDENTIAL, rolesAllowed = {
|
||||
"tom", "dick", "harry"
|
||||
}))
|
||||
@ServletSecurity(value = @HttpConstraint(value = EmptyRoleSemantic.PERMIT, transportGuarantee = TransportGuarantee.CONFIDENTIAL, rolesAllowed =
|
||||
{
|
||||
"tom", "dick", "harry"
|
||||
}))
|
||||
public static class RolesServlet extends HttpServlet
|
||||
{
|
||||
}
|
||||
|
||||
@ServletSecurity(value = @HttpConstraint(value = EmptyRoleSemantic.PERMIT, transportGuarantee = TransportGuarantee.CONFIDENTIAL, rolesAllowed = {
|
||||
"tom", "dick", "harry"
|
||||
}), httpMethodConstraints = {@HttpMethodConstraint(value = "GET")})
|
||||
@ServletSecurity(value = @HttpConstraint(value = EmptyRoleSemantic.PERMIT, transportGuarantee = TransportGuarantee.CONFIDENTIAL, rolesAllowed =
|
||||
{
|
||||
"tom", "dick", "harry"
|
||||
}), httpMethodConstraints = {@HttpMethodConstraint(value = "GET")})
|
||||
public static class Method1Servlet extends HttpServlet
|
||||
{
|
||||
}
|
||||
|
||||
@ServletSecurity(value = @HttpConstraint(value = EmptyRoleSemantic.PERMIT, transportGuarantee = TransportGuarantee.CONFIDENTIAL, rolesAllowed = {
|
||||
"tom", "dick", "harry"
|
||||
}), httpMethodConstraints = {@HttpMethodConstraint(value = "GET", transportGuarantee = TransportGuarantee.CONFIDENTIAL)})
|
||||
@ServletSecurity(value = @HttpConstraint(value = EmptyRoleSemantic.PERMIT, transportGuarantee = TransportGuarantee.CONFIDENTIAL, rolesAllowed =
|
||||
{
|
||||
"tom", "dick", "harry"
|
||||
}), httpMethodConstraints = {
|
||||
@HttpMethodConstraint(value = "GET", transportGuarantee = TransportGuarantee.CONFIDENTIAL)
|
||||
})
|
||||
public static class Method2Servlet extends HttpServlet
|
||||
{
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Adds the Jetty HTTP client to the server classpath.
|
||||
|
|
|
@ -92,10 +92,9 @@ public abstract class AbstractConnectionPool implements ConnectionPool, Dumpable
|
|||
public Connection acquire(boolean create)
|
||||
{
|
||||
Connection connection = activate();
|
||||
if (connection == null)
|
||||
if (connection == null && create)
|
||||
{
|
||||
if (create)
|
||||
tryCreate(destination.getQueuedRequestCount());
|
||||
tryCreate(destination.getQueuedRequestCount());
|
||||
connection = activate();
|
||||
}
|
||||
return connection;
|
||||
|
|
|
@ -199,6 +199,7 @@ public abstract class HttpReceiver
|
|||
if (updateResponseState(ResponseState.TRANSIENT, ResponseState.BEGIN))
|
||||
return true;
|
||||
|
||||
dispose();
|
||||
terminateResponse(exchange);
|
||||
return false;
|
||||
}
|
||||
|
@ -217,23 +218,17 @@ public abstract class HttpReceiver
|
|||
*/
|
||||
protected boolean responseHeader(HttpExchange exchange, HttpField field)
|
||||
{
|
||||
out:
|
||||
while (true)
|
||||
{
|
||||
ResponseState current = responseState.get();
|
||||
switch (current)
|
||||
if (current == ResponseState.BEGIN || current == ResponseState.HEADER)
|
||||
{
|
||||
case BEGIN:
|
||||
case HEADER:
|
||||
{
|
||||
if (updateResponseState(current, ResponseState.TRANSIENT))
|
||||
break out;
|
||||
if (updateResponseState(current, ResponseState.TRANSIENT))
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,6 +262,7 @@ public abstract class HttpReceiver
|
|||
if (updateResponseState(ResponseState.TRANSIENT, ResponseState.HEADER))
|
||||
return true;
|
||||
|
||||
dispose();
|
||||
terminateResponse(exchange);
|
||||
return false;
|
||||
}
|
||||
|
@ -334,7 +330,7 @@ public abstract class HttpReceiver
|
|||
{
|
||||
if (factory.getEncoding().equalsIgnoreCase(encoding))
|
||||
{
|
||||
decoder = new Decoder(response, factory.newContentDecoder());
|
||||
decoder = new Decoder(exchange, factory.newContentDecoder());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -350,6 +346,7 @@ public abstract class HttpReceiver
|
|||
return hasDemand;
|
||||
}
|
||||
|
||||
dispose();
|
||||
terminateResponse(exchange);
|
||||
return false;
|
||||
}
|
||||
|
@ -393,39 +390,28 @@ public abstract class HttpReceiver
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Response content {}{}{}", response, System.lineSeparator(), BufferUtil.toDetailString(buffer));
|
||||
|
||||
ContentListeners listeners = this.contentListeners;
|
||||
if (listeners != null)
|
||||
if (contentListeners.isEmpty())
|
||||
{
|
||||
if (listeners.isEmpty())
|
||||
{
|
||||
callback.succeeded();
|
||||
}
|
||||
else
|
||||
{
|
||||
Decoder decoder = this.decoder;
|
||||
if (decoder == null)
|
||||
{
|
||||
listeners.notifyContent(response, buffer, callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
proceed = decoder.decode(buffer, callback);
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
callback.failed(x);
|
||||
proceed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
callback.succeeded();
|
||||
}
|
||||
else
|
||||
{
|
||||
// May happen in case of concurrent abort.
|
||||
proceed = false;
|
||||
if (decoder == null)
|
||||
{
|
||||
contentListeners.notifyContent(response, buffer, callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
proceed = decoder.decode(buffer, callback);
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
callback.failed(x);
|
||||
proceed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -444,6 +430,7 @@ public abstract class HttpReceiver
|
|||
}
|
||||
}
|
||||
|
||||
dispose();
|
||||
terminateResponse(exchange);
|
||||
return false;
|
||||
}
|
||||
|
@ -567,6 +554,7 @@ public abstract class HttpReceiver
|
|||
*/
|
||||
protected void dispose()
|
||||
{
|
||||
assert responseState.get() != ResponseState.TRANSIENT;
|
||||
cleanup();
|
||||
}
|
||||
|
||||
|
@ -598,7 +586,8 @@ public abstract class HttpReceiver
|
|||
|
||||
this.failure = failure;
|
||||
|
||||
dispose();
|
||||
if (terminate)
|
||||
dispose();
|
||||
|
||||
HttpResponse response = exchange.getResponse();
|
||||
if (LOG.isDebugEnabled())
|
||||
|
@ -776,14 +765,14 @@ public abstract class HttpReceiver
|
|||
*/
|
||||
private class Decoder implements Destroyable
|
||||
{
|
||||
private final HttpResponse response;
|
||||
private final HttpExchange exchange;
|
||||
private final ContentDecoder decoder;
|
||||
private ByteBuffer encoded;
|
||||
private Callback callback;
|
||||
|
||||
private Decoder(HttpResponse response, ContentDecoder decoder)
|
||||
private Decoder(HttpExchange exchange, ContentDecoder decoder)
|
||||
{
|
||||
this.response = response;
|
||||
this.exchange = exchange;
|
||||
this.decoder = Objects.requireNonNull(decoder);
|
||||
}
|
||||
|
||||
|
@ -814,13 +803,13 @@ public abstract class HttpReceiver
|
|||
}
|
||||
ByteBuffer decoded = buffer;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Response content decoded ({}) {}{}{}", decoder, response, System.lineSeparator(), BufferUtil.toDetailString(decoded));
|
||||
LOG.debug("Response content decoded ({}) {}{}{}", decoder, exchange, System.lineSeparator(), BufferUtil.toDetailString(decoded));
|
||||
|
||||
contentListeners.notifyContent(response, decoded, Callback.from(() -> decoder.release(decoded), callback::failed));
|
||||
contentListeners.notifyContent(exchange.getResponse(), decoded, Callback.from(() -> decoder.release(decoded), callback::failed));
|
||||
|
||||
boolean hasDemand = hasDemandOrStall();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Response content decoded {}, hasDemand={}", response, hasDemand);
|
||||
LOG.debug("Response content decoded {}, hasDemand={}", exchange, hasDemand);
|
||||
if (!hasDemand)
|
||||
return false;
|
||||
}
|
||||
|
@ -829,9 +818,50 @@ public abstract class HttpReceiver
|
|||
private void resume()
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Response content resuming decoding {}", response);
|
||||
if (decode())
|
||||
LOG.debug("Response content resuming decoding {}", exchange);
|
||||
|
||||
// The content and callback may be null
|
||||
// if there is no initial content demand.
|
||||
if (callback == null)
|
||||
{
|
||||
receive();
|
||||
return;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
ResponseState current = responseState.get();
|
||||
if (current == ResponseState.HEADERS || current == ResponseState.CONTENT)
|
||||
{
|
||||
if (updateResponseState(current, ResponseState.TRANSIENT))
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
callback.failed(new IllegalStateException("Invalid response state " + current));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
boolean decoded = false;
|
||||
try
|
||||
{
|
||||
decoded = decode();
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
callback.failed(x);
|
||||
}
|
||||
|
||||
if (updateResponseState(ResponseState.TRANSIENT, ResponseState.CONTENT))
|
||||
{
|
||||
if (decoded)
|
||||
receive();
|
||||
return;
|
||||
}
|
||||
|
||||
dispose();
|
||||
terminateResponse(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -372,7 +372,7 @@ public class HttpRequest implements Request
|
|||
}
|
||||
|
||||
@Override
|
||||
public HttpFields.Mutable getHeaders()
|
||||
public HttpFields getHeaders()
|
||||
{
|
||||
return headers;
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ public class MultiplexConnectionPool extends AbstractConnectionPool implements C
|
|||
public Connection acquire(boolean create)
|
||||
{
|
||||
Connection connection = activate();
|
||||
if (connection == null)
|
||||
if (connection == null && create)
|
||||
{
|
||||
int queuedRequests = getHttpDestination().getQueuedRequestCount();
|
||||
int maxMultiplex = getMaxMultiplex();
|
||||
|
|
|
@ -100,7 +100,6 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
RetainableByteBuffer currentBuffer = networkBuffer;
|
||||
if (currentBuffer == null)
|
||||
throw new IllegalStateException();
|
||||
|
||||
if (currentBuffer.hasRemaining())
|
||||
throw new IllegalStateException();
|
||||
|
||||
|
@ -121,9 +120,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
private void releaseNetworkBuffer()
|
||||
{
|
||||
if (networkBuffer == null)
|
||||
throw new IllegalStateException();
|
||||
if (networkBuffer.hasRemaining())
|
||||
throw new IllegalStateException();
|
||||
return;
|
||||
networkBuffer.release();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Released {}", networkBuffer);
|
||||
|
@ -153,24 +150,27 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
while (true)
|
||||
{
|
||||
// Always parse even empty buffers to advance the parser.
|
||||
boolean stopProcessing = parse();
|
||||
if (parse())
|
||||
{
|
||||
// Return immediately, as this thread may be in a race
|
||||
// with e.g. another thread demanding more content.
|
||||
return;
|
||||
}
|
||||
|
||||
// Connection may be closed or upgraded in a parser callback.
|
||||
boolean upgraded = connection != endPoint.getConnection();
|
||||
if (connection.isClosed() || upgraded)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} {}", connection, upgraded ? "upgraded" : "closed");
|
||||
LOG.debug("{} {}", upgraded ? "Upgraded" : "Closed", connection);
|
||||
releaseNetworkBuffer();
|
||||
return;
|
||||
}
|
||||
|
||||
if (stopProcessing)
|
||||
return;
|
||||
|
||||
if (networkBuffer.getReferences() > 1)
|
||||
reacquireNetworkBuffer();
|
||||
|
||||
// The networkBuffer may have been reacquired.
|
||||
int read = endPoint.fill(networkBuffer.getBuffer());
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Read {} bytes in {} from {}", read, networkBuffer, endPoint);
|
||||
|
@ -196,8 +196,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
catch (Throwable x)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Unable to fill from endpoint {}", endPoint, x);
|
||||
networkBuffer.clear();
|
||||
LOG.debug("Error processing {}", endPoint, x);
|
||||
releaseNetworkBuffer();
|
||||
failAndClose(x);
|
||||
}
|
||||
|
@ -213,14 +212,24 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
while (true)
|
||||
{
|
||||
boolean handle = parser.parseNext(networkBuffer.getBuffer());
|
||||
boolean failed = isFailed();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Parse result={}, failed={}", handle, failed);
|
||||
// When failed, it's safe to close the parser because there
|
||||
// will be no races with other threads demanding more content.
|
||||
if (failed)
|
||||
parser.close();
|
||||
if (handle)
|
||||
return !failed;
|
||||
|
||||
boolean complete = this.complete;
|
||||
this.complete = false;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Parsed {}, remaining {} {}", handle, networkBuffer.remaining(), parser);
|
||||
if (handle)
|
||||
return true;
|
||||
LOG.debug("Parse complete={}, remaining {} {}", complete, networkBuffer.remaining(), parser);
|
||||
|
||||
if (networkBuffer.isEmpty())
|
||||
return false;
|
||||
|
||||
if (complete)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
|
@ -301,8 +310,13 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
if (exchange == null)
|
||||
return false;
|
||||
|
||||
RetainableByteBuffer networkBuffer = this.networkBuffer;
|
||||
networkBuffer.retain();
|
||||
return !responseContent(exchange, buffer, Callback.from(networkBuffer::release, this::failAndClose));
|
||||
return !responseContent(exchange, buffer, Callback.from(networkBuffer::release, failure ->
|
||||
{
|
||||
networkBuffer.release();
|
||||
failAndClose(failure);
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -333,17 +347,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
if (status != HttpStatus.CONTINUE_100)
|
||||
complete = true;
|
||||
|
||||
boolean proceed = responseSuccess(exchange);
|
||||
if (!proceed)
|
||||
return true;
|
||||
|
||||
if (status == HttpStatus.SWITCHING_PROTOCOLS_101)
|
||||
return true;
|
||||
|
||||
if (HttpMethod.CONNECT.is(exchange.getRequest().getMethod()) && status == HttpStatus.OK_200)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return !responseSuccess(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -376,13 +380,6 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
parser.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispose()
|
||||
{
|
||||
super.dispose();
|
||||
parser.close();
|
||||
}
|
||||
|
||||
private void failAndClose(Throwable failure)
|
||||
{
|
||||
if (responseFailure(failure))
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.client;
|
|||
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
|
||||
|
@ -45,13 +46,13 @@ public abstract class AbstractHttpClientServerTest
|
|||
protected HttpClient client;
|
||||
protected ServerConnector connector;
|
||||
|
||||
public void start(final Scenario scenario, Handler handler) throws Exception
|
||||
public void start(Scenario scenario, Handler handler) throws Exception
|
||||
{
|
||||
startServer(scenario, handler);
|
||||
startClient(scenario);
|
||||
}
|
||||
|
||||
protected void startServer(final Scenario scenario, Handler handler) throws Exception
|
||||
protected void startServer(Scenario scenario, Handler handler) throws Exception
|
||||
{
|
||||
if (server == null)
|
||||
{
|
||||
|
@ -66,23 +67,27 @@ public abstract class AbstractHttpClientServerTest
|
|||
server.start();
|
||||
}
|
||||
|
||||
protected void startClient(final Scenario scenario) throws Exception
|
||||
protected void startClient(Scenario scenario) throws Exception
|
||||
{
|
||||
startClient(scenario, null);
|
||||
}
|
||||
|
||||
protected void startClient(final Scenario scenario, Consumer<HttpClient> config) throws Exception
|
||||
protected void startClient(Scenario scenario, Consumer<HttpClient> config) throws Exception
|
||||
{
|
||||
startClient(scenario, HttpClientTransportOverHTTP::new, config);
|
||||
}
|
||||
|
||||
protected void startClient(Scenario scenario, Function<ClientConnector, HttpClientTransportOverHTTP> transport, Consumer<HttpClient> config) throws Exception
|
||||
{
|
||||
ClientConnector clientConnector = new ClientConnector();
|
||||
clientConnector.setSelectors(1);
|
||||
clientConnector.setSslContextFactory(scenario.newClientSslContextFactory());
|
||||
HttpClientTransport transport = new HttpClientTransportOverHTTP(clientConnector);
|
||||
QueuedThreadPool executor = new QueuedThreadPool();
|
||||
executor.setName("client");
|
||||
clientConnector.setExecutor(executor);
|
||||
Scheduler scheduler = new ScheduledExecutorScheduler("client-scheduler", false);
|
||||
clientConnector.setScheduler(scheduler);
|
||||
client = newHttpClient(transport);
|
||||
client = newHttpClient(transport.apply(clientConnector));
|
||||
client.setSocketAddressResolver(new SocketAddressResolver.Sync());
|
||||
if (config != null)
|
||||
config.accept(client);
|
||||
|
|
|
@ -18,21 +18,30 @@
|
|||
|
||||
package org.eclipse.jetty.client;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.LongConsumer;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.client.http.HttpChannelOverHTTP;
|
||||
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
|
||||
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
|
||||
import org.eclipse.jetty.client.http.HttpReceiverOverHTTP;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||
|
@ -46,10 +55,10 @@ public class HttpClientAsyncContentTest extends AbstractHttpClientServerTest
|
|||
@ArgumentsSource(ScenarioProvider.class)
|
||||
public void testSmallAsyncContent(Scenario scenario) throws Exception
|
||||
{
|
||||
start(scenario, new AbstractHandler()
|
||||
start(scenario, new EmptyServerHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
ServletOutputStream output = response.getOutputStream();
|
||||
output.write(65);
|
||||
|
@ -58,30 +67,19 @@ public class HttpClientAsyncContentTest extends AbstractHttpClientServerTest
|
|||
}
|
||||
});
|
||||
|
||||
final AtomicInteger contentCount = new AtomicInteger();
|
||||
final AtomicReference<Callback> callbackRef = new AtomicReference<>();
|
||||
final AtomicReference<CountDownLatch> contentLatch = new AtomicReference<>(new CountDownLatch(1));
|
||||
final CountDownLatch completeLatch = new CountDownLatch(1);
|
||||
AtomicInteger contentCount = new AtomicInteger();
|
||||
AtomicReference<Callback> callbackRef = new AtomicReference<>();
|
||||
AtomicReference<CountDownLatch> contentLatch = new AtomicReference<>(new CountDownLatch(1));
|
||||
CountDownLatch completeLatch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scenario.getScheme())
|
||||
.onResponseContentAsync(new Response.AsyncContentListener()
|
||||
.onResponseContentAsync((response, content, callback) ->
|
||||
{
|
||||
@Override
|
||||
public void onContent(Response response, ByteBuffer content, Callback callback)
|
||||
{
|
||||
contentCount.incrementAndGet();
|
||||
callbackRef.set(callback);
|
||||
contentLatch.get().countDown();
|
||||
}
|
||||
contentCount.incrementAndGet();
|
||||
callbackRef.set(callback);
|
||||
contentLatch.get().countDown();
|
||||
})
|
||||
.send(new Response.CompleteListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
completeLatch.countDown();
|
||||
}
|
||||
});
|
||||
.send(result -> completeLatch.countDown());
|
||||
|
||||
assertTrue(contentLatch.get().await(5, TimeUnit.SECONDS));
|
||||
Callback callback = callbackRef.get();
|
||||
|
@ -113,4 +111,294 @@ public class HttpClientAsyncContentTest extends AbstractHttpClientServerTest
|
|||
assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
|
||||
assertEquals(2, contentCount.get());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(ScenarioProvider.class)
|
||||
public void testConcurrentAsyncContent(Scenario scenario) throws Exception
|
||||
{
|
||||
AtomicReference<AsyncContext> asyncContextRef = new AtomicReference<>();
|
||||
startServer(scenario, new EmptyServerHandler()
|
||||
{
|
||||
@Override
|
||||
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
ServletOutputStream output = response.getOutputStream();
|
||||
output.write(new byte[1024]);
|
||||
output.flush();
|
||||
AsyncContext asyncContext = request.startAsync();
|
||||
asyncContext.setTimeout(0);
|
||||
asyncContextRef.set(asyncContext);
|
||||
}
|
||||
});
|
||||
AtomicReference<LongConsumer> demandRef = new AtomicReference<>();
|
||||
startClient(scenario, clientConnector -> new HttpClientTransportOverHTTP(clientConnector)
|
||||
{
|
||||
@Override
|
||||
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
|
||||
{
|
||||
return customize(new HttpConnectionOverHTTP(endPoint, context)
|
||||
{
|
||||
@Override
|
||||
protected HttpChannelOverHTTP newHttpChannel()
|
||||
{
|
||||
return new HttpChannelOverHTTP(this)
|
||||
{
|
||||
@Override
|
||||
protected HttpReceiverOverHTTP newHttpReceiver()
|
||||
{
|
||||
return new HttpReceiverOverHTTP(this)
|
||||
{
|
||||
@Override
|
||||
public boolean content(ByteBuffer buffer)
|
||||
{
|
||||
try
|
||||
{
|
||||
boolean result = super.content(buffer);
|
||||
// The content has been notified, but the listener has not demanded.
|
||||
|
||||
// Simulate an asynchronous demand from otherThread.
|
||||
// There is no further content, so otherThread will fill 0,
|
||||
// set the fill interest, and release the network buffer.
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
Thread otherThread = new Thread(() ->
|
||||
{
|
||||
demandRef.get().accept(1);
|
||||
latch.countDown();
|
||||
});
|
||||
otherThread.start();
|
||||
// Wait for otherThread to finish, then let this thread continue.
|
||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
}, context);
|
||||
}
|
||||
}, null);
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scenario.getScheme())
|
||||
.onResponseContentDemanded((response, demand, content, callback) ->
|
||||
{
|
||||
demandRef.set(demand);
|
||||
// Don't demand and don't succeed the callback.
|
||||
})
|
||||
.send(result ->
|
||||
{
|
||||
if (result.isSucceeded())
|
||||
latch.countDown();
|
||||
});
|
||||
|
||||
// Wait for the threads to finish their processing.
|
||||
Thread.sleep(1000);
|
||||
|
||||
// Complete the response.
|
||||
asyncContextRef.get().complete();
|
||||
|
||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(ScenarioProvider.class)
|
||||
public void testAsyncContentAbort(Scenario scenario) throws Exception
|
||||
{
|
||||
start(scenario, new EmptyServerHandler()
|
||||
{
|
||||
@Override
|
||||
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
response.getOutputStream().write(new byte[1024]);
|
||||
}
|
||||
});
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scenario.getScheme())
|
||||
.onResponseContentDemanded((response, demand, content, callback) -> response.abort(new Throwable()))
|
||||
.send(result ->
|
||||
{
|
||||
if (result.isFailed())
|
||||
latch.countDown();
|
||||
});
|
||||
|
||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(ScenarioProvider.class)
|
||||
public void testAsyncGzipContentAbortThenDemand(Scenario scenario) throws Exception
|
||||
{
|
||||
start(scenario, new EmptyServerHandler()
|
||||
{
|
||||
@Override
|
||||
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
response.setHeader("Content-Encoding", "gzip");
|
||||
GZIPOutputStream gzip = new GZIPOutputStream(response.getOutputStream());
|
||||
gzip.write(new byte[1024]);
|
||||
gzip.finish();
|
||||
}
|
||||
});
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scenario.getScheme())
|
||||
.onResponseContentDemanded((response, demand, content, callback) ->
|
||||
{
|
||||
response.abort(new Throwable());
|
||||
demand.accept(1);
|
||||
})
|
||||
.send(result ->
|
||||
{
|
||||
if (result.isFailed())
|
||||
latch.countDown();
|
||||
});
|
||||
|
||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(ScenarioProvider.class)
|
||||
public void testAsyncGzipContentDelayedDemand(Scenario scenario) throws Exception
|
||||
{
|
||||
start(scenario, new EmptyServerHandler()
|
||||
{
|
||||
@Override
|
||||
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
response.setHeader("Content-Encoding", "gzip");
|
||||
try (GZIPOutputStream gzip = new GZIPOutputStream(response.getOutputStream()))
|
||||
{
|
||||
gzip.write(new byte[1024]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AtomicReference<LongConsumer> demandRef = new AtomicReference<>();
|
||||
CountDownLatch headersLatch = new CountDownLatch(1);
|
||||
CountDownLatch resultLatch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scenario.getScheme())
|
||||
.onResponseContentDemanded(new Response.DemandedContentListener()
|
||||
{
|
||||
@Override
|
||||
public void onBeforeContent(Response response, LongConsumer demand)
|
||||
{
|
||||
// Don't demand yet.
|
||||
demandRef.set(demand);
|
||||
headersLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContent(Response response, LongConsumer demand, ByteBuffer content, Callback callback)
|
||||
{
|
||||
demand.accept(1);
|
||||
}
|
||||
})
|
||||
.send(result ->
|
||||
{
|
||||
if (result.isSucceeded())
|
||||
resultLatch.countDown();
|
||||
});
|
||||
|
||||
assertTrue(headersLatch.await(5, TimeUnit.SECONDS));
|
||||
// Wait to make sure the demand is really delayed.
|
||||
Thread.sleep(500);
|
||||
demandRef.get().accept(1);
|
||||
|
||||
assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(ScenarioProvider.class)
|
||||
public void testAsyncGzipContentAbortWhileDecodingWithDelayedDemand(Scenario scenario) throws Exception
|
||||
{
|
||||
// Use a large content so that the gzip decoding is done in multiple passes.
|
||||
byte[] bytes = new byte[8 * 1024 * 1024];
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try (GZIPOutputStream gzip = new GZIPOutputStream(baos))
|
||||
{
|
||||
gzip.write(bytes);
|
||||
}
|
||||
byte[] gzipBytes = baos.toByteArray();
|
||||
int half = gzipBytes.length / 2;
|
||||
byte[] gzip1 = Arrays.copyOfRange(gzipBytes, 0, half);
|
||||
byte[] gzip2 = Arrays.copyOfRange(gzipBytes, half, gzipBytes.length);
|
||||
|
||||
AtomicReference<AsyncContext> asyncContextRef = new AtomicReference<>();
|
||||
start(scenario, new EmptyServerHandler()
|
||||
{
|
||||
@Override
|
||||
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
AsyncContext asyncContext = request.startAsync();
|
||||
asyncContext.setTimeout(0);
|
||||
asyncContextRef.set(asyncContext);
|
||||
|
||||
response.setHeader("Content-Encoding", "gzip");
|
||||
ServletOutputStream output = response.getOutputStream();
|
||||
output.write(gzip1);
|
||||
output.flush();
|
||||
}
|
||||
});
|
||||
|
||||
AtomicReference<LongConsumer> demandRef = new AtomicReference<>();
|
||||
CountDownLatch firstChunkLatch = new CountDownLatch(1);
|
||||
CountDownLatch secondChunkLatch = new CountDownLatch(1);
|
||||
CountDownLatch resultLatch = new CountDownLatch(1);
|
||||
AtomicInteger chunks = new AtomicInteger();
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scenario.getScheme())
|
||||
.onResponseContentDemanded((response, demand, content, callback) ->
|
||||
{
|
||||
if (chunks.incrementAndGet() == 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Don't demand, but make the server write the second chunk.
|
||||
AsyncContext asyncContext = asyncContextRef.get();
|
||||
asyncContext.getResponse().getOutputStream().write(gzip2);
|
||||
asyncContext.complete();
|
||||
demandRef.set(demand);
|
||||
firstChunkLatch.countDown();
|
||||
}
|
||||
catch (IOException x)
|
||||
{
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
response.abort(new Throwable());
|
||||
demandRef.set(demand);
|
||||
secondChunkLatch.countDown();
|
||||
}
|
||||
})
|
||||
.send(result ->
|
||||
{
|
||||
if (result.isFailed())
|
||||
resultLatch.countDown();
|
||||
});
|
||||
|
||||
assertTrue(firstChunkLatch.await(5, TimeUnit.SECONDS));
|
||||
// Wait to make sure the demand is really delayed.
|
||||
Thread.sleep(500);
|
||||
demandRef.get().accept(1);
|
||||
|
||||
assertTrue(secondChunkLatch.await(5, TimeUnit.SECONDS));
|
||||
// Wait to make sure the demand is really delayed.
|
||||
Thread.sleep(500);
|
||||
demandRef.get().accept(1);
|
||||
|
||||
assertTrue(resultLatch.await(555, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1582,11 +1582,6 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
|||
ContentResponse response = listener.get(5, TimeUnit.SECONDS);
|
||||
assertEquals(200, response.getStatus());
|
||||
|
||||
// Because the tunnel was successful, this connection will be
|
||||
// upgraded to an SslConnection, so it will not be fill interested.
|
||||
// This test doesn't upgrade, so it needs to restore the fill interest.
|
||||
((AbstractConnection)connection).fillInterested();
|
||||
|
||||
// Test that I can send another request on the same connection.
|
||||
request = client.newRequest(host, port);
|
||||
listener = new FutureResponseListener(request);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables webapplication deployment from the webapps directory.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables Deployer to apply common configuration to all webapp deployments
|
||||
|
|
|
@ -989,3 +989,15 @@ As a reminder, when configuring your includes/excludes, *excludes always win*.
|
|||
|
||||
Dumps can be configured as part of the `jetty.xml` configuration for your server.
|
||||
Please see the documentation on the link:#jetty-dump-tool[Jetty Dump Tool] for more information.
|
||||
|
||||
|
||||
==== SslContextFactory KeyStore Reload
|
||||
|
||||
Jetty can be configured to monitor the directory of the KeyStore file specified in the SslContextFactory, and reload the
|
||||
SslContextFactory if any changes are detected to the KeyStore file.
|
||||
|
||||
If changes need to be done to other file such as the TrustStore file, this must be done before the change to the Keystore
|
||||
file which will then trigger the `SslContextFactory` reload.
|
||||
|
||||
With the Jetty distribution this feature can be used by simply activating the `ssl-reload` startup module.
|
||||
For embedded usage the `KeyStoreScanner` should be created given the `SslContextFactory` and added as a bean on the Server.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Adds the FastCGI implementation to the classpath.
|
||||
|
|
|
@ -39,8 +39,8 @@ import org.eclipse.jetty.util.StringUtil;
|
|||
/**
|
||||
* Inspired by nginx's try_files functionality.
|
||||
* <p>
|
||||
* This filter accepts the <code>files</code> init-param as a list of space-separated
|
||||
* file URIs. The special token <code>$path</code> represents the current request URL's
|
||||
* This filter accepts the {@code files} init-param as a list of space-separated
|
||||
* file URIs. The special token {@code $path} represents the current request URL's
|
||||
* path (the portion after the context path).
|
||||
* <p>
|
||||
* Typical example of how this filter can be configured is the following:
|
||||
|
@ -50,14 +50,14 @@ import org.eclipse.jetty.util.StringUtil;
|
|||
* <filter-class>org.eclipse.jetty.fcgi.server.proxy.TryFilesFilter</filter-class>
|
||||
* <init-param>
|
||||
* <param-name>files</param-name>
|
||||
* <param-value>maintenance.html $path index.php?p=$path</param-value>
|
||||
* <param-value>/maintenance.html $path /index.php?p=$path</param-value>
|
||||
* </init-param>
|
||||
* </filter>
|
||||
* </pre>
|
||||
* For a request such as <code>/context/path/to/resource.ext</code>, this filter will
|
||||
* try to serve the <code>/maintenance.html</code> file if it finds it; failing that,
|
||||
* it will try to serve the <code>/path/to/resource.ext</code> file if it finds it;
|
||||
* failing that it will forward the request to <code>index.php?p=/path/to/resource.ext</code>.
|
||||
* For a request such as {@code /context/path/to/resource.ext}, this filter will
|
||||
* try to serve the {@code /maintenance.html} file if it finds it; failing that,
|
||||
* it will try to serve the {@code /path/to/resource.ext} file if it finds it;
|
||||
* failing that it will forward the request to {@code /index.php?p=/path/to/resource.ext}.
|
||||
* The last file URI specified in the list is therefore the "fallback" to which the request
|
||||
* is forwarded to in case no previous files can be found.
|
||||
* <p>
|
||||
|
|
|
@ -93,6 +93,7 @@ public abstract class AbstractHttpClientServerTest
|
|||
{
|
||||
assertThat("Server BufferPool - leaked acquires", serverBufferPool.getLeakedAcquires(), Matchers.is(0L));
|
||||
assertThat("Server BufferPool - leaked releases", serverBufferPool.getLeakedReleases(), Matchers.is(0L));
|
||||
assertThat("Server BufferPool - leaked removes", serverBufferPool.getLeakedRemoves(), Matchers.is(0L));
|
||||
assertThat("Server BufferPool - unreleased", serverBufferPool.getLeakedResources(), Matchers.is(0L));
|
||||
}
|
||||
|
||||
|
@ -101,6 +102,7 @@ public abstract class AbstractHttpClientServerTest
|
|||
LeakTrackingByteBufferPool pool = (LeakTrackingByteBufferPool)clientBufferPool;
|
||||
assertThat("Client BufferPool - leaked acquires", pool.getLeakedAcquires(), Matchers.is(0L));
|
||||
assertThat("Client BufferPool - leaked releases", pool.getLeakedReleases(), Matchers.is(0L));
|
||||
assertThat("Client BufferPool - leaked removes", pool.getLeakedRemoves(), Matchers.is(0L));
|
||||
assertThat("Client BufferPool - unreleased", pool.getLeakedResources(), Matchers.is(0L));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables GCloud Datastore API and implementation
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Control GCloud API classpath
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables GCloudDatastore session management.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables session data store in an embedded Hazelcast Map
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables session data store in a remote Hazelcast Map
|
||||
|
|
|
@ -145,7 +145,7 @@
|
|||
org.eclipse.jetty.orbit,org.eclipse.jetty.http2,org.eclipse.jetty.websocket,org.eclipse.jetty.fcgi,org.eclipse.jetty.toolchain,org.apache.taglibs
|
||||
</excludeGroupIds>
|
||||
<excludeArtifactIds>
|
||||
jetty-all,apache-jsp,apache-jstl,jetty-start,jetty-spring
|
||||
jetty-all,apache-jsp,apache-jstl,jetty-start,jetty-spring,jetty-slf4j-impl
|
||||
</excludeArtifactIds>
|
||||
<includeTypes>jar</includeTypes>
|
||||
<outputDirectory>${assembly-directory}/lib</outputDirectory>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Installs the Conscrypt JSSE provider
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Deploys the Hawtio console as a webapplication.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Deploys the JAMon webapplication
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Deploys the Jminix JMX Console within the server
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Deploys the Jolokia console as a web application.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables JSP for all webapplications deployed on the server.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables JSTL for all webapplications deployed on the server
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
This module causes jetty to stop immediately after starting. This is good for testing configuration and/or precompiling quickstart webapps
|
||||
|
|
|
@ -37,11 +37,11 @@ public class HttpCookie
|
|||
private static final String __01Jan1970_COOKIE = DateGenerator.formatCookieDate(0).trim();
|
||||
|
||||
/**
|
||||
*If this string is found within the comment parsed with {@link #isHttpOnlyInComment(String)} the check will return true
|
||||
* If this string is found within the comment parsed with {@link #isHttpOnlyInComment(String)} the check will return true
|
||||
**/
|
||||
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
|
||||
* 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_";
|
||||
public static final String SAME_SITE_NONE_COMMENT = SAME_SITE_COMMENT + "NONE__";
|
||||
|
@ -474,10 +474,10 @@ public class HttpCookie
|
|||
LOG.debug("No default value for SameSite");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
if (o instanceof SameSite)
|
||||
return (SameSite)o;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
SameSite samesite = Enum.valueOf(SameSite.class, o.toString().trim().toUpperCase(Locale.ENGLISH));
|
||||
|
|
|
@ -29,7 +29,6 @@ import org.eclipse.jetty.http2.api.Session;
|
|||
import org.eclipse.jetty.http2.api.Stream;
|
||||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.http2.frames.PushPromiseFrame;
|
||||
import org.eclipse.jetty.http2.frames.ResetFrame;
|
||||
import org.eclipse.jetty.http2.generator.Generator;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
@ -118,17 +117,6 @@ public class HTTP2ClientSession extends HTTP2Session
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResetForUnknownStream(ResetFrame frame)
|
||||
{
|
||||
int streamId = frame.getStreamId();
|
||||
boolean closed = isClientStream(streamId) ? isLocalStreamClosed(streamId) : isRemoteStreamClosed(streamId);
|
||||
if (closed)
|
||||
notifyReset(this, frame);
|
||||
else
|
||||
onConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "unexpected_rst_stream_frame");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushPromise(PushPromiseFrame frame)
|
||||
{
|
||||
|
|
|
@ -38,6 +38,7 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.WriteListener;
|
||||
|
@ -51,6 +52,7 @@ import org.eclipse.jetty.http.HttpStatus;
|
|||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http2.BufferingFlowControlStrategy;
|
||||
import org.eclipse.jetty.http2.ErrorCode;
|
||||
import org.eclipse.jetty.http2.FlowControlStrategy;
|
||||
import org.eclipse.jetty.http2.HTTP2Flusher;
|
||||
|
@ -1042,6 +1044,64 @@ public class StreamResetTest extends AbstractTest
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResetBeforeReceivingWindowUpdate() throws Exception
|
||||
{
|
||||
int window = FlowControlStrategy.DEFAULT_WINDOW_SIZE;
|
||||
float ratio = 0.5F;
|
||||
AtomicReference<Stream> streamRef = new AtomicReference<>();
|
||||
Consumer<AbstractHTTP2ServerConnectionFactory> http2Factory = http2 ->
|
||||
{
|
||||
http2.setInitialSessionRecvWindow(window);
|
||||
http2.setInitialStreamRecvWindow(window);
|
||||
http2.setFlowControlStrategyFactory(() -> new BufferingFlowControlStrategy(ratio)
|
||||
{
|
||||
@Override
|
||||
protected void sendWindowUpdate(IStream stream, ISession session, WindowUpdateFrame frame)
|
||||
{
|
||||
// Before sending the window update, reset from the client side.
|
||||
if (stream != null)
|
||||
streamRef.get().reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
|
||||
super.sendWindowUpdate(stream, session, frame);
|
||||
}
|
||||
});
|
||||
};
|
||||
start(new ServerSessionListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
|
||||
{
|
||||
MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, 200, HttpFields.EMPTY);
|
||||
HeadersFrame responseFrame = new HeadersFrame(stream.getId(), response, null, false);
|
||||
Callback.Completable completable = new Callback.Completable();
|
||||
stream.headers(responseFrame, completable);
|
||||
// Consume the request content as it arrives.
|
||||
return new Stream.Listener.Adapter();
|
||||
}
|
||||
}, http2Factory);
|
||||
|
||||
CountDownLatch failureLatch = new CountDownLatch(1);
|
||||
Session client = newClient(new Session.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onFailure(Session session, Throwable failure)
|
||||
{
|
||||
failureLatch.countDown();
|
||||
}
|
||||
});
|
||||
MetaData.Request request = newRequest("GET", HttpFields.EMPTY);
|
||||
HeadersFrame requestFrame = new HeadersFrame(request, null, false);
|
||||
FuturePromise<Stream> promise = new FuturePromise<>();
|
||||
client.newStream(requestFrame, promise, new Stream.Listener.Adapter());
|
||||
Stream stream = promise.get(5, TimeUnit.SECONDS);
|
||||
streamRef.set(stream);
|
||||
// Send enough bytes to trigger the server to send a window update.
|
||||
ByteBuffer content = ByteBuffer.allocate((int)(window * ratio) + 1024);
|
||||
stream.data(new DataFrame(stream.getId(), content, false), Callback.NOOP);
|
||||
|
||||
assertFalse(failureLatch.await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
private void waitUntilTCPCongested(WriteFlusher flusher) throws TimeoutException, InterruptedException
|
||||
{
|
||||
long start = System.nanoTime();
|
||||
|
|
|
@ -199,18 +199,22 @@ public abstract class AbstractFlowControlStrategy implements FlowControlStrategy
|
|||
|
||||
protected void onSessionUnstalled(ISession session)
|
||||
{
|
||||
sessionStallTime.addAndGet(System.nanoTime() - sessionStall.getAndSet(0));
|
||||
long stallTime = System.nanoTime() - sessionStall.getAndSet(0);
|
||||
sessionStallTime.addAndGet(stallTime);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Session unstalled {}", session);
|
||||
LOG.debug("Session unstalled after {} ms {}", TimeUnit.NANOSECONDS.toMillis(stallTime), session);
|
||||
}
|
||||
|
||||
protected void onStreamUnstalled(IStream stream)
|
||||
{
|
||||
Long time = streamsStalls.remove(stream);
|
||||
if (time != null)
|
||||
streamsStallTime.addAndGet(System.nanoTime() - time);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Stream unstalled {}", stream);
|
||||
{
|
||||
long stallTime = System.nanoTime() - time;
|
||||
streamsStallTime.addAndGet(stallTime);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Stream unstalled after {} ms {}", TimeUnit.NANOSECONDS.toMillis(stallTime), stream);
|
||||
}
|
||||
}
|
||||
|
||||
@ManagedAttribute(value = "The time, in milliseconds, that the session flow control has stalled", readonly = true)
|
||||
|
|
|
@ -123,7 +123,7 @@ public class BufferingFlowControlStrategy extends AbstractFlowControlStrategy
|
|||
session.updateRecvWindow(level);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Data consumed, {} bytes, updated session recv window by {}/{} for {}", length, level, maxLevel, session);
|
||||
session.frames(null, Callback.NOOP, new WindowUpdateFrame(0, level), Frame.EMPTY_ARRAY);
|
||||
sendWindowUpdate(null, session, new WindowUpdateFrame(0, level));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -157,7 +157,7 @@ public class BufferingFlowControlStrategy extends AbstractFlowControlStrategy
|
|||
stream.updateRecvWindow(level);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Data consumed, {} bytes, updated stream recv window by {}/{} for {}", length, level, maxLevel, stream);
|
||||
session.frames(stream, Callback.NOOP, new WindowUpdateFrame(stream.getId(), level), Frame.EMPTY_ARRAY);
|
||||
sendWindowUpdate(stream, session, new WindowUpdateFrame(stream.getId(), level));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -169,6 +169,11 @@ public class BufferingFlowControlStrategy extends AbstractFlowControlStrategy
|
|||
}
|
||||
}
|
||||
|
||||
protected void sendWindowUpdate(IStream stream, ISession session, WindowUpdateFrame frame)
|
||||
{
|
||||
session.frames(stream, Callback.NOOP, frame, Frame.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowUpdate(ISession session, IStream stream, WindowUpdateFrame frame)
|
||||
{
|
||||
|
|
|
@ -101,8 +101,7 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("HTTP2 onUpgradeTo {} {}", this, BufferUtil.toDetailString(buffer));
|
||||
if (buffer != null)
|
||||
producer.setInputBuffer(buffer);
|
||||
producer.setInputBuffer(buffer);
|
||||
}
|
||||
|
||||
public boolean isUseInputDirectByteBuffers()
|
||||
|
|
|
@ -436,7 +436,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable
|
|||
// It's an application frame; is the stream gone already?
|
||||
if (stream == null)
|
||||
return true;
|
||||
return stream.isReset();
|
||||
return stream.isResetOrFailed();
|
||||
}
|
||||
|
||||
private boolean isProtocolFrame(Frame frame)
|
||||
|
|
|
@ -239,7 +239,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
public void onData(final DataFrame frame, Callback callback)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Received {}", frame);
|
||||
LOG.debug("Received {} on {}", frame, this);
|
||||
|
||||
int streamId = frame.getStreamId();
|
||||
IStream stream = getStream(streamId);
|
||||
|
@ -259,19 +259,27 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Stream #{} not found", streamId);
|
||||
LOG.debug("Stream #{} not found on {}", streamId, this);
|
||||
// We must enlarge the session flow control window,
|
||||
// otherwise other requests will be stalled.
|
||||
flowControl.onDataConsumed(this, null, flowControlLength);
|
||||
boolean local = (streamId & 1) == (localStreamIds.get() & 1);
|
||||
boolean closed = local ? isLocalStreamClosed(streamId) : isRemoteStreamClosed(streamId);
|
||||
if (closed)
|
||||
if (isStreamClosed(streamId))
|
||||
reset(new ResetFrame(streamId, ErrorCode.STREAM_CLOSED_ERROR.code), callback);
|
||||
else
|
||||
onConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "unexpected_data_frame", callback);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isStreamClosed(int streamId)
|
||||
{
|
||||
return isLocalStream(streamId) ? isLocalStreamClosed(streamId) : isRemoteStreamClosed(streamId);
|
||||
}
|
||||
|
||||
private boolean isLocalStream(int streamId)
|
||||
{
|
||||
return (streamId & 1) == (localStreamIds.get() & 1);
|
||||
}
|
||||
|
||||
protected boolean isLocalStreamClosed(int streamId)
|
||||
{
|
||||
return streamId <= localStreamIds.get();
|
||||
|
@ -289,14 +297,14 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
public void onPriority(PriorityFrame frame)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Received {}", frame);
|
||||
LOG.debug("Received {} on {}", frame, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReset(ResetFrame frame)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Received {}", frame);
|
||||
LOG.debug("Received {} on {}", frame, this);
|
||||
|
||||
int streamId = frame.getStreamId();
|
||||
IStream stream = getStream(streamId);
|
||||
|
@ -310,7 +318,13 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
}
|
||||
}
|
||||
|
||||
protected abstract void onResetForUnknownStream(ResetFrame frame);
|
||||
protected void onResetForUnknownStream(ResetFrame frame)
|
||||
{
|
||||
if (isStreamClosed(frame.getStreamId()))
|
||||
notifyReset(this, frame);
|
||||
else
|
||||
onConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "unexpected_rst_stream_frame");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettings(SettingsFrame frame)
|
||||
|
@ -322,7 +336,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
public void onSettings(SettingsFrame frame, boolean reply)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Received {}", frame);
|
||||
LOG.debug("Received {} on {}", frame, this);
|
||||
|
||||
if (frame.isReply())
|
||||
return;
|
||||
|
@ -406,7 +420,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
public void onPing(PingFrame frame)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Received {}", frame);
|
||||
LOG.debug("Received {} on {}", frame, this);
|
||||
|
||||
if (frame.isReply())
|
||||
{
|
||||
|
@ -440,7 +454,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
public void onGoAway(final GoAwayFrame frame)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Received {}", frame);
|
||||
LOG.debug("Received {} on {}", frame, this);
|
||||
|
||||
if (closed.compareAndSet(CloseState.NOT_CLOSED, CloseState.REMOTELY_CLOSED))
|
||||
{
|
||||
|
@ -459,7 +473,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
public void onWindowUpdate(WindowUpdateFrame frame)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Received {}", frame);
|
||||
LOG.debug("Received {} on {}", frame, this);
|
||||
|
||||
int streamId = frame.getStreamId();
|
||||
int windowDelta = frame.getWindowDelta();
|
||||
|
@ -481,7 +495,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
}
|
||||
else
|
||||
{
|
||||
if (!isRemoteStreamClosed(streamId))
|
||||
if (!isStreamClosed(streamId))
|
||||
onConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "unexpected_window_update_frame");
|
||||
}
|
||||
}
|
||||
|
@ -514,7 +528,9 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
public void onStreamFailure(int streamId, int error, String reason)
|
||||
{
|
||||
Callback callback = new ResetCallback(streamId, error, Callback.NOOP);
|
||||
Throwable failure = toFailure("Stream failure", error, reason);
|
||||
Throwable failure = toFailure(error, reason);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Stream #{} failure {}", streamId, this, failure);
|
||||
onStreamFailure(streamId, error, reason, failure, callback);
|
||||
}
|
||||
|
||||
|
@ -535,12 +551,16 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
|
||||
protected void onConnectionFailure(int error, String reason, Callback callback)
|
||||
{
|
||||
Throwable failure = toFailure("Session failure", error, reason);
|
||||
Throwable failure = toFailure(error, reason);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Session failure {}", this, failure);
|
||||
onFailure(error, reason, failure, new FailureCallback(error, reason, callback));
|
||||
}
|
||||
|
||||
protected void abort(Throwable failure)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Session abort {}", this, failure);
|
||||
onFailure(ErrorCode.NO_ERROR.code, null, failure, new TerminateCallback(failure));
|
||||
}
|
||||
|
||||
|
@ -560,7 +580,9 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
{
|
||||
int error = frame.getError();
|
||||
String reason = frame.tryConvertPayload();
|
||||
Throwable failure = toFailure("Session close", error, reason);
|
||||
Throwable failure = toFailure(error, reason);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Session close {}", this, failure);
|
||||
Collection<Stream> streams = getStreams();
|
||||
int count = streams.size();
|
||||
Callback countCallback = new CountingCallback(callback, count + 1);
|
||||
|
@ -571,9 +593,9 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
notifyClose(this, frame, countCallback);
|
||||
}
|
||||
|
||||
private Throwable toFailure(String message, int error, String reason)
|
||||
private Throwable toFailure(int error, String reason)
|
||||
{
|
||||
return new IOException(String.format("%s %s/%s", message, ErrorCode.toString(error, null), reason));
|
||||
return new IOException(String.format("%s/%s", ErrorCode.toString(error, null), reason));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -671,14 +693,14 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
if (closed.compareAndSet(CloseState.NOT_CLOSED, CloseState.LOCALLY_CLOSED))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Closing {}/{}", error, reason);
|
||||
LOG.debug("Closing {}/{} {}", error, reason, this);
|
||||
closeFrame = newGoAwayFrame(CloseState.LOCALLY_CLOSED, error, reason);
|
||||
control(null, callback, closeFrame);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Ignoring close {}/{}, already closed", error, reason);
|
||||
LOG.debug("Ignoring close {}/{}, already closed {}", error, reason, this);
|
||||
callback.succeeded();
|
||||
return false;
|
||||
}
|
||||
|
@ -763,7 +785,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
private void frame(HTTP2Flusher.Entry entry, boolean flush)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} {}", flush ? "Sending" : "Queueing", entry.frame);
|
||||
LOG.debug("{} {} on {}", flush ? "Sending" : "Queueing", entry.frame, this);
|
||||
// Ping frames are prepended to process them as soon as possible.
|
||||
boolean queued = entry.frame.getType() == FrameType.PING ? flusher.prepend(entry) : flusher.append(entry);
|
||||
if (queued && flush)
|
||||
|
@ -859,7 +881,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
onStreamClosed(stream);
|
||||
flowControl.onStreamDestroyed(stream);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Removed {} {}", stream.isLocal() ? "local" : "remote", stream);
|
||||
LOG.debug("Removed {} {} from {}", stream.isLocal() ? "local" : "remote", stream, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1129,7 +1151,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
return lastRemoteStreamId.get();
|
||||
}
|
||||
|
||||
private void updateLastRemoteStreamId(int streamId)
|
||||
protected void updateLastRemoteStreamId(int streamId)
|
||||
{
|
||||
Atomics.updateMax(lastRemoteStreamId, streamId);
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.eclipse.jetty.http2.frames.HeadersFrame;
|
|||
import org.eclipse.jetty.http2.frames.PushPromiseFrame;
|
||||
import org.eclipse.jetty.http2.frames.ResetFrame;
|
||||
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
|
||||
import org.eclipse.jetty.io.EofException;
|
||||
import org.eclipse.jetty.io.IdleTimeout;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.MathUtils;
|
||||
|
@ -61,7 +62,6 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
|||
private final AtomicReference<Object> attachment = new AtomicReference<>();
|
||||
private final AtomicReference<ConcurrentMap<String, Object>> attributes = new AtomicReference<>();
|
||||
private final AtomicReference<CloseState> closeState = new AtomicReference<>(CloseState.NOT_CLOSED);
|
||||
private final AtomicReference<Callback> writing = new AtomicReference<>();
|
||||
private final AtomicInteger sendWindow = new AtomicInteger();
|
||||
private final AtomicInteger recvWindow = new AtomicInteger();
|
||||
private final long timeStamp = System.nanoTime();
|
||||
|
@ -69,9 +69,11 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
|||
private final int streamId;
|
||||
private final MetaData.Request request;
|
||||
private final boolean local;
|
||||
private Callback sendCallback;
|
||||
private Throwable failure;
|
||||
private boolean localReset;
|
||||
private Listener listener;
|
||||
private boolean remoteReset;
|
||||
private Listener listener;
|
||||
private long dataLength;
|
||||
private long dataDemand;
|
||||
private boolean dataInitial;
|
||||
|
@ -141,17 +143,31 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
|||
@Override
|
||||
public void reset(ResetFrame frame, Callback callback)
|
||||
{
|
||||
if (isReset())
|
||||
return;
|
||||
localReset = true;
|
||||
synchronized (this)
|
||||
{
|
||||
if (isReset())
|
||||
return;
|
||||
localReset = true;
|
||||
failure = new EOFException("reset");
|
||||
}
|
||||
session.frames(this, callback, frame, Frame.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
private boolean startWrite(Callback callback)
|
||||
{
|
||||
if (writing.compareAndSet(null, callback))
|
||||
return true;
|
||||
callback.failed(new WritePendingException());
|
||||
Throwable failure;
|
||||
synchronized (this)
|
||||
{
|
||||
failure = this.failure;
|
||||
if (failure == null && sendCallback == null)
|
||||
{
|
||||
sendCallback = callback;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (failure == null)
|
||||
failure = new WritePendingException();
|
||||
callback.failed(failure);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -176,7 +192,27 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
|||
@Override
|
||||
public boolean isReset()
|
||||
{
|
||||
return localReset || remoteReset;
|
||||
synchronized (this)
|
||||
{
|
||||
return localReset || remoteReset;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFailed()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
return failure != null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResetOrFailed()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
return isReset() || isFailed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -440,7 +476,11 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
|||
|
||||
private void onReset(ResetFrame frame, Callback callback)
|
||||
{
|
||||
remoteReset = true;
|
||||
synchronized (this)
|
||||
{
|
||||
remoteReset = true;
|
||||
failure = new EofException("reset");
|
||||
}
|
||||
close();
|
||||
session.removeStream(this);
|
||||
notifyReset(this, frame, callback);
|
||||
|
@ -461,8 +501,12 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
|||
|
||||
private void onFailure(FailureFrame frame, Callback callback)
|
||||
{
|
||||
// Don't close or remove the stream, as the listener may
|
||||
// want to use it, for example to send a RST_STREAM frame.
|
||||
synchronized (this)
|
||||
{
|
||||
failure = frame.getFailure();
|
||||
}
|
||||
close();
|
||||
session.removeStream(this);
|
||||
notifyFailure(this, frame, callback);
|
||||
}
|
||||
|
||||
|
@ -645,7 +689,12 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
|||
|
||||
private Callback endWrite()
|
||||
{
|
||||
return writing.getAndSet(null);
|
||||
synchronized (this)
|
||||
{
|
||||
Callback callback = sendCallback;
|
||||
sendCallback = null;
|
||||
return callback;
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyNewStream(Stream stream)
|
||||
|
@ -794,10 +843,11 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s@%x#%d{sendWindow=%s,recvWindow=%s,demand=%d,reset=%b/%b,%s,age=%d,attachment=%s}",
|
||||
return String.format("%s@%x#%d@%x{sendWindow=%s,recvWindow=%s,demand=%d,reset=%b/%b,%s,age=%d,attachment=%s}",
|
||||
getClass().getSimpleName(),
|
||||
hashCode(),
|
||||
getId(),
|
||||
session.hashCode(),
|
||||
sendWindow,
|
||||
recvWindow,
|
||||
demand(),
|
||||
|
|
|
@ -114,4 +114,11 @@ public interface IStream extends Stream, Closeable
|
|||
* @see #isClosed()
|
||||
*/
|
||||
boolean isRemotelyClosed();
|
||||
|
||||
/**
|
||||
* @return whether this stream has been reset (locally or remotely) or has been failed
|
||||
* @see #isReset()
|
||||
* @see Listener#onFailure(Stream, int, String, Throwable, Callback)
|
||||
*/
|
||||
boolean isResetOrFailed();
|
||||
}
|
||||
|
|
|
@ -149,7 +149,7 @@ public class Parser
|
|||
return false;
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Parsed {} frame header from {}", headerParser, buffer);
|
||||
LOG.debug("Parsed {} frame header from {}@{}", headerParser, buffer, Integer.toHexString(buffer.hashCode()));
|
||||
|
||||
if (headerParser.getLength() > getMaxFrameLength())
|
||||
return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR, "invalid_frame_length");
|
||||
|
@ -199,7 +199,7 @@ public class Parser
|
|||
return false;
|
||||
}
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Parsed {} frame body from {}", FrameType.from(type), buffer);
|
||||
LOG.debug("Parsed {} frame body from {}@{}", FrameType.from(type), buffer, Integer.toHexString(buffer.hashCode()));
|
||||
reset();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -406,7 +406,9 @@ public class HpackEncoder
|
|||
encodeName(buffer, (byte)0x00, 4, header.asString(), name);
|
||||
encodeValue(buffer, true, field.getValue());
|
||||
if (_debug)
|
||||
encoding = "LitIdxNS" + (1 + NBitInteger.octectsNeeded(4, _context.index(name))) + "HuffV!Idx";
|
||||
encoding = "Lit" +
|
||||
((name == null) ? "HuffN" : "IdxNS" + (1 + NBitInteger.octectsNeeded(4, _context.index(name)))) +
|
||||
"HuffV!Idx";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -175,6 +175,7 @@ public class MaxConcurrentStreamsTest extends AbstractTest
|
|||
@Override
|
||||
public void onSettings(Session session, SettingsFrame frame)
|
||||
{
|
||||
super.onSettings(session, frame);
|
||||
// Send another request to simulate a request being
|
||||
// sent concurrently with connection establishment.
|
||||
// Sending this request will trigger the creation of
|
||||
|
@ -199,7 +200,6 @@ public class MaxConcurrentStreamsTest extends AbstractTest
|
|||
}
|
||||
});
|
||||
}
|
||||
super.onSettings(session, frame);
|
||||
}
|
||||
}, promise, context);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables HTTP2 protocol support on the TLS(SSL) Connector,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables the HTTP2C protocol on the HTTP Connector
|
||||
|
|
|
@ -106,6 +106,7 @@ public class HTTP2ServerSession extends HTTP2Session implements ServerParser.Lis
|
|||
{
|
||||
if (isClosed())
|
||||
{
|
||||
updateLastRemoteStreamId(streamId);
|
||||
reset(new ResetFrame(streamId, ErrorCode.REFUSED_STREAM_ERROR.code), Callback.NOOP);
|
||||
}
|
||||
else
|
||||
|
@ -157,17 +158,6 @@ public class HTTP2ServerSession extends HTTP2Session implements ServerParser.Lis
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResetForUnknownStream(ResetFrame frame)
|
||||
{
|
||||
int streamId = frame.getStreamId();
|
||||
boolean closed = isClientStream(streamId) ? isRemoteStreamClosed(streamId) : isLocalStreamClosed(streamId);
|
||||
if (closed)
|
||||
notifyReset(this, frame);
|
||||
else
|
||||
onConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "unexpected_rst_stream_frame");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushPromise(PushPromiseFrame frame)
|
||||
{
|
||||
|
|
|
@ -330,7 +330,7 @@ public class HttpTransportOverHTTP2 implements HttpTransport
|
|||
|
||||
public void onStreamFailure(Throwable failure)
|
||||
{
|
||||
transportCallback.failed(failure);
|
||||
transportCallback.abort(failure);
|
||||
}
|
||||
|
||||
public boolean onStreamTimeout(Throwable failure)
|
||||
|
@ -408,17 +408,6 @@ public class HttpTransportOverHTTP2 implements HttpTransport
|
|||
* being reset, or the connection being closed</li>
|
||||
* <li>an asynchronous idle timeout</li>
|
||||
* </ul>
|
||||
* <p>The last 2 cases may happen <em>during</em> a send, when the frames
|
||||
* are being generated in the flusher.
|
||||
* In such cases, this class must avoid that the nested callback is notified
|
||||
* while the frame generation is in progress, because the nested callback
|
||||
* may modify other states (such as clearing the {@code HttpOutput._buffer})
|
||||
* that are accessed during frame generation.</p>
|
||||
* <p>The solution implemented in this class works by splitting the send
|
||||
* operation in 3 parts: {@code pre-send}, {@code send} and {@code post-send}.
|
||||
* Asynchronous state changes happening during {@code send} are stored
|
||||
* and only executed in {@code post-send}, therefore never interfering
|
||||
* with frame generation.</p>
|
||||
*
|
||||
* @see State
|
||||
*/
|
||||
|
@ -442,14 +431,14 @@ public class HttpTransportOverHTTP2 implements HttpTransport
|
|||
{
|
||||
Throwable failure = sending(callback, commit);
|
||||
if (failure == null)
|
||||
{
|
||||
sendFrame.accept(this);
|
||||
pending();
|
||||
}
|
||||
else
|
||||
{
|
||||
callback.failed(failure);
|
||||
}
|
||||
}
|
||||
|
||||
private void abort(Throwable failure)
|
||||
{
|
||||
failed(failure);
|
||||
}
|
||||
|
||||
private Throwable sending(Callback callback, boolean commit)
|
||||
|
@ -477,58 +466,6 @@ public class HttpTransportOverHTTP2 implements HttpTransport
|
|||
}
|
||||
}
|
||||
|
||||
private void pending()
|
||||
{
|
||||
Callback callback;
|
||||
boolean commit;
|
||||
Throwable failure;
|
||||
synchronized (this)
|
||||
{
|
||||
switch (_state)
|
||||
{
|
||||
case SENDING:
|
||||
{
|
||||
// The send has not completed the callback yet,
|
||||
// wait for succeeded() or failed() to be called.
|
||||
_state = State.PENDING;
|
||||
return;
|
||||
}
|
||||
case SUCCEEDING:
|
||||
{
|
||||
// The send already completed successfully, but the
|
||||
// call to succeeded() was delayed, so call it now.
|
||||
callback = _callback;
|
||||
commit = _commit;
|
||||
failure = null;
|
||||
reset(null);
|
||||
break;
|
||||
}
|
||||
case FAILING:
|
||||
{
|
||||
// The send already completed with a failure, but
|
||||
// the call to failed() was delayed, so call it now.
|
||||
callback = _callback;
|
||||
commit = _commit;
|
||||
failure = _failure;
|
||||
reset(failure);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
callback = _callback;
|
||||
commit = _commit;
|
||||
failure = new IllegalStateException("Invalid transport state: " + _state);
|
||||
reset(failure);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (failure == null)
|
||||
succeed(callback, commit);
|
||||
else
|
||||
fail(callback, commit, failure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
|
@ -536,30 +473,21 @@ public class HttpTransportOverHTTP2 implements HttpTransport
|
|||
boolean commit;
|
||||
synchronized (this)
|
||||
{
|
||||
switch (_state)
|
||||
if (_state != State.SENDING)
|
||||
{
|
||||
case SENDING:
|
||||
{
|
||||
_state = State.SUCCEEDING;
|
||||
// Succeeding the callback will be done in postSend().
|
||||
return;
|
||||
}
|
||||
case PENDING:
|
||||
{
|
||||
callback = _callback;
|
||||
commit = _commit;
|
||||
reset(null);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// This thread lost the race to succeed the current
|
||||
// send, as other threads likely already failed it.
|
||||
return;
|
||||
}
|
||||
// This thread lost the race to succeed the current
|
||||
// send, as other threads likely already failed it.
|
||||
return;
|
||||
}
|
||||
callback = _callback;
|
||||
commit = _commit;
|
||||
reset(null);
|
||||
}
|
||||
succeed(callback, commit);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("HTTP2 Response #{}/{} {} success",
|
||||
stream.getId(), Integer.toHexString(stream.getSession().hashCode()),
|
||||
commit ? "commit" : "flush");
|
||||
callback.succeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -569,104 +497,37 @@ public class HttpTransportOverHTTP2 implements HttpTransport
|
|||
boolean commit;
|
||||
synchronized (this)
|
||||
{
|
||||
switch (_state)
|
||||
if (_state != State.SENDING)
|
||||
{
|
||||
case SENDING:
|
||||
{
|
||||
_state = State.FAILING;
|
||||
_failure = failure;
|
||||
// Failing the callback will be done in postSend().
|
||||
return;
|
||||
}
|
||||
case IDLE:
|
||||
case PENDING:
|
||||
{
|
||||
callback = _callback;
|
||||
commit = _commit;
|
||||
reset(failure);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// This thread lost the race to fail the current send,
|
||||
// as other threads already succeeded or failed it.
|
||||
return;
|
||||
}
|
||||
reset(failure);
|
||||
return;
|
||||
}
|
||||
callback = _callback;
|
||||
commit = _commit;
|
||||
reset(failure);
|
||||
}
|
||||
fail(callback, commit, failure);
|
||||
}
|
||||
|
||||
private boolean idleTimeout(Throwable failure)
|
||||
{
|
||||
Callback callback;
|
||||
boolean timeout;
|
||||
synchronized (this)
|
||||
{
|
||||
switch (_state)
|
||||
{
|
||||
case PENDING:
|
||||
{
|
||||
// The send was started but idle timed out, fail it.
|
||||
callback = _callback;
|
||||
timeout = true;
|
||||
reset(failure);
|
||||
break;
|
||||
}
|
||||
case IDLE:
|
||||
// The application may be suspended, ignore the idle timeout.
|
||||
case SENDING:
|
||||
// A send has been started at the same time of an idle timeout;
|
||||
// Ignore the idle timeout and let the write continue normally.
|
||||
case SUCCEEDING:
|
||||
case FAILING:
|
||||
// An idle timeout during these transient states is ignored.
|
||||
case FAILED:
|
||||
// Already failed, ignore the idle timeout.
|
||||
{
|
||||
callback = null;
|
||||
timeout = false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// Should not happen, but just in case.
|
||||
callback = _callback;
|
||||
if (callback == null)
|
||||
callback = Callback.NOOP;
|
||||
timeout = true;
|
||||
failure = new IllegalStateException("Invalid transport state: " + _state, failure);
|
||||
reset(failure);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
idleTimeout(callback, timeout, failure);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
private void succeed(Callback callback, boolean commit)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("HTTP2 Response #{}/{} {} success",
|
||||
stream.getId(), Integer.toHexString(stream.getSession().hashCode()),
|
||||
commit ? "commit" : "flush");
|
||||
callback.succeeded();
|
||||
}
|
||||
|
||||
private void fail(Callback callback, boolean commit, Throwable failure)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("HTTP2 Response #{}/{} {} failure",
|
||||
stream.getId(), Integer.toHexString(stream.getSession().hashCode()),
|
||||
commit ? "commit" : "flush",
|
||||
failure);
|
||||
if (callback != null)
|
||||
callback.failed(failure);
|
||||
callback.failed(failure);
|
||||
}
|
||||
|
||||
private void idleTimeout(Callback callback, boolean timeout, Throwable failure)
|
||||
private boolean idleTimeout(Throwable failure)
|
||||
{
|
||||
Callback callback = null;
|
||||
synchronized (this)
|
||||
{
|
||||
// Ignore idle timeouts if not writing,
|
||||
// as the application may be suspended.
|
||||
if (_state == State.SENDING)
|
||||
{
|
||||
callback = _callback;
|
||||
reset(failure);
|
||||
}
|
||||
}
|
||||
boolean timeout = callback != null;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("HTTP2 Response #{}/{} idle timeout {}",
|
||||
stream.getId(), Integer.toHexString(stream.getSession().hashCode()),
|
||||
|
@ -674,6 +535,18 @@ public class HttpTransportOverHTTP2 implements HttpTransport
|
|||
failure);
|
||||
if (timeout)
|
||||
callback.failed(failure);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvocationType getInvocationType()
|
||||
{
|
||||
Callback callback;
|
||||
synchronized (this)
|
||||
{
|
||||
callback = _callback;
|
||||
}
|
||||
return callback != null ? callback.getInvocationType() : Callback.super.getInvocationType();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -686,67 +559,12 @@ public class HttpTransportOverHTTP2 implements HttpTransport
|
|||
{
|
||||
/**
|
||||
* <p>No send initiated or in progress.</p>
|
||||
* <p>Next states could be:</p>
|
||||
* <ul>
|
||||
* <li>{@link #SENDING}, when {@link TransportCallback#send(Callback, boolean, Consumer)}
|
||||
* is called by the transport to initiate a send</li>
|
||||
* <li>{@link #FAILED}, when {@link TransportCallback#failed(Throwable)}
|
||||
* is called by an asynchronous failure</li>
|
||||
* </ul>
|
||||
*/
|
||||
IDLE,
|
||||
/**
|
||||
* <p>A send is initiated; the nested callback in {@link TransportCallback}
|
||||
* cannot be notified while in this state.</p>
|
||||
* <p>Next states could be:</p>
|
||||
* <ul>
|
||||
* <li>{@link #SUCCEEDING}, when {@link TransportCallback#succeeded()}
|
||||
* is called synchronously because the send succeeded</li>
|
||||
* <li>{@link #FAILING}, when {@link TransportCallback#failed(Throwable)}
|
||||
* is called synchronously because the send failed</li>
|
||||
* <li>{@link #PENDING}, when {@link TransportCallback#pending()}
|
||||
* is called before the send completes</li>
|
||||
* </ul>
|
||||
* <p>A send is initiated and possibly in progress.</p>
|
||||
*/
|
||||
SENDING,
|
||||
/**
|
||||
* <p>A send was initiated and is now pending, waiting for the {@link TransportCallback}
|
||||
* to be notified of success or failure.</p>
|
||||
* <p>Next states could be:</p>
|
||||
* <ul>
|
||||
* <li>{@link #IDLE}, when {@link TransportCallback#succeeded()}
|
||||
* is called because the send succeeded</li>
|
||||
* <li>{@link #FAILED}, when {@link TransportCallback#failed(Throwable)}
|
||||
* is called because either the send failed, or an asynchronous failure happened</li>
|
||||
* </ul>
|
||||
*/
|
||||
PENDING,
|
||||
/**
|
||||
* <p>A send was initiated and succeeded, but {@link TransportCallback#pending()}
|
||||
* has not been called yet.</p>
|
||||
* <p>This state indicates that the success actions (such as notifying the
|
||||
* {@link TransportCallback} nested callback) must be performed when
|
||||
* {@link TransportCallback#pending()} is called.</p>
|
||||
* <p>Next states could be:</p>
|
||||
* <ul>
|
||||
* <li>{@link #IDLE}, when {@link TransportCallback#pending()}
|
||||
* is called</li>
|
||||
* </ul>
|
||||
*/
|
||||
SUCCEEDING,
|
||||
/**
|
||||
* <p>A send was initiated and failed, but {@link TransportCallback#pending()}
|
||||
* has not been called yet.</p>
|
||||
* <p>This state indicates that the failure actions (such as notifying the
|
||||
* {@link TransportCallback} nested callback) must be performed when
|
||||
* {@link TransportCallback#pending()} is called.</p>
|
||||
* <p>Next states could be:</p>
|
||||
* <ul>
|
||||
* <li>{@link #FAILED}, when {@link TransportCallback#pending()}
|
||||
* is called</li>
|
||||
* </ul>
|
||||
*/
|
||||
FAILING,
|
||||
/**
|
||||
* <p>The terminal state indicating failure of the send.</p>
|
||||
*/
|
||||
|
|
|
@ -435,7 +435,7 @@ public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint
|
|||
{
|
||||
Connection oldConnection = getConnection();
|
||||
|
||||
ByteBuffer prefilled = (oldConnection instanceof Connection.UpgradeFrom)
|
||||
ByteBuffer buffer = (oldConnection instanceof Connection.UpgradeFrom)
|
||||
? ((Connection.UpgradeFrom)oldConnection).onUpgradeFrom()
|
||||
: null;
|
||||
oldConnection.onClose(null);
|
||||
|
@ -443,12 +443,15 @@ public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint
|
|||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} upgrading from {} to {} with {}",
|
||||
this, oldConnection, newConnection, BufferUtil.toDetailString(prefilled));
|
||||
this, oldConnection, newConnection, BufferUtil.toDetailString(buffer));
|
||||
|
||||
if (newConnection instanceof Connection.UpgradeTo)
|
||||
((Connection.UpgradeTo)newConnection).onUpgradeTo(prefilled);
|
||||
else if (BufferUtil.hasContent(prefilled))
|
||||
throw new IllegalStateException("Cannot upgrade: " + newConnection + " does not implement " + Connection.UpgradeTo.class.getName());
|
||||
if (BufferUtil.hasContent(buffer))
|
||||
{
|
||||
if (newConnection instanceof Connection.UpgradeTo)
|
||||
((Connection.UpgradeTo)newConnection).onUpgradeTo(buffer);
|
||||
else
|
||||
throw new IllegalStateException("Cannot upgrade: " + newConnection + " does not implement " + Connection.UpgradeTo.class.getName());
|
||||
}
|
||||
|
||||
newConnection.onOpen();
|
||||
}
|
||||
|
|
|
@ -23,8 +23,11 @@ import java.util.Arrays;
|
|||
import java.util.Objects;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* <p>A ByteBuffer pool where ByteBuffers are held in queues that are held in array elements.</p>
|
||||
|
@ -35,6 +38,8 @@ import org.eclipse.jetty.util.annotation.ManagedObject;
|
|||
@ManagedObject
|
||||
public class ArrayByteBufferPool extends AbstractByteBufferPool
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MappedByteBufferPool.class);
|
||||
|
||||
private final int _minCapacity;
|
||||
private final ByteBufferPool.Bucket[] _direct;
|
||||
private final ByteBufferPool.Bucket[] _indirect;
|
||||
|
@ -119,8 +124,18 @@ public class ArrayByteBufferPool extends AbstractByteBufferPool
|
|||
{
|
||||
if (buffer == null)
|
||||
return;
|
||||
|
||||
int capacity = buffer.capacity();
|
||||
// Validate that this buffer is from this pool.
|
||||
if ((capacity % getCapacityFactor()) != 0)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("ByteBuffer {} does not belong to this pool, discarding it", BufferUtil.toDetailString(buffer));
|
||||
return;
|
||||
}
|
||||
|
||||
boolean direct = buffer.isDirect();
|
||||
ByteBufferPool.Bucket bucket = bucketFor(buffer.capacity(), direct, this::newBucket);
|
||||
ByteBufferPool.Bucket bucket = bucketFor(capacity, direct, this::newBucket);
|
||||
if (bucket != null)
|
||||
{
|
||||
bucket.release(buffer);
|
||||
|
|
|
@ -57,6 +57,18 @@ public interface ByteBufferPool
|
|||
*/
|
||||
public void release(ByteBuffer buffer);
|
||||
|
||||
/**
|
||||
* <p>Removes a {@link ByteBuffer} that was previously obtained with {@link #acquire(int, boolean)}.</p>
|
||||
* <p>The buffer will not be available for further reuse.</p>
|
||||
*
|
||||
* @param buffer the buffer to remove
|
||||
* @see #acquire(int, boolean)
|
||||
* @see #release(ByteBuffer)
|
||||
*/
|
||||
default void remove(ByteBuffer buffer)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a new ByteBuffer of the given capacity and the given directness.</p>
|
||||
*
|
||||
|
|
|
@ -99,31 +99,49 @@ public interface Connection extends Closeable
|
|||
|
||||
public long getCreatedTimeStamp();
|
||||
|
||||
/**
|
||||
* <p>{@link Connection} implementations implement this interface when they
|
||||
* can upgrade from the protocol they speak (for example HTTP/1.1)
|
||||
* to a different protocol (e.g. HTTP/2).</p>
|
||||
*
|
||||
* @see EndPoint#upgrade(Connection)
|
||||
* @see UpgradeTo
|
||||
*/
|
||||
public interface UpgradeFrom
|
||||
{
|
||||
/**
|
||||
* <p>Takes the input buffer from the connection on upgrade.</p>
|
||||
* <p>This method is used to take any unconsumed input from
|
||||
* a connection during an upgrade.</p>
|
||||
* <p>Invoked during an {@link EndPoint#upgrade(Connection) upgrade}
|
||||
* to produce a buffer containing bytes that have not been consumed by
|
||||
* this connection, and that must be consumed by the upgrade-to
|
||||
* connection.</p>
|
||||
*
|
||||
* @return A buffer of unconsumed input. The caller must return the buffer
|
||||
* to the bufferpool when consumed and this connection must not.
|
||||
* @return a buffer of unconsumed bytes to pass to the upgrade-to connection.
|
||||
* The buffer does not belong to any pool and should be discarded after
|
||||
* having consumed its bytes.
|
||||
* The returned buffer may be null if there are no unconsumed bytes.
|
||||
*/
|
||||
ByteBuffer onUpgradeFrom();
|
||||
public ByteBuffer onUpgradeFrom();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>{@link Connection} implementations implement this interface when they
|
||||
* can be upgraded to the protocol they speak (e.g. HTTP/2)
|
||||
* from a different protocol (e.g. HTTP/1.1).</p>
|
||||
*/
|
||||
public interface UpgradeTo
|
||||
{
|
||||
/**
|
||||
* <p>Callback method invoked when this connection is upgraded.</p>
|
||||
* <p>This must be called before {@link #onOpen()}.</p>
|
||||
* <p>Invoked during an {@link EndPoint#upgrade(Connection) upgrade}
|
||||
* to receive a buffer containing bytes that have not been consumed by
|
||||
* the upgrade-from connection, and that must be consumed by this
|
||||
* connection.</p>
|
||||
*
|
||||
* @param prefilled An optional buffer that can contain prefilled data. Typically this
|
||||
* results from an upgrade of one protocol to the other where the old connection has buffered
|
||||
* data destined for the new connection. The new connection must take ownership of the buffer
|
||||
* and is responsible for returning it to the buffer pool
|
||||
* @param buffer a non-null buffer of unconsumed bytes received from
|
||||
* the upgrade-from connection.
|
||||
* The buffer does not belong to any pool and should be discarded after
|
||||
* having consumed its bytes.
|
||||
*/
|
||||
void onUpgradeTo(ByteBuffer prefilled);
|
||||
public void onUpgradeTo(ByteBuffer buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,7 +31,7 @@ import org.eclipse.jetty.util.IteratingCallback;
|
|||
import org.eclipse.jetty.util.thread.Invocable;
|
||||
|
||||
/**
|
||||
* A transport EndPoint
|
||||
* <p>EndPoint is the abstraction for an I/O channel that transports bytes.</p>
|
||||
*
|
||||
* <h3>Asynchronous Methods</h3>
|
||||
* <p>The asynchronous scheduling methods of {@link EndPoint}
|
||||
|
@ -40,86 +40,90 @@ import org.eclipse.jetty.util.thread.Invocable;
|
|||
* some inefficiencies.</p>
|
||||
* <p>This class will frequently be used in conjunction with some of the utility
|
||||
* implementations of {@link Callback}, such as {@link FutureCallback} and
|
||||
* {@link IteratingCallback}. Examples are:</p>
|
||||
* {@link IteratingCallback}.</p>
|
||||
*
|
||||
* <h3>Blocking Read</h3>
|
||||
* <p>A FutureCallback can be used to block until an endpoint is ready to be filled
|
||||
* from:</p>
|
||||
* <blockquote><pre>
|
||||
* FutureCallback<String> future = new FutureCallback<>();
|
||||
* endpoint.fillInterested("ContextObj",future);
|
||||
* ...
|
||||
* String context = future.get(); // This blocks
|
||||
* int filled=endpoint.fill(mybuffer);
|
||||
* </pre></blockquote>
|
||||
* <h3>Reads</h3>
|
||||
* <p>A {@link FutureCallback} can be used to block until an endpoint is ready
|
||||
* to fill bytes - the notification will be emitted by the NIO subsystem:</p>
|
||||
* <pre>
|
||||
* FutureCallback callback = new FutureCallback();
|
||||
* endPoint.fillInterested(callback);
|
||||
*
|
||||
* <h3>Dispatched Read</h3>
|
||||
* <p>By using a different callback, the read can be done asynchronously in its own dispatched thread:</p>
|
||||
* <blockquote><pre>
|
||||
* endpoint.fillInterested("ContextObj",new ExecutorCallback<String>(executor)
|
||||
* // Blocks until read to fill bytes.
|
||||
* callback.get();
|
||||
*
|
||||
* // Now bytes can be filled in a ByteBuffer.
|
||||
* int filled = endPoint.fill(byteBuffer);
|
||||
* </pre>
|
||||
*
|
||||
* <h3>Asynchronous Reads</h3>
|
||||
* <p>A {@link Callback} can be used to read asynchronously in its own dispatched
|
||||
* thread:</p>
|
||||
* <pre>
|
||||
* endPoint.fillInterested(new Callback()
|
||||
* {
|
||||
* public void onCompleted(String context)
|
||||
* public void onSucceeded()
|
||||
* {
|
||||
* int filled=endpoint.fill(mybuffer);
|
||||
* ...
|
||||
* executor.execute(() ->
|
||||
* {
|
||||
* // Fill bytes in a different thread.
|
||||
* int filled = endPoint.fill(byteBuffer);
|
||||
* });
|
||||
* }
|
||||
* public void onFailed(Throwable failure)
|
||||
* {
|
||||
* endPoint.close();
|
||||
* }
|
||||
* public void onFailed(String context,Throwable cause) {...}
|
||||
* });
|
||||
* </pre></blockquote>
|
||||
* <p>The executor callback can also be customized to not dispatch in some circumstances when
|
||||
* it knows it can use the callback thread and does not need to dispatch.</p>
|
||||
* </pre>
|
||||
*
|
||||
* <h3>Blocking Write</h3>
|
||||
* <p>The write contract is that the callback complete is not called until all data has been
|
||||
* written or there is a failure. For blocking this looks like:</p>
|
||||
* <blockquote><pre>
|
||||
* FutureCallback<String> future = new FutureCallback<>();
|
||||
* endpoint.write("ContextObj",future,headerBuffer,contentBuffer);
|
||||
* String context = future.get(); // This blocks
|
||||
* </pre></blockquote>
|
||||
* <h3>Blocking Writes</h3>
|
||||
* <p>The write contract is that the callback is completed when all the bytes
|
||||
* have been written or there is a failure.
|
||||
* Blocking writes look like this:</p>
|
||||
* <pre>
|
||||
* FutureCallback callback = new FutureCallback();
|
||||
* endpoint.write(callback, headerBuffer, contentBuffer);
|
||||
*
|
||||
* <h3>Dispatched Write</h3>
|
||||
* <p>Note also that multiple buffers may be passed in write so that gather writes
|
||||
* can be done:</p>
|
||||
* <blockquote><pre>
|
||||
* endpoint.write("ContextObj",new ExecutorCallback<String>(executor)
|
||||
* {
|
||||
* public void onCompleted(String context)
|
||||
* {
|
||||
* int filled=endpoint.fill(mybuffer);
|
||||
* ...
|
||||
* }
|
||||
* public void onFailed(String context,Throwable cause) {...}
|
||||
* },headerBuffer,contentBuffer);
|
||||
* </pre></blockquote>
|
||||
* // Blocks until the write succeeds or fails.
|
||||
* future.get();
|
||||
* </pre>
|
||||
* <p>Note also that multiple buffers may be passed in {@link #write(Callback, ByteBuffer...)}
|
||||
* so that gather writes can be performed for efficiency.</p>
|
||||
*/
|
||||
public interface EndPoint extends Closeable
|
||||
{
|
||||
/**
|
||||
* Marks an <code>EndPoint</code> that wraps another <code>EndPoint</code>.
|
||||
* Marks an {@code EndPoint} that wraps another {@code EndPoint}.
|
||||
*/
|
||||
public interface Wrapper
|
||||
{
|
||||
/**
|
||||
* @return The wrapped <code>EndPoint</code>
|
||||
* @return The wrapped {@code EndPoint}
|
||||
*/
|
||||
EndPoint unwrap();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return The local Inet address to which this <code>EndPoint</code> is bound, or <code>null</code>
|
||||
* if this <code>EndPoint</code> does not represent a network connection.
|
||||
* @return The local Inet address to which this {@code EndPoint} is bound, or {@code null}
|
||||
* if this {@code EndPoint} does not represent a network connection.
|
||||
*/
|
||||
InetSocketAddress getLocalAddress();
|
||||
|
||||
/**
|
||||
* @return The remote Inet address to which this <code>EndPoint</code> is bound, or <code>null</code>
|
||||
* if this <code>EndPoint</code> does not represent a network connection.
|
||||
* @return The remote Inet address to which this {@code EndPoint} is bound, or {@code null}
|
||||
* if this {@code EndPoint} does not represent a network connection.
|
||||
*/
|
||||
InetSocketAddress getRemoteAddress();
|
||||
|
||||
/**
|
||||
* @return whether this EndPoint is open
|
||||
*/
|
||||
boolean isOpen();
|
||||
|
||||
/**
|
||||
* @return the epoch time in milliseconds when this EndPoint was created
|
||||
*/
|
||||
long getCreatedTimeStamp();
|
||||
|
||||
/**
|
||||
|
@ -177,7 +181,7 @@ public interface EndPoint extends Closeable
|
|||
*
|
||||
* @param buffer The buffer to fill. The position and limit are modified during the fill. After the
|
||||
* operation, the position is unchanged and the limit is increased to reflect the new data filled.
|
||||
* @return an <code>int</code> value indicating the number of bytes
|
||||
* @return an {@code int} value indicating the number of bytes
|
||||
* filled or -1 if EOF is read or the input is shutdown.
|
||||
* @throws IOException if the endpoint is closed.
|
||||
*/
|
||||
|
@ -252,27 +256,27 @@ public interface EndPoint extends Closeable
|
|||
void write(Callback callback, ByteBuffer... buffers) throws WritePendingException;
|
||||
|
||||
/**
|
||||
* @return the {@link Connection} associated with this {@link EndPoint}
|
||||
* @return the {@link Connection} associated with this EndPoint
|
||||
* @see #setConnection(Connection)
|
||||
*/
|
||||
Connection getConnection();
|
||||
|
||||
/**
|
||||
* @param connection the {@link Connection} associated with this {@link EndPoint}
|
||||
* @param connection the {@link Connection} associated with this EndPoint
|
||||
* @see #getConnection()
|
||||
* @see #upgrade(Connection)
|
||||
*/
|
||||
void setConnection(Connection connection);
|
||||
|
||||
/**
|
||||
* <p>Callback method invoked when this {@link EndPoint} is opened.</p>
|
||||
* <p>Callback method invoked when this EndPoint is opened.</p>
|
||||
*
|
||||
* @see #onClose(Throwable)
|
||||
*/
|
||||
void onOpen();
|
||||
|
||||
/**
|
||||
* <p>Callback method invoked when this {@link EndPoint} is close.</p>
|
||||
* <p>Callback method invoked when this {@link EndPoint} is closed.</p>
|
||||
*
|
||||
* @param cause The reason for the close, or null if a normal close.
|
||||
* @see #onOpen()
|
||||
|
@ -280,13 +284,18 @@ public interface EndPoint extends Closeable
|
|||
void onClose(Throwable cause);
|
||||
|
||||
/**
|
||||
* Upgrade connections.
|
||||
* Close the old connection, update the endpoint and open the new connection.
|
||||
* If the oldConnection is an instance of {@link Connection.UpgradeFrom} then
|
||||
* a prefilled buffer is requested and passed to the newConnection if it is an instance
|
||||
* of {@link Connection.UpgradeTo}
|
||||
* <p>Upgrades this EndPoint from the current connection to the given new connection.</p>
|
||||
* <p>Closes the current connection, links this EndPoint to the new connection and
|
||||
* then opens the new connection.</p>
|
||||
* <p>If the current connection is an instance of {@link Connection.UpgradeFrom} then
|
||||
* a buffer of unconsumed bytes is requested.
|
||||
* If the buffer of unconsumed bytes is non-null and non-empty, then the new
|
||||
* connection is tested: if it is an instance of {@link Connection.UpgradeTo}, then
|
||||
* the unconsumed buffer is passed to the new connection; otherwise, an exception
|
||||
* is thrown since there are unconsumed bytes that cannot be consumed by the new
|
||||
* connection.</p>
|
||||
*
|
||||
* @param newConnection The connection to upgrade to
|
||||
* @param newConnection the connection to upgrade to
|
||||
*/
|
||||
public void upgrade(Connection newConnection);
|
||||
}
|
||||
|
|
|
@ -92,6 +92,8 @@ public abstract class FillInterest
|
|||
|
||||
/**
|
||||
* Call to signal that a read is now possible.
|
||||
*
|
||||
* @return whether the callback was notified that a read is now possible
|
||||
*/
|
||||
public boolean fillable()
|
||||
{
|
||||
|
|
|
@ -23,10 +23,13 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.LeakDetector;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ManagedObject
|
||||
public class LeakTrackingByteBufferPool extends ContainerLifeCycle implements ByteBufferPool
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(LeakTrackingByteBufferPool.class);
|
||||
|
@ -47,11 +50,11 @@ public class LeakTrackingByteBufferPool extends ContainerLifeCycle implements By
|
|||
}
|
||||
};
|
||||
|
||||
private static final boolean NOISY = Boolean.getBoolean(LeakTrackingByteBufferPool.class.getName() + ".NOISY");
|
||||
private final ByteBufferPool delegate;
|
||||
private final AtomicLong leakedReleases = new AtomicLong(0);
|
||||
private final AtomicLong leakedAcquires = new AtomicLong(0);
|
||||
private final AtomicLong leakedReleases = new AtomicLong(0);
|
||||
private final AtomicLong leakedRemoves = new AtomicLong(0);
|
||||
private final AtomicLong leaked = new AtomicLong(0);
|
||||
private final ByteBufferPool delegate;
|
||||
|
||||
public LeakTrackingByteBufferPool(ByteBufferPool delegate)
|
||||
{
|
||||
|
@ -64,12 +67,12 @@ public class LeakTrackingByteBufferPool extends ContainerLifeCycle implements By
|
|||
public ByteBuffer acquire(int size, boolean direct)
|
||||
{
|
||||
ByteBuffer buffer = delegate.acquire(size, direct);
|
||||
boolean leaked = leakDetector.acquired(buffer);
|
||||
if (NOISY || !leaked)
|
||||
boolean acquired = leakDetector.acquired(buffer);
|
||||
if (!acquired)
|
||||
{
|
||||
leakedAcquires.incrementAndGet();
|
||||
LOG.info(String.format("ByteBuffer acquire %s leaked.acquired=%s", leakDetector.id(buffer), leaked ? "normal" : "LEAK"),
|
||||
new Throwable("LeakStack.Acquire"));
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("ByteBuffer leaked acquire for id {}", leakDetector.id(buffer), new Throwable("acquire"));
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
@ -79,16 +82,36 @@ public class LeakTrackingByteBufferPool extends ContainerLifeCycle implements By
|
|||
{
|
||||
if (buffer == null)
|
||||
return;
|
||||
boolean leaked = leakDetector.released(buffer);
|
||||
if (NOISY || !leaked)
|
||||
boolean released = leakDetector.released(buffer);
|
||||
if (!released)
|
||||
{
|
||||
leakedReleases.incrementAndGet();
|
||||
LOG.info(String.format("ByteBuffer release %s leaked.released=%s", leakDetector.id(buffer), leaked ? "normal" : "LEAK"), new Throwable(
|
||||
"LeakStack.Release"));
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("ByteBuffer leaked release for id {}", leakDetector.id(buffer), new Throwable("release"));
|
||||
}
|
||||
delegate.release(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(ByteBuffer buffer)
|
||||
{
|
||||
if (buffer == null)
|
||||
return;
|
||||
boolean released = leakDetector.released(buffer);
|
||||
if (!released)
|
||||
{
|
||||
leakedRemoves.incrementAndGet();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("ByteBuffer leaked remove for id {}", leakDetector.id(buffer), new Throwable("remove"));
|
||||
}
|
||||
delegate.remove(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the tracking data returned by {@link #getLeakedAcquires()},
|
||||
* {@link #getLeakedReleases()}, {@link #getLeakedResources()}.
|
||||
*/
|
||||
@ManagedAttribute("Clears the tracking data")
|
||||
public void clearTracking()
|
||||
{
|
||||
leakedAcquires.set(0);
|
||||
|
@ -96,24 +119,36 @@ public class LeakTrackingByteBufferPool extends ContainerLifeCycle implements By
|
|||
}
|
||||
|
||||
/**
|
||||
* @return count of BufferPool.acquire() calls that detected a leak
|
||||
* @return count of ByteBufferPool.acquire() calls that detected a leak
|
||||
*/
|
||||
@ManagedAttribute("The number of acquires that produced a leak")
|
||||
public long getLeakedAcquires()
|
||||
{
|
||||
return leakedAcquires.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return count of BufferPool.release() calls that detected a leak
|
||||
* @return count of ByteBufferPool.release() calls that detected a leak
|
||||
*/
|
||||
@ManagedAttribute("The number of releases that produced a leak")
|
||||
public long getLeakedReleases()
|
||||
{
|
||||
return leakedReleases.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return count of ByteBufferPool.remove() calls that detected a leak
|
||||
*/
|
||||
@ManagedAttribute("The number of removes that produced a leak")
|
||||
public long getLeakedRemoves()
|
||||
{
|
||||
return leakedRemoves.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return count of resources that were acquired but not released
|
||||
*/
|
||||
@ManagedAttribute("The number of resources that were leaked")
|
||||
public long getLeakedResources()
|
||||
{
|
||||
return leaked.get();
|
||||
|
|
|
@ -28,6 +28,8 @@ import java.util.function.Function;
|
|||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* <p>A ByteBuffer pool where ByteBuffers are held in queues that are held in a Map.</p>
|
||||
|
@ -38,6 +40,8 @@ import org.eclipse.jetty.util.annotation.ManagedObject;
|
|||
@ManagedObject
|
||||
public class MappedByteBufferPool extends AbstractByteBufferPool
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MappedByteBufferPool.class);
|
||||
|
||||
private final ConcurrentMap<Integer, Bucket> _directBuffers = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<Integer, Bucket> _heapBuffers = new ConcurrentHashMap<>();
|
||||
private final Function<Integer, Bucket> _newBucket;
|
||||
|
@ -127,7 +131,12 @@ public class MappedByteBufferPool extends AbstractByteBufferPool
|
|||
|
||||
int capacity = buffer.capacity();
|
||||
// Validate that this buffer is from this pool.
|
||||
assert ((capacity % getCapacityFactor()) == 0);
|
||||
if ((capacity % getCapacityFactor()) != 0)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("ByteBuffer {} does not belong to this pool, discarding it", BufferUtil.toDetailString(buffer));
|
||||
return;
|
||||
}
|
||||
|
||||
int b = bucketFor(capacity);
|
||||
boolean direct = buffer.isDirect();
|
||||
|
|
|
@ -326,11 +326,8 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
|
|||
@Override
|
||||
public void onUpgradeTo(ByteBuffer buffer)
|
||||
{
|
||||
if (BufferUtil.hasContent(buffer))
|
||||
{
|
||||
acquireEncryptedInput();
|
||||
BufferUtil.append(_encryptedInput, buffer);
|
||||
}
|
||||
acquireEncryptedInput();
|
||||
BufferUtil.append(_encryptedInput, buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.Arrays;
|
|||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jetty.io.ByteBufferPool.Bucket;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
@ -150,6 +151,17 @@ public class ArrayByteBufferPoolTest
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReleaseNonPooledBuffer()
|
||||
{
|
||||
ArrayByteBufferPool bufferPool = new ArrayByteBufferPool();
|
||||
|
||||
// Release a few small non-pool buffers
|
||||
bufferPool.release(ByteBuffer.wrap(StringUtil.getUtf8Bytes("Hello")));
|
||||
|
||||
assertEquals(0, bufferPool.getHeapByteBufferCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxQueue()
|
||||
{
|
||||
|
|
|
@ -35,7 +35,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class MappedByteBufferPoolTest
|
||||
{
|
||||
|
@ -95,34 +94,15 @@ public class MappedByteBufferPoolTest
|
|||
assertTrue(buckets.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* In a scenario where MappedByteBufferPool is being used improperly,
|
||||
* such as releasing a buffer that wasn't created/acquired by the
|
||||
* MappedByteBufferPool, an assertion is tested for.
|
||||
*/
|
||||
@Test
|
||||
public void testReleaseAssertion()
|
||||
public void testReleaseNonPooledBuffer()
|
||||
{
|
||||
int factor = 1024;
|
||||
MappedByteBufferPool bufferPool = new MappedByteBufferPool(factor);
|
||||
MappedByteBufferPool bufferPool = new MappedByteBufferPool();
|
||||
|
||||
try
|
||||
{
|
||||
// Release a few small non-pool buffers
|
||||
bufferPool.release(ByteBuffer.wrap(StringUtil.getUtf8Bytes("Hello")));
|
||||
// Release a few small non-pool buffers
|
||||
bufferPool.release(ByteBuffer.wrap(StringUtil.getUtf8Bytes("Hello")));
|
||||
|
||||
/* NOTES:
|
||||
*
|
||||
* 1) This test will pass on command line maven build, as its surefire setup uses "-ea" already.
|
||||
* 2) In Eclipse, goto the "Run Configuration" for this test case.
|
||||
* Select the "Arguments" tab, and make sure "-ea" is present in the text box titled "VM arguments"
|
||||
*/
|
||||
fail("Expected java.lang.AssertionError, do you have '-ea' JVM command line option enabled?");
|
||||
}
|
||||
catch (java.lang.AssertionError e)
|
||||
{
|
||||
// Expected path.
|
||||
}
|
||||
assertEquals(0, bufferPool.getHeapByteBufferCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enable JAAS for deployed webapplications.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enable JASPI authentication for deployed webapplications.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables remote RMI access to JMX
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables JMX instrumentation for server beans and
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Adds the Jetty JNDI implementation to the classpath.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables test setup
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables test setup
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables an unassembled maven webapp to run in a jetty distro
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Memcache cache for SessionData
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables NoSql session management with a MongoDB driver.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Server/port address connections for session storage
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
MongoURI connections for session storage
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Adds OpenId Connect authentication.
|
||||
|
|
|
@ -56,77 +56,73 @@
|
|||
<Bundle-Classpath />
|
||||
<Fragment-Host>org.eclipse.jetty.osgi.boot</Fragment-Host>
|
||||
<Export-Package>!org.eclipse.jetty.osgi.boot.*</Export-Package>
|
||||
<Import-Package>
|
||||
org.eclipse.jdt.*;resolution:=optional,
|
||||
org.eclipse.jdt.core.compiler.*;resolution:=optional,
|
||||
com.sun.el;resolution:=optional,
|
||||
com.sun.el.lang;resolution:=optional,
|
||||
com.sun.el.parser;resolution:=optional,
|
||||
com.sun.el.util;resolution:=optional,
|
||||
javax.el;version="[3.0,3.1)",
|
||||
javax.servlet;version="[3.1,4.1)",
|
||||
javax.servlet.resources;version="[3.1,4.1)",
|
||||
javax.servlet.jsp.resources;version="[2.3,4.1)",
|
||||
javax.servlet.jsp;version="[2.3,2.4.1)",
|
||||
javax.servlet.jsp.el;version="[2.3,2.4.1)",
|
||||
javax.servlet.jsp.tagext;version="[2.3,2.4.1)",
|
||||
javax.servlet.jsp.jstl.core;version="1.2";resolution:=optional,
|
||||
javax.servlet.jsp.jstl.fmt;version="1.2";resolution:=optional,
|
||||
javax.servlet.jsp.jstl.sql;version="1.2";resolution:=optional,
|
||||
javax.servlet.jsp.jstl.tlv;version="1.2";resolution:=optional,
|
||||
org.apache.el;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.el.lang;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.el.stream;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.el.util;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.el.parser;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.jasper;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.jasper.compiler;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.jasper.compiler.tagplugin;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.jasper.runtime;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.jasper.security;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.jasper.servlet;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.jasper.tagplugins.jstl;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.jasper.util;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.jasper.xmlparser;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.taglibs.standard;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.extra.spath;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.functions;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.jstl;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.jstl.parser;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.jstl.test;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.jstl.test.beans;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.support;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.resources;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.common.core;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.common.fmt;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.common.sql;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.common.xml;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.el.core;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.el.fmt;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.el.sql;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.el.xml;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.rt.core;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.rt.fmt;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.rt.sql;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.rt.xml;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tei;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tlv;version="1.2";resolution:=optional,
|
||||
org.apache.tomcat;version="[8.0.23,10)";resolution:=optional,
|
||||
org.eclipse.jetty.jsp;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))";resolution:=optional,
|
||||
org.slf4j.*,
|
||||
org.osgi.*,
|
||||
org.xml.*;resolution:=optional,
|
||||
org.xml.sax.*;resolution:=optional,
|
||||
javax.xml.*;resolution:=optional,
|
||||
org.w3c.dom;resolution:=optional,
|
||||
org.w3c.dom.ls;resolution:=optional,
|
||||
javax.xml.parser;resolution:=optional
|
||||
<Import-Package>org.eclipse.jdt.*;resolution:=optional,
|
||||
org.eclipse.jdt.core.compiler.*;resolution:=optional,
|
||||
com.sun.el;resolution:=optional,
|
||||
com.sun.el.lang;resolution:=optional,
|
||||
com.sun.el.parser;resolution:=optional,
|
||||
com.sun.el.util;resolution:=optional,
|
||||
javax.el;version="[3.0,3.1)",
|
||||
javax.servlet;version="[$(version;==;${servlet.api.version}),$(version;+;${servlet.api.version}))",
|
||||
javax.servlet.resources;version="[$(version;==;${servlet.api.version}),$(version;+;${servlet.api.version}))",
|
||||
javax.servlet.jsp.resources;version="[2.3,4.1)",
|
||||
javax.servlet.jsp;version="[2.3,2.4.1)",
|
||||
javax.servlet.jsp.el;version="[2.3,2.4.1)",
|
||||
javax.servlet.jsp.tagext;version="[2.3,2.4.1)",
|
||||
javax.servlet.jsp.jstl.core;version="1.2";resolution:=optional,
|
||||
javax.servlet.jsp.jstl.fmt;version="1.2";resolution:=optional,
|
||||
javax.servlet.jsp.jstl.sql;version="1.2";resolution:=optional,
|
||||
javax.servlet.jsp.jstl.tlv;version="1.2";resolution:=optional,
|
||||
org.apache.el;version="[$(version;===;${jsp.version}),$(version;+;${jsp.version}))";resolution:=optional,
|
||||
org.apache.el.lang;version="[$(version;===;${jsp.version}),$(version;+;${jsp.version}))";resolution:=optional,
|
||||
org.apache.el.stream;version="[$(version;===;${jsp.version}),$(version;+;${jsp.version}))";resolution:=optional,
|
||||
org.apache.el.util;version="[$(version;===;${jsp.version}),$(version;+;${jsp.version}))";resolution:=optional,
|
||||
org.apache.el.parser;version="[$(version;===;${jsp.version}),$(version;+;${jsp.version}))";resolution:=optional,
|
||||
org.apache.jasper;version="[$(version;===;${jsp.version}),$(version;+;${jsp.version}))";resolution:=optional,
|
||||
org.apache.jasper.compiler;version="[$(version;===;${jsp.version}),$(version;+;${jsp.version}))";resolution:=optional,
|
||||
org.apache.jasper.compiler.tagplugin;version="[$(version;===;${jsp.version}),$(version;+;${jsp.version}))";resolution:=optional,
|
||||
org.apache.jasper.runtime;version="[$(version;===;${jsp.version}),$(version;+;${jsp.version}))";resolution:=optional,
|
||||
org.apache.jasper.security;version="[$(version;===;${jsp.version}),$(version;+;${jsp.version}))";resolution:=optional,
|
||||
org.apache.jasper.servlet;version="[$(version;===;${jsp.version}),$(version;+;${jsp.version}))";resolution:=optional,
|
||||
org.apache.jasper.tagplugins.jstl;version="[$(version;===;${jsp.version}),$(version;+;${jsp.version}))";resolution:=optional,
|
||||
org.apache.jasper.util;version="[$(version;===;${jsp.version}),$(version;+;${jsp.version}))";resolution:=optional,
|
||||
org.apache.jasper.xmlparser;version="[$(version;===;${jsp.version}),$(version;+;${jsp.version}))";resolution:=optional,
|
||||
org.apache.taglibs.standard;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.extra.spath;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.functions;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.jstl;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.jstl.parser;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.jstl.test;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.jstl.test.beans;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.support;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.resources;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.common.core;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.common.fmt;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.common.sql;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.common.xml;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.el.core;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.el.fmt;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.el.sql;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.el.xml;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.rt.core;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.rt.fmt;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.rt.sql;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.rt.xml;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tei;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tlv;version="1.2";resolution:=optional,
|
||||
org.apache.tomcat;version="[$(version;===;${jsp.version}),$(version;+;${jsp.version}))";resolution:=optional,
|
||||
org.eclipse.jetty.jsp;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))";resolution:=optional,
|
||||
org.osgi.*,
|
||||
org.xml.*;resolution:=optional,
|
||||
org.xml.sax.*;resolution:=optional,
|
||||
javax.xml.*;resolution:=optional,
|
||||
org.w3c.dom;resolution:=optional,
|
||||
org.w3c.dom.ls;resolution:=optional,
|
||||
javax.xml.parser;resolution:=optional
|
||||
</Import-Package>
|
||||
<DynamicImport-Package>
|
||||
org.eclipse.jetty.jsp.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))",
|
||||
org.apache.jasper.*;version="8.0.23",
|
||||
org.apache.el.*;version="8.0.23"
|
||||
</DynamicImport-Package>
|
||||
<DynamicImport-Package>org.eclipse.jetty.jsp.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))",
|
||||
org.apache.jasper.*;version="[$(version;===;${jsp.version}),$(version;+;${jsp.version}))",
|
||||
org.apache.el.*;version="[$(version;===;${jsp.version}),$(version;+;${jsp.version}))"</DynamicImport-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
|
|
@ -70,8 +70,30 @@
|
|||
<instructions>
|
||||
<Bundle-SymbolicName>org.eclipse.jetty.osgi.boot;singleton:=true</Bundle-SymbolicName>
|
||||
<Bundle-Activator>org.eclipse.jetty.osgi.boot.JettyBootstrapActivator</Bundle-Activator>
|
||||
<DynamicImport-Package>org.eclipse.jetty.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))"</DynamicImport-Package>
|
||||
<Import-Package>javax.mail;version="1.4.0";resolution:=optional, javax.mail.event;version="1.4.0";resolution:=optional, javax.mail.internet;version="1.4.0";resolution:=optional, javax.mail.search;version="1.4.0";resolution:=optional, javax.mail.util;version="1.4.0";resolution:=optional, javax.servlet;version="[3.1,4.1)", javax.servlet.http;version="[3.1,4.1)", javax.transaction;version="1.1.0";resolution:=optional, javax.transaction.xa;version="1.1.0";resolution:=optional, org.objectweb.asm;version="5";resolution:=optional, org.osgi.framework, org.osgi.service.cm;version="1.2.0", org.osgi.service.packageadmin, org.osgi.service.startlevel;version="1.0.0", org.osgi.service.url;version="1.0.0", org.osgi.util.tracker;version="1.3.0", org.xml.sax, org.xml.sax.helpers, org.eclipse.jetty.annotations;resolution:=optional, *
|
||||
<DynamicImport-Package>org.eclipse.jetty.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;+;${parsedVersion.osgiVersion}))"</DynamicImport-Package>
|
||||
<Import-Package>javax.mail;version="1.4.0";resolution:=optional,
|
||||
javax.mail.event;version="1.4.0";resolution:=optional,
|
||||
javax.mail.internet;version="1.4.0";resolution:=optional,
|
||||
javax.mail.search;version="1.4.0";resolution:=optional,
|
||||
javax.mail.util;version="1.4.0";resolution:=optional,
|
||||
javax.servlet;version="[$(version;==;${servlet.api.version}),$(version;+;${servlet.api.version}))",
|
||||
javax.servlet.http;version="[$(version;==;${servlet.api.version}),$(version;+;${servlet.api.version}))",
|
||||
javax.transaction;version="1.1.0";resolution:=optional,
|
||||
javax.transaction.xa;version="1.1.0";resolution:=optional,
|
||||
org.objectweb.asm;version="$(version;=;${asm.version})";resolution:=optional,
|
||||
org.osgi.framework,
|
||||
org.osgi.service.cm;version="1.2.0",
|
||||
org.osgi.service.packageadmin,
|
||||
org.osgi.service.startlevel;version="1.0.0",
|
||||
org.osgi.service.url;version="1.0.0",
|
||||
org.osgi.util.tracker;version="1.3.0",
|
||||
org.slf4j;resolution:=optional,
|
||||
org.slf4j.spi;resolution:=optional,
|
||||
org.slf4j.helpers;resolution:=optional,
|
||||
org.xml.sax,
|
||||
org.xml.sax.helpers,
|
||||
org.eclipse.jetty.annotations;resolution:=optional,
|
||||
*
|
||||
</Import-Package>
|
||||
<Require-Capability>
|
||||
osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)"
|
||||
|
|
|
@ -50,7 +50,7 @@ public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationPa
|
|||
|
||||
public AnnotationParser(int javaPlatform)
|
||||
{
|
||||
super(javaPlatform, Opcodes.ASM7);
|
||||
super(javaPlatform);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,7 +60,7 @@ public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationPa
|
|||
* @return the resource for the bundle
|
||||
* @throws Exception if unable to create the resource reference
|
||||
*/
|
||||
protected Resource indexBundle(Bundle bundle) throws Exception
|
||||
public Resource indexBundle(Bundle bundle) throws Exception
|
||||
{
|
||||
File bundleFile = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle);
|
||||
Resource resource = Resource.newResource(bundleFile.toURI());
|
||||
|
@ -114,7 +114,7 @@ public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationPa
|
|||
}
|
||||
}
|
||||
|
||||
protected void parse(Set<? extends Handler> handlers, Bundle bundle)
|
||||
public void parse(Set<? extends Handler> handlers, Bundle bundle)
|
||||
throws Exception
|
||||
{
|
||||
URI uri = _bundleToUri.get(bundle);
|
||||
|
@ -211,10 +211,17 @@ public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationPa
|
|||
//or the bundle classpath wasn't simply ".", so skip it
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isValidClassFileName(name))
|
||||
{
|
||||
continue; //eg skip module-info.class
|
||||
}
|
||||
|
||||
//transform into a classname to pass to the resolver
|
||||
String shortName = StringUtil.replace(name, '/', '.').substring(0, name.length() - 6);
|
||||
|
||||
addParsedClass(shortName, getResource(bundle));
|
||||
|
||||
try (InputStream classInputStream = classUrl.openStream())
|
||||
{
|
||||
scanClass(handlers, getResource(bundle), classInputStream);
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
<Import-Package>
|
||||
javax.servlet;version="[3.1,4.1)", javax.servlet.resources;version="[3.1,4.1)", org.osgi.framework, org.osgi.service.cm;version="1.2.0", org.osgi.service.packageadmin, org.osgi.service.startlevel;version="1.0.0", org.osgi.service.url;version="1.0.0", org.osgi.util.tracker;version="1.3.0", org.slf4j;resolution:=optional, org.slf4j.spi;resolution:=optional, org.slf4j.helpers;resolution:=optional, org.xml.sax, org.xml.sax.helpers, *
|
||||
</Import-Package>
|
||||
<DynamicImport-Package>org.eclipse.jetty.*;version="[9.1,10.0)"</DynamicImport-Package>
|
||||
<DynamicImport-Package>org.eclipse.jetty.*;version="[$(version;==;${parsedVersion.osgiVersion}),$(version;+;${parsedVersion.osgiVersion}))"</DynamicImport-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
<Import-Package>
|
||||
javax.servlet;version="[3.1,4.1)", javax.servlet.resources;version="[3.1,4.1)", org.osgi.framework, org.osgi.service.cm;version="1.2.0", org.osgi.service.packageadmin, org.osgi.service.startlevel;version="1.0.o", org.osgi.service.url;version="1.0.0", org.osgi.util.tracker;version="1.3.0", org.slf4j;resolution:=optional, org.slf4j.spi;resolution:=optional, org.slf4j.helpers;resolution:=optional, org.xml.sax, org.xml.sax.helpers, *
|
||||
</Import-Package>
|
||||
<DynamicImport-Package>org.eclipse.jetty.*;version="[9.1,10.0)"</DynamicImport-Package>
|
||||
<DynamicImport-Package>org.eclipse.jetty.*;version="[$(version;==;${parsedVersion.osgiVersion}),$(version;+;${parsedVersion.osgiVersion}))"</DynamicImport-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
|
|
@ -73,7 +73,6 @@
|
|||
<groupId>org.ops4j.pax.tinybundles</groupId>
|
||||
<artifactId>tinybundles</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>biz.aQute.bnd</groupId>
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.osgi.test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import aQute.bnd.osgi.Constants;
|
||||
import org.eclipse.jetty.annotations.ClassInheritanceHandler;
|
||||
import org.eclipse.jetty.osgi.annotations.AnnotationParser;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.ops4j.pax.exam.Configuration;
|
||||
import org.ops4j.pax.exam.CoreOptions;
|
||||
import org.ops4j.pax.exam.Option;
|
||||
import org.ops4j.pax.exam.junit.PaxExam;
|
||||
import org.ops4j.pax.tinybundles.core.TinyBundle;
|
||||
import org.ops4j.pax.tinybundles.core.TinyBundles;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleContext;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
|
||||
/**
|
||||
* TestJettyOSGiAnnotationParser
|
||||
*
|
||||
*/
|
||||
|
||||
@RunWith(PaxExam.class)
|
||||
public class TestJettyOSGiAnnotationParser
|
||||
{
|
||||
@Inject
|
||||
BundleContext bundleContext = null;
|
||||
|
||||
@Configuration
|
||||
public static Option[] configure() throws IOException
|
||||
{
|
||||
ArrayList<Option> options = new ArrayList<>();
|
||||
options.add(TestOSGiUtil.optionalRemoteDebug());
|
||||
options.add(CoreOptions.junitBundles());
|
||||
options.addAll(TestOSGiUtil.coreJettyDependencies());
|
||||
|
||||
//get a reference to a pre-prepared module-info
|
||||
Path path = Paths.get("src", "test", "resources", "module-info.clazz");
|
||||
File moduleInfo = path.toFile();
|
||||
assertTrue(moduleInfo.exists());
|
||||
|
||||
TinyBundle bundle = TinyBundles.bundle();
|
||||
bundle.set(Constants.BUNDLE_SYMBOLICNAME, "bundle.with.module.info");
|
||||
bundle.add("module-info.class", new FileInputStream(moduleInfo)); //copy it into the fake bundle
|
||||
options.add(CoreOptions.streamBundle(bundle.build()).startLevel(1));
|
||||
return options.toArray(new Option[options.size()]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParse() throws Exception
|
||||
{
|
||||
|
||||
//test the osgi annotation parser ignore the module-info.class file in the fake bundle
|
||||
//Get a reference to the deployed fake bundle
|
||||
Bundle b = TestOSGiUtil.getBundle(bundleContext, "bundle.with.module.info");
|
||||
AnnotationParser parser = new AnnotationParser(0);
|
||||
parser.indexBundle(b);
|
||||
ClassInheritanceHandler handler = new ClassInheritanceHandler(new ConcurrentHashMap<>());
|
||||
parser.parse(Collections.singleton(handler), b);
|
||||
|
||||
}
|
||||
}
|
|
@ -20,7 +20,6 @@ package org.eclipse.jetty.osgi.test;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
|
@ -54,7 +53,7 @@ public class TestJettyOSGiBootWithJsp
|
|||
public static Option[] configure()
|
||||
{
|
||||
ArrayList<Option> options = new ArrayList<>();
|
||||
|
||||
|
||||
options.addAll(TestOSGiUtil.configurePaxExamLogging());
|
||||
|
||||
options.add(CoreOptions.junitBundles());
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
module com.bubble
|
||||
{
|
||||
}
|
|
@ -19,7 +19,6 @@
|
|||
package org.eclipse.jetty.plus.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.servlet.http.HttpServlet;
|
||||
|
||||
import org.eclipse.jetty.plus.webapp.PlusDecorator;
|
||||
|
@ -41,12 +40,12 @@ public class LifeCycleCallbackCollectionTest
|
|||
{
|
||||
public static int postConstructCount = 0;
|
||||
public static int preDestroyCount = 0;
|
||||
|
||||
|
||||
public void postconstruct()
|
||||
{
|
||||
++postConstructCount;
|
||||
}
|
||||
|
||||
|
||||
public void predestroy()
|
||||
{
|
||||
++preDestroyCount;
|
||||
|
@ -55,7 +54,6 @@ public class LifeCycleCallbackCollectionTest
|
|||
|
||||
/**
|
||||
* An unsupported lifecycle callback type
|
||||
*
|
||||
*/
|
||||
public class TestLifeCycleCallback extends LifeCycleCallback
|
||||
{
|
||||
|
@ -79,7 +77,6 @@ public class LifeCycleCallbackCollectionTest
|
|||
/**
|
||||
* A class that we can use to simulate having PostConstruct and
|
||||
* PreDestroy annotations on.
|
||||
*
|
||||
*/
|
||||
public class SomeTestClass
|
||||
{
|
||||
|
@ -177,7 +174,7 @@ public class LifeCycleCallbackCollectionTest
|
|||
//expected
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testServletPostConstructPreDestroy() throws Exception
|
||||
{
|
||||
|
@ -186,7 +183,7 @@ public class LifeCycleCallbackCollectionTest
|
|||
context.setResourceBase(MavenTestingUtils.getTargetTestingDir("predestroy-test").toURI().toURL().toString());
|
||||
context.setContextPath("/");
|
||||
server.setHandler(context);
|
||||
|
||||
|
||||
//add a non-async servlet
|
||||
ServletHolder notAsync = new ServletHolder();
|
||||
notAsync.setHeldClass(TestServlet.class);
|
||||
|
@ -194,7 +191,7 @@ public class LifeCycleCallbackCollectionTest
|
|||
notAsync.setAsyncSupported(false);
|
||||
notAsync.setInitOrder(1);
|
||||
context.getServletHandler().addServletWithMapping(notAsync, "/notasync/*");
|
||||
|
||||
|
||||
//add an async servlet
|
||||
ServletHolder async = new ServletHolder();
|
||||
async.setHeldClass(TestServlet.class);
|
||||
|
@ -202,7 +199,7 @@ public class LifeCycleCallbackCollectionTest
|
|||
async.setAsyncSupported(true);
|
||||
async.setInitOrder(1);
|
||||
context.getServletHandler().addServletWithMapping(async, "/async/*");
|
||||
|
||||
|
||||
//add a run-as servlet
|
||||
ServletHolder runas = new ServletHolder();
|
||||
runas.setHeldClass(TestServlet.class);
|
||||
|
@ -210,7 +207,7 @@ public class LifeCycleCallbackCollectionTest
|
|||
runas.setRunAsRole("admin");
|
||||
runas.setInitOrder(1);
|
||||
context.getServletHandler().addServletWithMapping(runas, "/runas/*");
|
||||
|
||||
|
||||
//add both run-as and non async servlet
|
||||
ServletHolder both = new ServletHolder();
|
||||
both.setHeldClass(TestServlet.class);
|
||||
|
@ -219,7 +216,7 @@ public class LifeCycleCallbackCollectionTest
|
|||
both.setAsyncSupported(false);
|
||||
both.setInitOrder(1);
|
||||
context.getServletHandler().addServletWithMapping(both, "/both/*");
|
||||
|
||||
|
||||
//Make fake lifecycle callbacks for all servlets
|
||||
LifeCycleCallbackCollection collection = new LifeCycleCallbackCollection();
|
||||
context.setAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION, collection);
|
||||
|
@ -227,28 +224,28 @@ public class LifeCycleCallbackCollectionTest
|
|||
collection.add(pcNotAsync);
|
||||
PreDestroyCallback pdNotAsync = new PreDestroyCallback(TestServlet.class, "predestroy");
|
||||
collection.add(pdNotAsync);
|
||||
|
||||
|
||||
PostConstructCallback pcAsync = new PostConstructCallback(TestServlet.class, "postconstruct");
|
||||
collection.add(pcAsync);
|
||||
PreDestroyCallback pdAsync = new PreDestroyCallback(TestServlet.class, "predestroy");
|
||||
collection.add(pdAsync);
|
||||
|
||||
|
||||
PostConstructCallback pcRunAs = new PostConstructCallback(TestServlet.class, "postconstruct");
|
||||
collection.add(pcRunAs);
|
||||
PreDestroyCallback pdRunAs = new PreDestroyCallback(TestServlet.class, "predestroy");
|
||||
collection.add(pdRunAs);
|
||||
|
||||
|
||||
PostConstructCallback pcBoth = new PostConstructCallback(TestServlet.class, "postconstruct");
|
||||
collection.add(pcBoth);
|
||||
PreDestroyCallback pdBoth = new PreDestroyCallback(TestServlet.class, "predestroy");
|
||||
collection.add(pdBoth);
|
||||
|
||||
|
||||
server.start();
|
||||
|
||||
|
||||
assertEquals(4, TestServlet.postConstructCount);
|
||||
|
||||
|
||||
server.stop();
|
||||
|
||||
|
||||
assertEquals(4, TestServlet.preDestroyCount);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enable the Jetty Proxy, that allows the server to act
|
||||
|
|
|
@ -606,7 +606,7 @@ public class ConnectHandler extends HandlerWrapper
|
|||
|
||||
public class DownstreamConnection extends ProxyConnection implements Connection.UpgradeTo
|
||||
{
|
||||
private ByteBuffer buffer = BufferUtil.EMPTY_BUFFER;
|
||||
private ByteBuffer buffer;
|
||||
|
||||
public DownstreamConnection(EndPoint endPoint, Executor executor, ByteBufferPool bufferPool, ConcurrentMap<String, Object> context)
|
||||
{
|
||||
|
@ -616,20 +616,27 @@ public class ConnectHandler extends HandlerWrapper
|
|||
@Override
|
||||
public void onUpgradeTo(ByteBuffer buffer)
|
||||
{
|
||||
if (buffer != null)
|
||||
this.buffer = buffer;
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen()
|
||||
{
|
||||
super.onOpen();
|
||||
final int remaining = buffer.remaining();
|
||||
|
||||
if (buffer == null)
|
||||
{
|
||||
fillInterested();
|
||||
return;
|
||||
}
|
||||
|
||||
int remaining = buffer.remaining();
|
||||
write(getConnection().getEndPoint(), buffer, new Callback()
|
||||
{
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
buffer = null;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} wrote initial {} bytes to server", DownstreamConnection.this, remaining);
|
||||
fillInterested();
|
||||
|
@ -638,6 +645,7 @@ public class ConnectHandler extends HandlerWrapper
|
|||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
buffer = null;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug(this + " failed to write initial " + remaining + " bytes to server", x);
|
||||
close();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables the Jetty Quickstart module for rapid
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Add a rule to the rewrite module to compact paths so that double slashes
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Adds servlet standard security handling to the classpath.
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
|
||||
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
<Call name="addBean">
|
||||
<Arg>
|
||||
<New id="keyStoreScanner" class="org.eclipse.jetty.util.ssl.KeyStoreScanner">
|
||||
<Arg><Ref refid="sslContextFactory"/></Arg>
|
||||
<Set name="scanInterval"><Property name="jetty.sslContext.reload.scanInterval" default="1"/></Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
</Configure>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue