Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-10.0.x-1743-refactor-maven-plugin-redux

Signed-off-by: Jan Bartel <janb@webtide.com>
This commit is contained in:
Jan Bartel 2019-09-24 14:21:36 +10:00
commit 08790a45a4
64 changed files with 542 additions and 344 deletions

11
Jenkinsfile vendored
View File

@ -51,6 +51,17 @@ pipeline {
}
}
stage("Build / Test - JDK13") {
agent { node { label 'linux' } }
steps {
timeout(time: 120, unit: 'MINUTES') {
mavenBuild("jdk13", "-Pmongodb install", "maven3", true)
warnings consoleParsers: [[parserName: 'Maven'], [parserName: 'Java']]
junit testResults: '**/target/surefire-reports/*.xml,**/target/invoker-reports/TEST*.xml'
}
}
}
stage("Build Javadoc") {
agent { node { label 'linux' } }
steps {

View File

@ -152,17 +152,7 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements IConne
@Override
public void onFillable()
{
HttpExchange exchange = channel.getHttpExchange();
if (exchange != null)
{
channel.receive();
}
else
{
// If there is no exchange, then could be either a remote close,
// or garbage bytes; in both cases we close the connection
close();
}
channel.receive();
}
@Override

View File

@ -50,12 +50,6 @@ public class HttpTransportOverFCGI implements HttpTransport
this.request = request;
}
@Override
public boolean isOptimizedForDirectBuffers()
{
return false;
}
@Override
public void send(MetaData.Request request, MetaData.Response response, ByteBuffer content, boolean lastContent, Callback callback)
{

View File

@ -56,6 +56,8 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
private final ISession session;
private final int bufferSize;
private final ExecutionStrategy strategy;
private boolean useInputDirectByteBuffers;
private boolean useOutputDirectByteBuffers;
public HTTP2Connection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize)
{
@ -99,6 +101,26 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
producer.setInputBuffer(buffer);
}
public boolean isUseInputDirectByteBuffers()
{
return useInputDirectByteBuffers;
}
public void setUseInputDirectByteBuffers(boolean useInputDirectByteBuffers)
{
this.useInputDirectByteBuffers = useInputDirectByteBuffers;
}
public boolean isUseOutputDirectByteBuffers()
{
return useOutputDirectByteBuffers;
}
public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers)
{
this.useOutputDirectByteBuffers = useOutputDirectByteBuffers;
}
@Override
public void onOpen()
{
@ -389,7 +411,7 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
{
private NetworkBuffer()
{
super(byteBufferPool, bufferSize, false);
super(byteBufferPool, bufferSize, isUseInputDirectByteBuffers());
}
private void put(ByteBuffer source)

View File

@ -493,12 +493,6 @@ public abstract class HTTP2StreamEndPoint implements EndPoint
LOG.debug("onClose {}", this);
}
@Override
public boolean isOptimizedForDirectBuffers()
{
return true;
}
@Override
public void upgrade(Connection newConnection)
{

View File

@ -44,4 +44,9 @@ public abstract class FrameGenerator
{
return headerGenerator.getMaxFrameSize();
}
public boolean isUseDirectByteBuffers()
{
return headerGenerator.isUseDirectByteBuffers();
}
}

View File

@ -38,10 +38,15 @@ public class Generator
}
public Generator(ByteBufferPool byteBufferPool, int maxDynamicTableSize, int maxHeaderBlockFragment)
{
this(byteBufferPool, true, maxDynamicTableSize, maxHeaderBlockFragment);
}
public Generator(ByteBufferPool byteBufferPool, boolean useDirectByteBuffers, int maxDynamicTableSize, int maxHeaderBlockFragment)
{
this.byteBufferPool = byteBufferPool;
headerGenerator = new HeaderGenerator();
headerGenerator = new HeaderGenerator(useDirectByteBuffers);
hpackEncoder = new HpackEncoder(maxDynamicTableSize);
this.generators = new FrameGenerator[FrameType.values().length];

View File

@ -27,10 +27,26 @@ import org.eclipse.jetty.io.ByteBufferPool;
public class HeaderGenerator
{
private int maxFrameSize = Frame.DEFAULT_MAX_LENGTH;
private final boolean useDirectByteBuffers;
public HeaderGenerator()
{
this(true);
}
public HeaderGenerator(boolean useDirectByteBuffers)
{
this.useDirectByteBuffers = useDirectByteBuffers;
}
public boolean isUseDirectByteBuffers()
{
return useDirectByteBuffers;
}
public ByteBuffer generate(ByteBufferPool.Lease lease, FrameType frameType, int capacity, int length, int flags, int streamId)
{
ByteBuffer header = lease.acquire(capacity, true);
ByteBuffer header = lease.acquire(capacity, isUseDirectByteBuffers());
header.put((byte)((length & 0x00_FF_00_00) >>> 16));
header.put((byte)((length & 0x00_00_FF_00) >>> 8));
header.put((byte)((length & 0x00_00_00_FF)));

View File

@ -67,7 +67,7 @@ public class HeadersGenerator extends FrameGenerator
flags = Flags.PRIORITY;
int maxFrameSize = getMaxFrameSize();
ByteBuffer hpacked = lease.acquire(maxFrameSize, false);
ByteBuffer hpacked = lease.acquire(maxFrameSize, isUseDirectByteBuffers());
BufferUtil.clearToFill(hpacked);
encoder.encode(hpacked, metaData);
int hpackedLength = hpacked.position();

View File

@ -58,7 +58,7 @@ public class PushPromiseGenerator extends FrameGenerator
int extraSpace = 4;
maxFrameSize -= extraSpace;
ByteBuffer hpacked = lease.acquire(maxFrameSize, false);
ByteBuffer hpacked = lease.acquire(maxFrameSize, isUseDirectByteBuffers());
BufferUtil.clearToFill(hpacked);
encoder.encode(hpacked, metaData);
int hpackedLength = hpacked.position();

View File

@ -25,7 +25,7 @@ import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpTokens;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.hpack.HpackContext.Entry;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -77,13 +77,8 @@ public class HpackDecoder
while (buffer.hasRemaining())
{
if (LOG.isDebugEnabled() && buffer.hasArray())
{
int l = Math.min(buffer.remaining(), 32);
LOG.debug("decode {}{}",
TypeUtil.toHexString(buffer.array(), buffer.arrayOffset() + buffer.position(), l),
l < buffer.remaining() ? "..." : "");
}
if (LOG.isDebugEnabled())
LOG.debug("decode {}", BufferUtil.toHexString(buffer));
byte b = buffer.get();
if (b < 0)
@ -280,14 +275,9 @@ public class HpackDecoder
public static String toASCIIString(ByteBuffer buffer, int length)
{
StringBuilder builder = new StringBuilder(length);
int position = buffer.position();
int start = buffer.arrayOffset() + position;
int end = start + length;
buffer.position(position + length);
byte[] array = buffer.array();
for (int i = start; i < end; i++)
for (int i = 0; i < length; ++i)
{
builder.append((char)(0x7f & array[i]));
builder.append((char)(0x7F & buffer.get()));
}
return builder.toString();
}

View File

@ -35,6 +35,7 @@ import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.http2.hpack.HpackContext.Entry;
import org.eclipse.jetty.http2.hpack.HpackContext.StaticEntry;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
@ -363,9 +364,8 @@ public class HpackEncoder
if (_debug)
{
int e = buffer.position();
if (LOG.isDebugEnabled())
LOG.debug("encode {}:'{}' to '{}'", encoding, field, TypeUtil.toHexString(buffer.array(), buffer.arrayOffset() + p, e - p));
LOG.debug("encode {}:'{}' to '{}'", encoding, field, BufferUtil.toHexString(buffer));
}
}

View File

@ -64,6 +64,8 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
private RateControl rateControl = new WindowRateControl(20, Duration.ofSeconds(1));
private FlowControlStrategy.Factory flowControlStrategyFactory = () -> new BufferingFlowControlStrategy(0.5F);
private long streamIdleTimeout;
private boolean _useInputDirectByteBuffers;
private boolean _useOutputDirectByteBuffers;
public AbstractHTTP2ServerConnectionFactory(@Name("config") HttpConfiguration httpConfiguration)
{
@ -82,6 +84,8 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
this.httpConfiguration = Objects.requireNonNull(httpConfiguration);
addBean(httpConfiguration);
setInputBufferSize(Frame.DEFAULT_MAX_LENGTH + Frame.HEADER_LENGTH);
setUseInputDirectByteBuffers(httpConfiguration.isUseInputDirectByteBuffers());
setUseOutputDirectByteBuffers(httpConfiguration.isUseOutputDirectByteBuffers());
}
@ManagedAttribute("The HPACK dynamic table maximum size")
@ -192,6 +196,26 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
this.rateControl = rateControl;
}
public boolean isUseInputDirectByteBuffers()
{
return _useInputDirectByteBuffers;
}
public void setUseInputDirectByteBuffers(boolean useInputDirectByteBuffers)
{
_useInputDirectByteBuffers = useInputDirectByteBuffers;
}
public boolean isUseOutputDirectByteBuffers()
{
return _useOutputDirectByteBuffers;
}
public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers)
{
_useOutputDirectByteBuffers = useOutputDirectByteBuffers;
}
public HttpConfiguration getHttpConfiguration()
{
return httpConfiguration;
@ -214,7 +238,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
{
ServerSessionListener listener = newSessionListener(connector, endPoint);
Generator generator = new Generator(connector.getByteBufferPool(), getMaxDynamicTableSize(), getMaxHeaderBlockFragment());
Generator generator = new Generator(connector.getByteBufferPool(), isUseOutputDirectByteBuffers(), getMaxDynamicTableSize(), getMaxHeaderBlockFragment());
FlowControlStrategy flowControl = getFlowControlStrategyFactory().newFlowControlStrategy();
HTTP2ServerSession session = new HTTP2ServerSession(connector.getScheduler(), endPoint, generator, listener, flowControl);
session.setMaxLocalStreams(getMaxConcurrentStreams());
@ -235,6 +259,8 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
HTTP2Connection connection = new HTTP2ServerConnection(connector.getByteBufferPool(), connector.getExecutor(),
endPoint, httpConfiguration, parser, session, getInputBufferSize(), listener);
connection.setUseInputDirectByteBuffers(isUseInputDirectByteBuffers());
connection.setUseOutputDirectByteBuffers(isUseOutputDirectByteBuffers());
connection.addListener(sessionContainer);
return configure(connection, connector, endPoint);
}

View File

@ -289,6 +289,7 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection
HttpTransportOverHTTP2 transport = new HttpTransportOverHTTP2(connector, this);
transport.setStream(stream);
channel = newServerHttpChannelOverHTTP2(connector, httpConfig, transport);
channel.setUseOutputDirectByteBuffers(isUseOutputDirectByteBuffers());
if (LOG.isDebugEnabled())
LOG.debug("Creating channel {} for {}", channel, this);
}

View File

@ -57,6 +57,7 @@ public class HttpChannelOverHTTP2 extends HttpChannel implements Closeable, Writ
private boolean _expect100Continue;
private boolean _delayedUntilContent;
private boolean _useOutputDirectByteBuffers;
public HttpChannelOverHTTP2(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransportOverHTTP2 transport)
{
@ -68,6 +69,17 @@ public class HttpChannelOverHTTP2 extends HttpChannel implements Closeable, Writ
return getHttpTransport().getStream();
}
@Override
public boolean isUseOutputDirectByteBuffers()
{
return _useOutputDirectByteBuffers;
}
public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers)
{
_useOutputDirectByteBuffers = useOutputDirectByteBuffers;
}
@Override
public boolean isExpecting100Continue()
{

View File

@ -62,14 +62,6 @@ public class HttpTransportOverHTTP2 implements HttpTransport
this.connection = connection;
}
@Override
public boolean isOptimizedForDirectBuffers()
{
// Because sent buffers are passed directly to the endpoint without
// copying we can defer to the endpoint
return connection.getEndPoint().isOptimizedForDirectBuffers();
}
public IStream getStream()
{
return stream;

View File

@ -327,12 +327,6 @@ public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint
_connection = connection;
}
@Override
public boolean isOptimizedForDirectBuffers()
{
return false;
}
protected void reset()
{
_state.set(State.OPEN);

View File

@ -175,12 +175,6 @@ public abstract class ChannelEndPoint extends AbstractEndPoint implements Manage
_gather = (channel instanceof GatheringByteChannel) ? (GatheringByteChannel)channel : null;
}
@Override
public boolean isOptimizedForDirectBuffers()
{
return true;
}
@Override
public boolean isOpen()
{

View File

@ -269,13 +269,6 @@ public interface EndPoint extends Closeable
*/
void onClose(Throwable cause);
/**
* Is the endpoint optimized for DirectBuffer usage
*
* @return True if direct buffers can be used optimally.
*/
boolean isOptimizedForDirectBuffers();
/**
* Upgrade connections.
* Close the old connection, update the endpoint and open the new connection.

View File

@ -95,7 +95,6 @@ public class SslClientConnectionFactory implements ClientConnectionFactory
context.put(SSL_ENGINE_CONTEXT_KEY, engine);
SslConnection sslConnection = newSslConnection(byteBufferPool, executor, endPoint, engine);
endPoint.setConnection(sslConnection);
EndPoint appEndPoint = sslConnection.getDecryptedEndPoint();
appEndPoint.setConnection(connectionFactory.newConnection(appEndPoint, context));

View File

@ -42,10 +42,10 @@ import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceCollection;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.Configurations;
import org.eclipse.jetty.webapp.MetaInfConfiguration;
import org.eclipse.jetty.webapp.WebAppContext;
@ -303,22 +303,27 @@ public class JettyWebAppContext extends WebAppContext
}
@Override
protected void loadConfigurations()
protected Configurations newConfigurations()
{
super.loadConfigurations();
try
Configurations configurations = super.newConfigurations();
if (getJettyEnvXml() != null)
{
// inject configurations with config from maven plugin
for (Configuration c : getWebAppConfigurations())
try
{
if (c instanceof EnvConfiguration && getJettyEnvXml() != null)
((EnvConfiguration)c).setJettyEnvResource(new PathResource(new File(getJettyEnvXml())));
// inject configurations with config from maven plugin
for (Configuration c : configurations)
{
if (c instanceof EnvConfiguration)
((EnvConfiguration)c).setJettyEnvResource(Resource.newResource(getJettyEnvXml()));
}
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
catch (Exception e)
{
throw new RuntimeException(e);
}
return configurations;
}
@Override

View File

@ -85,7 +85,7 @@ public class QuickStartConfiguration extends AbstractConfiguration
public QuickStartConfiguration()
{
super(false);
super(true);
addDependencies(WebInfConfiguration.class);
addDependents(WebXmlConfiguration.class);
}
@ -208,7 +208,7 @@ public class QuickStartConfiguration extends AbstractConfiguration
{
LOG.info("Quickstarting {}", context);
_quickStart = true;
context.setConfigurations(context.getWebAppConfigurations().stream()
context.setConfigurations(context.getConfigurations().stream()
.filter(c -> !__replacedConfigurations.contains(c.replaces()) && !__replacedConfigurations.contains(c.getClass()))
.collect(Collectors.toList()).toArray(new Configuration[]{}));
context.getMetaData().setWebXml((Resource)context.getAttribute(QUICKSTART_WEB_XML));

View File

@ -93,7 +93,7 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration
public QuickStartGeneratorConfiguration(boolean abort)
{
super(true);
super(false);
_count = 0;
_abort = abort;
}

View File

@ -35,7 +35,11 @@
<!-- =========================================================== -->
<Call name="addBean">
<Arg>
<New class="org.eclipse.jetty.util.thread.ScheduledExecutorScheduler"/>
<New class="org.eclipse.jetty.util.thread.ScheduledExecutorScheduler">
<Arg name="name"><Property name="jetty.scheduler.name"/></Arg>
<Arg name="daemon" type="boolean"><Property name="jetty.scheduler.daemon" default="false" /></Arg>
<Arg name="threads" type="int"><Property name="jetty.scheduler.threads" default="-1" /></Arg>
</New>
</Arg>
</Call>

View File

@ -0,0 +1,4 @@
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
[jpms]
add-modules: java.sql

View File

@ -80,3 +80,8 @@ etc/jetty.xml
## Dump the state of the Jetty server, components, and webapps before shutdown
# jetty.server.dumpBeforeStop=false
## Scheduler Configuration
# jetty.scheduler.name=
# jetty.scheduler.deamon=false
# jetty.scheduler.threads=-1

View File

@ -10,6 +10,7 @@ session
session-store
[depend]
jdbc
sessions
sessions/jdbc/${db-connection-type}
@ -54,7 +55,3 @@ db-connection-type=datasource
#jetty.session.jdbc.schema.maxIntervalColumn=maxInterval
#jetty.session.jdbc.schema.mapColumn=map
#jetty.session.jdbc.schema.table=JettySessions

View File

@ -3,5 +3,8 @@ DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-m
[description]
JDBC Datasource connections for session storage
[depends]
jdbc
[xml]
etc/sessions/jdbc/datasource.xml

View File

@ -3,5 +3,8 @@ DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-m
[description]
JDBC Driver connections for session storage
[depend]
jdbc
[xml]
etc/sessions/jdbc/driver.xml

View File

@ -44,7 +44,6 @@ import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ChannelEndPoint;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.QuietException;
@ -251,12 +250,6 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
return _configuration;
}
@Override
public boolean isOptimizedForDirectBuffers()
{
return getHttpTransport().isOptimizedForDirectBuffers();
}
public Server getServer()
{
return _connector.getServer();
@ -971,12 +964,9 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
return _connector.getScheduler();
}
/**
* @return true if the HttpChannel can efficiently use direct buffer (typically this means it is not over SSL or a multiplexed protocol)
*/
public boolean useDirectBuffers()
public boolean isUseOutputDirectByteBuffers()
{
return getEndPoint() instanceof ChannelEndPoint;
return getHttpConfiguration().isUseOutputDirectByteBuffers();
}
/**

View File

@ -69,6 +69,12 @@ public class HttpChannelOverHttp extends HttpChannel implements HttpParser.Reque
_metadata.setURI(new HttpURI());
}
@Override
public boolean isUseOutputDirectByteBuffers()
{
return _httpConnection.isUseOutputDirectByteBuffers();
}
@Override
protected HttpInput newHttpInput(HttpChannelState state)
{

View File

@ -67,7 +67,8 @@ public class HttpConfiguration implements Dumpable
private boolean _delayDispatchUntilContent = true;
private boolean _persistentConnectionsEnabled = true;
private int _maxErrorDispatches = 10;
private boolean _useDirectByteBuffers = false;
private boolean _useInputDirectByteBuffers = true;
private boolean _useOutputDirectByteBuffers = true;
private long _minRequestDataRate;
private long _minResponseDataRate;
private HttpCompliance _httpCompliance = HttpCompliance.RFC7230;
@ -134,7 +135,8 @@ public class HttpConfiguration implements Dumpable
_delayDispatchUntilContent = config._delayDispatchUntilContent;
_persistentConnectionsEnabled = config._persistentConnectionsEnabled;
_maxErrorDispatches = config._maxErrorDispatches;
_useDirectByteBuffers = config._useDirectByteBuffers;
_useInputDirectByteBuffers = config._useInputDirectByteBuffers;
_useOutputDirectByteBuffers = config._useOutputDirectByteBuffers;
_minRequestDataRate = config._minRequestDataRate;
_minResponseDataRate = config._minResponseDataRate;
_httpCompliance = config._httpCompliance;
@ -327,17 +329,31 @@ public class HttpConfiguration implements Dumpable
}
/**
* @param useDirectByteBuffers if true, use direct byte buffers for requests
* @param useInputDirectByteBuffers whether to use direct ByteBuffers for reading
*/
public void setUseDirectByteBuffers(boolean useDirectByteBuffers)
public void setUseInputDirectByteBuffers(boolean useInputDirectByteBuffers)
{
_useDirectByteBuffers = useDirectByteBuffers;
_useInputDirectByteBuffers = useInputDirectByteBuffers;
}
@ManagedAttribute("Whether to use direct byte buffers for requests")
public boolean isUseDirectByteBuffers()
@ManagedAttribute("Whether to use direct ByteBuffers for reading")
public boolean isUseInputDirectByteBuffers()
{
return _useDirectByteBuffers;
return _useInputDirectByteBuffers;
}
/**
* @param useOutputDirectByteBuffers whether to use direct ByteBuffers for writing
*/
public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers)
{
_useOutputDirectByteBuffers = useOutputDirectByteBuffers;
}
@ManagedAttribute("Whether to use direct ByteBuffers for writing")
public boolean isUseOutputDirectByteBuffers()
{
return _useOutputDirectByteBuffers;
}
/**

View File

@ -72,6 +72,8 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
private final boolean _recordHttpComplianceViolations;
private final LongAdder bytesIn = new LongAdder();
private final LongAdder bytesOut = new LongAdder();
private boolean _useInputDirectByteBuffers;
private boolean _useOutputDirectByteBuffers;
/**
* Get the current connection that this thread is dispatched to.
@ -163,12 +165,6 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
return _generator;
}
@Override
public boolean isOptimizedForDirectBuffers()
{
return getEndPoint().isOptimizedForDirectBuffers();
}
@Override
public long getMessagesIn()
{
@ -181,6 +177,26 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
return getHttpChannel().getRequests();
}
public boolean isUseInputDirectByteBuffers()
{
return _useInputDirectByteBuffers;
}
public void setUseInputDirectByteBuffers(boolean useInputDirectByteBuffers)
{
_useInputDirectByteBuffers = useInputDirectByteBuffers;
}
public boolean isUseOutputDirectByteBuffers()
{
return _useOutputDirectByteBuffers;
}
public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers)
{
_useOutputDirectByteBuffers = useOutputDirectByteBuffers;
}
@Override
public ByteBuffer onUpgradeFrom()
{
@ -223,7 +239,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
public ByteBuffer getRequestBuffer()
{
if (_requestBuffer == null)
_requestBuffer = _bufferPool.acquire(getInputBufferSize(), _config.isUseDirectByteBuffers());
{
boolean useDirectByteBuffers = isUseInputDirectByteBuffers();
_requestBuffer = _bufferPool.acquire(getInputBufferSize(), useDirectByteBuffers);
}
return _requestBuffer;
}
@ -731,6 +750,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
if (_callback == null)
throw new IllegalStateException();
boolean useDirectByteBuffers = isUseOutputDirectByteBuffers();
ByteBuffer chunk = _chunk;
while (true)
{
@ -751,19 +771,19 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
case NEED_HEADER:
{
_header = _bufferPool.acquire(_config.getResponseHeaderSize(), _config.isUseDirectByteBuffers());
_header = _bufferPool.acquire(_config.getResponseHeaderSize(), useDirectByteBuffers);
continue;
}
case NEED_CHUNK:
{
chunk = _chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, _config.isUseDirectByteBuffers());
chunk = _chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, useDirectByteBuffers);
continue;
}
case NEED_CHUNK_TRAILER:
{
if (_chunk != null)
_bufferPool.release(_chunk);
chunk = _chunk = _bufferPool.acquire(_config.getResponseHeaderSize(), _config.isUseDirectByteBuffers());
chunk = _chunk = _bufferPool.acquire(_config.getResponseHeaderSize(), useDirectByteBuffers);
continue;
}
case FLUSH:

View File

@ -18,6 +18,8 @@
package org.eclipse.jetty.server;
import java.util.Objects;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
@ -32,7 +34,9 @@ import org.eclipse.jetty.util.annotation.Name;
public class HttpConnectionFactory extends AbstractConnectionFactory implements HttpConfiguration.ConnectionFactory
{
private final HttpConfiguration _config;
private boolean _recordHttpComplianceViolations = false;
private boolean _recordHttpComplianceViolations;
private boolean _useInputDirectByteBuffers;
private boolean _useOutputDirectByteBuffers;
public HttpConnectionFactory()
{
@ -42,10 +46,10 @@ public class HttpConnectionFactory extends AbstractConnectionFactory implements
public HttpConnectionFactory(@Name("config") HttpConfiguration config)
{
super(HttpVersion.HTTP_1_1.asString());
_config = config;
if (config == null)
throw new IllegalArgumentException("Null HttpConfiguration");
_config = Objects.requireNonNull(config);
addBean(_config);
setUseInputDirectByteBuffers(_config.isUseInputDirectByteBuffers());
setUseOutputDirectByteBuffers(_config.isUseOutputDirectByteBuffers());
}
@Override
@ -59,15 +63,37 @@ public class HttpConnectionFactory extends AbstractConnectionFactory implements
return _recordHttpComplianceViolations;
}
@Override
public Connection newConnection(Connector connector, EndPoint endPoint)
{
HttpConnection conn = new HttpConnection(_config, connector, endPoint, isRecordHttpComplianceViolations());
return configure(conn, connector, endPoint);
}
public void setRecordHttpComplianceViolations(boolean recordHttpComplianceViolations)
{
this._recordHttpComplianceViolations = recordHttpComplianceViolations;
}
public boolean isUseInputDirectByteBuffers()
{
return _useInputDirectByteBuffers;
}
public void setUseInputDirectByteBuffers(boolean useInputDirectByteBuffers)
{
_useInputDirectByteBuffers = useInputDirectByteBuffers;
}
public boolean isUseOutputDirectByteBuffers()
{
return _useOutputDirectByteBuffers;
}
public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers)
{
_useOutputDirectByteBuffers = useOutputDirectByteBuffers;
}
@Override
public Connection newConnection(Connector connector, EndPoint endPoint)
{
HttpConnection connection = new HttpConnection(_config, connector, endPoint, isRecordHttpComplianceViolations());
connection.setUseInputDirectByteBuffers(isUseInputDirectByteBuffers());
connection.setUseOutputDirectByteBuffers(isUseOutputDirectByteBuffers());
return configure(connection, connector, endPoint);
}
}

View File

@ -127,14 +127,6 @@ public class HttpOutput extends ServletOutputStream implements Runnable
*/
Interceptor getNextInterceptor();
/**
* @return True if the Interceptor is optimized to receive direct
* {@link ByteBuffer}s in the {@link #write(ByteBuffer, boolean, Callback)}
* method. If false is returned, then passing direct buffers may cause
* inefficiencies.
*/
boolean isOptimizedForDirectBuffers();
/**
* Reset the buffers.
* <p>If the Interceptor contains buffers then reset them.
@ -417,7 +409,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
public ByteBuffer acquireBuffer()
{
if (_aggregate == null)
_aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), _interceptor.isOptimizedForDirectBuffers());
_aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), _channel.isUseOutputDirectByteBuffers());
return _aggregate;
}
@ -591,7 +583,6 @@ public class HttpOutput extends ServletOutputStream implements Runnable
// handle blocking write
// Should we aggregate?
int capacity = getBufferSize();
boolean last = isLastContentToWrite(len);
if (!last && len <= _commitSize)
{
@ -1065,7 +1056,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
break;
}
ByteBuffer buffer = _channel.useDirectBuffers() ? httpContent.getDirectBuffer() : null;
ByteBuffer buffer = _channel.isUseOutputDirectByteBuffers() ? httpContent.getDirectBuffer() : null;
if (buffer == null)
buffer = httpContent.getIndirectBuffer();
@ -1483,6 +1474,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
{
super(callback);
_in = in;
// Reading from InputStream requires byte[], don't use direct buffers.
_buffer = _channel.getByteBufferPool().acquire(getBufferSize(), false);
}
@ -1535,7 +1527,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
* An iterating callback that will take content from a
* ReadableByteChannel and write it to the {@link HttpChannel}.
* A {@link ByteBuffer} of size {@link HttpOutput#getBufferSize()} is used that will be direct if
* {@link HttpChannel#useDirectBuffers()} is true.
* {@link HttpChannel#isUseOutputDirectByteBuffers()} is true.
* This callback is passed to the {@link HttpChannel#write(ByteBuffer, boolean, Callback)} to
* be notified as each buffer is written and only once all the input is consumed will the
* wrapped {@link Callback#succeeded()} method be called.
@ -1550,7 +1542,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
{
super(callback);
_in = in;
_buffer = _channel.getByteBufferPool().acquire(getBufferSize(), _channel.useDirectBuffers());
_buffer = _channel.getByteBufferPool().acquire(getBufferSize(), _channel.isUseOutputDirectByteBuffers());
}
@Override

View File

@ -73,11 +73,4 @@ public interface HttpTransport
* @param failure the failure that caused the abort.
*/
void abort(Throwable failure);
/**
* Is the underlying transport optimized for DirectBuffer usage
*
* @return True if direct buffers can be used optimally.
*/
boolean isOptimizedForDirectBuffers();
}

