Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-10.0.x-4919-WebSocketContainerStop

This commit is contained in:
Lachlan Roberts 2020-07-17 15:32:10 +10:00
commit c94e82470d
234 changed files with 4344 additions and 2299 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"/>

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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)

View File

@ -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
{
}

View File

@ -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.

View File

@ -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;

View File

@ -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

View File

@ -372,7 +372,7 @@ public class HttpRequest implements Request
}
@Override
public HttpFields.Mutable getHeaders()
public HttpFields getHeaders()
{
return headers;
}

View File

@ -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();

View File

@ -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))

View File

@ -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);

View File

@ -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));
}
}

View File

@ -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);

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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;
* &lt;filter-class&gt;org.eclipse.jetty.fcgi.server.proxy.TryFilesFilter&lt;/filter-class&gt;
* &lt;init-param&gt;
* &lt;param-name&gt;files&lt;/param-name&gt;
* &lt;param-value&gt;maintenance.html $path index.php?p=$path&lt;/param-value&gt;
* &lt;param-value&gt;/maintenance.html $path /index.php?p=$path&lt;/param-value&gt;
* &lt;/init-param&gt;
* &lt;/filter&gt;
* </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>

View File

@ -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));
}

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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));

View File

@ -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)
{

View File

@ -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();

View File

@ -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)

View File

@ -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)
{

View File

@ -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()

View File

@ -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)

View File

@ -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);
}

View File

@ -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(),

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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
{

View File

@ -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);
}

View File

@ -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,

View File

@ -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

View File

@ -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)
{

View File

@ -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>
*/

View File

@ -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();
}

View File

@ -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);

View File

@ -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>
*

View File

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

View File

@ -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&lt;String&gt; future = new FutureCallback&lt;&gt;();
* 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&lt;String&gt;(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(() -&gt;
* {
* // 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&lt;String&gt; future = new FutureCallback&lt;&gt;();
* 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&lt;String&gt;(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);
}

View File

@ -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()
{

View File

@ -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();

View File

@ -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();

View File

@ -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

View File

@ -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()
{

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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>

View File

@ -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)"

View File

@ -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);

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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());

View File

@ -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
{
}

View File

@ -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);
}

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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