View File

@ -601,12 +601,6 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
_local = local;
}
@Override
public boolean isOptimizedForDirectBuffers()
{
return _endp.isOptimizedForDirectBuffers();
}
@Override
public InetSocketAddress getLocalAddress()
{

View File

@ -271,12 +271,6 @@ public class BufferedResponseHandler extends HandlerWrapper
return _next;
}
@Override
public boolean isOptimizedForDirectBuffers()
{
return false;
}
protected void commit(Queue<ByteBuffer> buffers, Callback callback)
{
// If only 1 buffer

View File

@ -93,12 +93,6 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
return _interceptor;
}
@Override
public boolean isOptimizedForDirectBuffers()
{
return false; // No point as deflator is in user space.
}
@Override
public void write(ByteBuffer content, boolean complete, Callback callback)
{

View File

@ -651,12 +651,6 @@ public class HttpOutputTest
_next.write(BufferUtil.toBuffer(s), complete, callback);
}
@Override
public boolean isOptimizedForDirectBuffers()
{
return _next.isOptimizedForDirectBuffers();
}
@Override
public Interceptor getNextInterceptor()
{

View File

@ -174,12 +174,6 @@ public class ResponseTest
{
_channelError = failure;
}
@Override
public boolean isOptimizedForDirectBuffers()
{
return false;
}
});
}

View File

@ -22,7 +22,10 @@ import java.io.IOException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.annotation.Name;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.Dumpable;
@ -40,6 +43,8 @@ public class ScheduledExecutorScheduler extends AbstractLifeCycle implements Sch
private final boolean daemon;
private final ClassLoader classloader;
private final ThreadGroup threadGroup;
private final int threads;
private final AtomicInteger count = new AtomicInteger();
private volatile ScheduledThreadPoolExecutor scheduler;
private volatile Thread thread;
@ -50,28 +55,48 @@ public class ScheduledExecutorScheduler extends AbstractLifeCycle implements Sch
public ScheduledExecutorScheduler(String name, boolean daemon)
{
this(name, daemon, Thread.currentThread().getContextClassLoader());
this(name, daemon, null);
}
public ScheduledExecutorScheduler(String name, boolean daemon, ClassLoader threadFactoryClassLoader)
public ScheduledExecutorScheduler(@Name("name") String name, @Name("daemon") boolean daemon, @Name("threads") int threads)
{
this(name, daemon, threadFactoryClassLoader, null);
this(name, daemon, null, null, threads);
}
public ScheduledExecutorScheduler(String name, boolean daemon, ClassLoader threadFactoryClassLoader, ThreadGroup threadGroup)
public ScheduledExecutorScheduler(String name, boolean daemon, ClassLoader classLoader)
{
this.name = name == null ? "Scheduler-" + hashCode() : name;
this(name, daemon, classLoader, null);
}
public ScheduledExecutorScheduler(String name, boolean daemon, ClassLoader classLoader, ThreadGroup threadGroup)
{
this(name, daemon, classLoader, threadGroup, -1);
}
/**
* @param name The name of the scheduler threads or null for automatic name
* @param daemon True if scheduler threads should be daemon
* @param classLoader The classloader to run the threads with or null to use the current thread context classloader
* @param threadGroup The threadgroup to use or null for no thread group
* @param threads The number of threads to pass to the the core {@link ScheduledThreadPoolExecutor} or -1 for a
* heuristic determined number of threads.
*/
public ScheduledExecutorScheduler(@Name("name") String name, @Name("daemon") boolean daemon, @Name("classLoader") ClassLoader classLoader, @Name("threadGroup") ThreadGroup threadGroup, @Name("threads") int threads)
{
this.name = StringUtil.isBlank(name) ? "Scheduler-" + hashCode() : name;
this.daemon = daemon;
this.classloader = threadFactoryClassLoader == null ? Thread.currentThread().getContextClassLoader() : threadFactoryClassLoader;
this.classloader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
this.threadGroup = threadGroup;
this.threads = threads;
}
@Override
protected void doStart() throws Exception
{
scheduler = new ScheduledThreadPoolExecutor(1, r ->
int size = threads > 0 ? threads : 1;
scheduler = new ScheduledThreadPoolExecutor(size, r ->
{
Thread thread = ScheduledExecutorScheduler.this.thread = new Thread(threadGroup, r, name);
Thread thread = ScheduledExecutorScheduler.this.thread = new Thread(threadGroup, r, name + "-" + count.incrementAndGet());
thread.setDaemon(daemon);
thread.setContextClassLoader(classloader);
return thread;

View File

@ -26,7 +26,7 @@ import java.util.stream.Collectors;
public class AbstractConfiguration implements Configuration
{
private final boolean _disabledByDefault;
private final boolean _enabledByDefault;
private final List<String> _after = new ArrayList<>();
private final List<String> _beforeThis = new ArrayList<>();
private final ClassMatcher _system = new ClassMatcher();
@ -34,12 +34,12 @@ public class AbstractConfiguration implements Configuration
protected AbstractConfiguration()
{
this(false);
this(true);
}
protected AbstractConfiguration(boolean disabledByDefault)
protected AbstractConfiguration(boolean enabledByDefault)
{
_disabledByDefault = disabledByDefault;
_enabledByDefault = enabledByDefault;
}
/**
@ -196,9 +196,9 @@ public class AbstractConfiguration implements Configuration
}
@Override
public boolean isDisabledByDefault()
public boolean isEnabledByDefault()
{
return _disabledByDefault;
return _enabledByDefault;
}
@Override

View File

@ -34,12 +34,12 @@ import org.eclipse.jetty.util.TopologicalSort;
* </p>
* <p>Configuration instances are discovered by the {@link Configurations} class using either the
* {@link ServiceLoader} mechanism or by an explicit call to {@link Configurations#setKnown(String...)}.
* By default, all Configurations that do not implement the {@link #isDisabledByDefault()} interface
* By default, all Configurations that do not return false from {@link #isEnabledByDefault()}
* are applied to all {@link WebAppContext}s within the JVM. However a Server wide default {@link Configurations}
* collection may also be defined with {@link Configurations#setServerDefault(org.eclipse.jetty.server.Server)}.
* Furthermore, each individual Context may have its Configurations list explicitly set and/or amended with
* {@link WebAppContext#setConfigurations(Configuration[])}, {@link WebAppContext#addConfiguration(Configuration...)}
* or {@link WebAppContext#getWebAppConfigurations()}.
* or {@link WebAppContext#getConfigurations()}.
* </p>
* <p>Since Jetty-9.4, Configurations are self ordering using the {@link #getDependencies()} and
* {@link #getDependents()} methods for a {@link TopologicalSort} initiated by {@link Configurations#sort()}
@ -171,9 +171,9 @@ public interface Configuration
void destroy(WebAppContext context) throws Exception;
/**
* @return true if configuration is disabled by default
* @return true if configuration is enabled by default
*/
boolean isDisabledByDefault();
boolean isEnabledByDefault();
/**
* @return true if configuration should be aborted

View File

@ -209,7 +209,7 @@ public class Configurations extends AbstractList<Configuration> implements Dumpa
if (configurations == null)
{
configurations = new Configurations(Configurations.getKnown().stream()
.filter(c -> !c.isDisabledByDefault())
.filter(c -> c.isEnabledByDefault())
.map(c -> c.getClass().getName())
.toArray(String[]::new));
}
@ -279,6 +279,27 @@ public class Configurations extends AbstractList<Configuration> implements Dumpa
}
}
public <T> T get(Class<? extends T> configClass)
{
for (Configuration configuration : _configurations)
{
if (configClass.isAssignableFrom(configuration.getClass()))
return (T)configuration;
}
return null;
}
public <T> List<T> getConfigurations(Class<? extends T> configClass)
{
List<T> list = new ArrayList<>();
for (Configuration configuration : _configurations)
{
if (configClass.isAssignableFrom(configuration.getClass()))
list.add((T)configuration);
}
return list;
}
public void clear()
{
_configurations.clear();

View File

@ -95,7 +95,7 @@ import org.eclipse.jetty.util.resource.ResourceCollection;
* <ul>
* <li>Add all Server class inclusions from all known configurations {@link Configurations#getKnown()}</li>
* <li>{@link #loadConfigurations()}, which uses either explicitly set Configurations or takes the server
* default (which is all known non {@link Configuration#isDisabledByDefault()} Configurations.</li>
* default (which is all known {@link Configuration#isEnabledByDefault()} Configurations.</li>
* <li>Sort the configurations using {@link TopologicalSort} in {@link Configurations#sort()}.</li>
* <li>Add all Server class exclusions from this webapps {@link Configurations}</li>
* <li>Add all System classes inclusions and exclusions for this webapps {@link Configurations}</li>
@ -183,10 +183,10 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
"org.eclipse.jetty." // hide jetty classes
);
private final Configurations _configurations = new Configurations();
private final ClassMatcher _systemClasses = new ClassMatcher(__dftSystemClasses);
private final ClassMatcher _serverClasses = new ClassMatcher(__dftServerClasses);
private Configurations _configurations;
private String _defaultsDescriptor = WEB_DEFAULTS_XML;
private String _descriptor = null;
private final List<String> _overrideDescriptors = new ArrayList<>();
@ -570,18 +570,21 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
{
// Prepare for configuration
MultiException mx = new MultiException();
for (Configuration configuration : _configurations)
if (_configurations != null)
{
try
for (Configuration configuration : _configurations)
{
configuration.destroy(this);
}
catch (Exception e)
{
mx.add(e);
try
{
configuration.destroy(this);
}
catch (Exception e)
{
mx.add(e);
}
}
}
_configurations.clear();
_configurations = null;
super.destroy();
mx.ifExceptionThrowRuntime();
}
@ -615,10 +618,9 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
/**
* @return Returns the configurations.
*/
public Configurations getWebAppConfigurations()
public Configurations getConfigurations()
{
if (_configurations.size() == 0)
loadConfigurations();
loadConfigurations();
return _configurations;
}
@ -885,10 +887,18 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
protected void loadConfigurations()
{
//if the configuration instances have been set explicitly, use them
if (!_configurations.isEmpty())
if (_configurations != null)
return;
if (isStarted())
throw new IllegalStateException();
_configurations = newConfigurations();
}
_configurations.add(Configurations.getServerDefault(getServer()).toArray());
protected Configurations newConfigurations()
{
Configurations configurations = new Configurations();
configurations.add(Configurations.getServerDefault(getServer()).toArray());
return configurations;
}
@Override
@ -958,8 +968,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
*/
public void setConfigurationClasses(String[] configurations)
{
if (isStarted())
throw new IllegalStateException();
if (_configurations == null)
_configurations = new Configurations();
_configurations.set(configurations);
}
@ -973,15 +983,13 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
*/
public void setConfigurations(Configuration[] configurations)
{
if (isStarted())
throw new IllegalStateException();
if (_configurations == null)
_configurations = new Configurations();
_configurations.set(configurations);
}
public void addConfiguration(Configuration... configuration)
{
if (isStarted())
throw new IllegalStateException();
loadConfigurations();
_configurations.add(configuration);
}
@ -989,12 +997,19 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
public <T> T getConfiguration(Class<? extends T> configClass)
{
loadConfigurations();
for (Configuration configuration : _configurations)
{
if (configClass.isAssignableFrom(configuration.getClass()))
return (T)configuration;
}
return null;
return _configurations.get(configClass);
}
public void removeConfiguration(Configuration... configurations)
{
if (_configurations != null)
_configurations.remove(configurations);
}
public void removeConfiguration(Class<? extends Configuration>... configurations)
{
if (_configurations != null)
_configurations.remove(configurations);
}
/**

View File

@ -100,7 +100,7 @@ public class WebAppContextTest
{
Configurations.cleanKnown();
String[] known_and_enabled = Configurations.getKnown().stream()
.filter(c -> !c.isDisabledByDefault())
.filter(c -> c.isEnabledByDefault())
.map(c -> c.getClass().getName())
.toArray(String[]::new);
@ -108,7 +108,7 @@ public class WebAppContextTest
//test if no classnames set, its the defaults
WebAppContext wac = new WebAppContext();
assertThat(wac.getWebAppConfigurations().stream()
assertThat(wac.getConfigurations().stream()
.map(c -> c.getClass().getName())
.collect(Collectors.toList()),
Matchers.containsInAnyOrder(known_and_enabled));
@ -126,7 +126,7 @@ public class WebAppContextTest
Configurations.cleanKnown();
WebAppContext wac = new WebAppContext();
wac.setServer(new Server());
assertThat(wac.getWebAppConfigurations().stream().map(c -> c.getClass().getName()).collect(Collectors.toList()),
assertThat(wac.getConfigurations().stream().map(c -> c.getClass().getName()).collect(Collectors.toList()),
Matchers.contains(
"org.eclipse.jetty.webapp.JmxConfiguration",
"org.eclipse.jetty.webapp.WebInfConfiguration",
@ -144,14 +144,14 @@ public class WebAppContextTest
Configuration[] configs = {new WebInfConfiguration()};
WebAppContext wac = new WebAppContext();
wac.setConfigurations(configs);
assertThat(wac.getWebAppConfigurations(), Matchers.contains(configs));
assertThat(wac.getConfigurations(), Matchers.contains(configs));
//test that explicit config instances override any from server
String[] classNames = {"x.y.z"};
Server server = new Server();
server.setAttribute(Configuration.ATTR, classNames);
wac.setServer(server);
assertThat(wac.getWebAppConfigurations(), Matchers.contains(configs));
assertThat(wac.getConfigurations(), Matchers.contains(configs));
}
@Test

View File

@ -29,6 +29,12 @@
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-javax-websocket-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-xml</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>

View File

@ -24,9 +24,11 @@ import javax.websocket.ClientEndpointConfig;
import javax.websocket.WebSocketContainer;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.jsr356.ClientContainer;
import org.eclipse.jetty.websocket.javax.client.JavaxWebSocketClientContainer;
public class SecureClientContainerExample
{
@ -73,11 +75,14 @@ public class SecureClientContainerExample
*/
public static WebSocketContainer getConfiguredWebSocketContainer() throws Exception
{
SslContextFactory ssl = new SslContextFactory.Client();
SslContextFactory.Client ssl = new SslContextFactory.Client();
ssl.setExcludeCipherSuites(); // echo.websocket.org use WEAK cipher suites
HttpClient httpClient = new HttpClient(ssl);
ClientContainer clientContainer = new ClientContainer(httpClient);
clientContainer.getClient().addManaged(httpClient); // allow clientContainer to own httpClient (for start/stop lifecycle)
ClientConnector clientConnector = new ClientConnector();
clientConnector.setSslContextFactory(ssl);
HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP(clientConnector));
JavaxWebSocketClientContainer clientContainer = new JavaxWebSocketClientContainer(httpClient);
clientContainer.addManaged(httpClient); // allow clientContainer to own httpClient (for start/stop lifecycle)
clientContainer.start();
return clientContainer;
}

View File

@ -21,14 +21,12 @@ package examples;
import java.io.FileNotFoundException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.concurrent.TimeUnit;
import javax.websocket.ClientEndpointConfig;
import javax.websocket.ContainerProvider;
import javax.websocket.WebSocketContainer;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.thread.ThreadClassLoaderScope;
public class SecureWebSocketContainerExample
{
@ -69,47 +67,27 @@ public class SecureWebSocketContainerExample
* Since javax.websocket does not have an API for configuring SSL, each implementation
* of javax.websocket has to come up with their own SSL configuration mechanism.
* <p>
* When the call to {@link javax.websocket.ContainerProvider}.{@link ContainerProvider#getWebSocketContainer()}
* occurs, that needs to have a started and available WebSocket Client.
* Jetty's {@code WebSocketClient} must have a Jetty {@code HttpClient} started as well.
* If you want SSL, then that configuration has to be passed into the Jetty {@code HttpClient} at initialization.
* When the {@link WebSocketContainer} is used it will need to create and start a Jetty WebSocket Client.
* The {@code WebSocketClient} must use a Jetty {@code HttpClient} which can be configured for SSL, this
* configuration needs to be passed into the Jetty {@code HttpClient} at initialization.
* </p>
* <p>
* How Jetty makes this available, is via the {@code jetty-websocket-httpclient.xml} classloader resource
* along with the jetty-xml artifact.
* </p>
* <p>
* This method will look for the file in the classloader resources, and then
* sets up a {@link URLClassLoader} to make that {@code jetty-websocket-httpclient.xml} available
* for this specific example.
* If we had put the `jetty-websocket-httpclient.xml` in the root of a JAR file loaded by this
* project then you can skip all of the classloader trickery this method performs.
* </p>
*
* @return the client WebSocketContainer
* @see <a href="https://github.com/eclipse-ee4j/websocket-api/issues/210">javax.websocket issue #210</a>
*/
public static WebSocketContainer getConfiguredWebSocketContainer() throws Exception
{
URL jettyHttpClientConfigUrl = Thread.currentThread().getContextClassLoader()
.getResource("examples/jetty-websocket-httpclient.xml");
.getResource("jetty-websocket-httpclient.xml");
if (jettyHttpClientConfigUrl == null)
{
throw new FileNotFoundException("Unable to find Jetty HttpClient configuration XML");
}
URI jettyConfigDirUri = jettyHttpClientConfigUrl.toURI().resolve("./");
ClassLoader parentClassLoader = Thread.currentThread().getContextClassLoader();
URL[] urls = new URL[]{
jettyConfigDirUri.toURL()
};
URLClassLoader classLoader = new URLClassLoader(urls, parentClassLoader);
try (ThreadClassLoaderScope ignore = new ThreadClassLoaderScope(classLoader))
{
return ContainerProvider.getWebSocketContainer();
}
return ContainerProvider.getWebSocketContainer();
}
}

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure class="org.eclipse.jetty.client.HttpClient">
<Arg>
<New class="org.eclipse.jetty.util.ssl.SslContextFactory$Client">
<Set name="trustAll" type="java.lang.Boolean">false</Set>
<Call name="addExcludeProtocols">
<Arg>
<Array type="java.lang.String">
<Item>TLS/1.3</Item>
</Array>
</Arg>
</Call>
<Call name="setExcludeCipherSuites"><!-- websocket.org uses WEAK cipher suites -->
<Arg>
<Array type="java.lang.String" />
</Arg>
</Call>
</New>
</Arg>
<Set name="connectTimeout">5000</Set>
</Configure>

View File

@ -0,0 +1,28 @@
#
#
# ========================================================================
# Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
# ------------------------------------------------------------------------
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# and Apache License v2.0 which accompanies this distribution.
#
# The Eclipse Public License is available at
# http://www.eclipse.org/legal/epl-v10.html
#
# The Apache License v2.0 is available at
# http://www.opensource.org/licenses/apache2.0.php
#
# You may elect to redistribute this code under either of these licenses.
# ========================================================================
#
#
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.eclipse.jetty.LEVEL=INFO
# org.eclipse.jetty.websocket.LEVEL=DEBUG
# org.eclipse.jetty.server.AbstractConnector.LEVEL=DEBUG
# org.eclipse.jetty.io.WriteFlusher.LEVEL=DEBUG
# org.eclipse.jetty.io.FillInterest.LEVEL=DEBUG
# org.eclipse.jetty.client.LEVEL=DEBUG
# org.eclipse.jetty.io.LEVEL=DEBUG
# org.eclipse.jetty.io.ManagedSelector.LEVEL=INFO

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure class="org.eclipse.jetty.client.HttpClient">
<Arg>
<New class="org.eclipse.jetty.client.http.HttpClientTransportOverHTTP">
<Arg>
<New class="org.eclipse.jetty.io.ClientConnector">
<Call name="setSslContextFactory">
<Arg>
<New class="org.eclipse.jetty.util.ssl.SslContextFactory$Client">
<!-- websocket.org uses WEAK cipher suites -->
<Set name="excludeCipherSuites">
<Array type="java.lang.String"></Array>
</Set>
</New>
</Arg>
</Call>
</New>
</Arg>
</New>
</Arg>
</Configure>

View File

@ -50,6 +50,8 @@ public class FrameFlusher extends IteratingCallback
private static final Logger LOG = Log.getLogger(FrameFlusher.class);
private static final Throwable CLOSED_CHANNEL = new ClosedChannelException();
private final LongAdder messagesOut = new LongAdder();
private final LongAdder bytesOut = new LongAdder();
private final ByteBufferPool bufferPool;
private final EndPoint endPoint;
private final int bufferSize;
@ -62,13 +64,12 @@ public class FrameFlusher extends IteratingCallback
private final List<Entry> previousEntries;
private final List<Entry> failedEntries;
private ByteBuffer batchBuffer = null;
private ByteBuffer batchBuffer;
private boolean canEnqueue = true;
private boolean flushed = true;
private Throwable closedCause;
private LongAdder messagesOut = new LongAdder();
private LongAdder bytesOut = new LongAdder();
private long idleTimeout = 0;
private long idleTimeout;
private boolean useDirectByteBuffers;
public FrameFlusher(ByteBufferPool bufferPool, Scheduler scheduler, Generator generator, EndPoint endPoint, int bufferSize, int maxGather)
{
@ -84,6 +85,16 @@ public class FrameFlusher extends IteratingCallback
this.timeoutScheduler = scheduler;
}
public boolean isUseDirectByteBuffers()
{
return useDirectByteBuffers;
}
public void setUseDirectByteBuffers(boolean useDirectByteBuffers)
{
this.useDirectByteBuffers = useDirectByteBuffers;
}
/**
* Enqueue a Frame to be written to the endpoint.
*
@ -225,7 +236,7 @@ public class FrameFlusher extends IteratingCallback
// Acquire a batchBuffer if we don't have one
if (batchBuffer == null)
{
batchBuffer = bufferPool.acquire(bufferSize, true);
batchBuffer = acquireBuffer(bufferSize);
buffers.add(batchBuffer);
}
@ -249,7 +260,10 @@ public class FrameFlusher extends IteratingCallback
else
{
// Add headers and payload to the list of buffers
buffers.add(entry.generateHeaderBytes());
// TODO: release this buffer.
ByteBuffer buffer = acquireBuffer(Generator.MAX_HEADER_LENGTH);
buffers.add(buffer);
entry.generateHeaderBytes(buffer);
flush = true;
ByteBuffer payload = entry.frame.getPayload();
if (BufferUtil.hasContent(payload))
@ -308,6 +322,11 @@ public class FrameFlusher extends IteratingCallback
return Action.SCHEDULED;
}
private ByteBuffer acquireBuffer(int capacity)
{
return bufferPool.acquire(capacity, isUseDirectByteBuffers());
}
private int getQueueSize()
{
synchronized (this)
@ -474,11 +493,6 @@ public class FrameFlusher extends IteratingCallback
super(frame, callback, batch);
}
private ByteBuffer generateHeaderBytes()
{
return headerBuffer = generator.generateHeaderBytes(frame);
}
private void generateHeaderBytes(ByteBuffer buffer)
{
int pos = BufferUtil.flipToFill(buffer);

View File

@ -68,6 +68,8 @@ public class WebSocketConnection extends AbstractConnection implements Connectio
// Read / Parse variables
private RetainableByteBuffer networkBuffer;
private boolean useInputDirectByteBuffers;
private boolean useOutputDirectByteBuffers;
/**
* Create a WSConnection.
@ -132,6 +134,26 @@ public class WebSocketConnection extends AbstractConnection implements Connectio
return getEndPoint().getRemoteAddress();
}
public boolean isUseInputDirectByteBuffers()
{
return useInputDirectByteBuffers;
}
public void setUseInputDirectByteBuffers(boolean useInputDirectByteBuffers)
{
this.useInputDirectByteBuffers = useInputDirectByteBuffers;
}
public boolean isUseOutputDirectByteBuffers()
{
return useOutputDirectByteBuffers;
}
public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers)
{
this.useOutputDirectByteBuffers = useOutputDirectByteBuffers;
}
/**
* Physical connection disconnect.
* <p>
@ -222,7 +244,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio
synchronized (this)
{
if (networkBuffer == null)
networkBuffer = new RetainableByteBuffer(bufferPool, getInputBufferSize());
networkBuffer = newNetworkBuffer(getInputBufferSize());
}
}
@ -237,10 +259,15 @@ public class WebSocketConnection extends AbstractConnection implements Connectio
throw new IllegalStateException();
networkBuffer.release();
networkBuffer = new RetainableByteBuffer(bufferPool, getInputBufferSize());
networkBuffer = newNetworkBuffer(getInputBufferSize());
}
}
private RetainableByteBuffer newNetworkBuffer(int capacity)
{
return new RetainableByteBuffer(bufferPool, capacity, isUseInputDirectByteBuffers());
}
private void releaseNetworkBuffer()
{
synchronized (this)
@ -445,7 +472,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio
{
synchronized (this)
{
networkBuffer = new RetainableByteBuffer(bufferPool, prefilled.remaining());
networkBuffer = newNetworkBuffer(prefilled.remaining());
}
ByteBuffer buffer = networkBuffer.getBuffer();
BufferUtil.clearToFill(buffer);
@ -572,6 +599,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio
private Flusher(Scheduler scheduler, int bufferSize, Generator generator, EndPoint endpoint)
{
super(bufferPool, scheduler, generator, endpoint, bufferSize, 8);
setUseDirectByteBuffers(isUseOutputDirectByteBuffers());
}
@Override

View File

@ -201,6 +201,11 @@ public final class RFC6455Handshaker implements Handshaker
// Create a connection
WebSocketConnection connection = newWebSocketConnection(httpChannel.getEndPoint(), connector.getExecutor(), connector.getScheduler(), connector.getByteBufferPool(), coreSession);
// TODO: perhaps use of direct buffers should be WebSocket specific
// rather than inheriting the setting from HttpConfiguration.
HttpConfiguration httpConfig = httpChannel.getHttpConfiguration();
connection.setUseInputDirectByteBuffers(httpConfig.isUseInputDirectByteBuffers());
connection.setUseOutputDirectByteBuffers(httpChannel.isUseOutputDirectByteBuffers());
if (LOG.isDebugEnabled())
LOG.debug("connection {}", connection);
if (connection == null)

View File

@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.core.CloseStatus;
import org.eclipse.jetty.websocket.core.ExtensionConfig;
import org.eclipse.jetty.websocket.core.Frame;
@ -88,7 +89,7 @@ public class ValidationExtensionTest extends WebSocketTester
Frame frame = serverHandler.receivedFrames.poll(5, TimeUnit.SECONDS);
assertNotNull(frame);
assertThat(frame.getOpCode(), is(OpCode.BINARY));
assertThat(frame.getPayload().array(), is(nonUtf8Payload));
assertThat(BufferUtil.toArray(frame.getPayload()), is(nonUtf8Payload));
//close normally
client.getOutputStream().write(RawFrameBuilder.buildClose(CloseStatus.NORMAL_STATUS, true));
@ -113,13 +114,13 @@ public class ValidationExtensionTest extends WebSocketTester
Frame frame = serverHandler.receivedFrames.poll(5, TimeUnit.SECONDS);
assertNotNull(frame);
assertThat(frame.getOpCode(), is(OpCode.TEXT));
assertThat(frame.getPayload().array(), is(initialPayload));
assertThat(BufferUtil.toArray(frame.getPayload()), is(initialPayload));
client.getOutputStream().write(RawFrameBuilder.buildFrame(OpCode.CONTINUATION, continuationPayload, true));
frame = serverHandler.receivedFrames.poll(5, TimeUnit.SECONDS);
assertNotNull(frame);
assertThat(frame.getOpCode(), is(OpCode.CONTINUATION));
assertThat(frame.getPayload().array(), is(continuationPayload));
assertThat(BufferUtil.toArray(frame.getPayload()), is(continuationPayload));
//close normally
client.getOutputStream().write(RawFrameBuilder.buildClose(CloseStatus.NORMAL_STATUS, true));
@ -144,7 +145,7 @@ public class ValidationExtensionTest extends WebSocketTester
Frame frame = serverHandler.receivedFrames.poll(5, TimeUnit.SECONDS);
assertNotNull(frame);
assertThat(frame.getOpCode(), is(OpCode.TEXT));
assertThat(frame.getPayload().array(), is(initialPayload));
assertThat(BufferUtil.toArray(frame.getPayload()), is(initialPayload));
client.getOutputStream().write(RawFrameBuilder.buildFrame(OpCode.CONTINUATION, incompleteContinuationPayload, true));
frame = receiveFrame(client.getInputStream());

View File

@ -164,12 +164,6 @@ public class MockEndpoint implements EndPoint
throw new UnsupportedOperationException(NOT_SUPPORTED);
}
@Override
public boolean isOptimizedForDirectBuffers()
{
throw new UnsupportedOperationException(NOT_SUPPORTED);
}
@Override
public void upgrade(Connection newConnection)
{

View File

@ -24,7 +24,6 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
@ -43,8 +42,6 @@ import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
@ -207,29 +204,6 @@ public class WebSocketServerTest extends WebSocketTester
}
assertThat(serverHandler.receivedFrames.size(), is(5));
assertThat(receivedCallbacks.size(), is(5));
byte[] first = serverHandler.receivedFrames.poll().getPayload().array();
assertThat(serverHandler.receivedFrames.poll().getPayload().array(), sameInstance(first));
assertThat(serverHandler.receivedFrames.poll().getPayload().array(), sameInstance(first));
byte[] second = serverHandler.receivedFrames.poll().getPayload().array();
assertThat(serverHandler.receivedFrames.poll().getPayload().array(), sameInstance(second));
assertThat(first, not(sameInstance(second)));
ByteBufferPool pool = server.getServer().getConnectors()[0].getByteBufferPool();
assertThat(pool.acquire(first.length, false).array(), not(sameInstance(first)));
receivedCallbacks.poll().succeeded();
assertThat(pool.acquire(first.length, false).array(), not(sameInstance(first)));
receivedCallbacks.poll().succeeded();
assertThat(pool.acquire(first.length, false).array(), not(sameInstance(first)));
receivedCallbacks.poll().succeeded();
assertThat(pool.acquire(first.length, false).array(), sameInstance(first));
assertThat(pool.acquire(second.length, false).array(), not(sameInstance(second)));
receivedCallbacks.poll().succeeded();
assertThat(pool.acquire(second.length, false).array(), not(sameInstance(second)));
receivedCallbacks.poll().succeeded();
assertThat(pool.acquire(second.length, false).array(), sameInstance(second));
}
}

View File

@ -1146,6 +1146,7 @@ public class XmlConfigurationTest
ByteArrayOutputStream logBytes = null;
Logger logger = Log.getLogger(XmlConfiguration.class);
logger.setDebugEnabled(true);
if (logger instanceof StdErrLog)
{
StdErrLog stdErrLog = (StdErrLog)logger;
@ -1155,6 +1156,7 @@ public class XmlConfigurationTest
xmlConfiguration.configure();
logger.setDebugEnabled(false);
if (logBytes != null)
{
String[] lines = logBytes.toString(UTF_8.name()).split(System.lineSeparator());

View File

@ -272,7 +272,7 @@
<configuration>
<autoVersionSubmodules>true</autoVersionSubmodules>
<useReleaseProfile>false</useReleaseProfile>
<goals>javadoc:aggregate-jar deploy</goals>
<goals>deploy</goals>
<arguments>-Peclipse-release</arguments>
<preparationGoals>clean install</preparationGoals>
<mavenExecutorId>forked-path</mavenExecutorId>

View File

@ -142,4 +142,32 @@ public class DemoBaseTests extends AbstractDistributionTest
assertThat(response.getContentAsString(), not(containsString("<span class=\"fail\">FAIL</span>")));
}
}
@Test
public void testJPMS() throws Exception
{
String jettyVersion = System.getProperty("jettyVersion");
DistributionTester distribution = DistributionTester.Builder.newInstance()
.jettyVersion(jettyVersion)
.jettyBase(Paths.get("demo-base"))
.mavenLocalRepository(System.getProperty("mavenRepoPath"))
.build();
int httpPort = distribution.freePort();
int httpsPort = distribution.freePort();
String[] args = {
"--jpms",
"jetty.http.port=" + httpPort,
"jetty.httpConfig.port=" + httpsPort,
"jetty.ssl.port=" + httpsPort
};
try (DistributionTester.Run run = distribution.start(args))
{
assertTrue(run.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
startHttpClient();
ContentResponse response = client.GET("http://localhost:" + httpPort + "/test/hello");
assertEquals(HttpStatus.OK_200, response.getStatus());
}
}
}

View File

@ -193,7 +193,7 @@ public class DeploymentErrorTest
assertThat("ContextHandler.isAvailable", context.isAvailable(), is(false));
WebAppContext webapp = (WebAppContext)context;
TrackedConfiguration trackedConfiguration = null;
for (Configuration webappConfig : webapp.getWebAppConfigurations())
for (Configuration webappConfig : webapp.getConfigurations())
{
if (webappConfig instanceof TrackedConfiguration)
trackedConfiguration = (TrackedConfiguration)webappConfig;
@ -239,7 +239,7 @@ public class DeploymentErrorTest
assertThat("ContextHandler.isAvailable", context.isAvailable(), is(false));
WebAppContext webapp = (WebAppContext)context;
TrackedConfiguration trackedConfiguration = null;
for (Configuration webappConfig : webapp.getWebAppConfigurations())
for (Configuration webappConfig : webapp.getConfigurations())
{
if (webappConfig instanceof TrackedConfiguration)
trackedConfiguration = (TrackedConfiguration)webappConfig;
@ -285,7 +285,7 @@ public class DeploymentErrorTest
assertThat("ContextHandler.isAvailable", context.isAvailable(), is(false));
WebAppContext webapp = (WebAppContext)context;
TrackedConfiguration trackedConfiguration = null;
for (Configuration webappConfig : webapp.getWebAppConfigurations())
for (Configuration webappConfig : webapp.getConfigurations())
{
if (webappConfig instanceof TrackedConfiguration)
trackedConfiguration = (TrackedConfiguration)webappConfig;

View File

@ -28,3 +28,5 @@ org.eclipse.jetty.websocket.jsr356=false
# Create and configure the test realm
jetty.demo.realm=etc/realm.properties
# JDBC needed by test-jndi and test-spec
--module=jdbc