Merge branch 'jetty-9' of ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project into jetty-9
This commit is contained in:
commit
b6fcdf0f8f
|
@ -0,0 +1,186 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2012 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.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.embedded;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
||||
import org.eclipse.jetty.deploy.DeploymentManager;
|
||||
import org.eclipse.jetty.deploy.providers.ContextProvider;
|
||||
import org.eclipse.jetty.deploy.providers.WebAppProvider;
|
||||
import org.eclipse.jetty.io.ArrayByteBufferPool;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.FilterConnection;
|
||||
import org.eclipse.jetty.jmx.MBeanContainer;
|
||||
import org.eclipse.jetty.security.HashLoginService;
|
||||
import org.eclipse.jetty.server.FilterConnectionFactory;
|
||||
import org.eclipse.jetty.server.ForwardedRequestCustomizer;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpChannelConfig;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.NCSARequestLog;
|
||||
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.SslConnectionFactory;
|
||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.RequestLogHandler;
|
||||
import org.eclipse.jetty.server.handler.StatisticsHandler;
|
||||
import org.eclipse.jetty.spdy.server.NPNServerConnectionFactory;
|
||||
import org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory;
|
||||
import org.eclipse.jetty.spdy.server.http.PushStrategy;
|
||||
import org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.eclipse.jetty.util.thread.TimerScheduler;
|
||||
|
||||
public class SpdyServer
|
||||
{
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
String jetty_home = System.getProperty("jetty.home","../jetty-distribution/target/distribution");
|
||||
System.setProperty("jetty.home",jetty_home);
|
||||
|
||||
// Setup Threadpool
|
||||
QueuedThreadPool threadPool = new QueuedThreadPool();
|
||||
threadPool.setMaxThreads(500);
|
||||
|
||||
Server server = new Server(threadPool);
|
||||
server.manage(threadPool);
|
||||
server.setDumpAfterStart(false);
|
||||
server.setDumpBeforeStop(false);
|
||||
|
||||
// Setup JMX
|
||||
MBeanContainer mbContainer=new MBeanContainer(ManagementFactory.getPlatformMBeanServer());
|
||||
server.addBean(mbContainer);
|
||||
|
||||
|
||||
// Common HTTP configuration
|
||||
HttpChannelConfig config = new HttpChannelConfig();
|
||||
config.setSecurePort(8443);
|
||||
config.addCustomizer(new ForwardedRequestCustomizer());
|
||||
config.addCustomizer(new SecureRequestCustomizer());
|
||||
|
||||
|
||||
// Http Connector
|
||||
HttpConnectionFactory http = new HttpConnectionFactory(config);
|
||||
FilterConnectionFactory filter = new FilterConnectionFactory(http.getProtocol());
|
||||
filter.addFilter(new FilterConnection.DumpToFileFilter("http-"));
|
||||
ServerConnector httpConnector = new ServerConnector(server,filter,http);
|
||||
httpConnector.setPort(8080);
|
||||
httpConnector.setIdleTimeout(30000);
|
||||
|
||||
server.addConnector(httpConnector);
|
||||
|
||||
|
||||
// SSL configurations
|
||||
SslContextFactory sslContextFactory = new SslContextFactory();
|
||||
sslContextFactory.setKeyStorePath(jetty_home + "/etc/keystore");
|
||||
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
|
||||
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
|
||||
sslContextFactory.setTrustStorePath(jetty_home + "/etc/keystore");
|
||||
sslContextFactory.setTrustStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
|
||||
sslContextFactory.setExcludeCipherSuites(
|
||||
"SSL_RSA_WITH_DES_CBC_SHA",
|
||||
"SSL_DHE_RSA_WITH_DES_CBC_SHA",
|
||||
"SSL_DHE_DSS_WITH_DES_CBC_SHA",
|
||||
"SSL_RSA_EXPORT_WITH_RC4_40_MD5",
|
||||
"SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
|
||||
"SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
|
||||
"SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA");
|
||||
|
||||
|
||||
|
||||
|
||||
// Spdy Connector
|
||||
|
||||
PushStrategy push = new ReferrerPushStrategy();
|
||||
HTTPSPDYServerConnectionFactory spdy2 = new HTTPSPDYServerConnectionFactory(2,config,push);
|
||||
spdy2.setInputBufferSize(8192);
|
||||
spdy2.setInitialWindowSize(32768);
|
||||
|
||||
HTTPSPDYServerConnectionFactory spdy3 = new HTTPSPDYServerConnectionFactory(3,config,push);
|
||||
spdy2.setInputBufferSize(8192);
|
||||
|
||||
NPNServerConnectionFactory npn = new NPNServerConnectionFactory(spdy3.getProtocol(),spdy2.getProtocol(),http.getProtocol());
|
||||
npn.setDefaultProtocol(http.getProtocol());
|
||||
npn.setInputBufferSize(1024);
|
||||
|
||||
FilterConnectionFactory npn_filter = new FilterConnectionFactory(npn.getProtocol());
|
||||
npn_filter.addFilter(new FilterConnection.DumpToFileFilter("npn-"));
|
||||
|
||||
SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory,npn_filter.getProtocol());
|
||||
FilterConnectionFactory ssl_filter = new FilterConnectionFactory(ssl.getProtocol());
|
||||
ssl_filter.addFilter(new FilterConnection.DumpToFileFilter("ssl-"));
|
||||
|
||||
ServerConnector spdyConnector = new ServerConnector(server,ssl_filter,ssl,npn_filter,npn,spdy3,spdy2,http);
|
||||
spdyConnector.setPort(8443);
|
||||
|
||||
server.addConnector(spdyConnector);
|
||||
|
||||
|
||||
// Setup handlers
|
||||
HandlerCollection handlers = new HandlerCollection();
|
||||
ContextHandlerCollection contexts = new ContextHandlerCollection();
|
||||
RequestLogHandler requestLogHandler = new RequestLogHandler();
|
||||
|
||||
handlers.setHandlers(new Handler[] { contexts, new DefaultHandler(), requestLogHandler });
|
||||
|
||||
StatisticsHandler stats = new StatisticsHandler();
|
||||
stats.setHandler(handlers);
|
||||
|
||||
server.setHandler(stats);
|
||||
|
||||
// Setup deployers
|
||||
DeploymentManager deployer = new DeploymentManager();
|
||||
deployer.setContexts(contexts);
|
||||
server.addBean(deployer);
|
||||
|
||||
ContextProvider context_provider = new ContextProvider();
|
||||
context_provider.setMonitoredDirName(jetty_home + "/contexts");
|
||||
context_provider.setScanInterval(2);
|
||||
deployer.addAppProvider(context_provider);
|
||||
|
||||
WebAppProvider webapp_provider = new WebAppProvider();
|
||||
webapp_provider.setMonitoredDirName(jetty_home + "/webapps");
|
||||
webapp_provider.setParentLoaderPriority(false);
|
||||
webapp_provider.setExtractWars(true);
|
||||
webapp_provider.setScanInterval(2);
|
||||
webapp_provider.setDefaultsDescriptor(jetty_home + "/etc/webdefault.xml");
|
||||
webapp_provider.setContextXmlDir(jetty_home + "/contexts");
|
||||
deployer.addAppProvider(webapp_provider);
|
||||
|
||||
HashLoginService login = new HashLoginService();
|
||||
login.setName("Test Realm");
|
||||
login.setConfig(jetty_home + "/etc/realm.properties");
|
||||
server.addBean(login);
|
||||
|
||||
NCSARequestLog requestLog = new NCSARequestLog(jetty_home + "/logs/jetty-yyyy_mm_dd.log");
|
||||
requestLog.setExtended(false);
|
||||
requestLogHandler.setRequestLog(requestLog);
|
||||
|
||||
server.setStopAtShutdown(true);
|
||||
server.setSendServerVersion(true);
|
||||
|
||||
server.start();
|
||||
server.dumpStdErr();
|
||||
server.join();
|
||||
}
|
||||
}
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package org.eclipse.jetty.client;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
@ -40,9 +39,9 @@ public class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
public static final Logger LOG = Log.getLogger(AuthenticationProtocolHandler.class);
|
||||
private static final Pattern WWW_AUTHENTICATE_PATTERN = Pattern.compile("([^\\s]+)\\s+realm=\"([^\"]+)\".*", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private final ResponseNotifier notifier = new ResponseNotifier();
|
||||
private final HttpClient client;
|
||||
private final int maxContentLength;
|
||||
private final ResponseNotifier notifier;
|
||||
|
||||
public AuthenticationProtocolHandler(HttpClient client)
|
||||
{
|
||||
|
@ -53,6 +52,7 @@ public class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
{
|
||||
this.client = client;
|
||||
this.maxContentLength = maxContentLength;
|
||||
this.notifier = new ResponseNotifier(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -64,6 +64,7 @@ public class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
@Override
|
||||
public Response.Listener getResponseListener()
|
||||
{
|
||||
// Return new instances every time to keep track of the response content
|
||||
return new AuthenticationListener();
|
||||
}
|
||||
|
||||
|
@ -78,12 +79,14 @@ public class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
public void onComplete(Result result)
|
||||
{
|
||||
Request request = result.getRequest();
|
||||
HttpConversation conversation = client.getConversation(request.conversation());
|
||||
Response.Listener listener = conversation.exchanges().peekFirst().listener();
|
||||
ContentResponse response = new HttpContentResponse(result.getResponse(), getContent(), getEncoding());
|
||||
if (result.isFailed())
|
||||
{
|
||||
Throwable failure = result.getFailure();
|
||||
LOG.debug("Authentication challenge failed {}", failure);
|
||||
forwardFailure(request, response, failure);
|
||||
notifier.forwardFailureComplete(listener, request, result.getRequestFailure(), response, result.getResponseFailure());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -91,7 +94,7 @@ public class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
if (wwwAuthenticates.isEmpty())
|
||||
{
|
||||
LOG.debug("Authentication challenge without WWW-Authenticate header");
|
||||
forwardFailure(request, response, new HttpResponseException("HTTP protocol violation: 401 without WWW-Authenticate header", response));
|
||||
notifier.forwardFailureComplete(listener, request, null, response, new HttpResponseException("HTTP protocol violation: 401 without WWW-Authenticate header", response));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -110,16 +113,15 @@ public class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
if (authentication == null)
|
||||
{
|
||||
LOG.debug("No authentication available for {}", request);
|
||||
forwardSuccess(request, response);
|
||||
notifier.forwardSuccessComplete(listener, request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
HttpConversation conversation = client.getConversation(request);
|
||||
final Authentication.Result authnResult = authentication.authenticate(request, response, wwwAuthenticate.value, conversation);
|
||||
LOG.debug("Authentication result {}", authnResult);
|
||||
if (authnResult == null)
|
||||
{
|
||||
forwardSuccess(request, response);
|
||||
notifier.forwardSuccessComplete(listener, request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -134,32 +136,6 @@ public class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
});
|
||||
}
|
||||
|
||||
private void forwardFailure(Request request, Response response, Throwable failure)
|
||||
{
|
||||
HttpConversation conversation = client.getConversation(request);
|
||||
Response.Listener listener = conversation.exchanges().peekFirst().listener();
|
||||
notifier.notifyBegin(listener, response);
|
||||
notifier.notifyHeaders(listener, response);
|
||||
if (response instanceof ContentResponse)
|
||||
notifier.notifyContent(listener, response, ByteBuffer.wrap(((ContentResponse)response).content()));
|
||||
notifier.notifyFailure(listener, response, failure);
|
||||
conversation.complete();
|
||||
notifier.notifyComplete(listener, new Result(request, response, failure));
|
||||
}
|
||||
|
||||
private void forwardSuccess(Request request, Response response)
|
||||
{
|
||||
HttpConversation conversation = client.getConversation(request);
|
||||
Response.Listener listener = conversation.exchanges().peekFirst().listener();
|
||||
notifier.notifyBegin(listener, response);
|
||||
notifier.notifyHeaders(listener, response);
|
||||
if (response instanceof ContentResponse)
|
||||
notifier.notifyContent(listener, response, ByteBuffer.wrap(((ContentResponse)response).content()));
|
||||
notifier.notifySuccess(listener, response);
|
||||
conversation.complete();
|
||||
notifier.notifyComplete(listener, new Result(request, response));
|
||||
}
|
||||
|
||||
private List<WWWAuthenticate> parseWWWAuthenticate(Response response)
|
||||
{
|
||||
// TODO: these should be ordered by strength
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2012 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.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.client;
|
||||
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||
|
||||
public class ContinueProtocolHandler implements ProtocolHandler
|
||||
{
|
||||
private static final String ATTRIBUTE = ContinueProtocolHandler.class.getName() + ".100continue";
|
||||
|
||||
private final HttpClient client;
|
||||
private final ResponseNotifier notifier;
|
||||
|
||||
public ContinueProtocolHandler(HttpClient client)
|
||||
{
|
||||
this.client = client;
|
||||
this.notifier = new ResponseNotifier(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(Request request, Response response)
|
||||
{
|
||||
boolean expect100 = request.headers().contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString());
|
||||
boolean handled100 = client.getConversation(request.conversation()).getAttribute(ATTRIBUTE) != null;
|
||||
return expect100 && !handled100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response.Listener getResponseListener()
|
||||
{
|
||||
// Return new instances every time to keep track of the response content
|
||||
return new ContinueListener();
|
||||
}
|
||||
|
||||
private class ContinueListener extends BufferingResponseListener
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(Response response)
|
||||
{
|
||||
// Handling of success must be done here and not from onComplete(),
|
||||
// since the onComplete() is not invoked because the request is not completed yet.
|
||||
|
||||
HttpConversation conversation = client.getConversation(response.conversation());
|
||||
// Mark the 100 Continue response as handled
|
||||
conversation.setAttribute(ATTRIBUTE, Boolean.TRUE);
|
||||
|
||||
HttpExchange exchange = conversation.exchanges().peekLast();
|
||||
assert exchange.response() == response;
|
||||
Response.Listener listener = exchange.listener();
|
||||
switch (response.status())
|
||||
{
|
||||
case 100:
|
||||
{
|
||||
// All good, continue
|
||||
exchange.resetResponse(true);
|
||||
conversation.listener(listener);
|
||||
exchange.proceed(true);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// Server either does not support 100 Continue, or it does and wants to refuse the request content
|
||||
HttpContentResponse contentResponse = new HttpContentResponse(response, getContent(), getEncoding());
|
||||
notifier.forwardSuccess(listener, contentResponse);
|
||||
conversation.listener(listener);
|
||||
exchange.proceed(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Response response, Throwable failure)
|
||||
{
|
||||
HttpConversation conversation = client.getConversation(response.conversation());
|
||||
// Mark the 100 Continue response as handled
|
||||
conversation.setAttribute(ATTRIBUTE, Boolean.TRUE);
|
||||
|
||||
HttpExchange exchange = conversation.exchanges().peekLast();
|
||||
assert exchange.response() == response;
|
||||
Response.Listener listener = exchange.listener();
|
||||
HttpContentResponse contentResponse = new HttpContentResponse(response, getContent(), getEncoding());
|
||||
notifier.forwardFailureComplete(listener, exchange.request(), exchange.requestFailure(), contentResponse, failure);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
|||
import java.net.ConnectException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.URI;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.SocketChannel;
|
||||
|
@ -120,6 +121,8 @@ public class HttpClient extends ContainerLifeCycle
|
|||
private volatile int maxRedirects = 8;
|
||||
private volatile SocketAddress bindAddress;
|
||||
private volatile long idleTimeout;
|
||||
private volatile boolean tcpNoDelay = true;
|
||||
private volatile boolean dispatchIO = true;
|
||||
|
||||
public HttpClient()
|
||||
{
|
||||
|
@ -140,7 +143,11 @@ public class HttpClient extends ContainerLifeCycle
|
|||
protected void doStart() throws Exception
|
||||
{
|
||||
if (sslContextFactory != null)
|
||||
{
|
||||
addBean(sslContextFactory);
|
||||
// Avoid to double dispatch when using SSL
|
||||
setDispatchIO(false);
|
||||
}
|
||||
|
||||
if (executor == null)
|
||||
executor = new QueuedThreadPool();
|
||||
|
@ -151,12 +158,13 @@ public class HttpClient extends ContainerLifeCycle
|
|||
addBean(byteBufferPool);
|
||||
|
||||
if (scheduler == null)
|
||||
scheduler = new TimerScheduler();
|
||||
scheduler = new TimerScheduler(HttpClient.class.getSimpleName() + "@" + hashCode() + "-Scheduler");
|
||||
addBean(scheduler);
|
||||
|
||||
selectorManager = newSelectorManager();
|
||||
addBean(selectorManager);
|
||||
|
||||
handlers.add(new ContinueProtocolHandler(this));
|
||||
handlers.add(new RedirectProtocolHandler(this));
|
||||
handlers.add(new AuthenticationProtocolHandler(this));
|
||||
|
||||
|
@ -169,7 +177,7 @@ public class HttpClient extends ContainerLifeCycle
|
|||
|
||||
protected SelectorManager newSelectorManager()
|
||||
{
|
||||
return new ClientSelectorManager();
|
||||
return new ClientSelectorManager(getExecutor(), getScheduler());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -314,7 +322,7 @@ public class HttpClient extends ContainerLifeCycle
|
|||
SocketAddress bindAddress = getBindAddress();
|
||||
if (bindAddress != null)
|
||||
channel.bind(bindAddress);
|
||||
channel.socket().setTcpNoDelay(true);
|
||||
configure(channel);
|
||||
channel.configureBlocking(false);
|
||||
channel.connect(new InetSocketAddress(destination.host(), destination.port()));
|
||||
|
||||
|
@ -329,6 +337,11 @@ public class HttpClient extends ContainerLifeCycle
|
|||
}
|
||||
}
|
||||
|
||||
protected void configure(SocketChannel channel) throws SocketException
|
||||
{
|
||||
channel.socket().setTcpNoDelay(isTCPNoDelay());
|
||||
}
|
||||
|
||||
private void close(SocketChannel channel)
|
||||
{
|
||||
try
|
||||
|
@ -341,9 +354,8 @@ public class HttpClient extends ContainerLifeCycle
|
|||
}
|
||||
}
|
||||
|
||||
protected HttpConversation getConversation(Request request)
|
||||
protected HttpConversation getConversation(long id)
|
||||
{
|
||||
long id = request.id();
|
||||
HttpConversation conversation = conversations.get(id);
|
||||
if (conversation == null)
|
||||
{
|
||||
|
@ -363,13 +375,17 @@ public class HttpClient extends ContainerLifeCycle
|
|||
LOG.debug("{} removed", conversation);
|
||||
}
|
||||
|
||||
// TODO: find a better method name
|
||||
protected Response.Listener lookup(Request request, Response response)
|
||||
protected List<ProtocolHandler> getProtocolHandlers()
|
||||
{
|
||||
for (ProtocolHandler handler : handlers)
|
||||
return handlers;
|
||||
}
|
||||
|
||||
protected ProtocolHandler findProtocolHandler(Request request, Response response)
|
||||
{
|
||||
for (ProtocolHandler handler : getProtocolHandlers())
|
||||
{
|
||||
if (handler.accept(request, response))
|
||||
return handler.getResponseListener();
|
||||
return handler;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -507,6 +523,42 @@ public class HttpClient extends ContainerLifeCycle
|
|||
this.maxRedirects = maxRedirects;
|
||||
}
|
||||
|
||||
public boolean isTCPNoDelay()
|
||||
{
|
||||
return tcpNoDelay;
|
||||
}
|
||||
|
||||
public void setTCPNoDelay(boolean tcpNoDelay)
|
||||
{
|
||||
this.tcpNoDelay = tcpNoDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true to dispatch I/O operations in a different thread, false to execute them in the selector thread
|
||||
* @see #setDispatchIO(boolean)
|
||||
*/
|
||||
public boolean isDispatchIO()
|
||||
{
|
||||
return dispatchIO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to dispatch I/O operations from the selector thread to a different thread.
|
||||
* <p />
|
||||
* This implementation never blocks on I/O operation, but invokes application callbacks that may
|
||||
* take time to execute or block on other I/O.
|
||||
* If application callbacks are known to take time or block on I/O, then parameter {@code dispatchIO}
|
||||
* must be set to true.
|
||||
* If application callbacks are known to be quick and never block on I/O, then parameter {@code dispatchIO}
|
||||
* may be set to false.
|
||||
*
|
||||
* @param dispatchIO true to dispatch I/O operations in a different thread, false to execute them in the selector thread
|
||||
*/
|
||||
public void setDispatchIO(boolean dispatchIO)
|
||||
{
|
||||
this.dispatchIO = dispatchIO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(Appendable out, String indent) throws IOException
|
||||
{
|
||||
|
@ -516,20 +568,20 @@ public class HttpClient extends ContainerLifeCycle
|
|||
|
||||
protected class ClientSelectorManager extends SelectorManager
|
||||
{
|
||||
public ClientSelectorManager()
|
||||
public ClientSelectorManager(Executor executor, Scheduler scheduler)
|
||||
{
|
||||
this(1);
|
||||
this(executor, scheduler, 1);
|
||||
}
|
||||
|
||||
public ClientSelectorManager(int selectors)
|
||||
public ClientSelectorManager(Executor executor, Scheduler scheduler, int selectors)
|
||||
{
|
||||
super(selectors);
|
||||
super(executor, scheduler, selectors);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EndPoint newEndPoint(SocketChannel channel, ManagedSelector selector, SelectionKey key)
|
||||
{
|
||||
return new SelectChannelEndPoint(channel, selector, key, scheduler, getIdleTimeout());
|
||||
return new SelectChannelEndPoint(channel, selector, key, getScheduler(), getIdleTimeout());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -574,19 +626,12 @@ public class HttpClient extends ContainerLifeCycle
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void connectionFailed(SocketChannel channel, Throwable ex, Object attachment)
|
||||
{
|
||||
ConnectionCallback callback = (ConnectionCallback)attachment;
|
||||
callback.callback.failed(null, ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(Runnable task)
|
||||
{
|
||||
getExecutor().execute(task);
|
||||
}
|
||||
}
|
||||
|
||||
private class ConnectionCallback extends FutureCallback<Connection>
|
||||
|
|
|
@ -54,10 +54,11 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
private final HttpDestination destination;
|
||||
private final HttpSender sender;
|
||||
private final HttpReceiver receiver;
|
||||
private long idleTimeout;
|
||||
|
||||
public HttpConnection(HttpClient client, EndPoint endPoint, HttpDestination destination)
|
||||
{
|
||||
super(endPoint, client.getExecutor());
|
||||
super(endPoint, client.getExecutor(), client.isDispatchIO());
|
||||
this.client = client;
|
||||
this.destination = destination;
|
||||
this.sender = new HttpSender(this);
|
||||
|
@ -104,11 +105,18 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
public void send(Request request, Response.Listener listener)
|
||||
{
|
||||
normalizeRequest(request);
|
||||
HttpConversation conversation = client.getConversation(request);
|
||||
|
||||
// Save the old idle timeout to restore it
|
||||
EndPoint endPoint = getEndPoint();
|
||||
idleTimeout = endPoint.getIdleTimeout();
|
||||
endPoint.setIdleTimeout(request.idleTimeout());
|
||||
|
||||
HttpConversation conversation = client.getConversation(request.conversation());
|
||||
HttpExchange exchange = new HttpExchange(conversation, this, request, listener);
|
||||
setExchange(exchange);
|
||||
conversation.exchanges().offer(exchange);
|
||||
conversation.listener(listener);
|
||||
|
||||
sender.send(exchange);
|
||||
}
|
||||
|
||||
|
@ -292,6 +300,9 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
HttpExchange existing = this.exchange.getAndSet(null);
|
||||
if (existing == exchange)
|
||||
{
|
||||
// Restore idle timeout
|
||||
getEndPoint().setIdleTimeout(idleTimeout);
|
||||
|
||||
LOG.debug("{} disassociated from {}", exchange, this);
|
||||
if (success)
|
||||
{
|
||||
|
@ -337,6 +348,11 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
receiver.fail(new HttpResponseException("Response aborted", response));
|
||||
}
|
||||
|
||||
public void proceed(boolean proceed)
|
||||
{
|
||||
sender.proceed(proceed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
|
|
|
@ -39,6 +39,12 @@ public class HttpContentResponse implements ContentResponse
|
|||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long conversation()
|
||||
{
|
||||
return response.conversation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener listener()
|
||||
{
|
||||
|
@ -94,4 +100,15 @@ public class HttpContentResponse implements ContentResponse
|
|||
throw new UnsupportedCharsetException(encoding);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s[%s %d %s - %d bytes]",
|
||||
HttpContentResponse.class.getSimpleName(),
|
||||
version(),
|
||||
status(),
|
||||
reason(),
|
||||
content().length);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,16 +63,25 @@ public class HttpConversation implements Attributes
|
|||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the exchange that has been identified as the last of this conversation
|
||||
* @see #last(HttpExchange)
|
||||
*/
|
||||
public HttpExchange last()
|
||||
{
|
||||
return last;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remembers the given {@code exchange} as the last of this conversation.
|
||||
*
|
||||
* @param exchange the exchange that is the last of this conversation
|
||||
* @see #last()
|
||||
*/
|
||||
public void last(HttpExchange exchange)
|
||||
{
|
||||
if (last == null)
|
||||
|
||||
last = exchange;
|
||||
last = exchange;
|
||||
}
|
||||
|
||||
public void complete()
|
||||
|
|
|
@ -46,7 +46,6 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable
|
|||
private static final Logger LOG = Log.getLogger(HttpDestination.class);
|
||||
|
||||
private final AtomicInteger connectionCount = new AtomicInteger();
|
||||
private final ResponseNotifier responseNotifier = new ResponseNotifier();
|
||||
private final HttpClient client;
|
||||
private final String scheme;
|
||||
private final String host;
|
||||
|
@ -55,6 +54,7 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable
|
|||
private final BlockingQueue<Connection> idleConnections;
|
||||
private final BlockingQueue<Connection> activeConnections;
|
||||
private final RequestNotifier requestNotifier;
|
||||
private final ResponseNotifier responseNotifier;
|
||||
|
||||
public HttpDestination(HttpClient client, String scheme, String host, int port)
|
||||
{
|
||||
|
@ -66,6 +66,7 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable
|
|||
this.idleConnections = new ArrayBlockingQueue<>(client.getMaxConnectionsPerAddress());
|
||||
this.activeConnections = new ArrayBlockingQueue<>(client.getMaxConnectionsPerAddress());
|
||||
this.requestNotifier = new RequestNotifier(client);
|
||||
this.responseNotifier = new ResponseNotifier(client);
|
||||
}
|
||||
|
||||
protected BlockingQueue<Connection> getIdleConnections()
|
||||
|
|
|
@ -58,6 +58,11 @@ public class HttpExchange
|
|||
return request;
|
||||
}
|
||||
|
||||
public Throwable requestFailure()
|
||||
{
|
||||
return requestFailure;
|
||||
}
|
||||
|
||||
public Response.Listener listener()
|
||||
{
|
||||
return listener;
|
||||
|
@ -68,6 +73,11 @@ public class HttpExchange
|
|||
return response;
|
||||
}
|
||||
|
||||
public Throwable responseFailure()
|
||||
{
|
||||
return responseFailure;
|
||||
}
|
||||
|
||||
public void receive()
|
||||
{
|
||||
connection.receive();
|
||||
|
@ -84,9 +94,17 @@ public class HttpExchange
|
|||
public Result responseComplete(Throwable failure)
|
||||
{
|
||||
this.responseFailure = failure;
|
||||
int responseSuccess = 0b1100;
|
||||
int responseFailure = 0b0100;
|
||||
return complete(failure == null ? responseSuccess : responseFailure);
|
||||
if (failure == null)
|
||||
{
|
||||
int responseSuccess = 0b1100;
|
||||
return complete(responseSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
proceed(false);
|
||||
int responseFailure = 0b0100;
|
||||
return complete(responseFailure);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -117,7 +135,7 @@ public class HttpExchange
|
|||
if (this == conversation.last())
|
||||
conversation.complete();
|
||||
connection.complete(this, success);
|
||||
return new Result(request, requestFailure, response, responseFailure);
|
||||
return new Result(request(), requestFailure(), response(), responseFailure());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -128,6 +146,19 @@ public class HttpExchange
|
|||
connection.abort(response);
|
||||
}
|
||||
|
||||
public void resetResponse(boolean success)
|
||||
{
|
||||
int responseSuccess = 0b1100;
|
||||
int responseFailure = 0b0100;
|
||||
int code = success ? responseSuccess : responseFailure;
|
||||
complete.addAndGet(-code);
|
||||
}
|
||||
|
||||
public void proceed(boolean proceed)
|
||||
{
|
||||
connection.proceed(proceed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
|
|
@ -42,14 +42,15 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
private static final Logger LOG = Log.getLogger(HttpReceiver.class);
|
||||
|
||||
private final HttpParser parser = new HttpParser(this);
|
||||
private final ResponseNotifier notifier = new ResponseNotifier();
|
||||
private final HttpConnection connection;
|
||||
private volatile boolean failed;
|
||||
private volatile ContentDecoder decoder;
|
||||
private final ResponseNotifier notifier;
|
||||
private ContentDecoder decoder;
|
||||
private State state = State.IDLE;
|
||||
|
||||
public HttpReceiver(HttpConnection connection)
|
||||
{
|
||||
this.connection = connection;
|
||||
this.notifier = new ResponseNotifier(connection.getHttpClient());
|
||||
}
|
||||
|
||||
public void receive()
|
||||
|
@ -76,8 +77,9 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
}
|
||||
else
|
||||
{
|
||||
// Shutting down the parser may invoke messageComplete()
|
||||
if (!parser.shutdownInput())
|
||||
// Shutting down the parser may invoke messageComplete() or fail()
|
||||
parser.shutdownInput();
|
||||
if (state == State.IDLE || state == State.RECEIVE)
|
||||
fail(new EOFException());
|
||||
break;
|
||||
}
|
||||
|
@ -102,6 +104,8 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
@Override
|
||||
public boolean startResponse(HttpVersion version, int status, String reason)
|
||||
{
|
||||
state = State.RECEIVE;
|
||||
|
||||
HttpExchange exchange = connection.getExchange();
|
||||
HttpConversation conversation = exchange.conversation();
|
||||
HttpResponse response = exchange.response();
|
||||
|
@ -112,7 +116,8 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
Response.Listener currentListener = exchange.listener();
|
||||
Response.Listener initialListener = conversation.exchanges().peekFirst().listener();
|
||||
HttpClient client = connection.getHttpClient();
|
||||
Response.Listener handlerListener = client.lookup(exchange.request(), response);
|
||||
ProtocolHandler protocolHandler = client.findProtocolHandler(exchange.request(), response);
|
||||
Response.Listener handlerListener = protocolHandler == null ? null : protocolHandler.getResponseListener();
|
||||
if (handlerListener == null)
|
||||
{
|
||||
conversation.last(exchange);
|
||||
|
@ -123,6 +128,7 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
}
|
||||
else
|
||||
{
|
||||
LOG.debug("Found protocol handler {}", protocolHandler);
|
||||
if (currentListener == initialListener)
|
||||
conversation.listener(handlerListener);
|
||||
else
|
||||
|
@ -214,7 +220,7 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
{
|
||||
HttpExchange exchange = connection.getExchange();
|
||||
// The exchange may be null if it was failed before
|
||||
if (exchange != null && !failed)
|
||||
if (exchange != null && state == State.RECEIVE)
|
||||
success();
|
||||
return true;
|
||||
}
|
||||
|
@ -222,7 +228,7 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
protected void success()
|
||||
{
|
||||
parser.reset();
|
||||
decoder = null;
|
||||
state = State.SUCCESS;
|
||||
|
||||
HttpExchange exchange = connection.getExchange();
|
||||
HttpResponse response = exchange.response();
|
||||
|
@ -233,7 +239,10 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
HttpConversation conversation = exchange.conversation();
|
||||
notifier.notifySuccess(conversation.listener(), response);
|
||||
if (result != null)
|
||||
{
|
||||
notifier.notifyComplete(conversation.listener(), result);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
protected void fail(Throwable failure)
|
||||
|
@ -247,7 +256,7 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
return;
|
||||
|
||||
parser.close();
|
||||
failed = true;
|
||||
state = State.FAILURE;
|
||||
|
||||
HttpResponse response = exchange.response();
|
||||
LOG.debug("Failed {} {}", response, failure);
|
||||
|
@ -257,7 +266,10 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
HttpConversation conversation = exchange.conversation();
|
||||
notifier.notifyFailure(conversation.listener(), response, failure);
|
||||
if (result != null)
|
||||
{
|
||||
notifier.notifyComplete(conversation.listener(), result);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -281,9 +293,14 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
fail(new TimeoutException());
|
||||
}
|
||||
|
||||
private void reset()
|
||||
{
|
||||
decoder = null;
|
||||
state = State.IDLE;
|
||||
}
|
||||
|
||||
private class DoubleResponseListener implements Response.Listener
|
||||
{
|
||||
private final ResponseNotifier notifier = new ResponseNotifier();
|
||||
private final Response.Listener listener1;
|
||||
private final Response.Listener listener2;
|
||||
|
||||
|
@ -335,4 +352,9 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
notifier.notifyComplete(listener2, result);
|
||||
}
|
||||
}
|
||||
|
||||
private enum State
|
||||
{
|
||||
IDLE, RECEIVE, SUCCESS, FAILURE
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@ public class HttpRequest implements Request
|
|||
}
|
||||
|
||||
@Override
|
||||
public long id()
|
||||
public long conversation()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
|
|
@ -77,6 +77,12 @@ public class HttpResponse implements Response
|
|||
return headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long conversation()
|
||||
{
|
||||
return exchange.request().conversation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener listener()
|
||||
{
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.client;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
|
@ -28,6 +29,8 @@ import org.eclipse.jetty.client.api.ContentProvider;
|
|||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.http.HttpGenerator;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
|
@ -38,22 +41,22 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
public class HttpSender
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(HttpSender.class);
|
||||
private static final String EXPECT_100_ATTRIBUTE = HttpSender.class.getName() + ".expect100";
|
||||
|
||||
private final HttpGenerator generator = new HttpGenerator();
|
||||
private final ResponseNotifier responseNotifier = new ResponseNotifier();
|
||||
private final HttpConnection connection;
|
||||
private final RequestNotifier requestNotifier;
|
||||
private long contentLength;
|
||||
private Iterator<ByteBuffer> contentChunks;
|
||||
private ByteBuffer header;
|
||||
private ByteBuffer chunk;
|
||||
private volatile boolean committed;
|
||||
private volatile boolean failed;
|
||||
private final ResponseNotifier responseNotifier;
|
||||
private Iterator<ByteBuffer> contentIterator;
|
||||
private ContentInfo expectedContent;
|
||||
private boolean committed;
|
||||
private boolean failed;
|
||||
|
||||
public HttpSender(HttpConnection connection)
|
||||
{
|
||||
this.connection = connection;
|
||||
this.requestNotifier = new RequestNotifier(connection.getHttpClient());
|
||||
this.responseNotifier = new ResponseNotifier(connection.getHttpClient());
|
||||
}
|
||||
|
||||
public void send(HttpExchange exchange)
|
||||
|
@ -68,42 +71,70 @@ public class HttpSender
|
|||
LOG.debug("Sending {}", request);
|
||||
requestNotifier.notifyBegin(request);
|
||||
ContentProvider content = request.content();
|
||||
this.contentLength = content == null ? -1 : content.length();
|
||||
this.contentChunks = content == null ? Collections.<ByteBuffer>emptyIterator() : content.iterator();
|
||||
this.contentIterator = content == null ? Collections.<ByteBuffer>emptyIterator() : content.iterator();
|
||||
send();
|
||||
}
|
||||
}
|
||||
|
||||
public void proceed(boolean proceed)
|
||||
{
|
||||
ContentInfo contentInfo = expectedContent;
|
||||
if (contentInfo != null)
|
||||
{
|
||||
contentInfo.await();
|
||||
if (proceed)
|
||||
send();
|
||||
else
|
||||
fail(new HttpRequestException("Expectation failed", connection.getExchange().request()));
|
||||
}
|
||||
}
|
||||
|
||||
private void send()
|
||||
{
|
||||
HttpClient client = connection.getHttpClient();
|
||||
ByteBufferPool bufferPool = client.getByteBufferPool();
|
||||
ByteBuffer header = null;
|
||||
ByteBuffer chunk = null;
|
||||
try
|
||||
{
|
||||
HttpClient client = connection.getHttpClient();
|
||||
EndPoint endPoint = connection.getEndPoint();
|
||||
HttpExchange exchange = connection.getExchange();
|
||||
ByteBufferPool byteBufferPool = client.getByteBufferPool();
|
||||
final Request request = exchange.request();
|
||||
HttpGenerator.RequestInfo info = null;
|
||||
ByteBuffer content = contentChunks.hasNext() ? contentChunks.next() : BufferUtil.EMPTY_BUFFER;
|
||||
boolean lastContent = !contentChunks.hasNext();
|
||||
HttpConversation conversation = client.getConversation(request.conversation());
|
||||
HttpGenerator.RequestInfo requestInfo = null;
|
||||
|
||||
boolean expect100 = request.headers().contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString());
|
||||
expect100 &= conversation.getAttribute(EXPECT_100_ATTRIBUTE) == null;
|
||||
if (expect100)
|
||||
conversation.setAttribute(EXPECT_100_ATTRIBUTE, Boolean.TRUE);
|
||||
|
||||
ContentInfo contentInfo = this.expectedContent;
|
||||
if (contentInfo == null)
|
||||
contentInfo = new ContentInfo(contentIterator);
|
||||
else
|
||||
expect100 = false;
|
||||
this.expectedContent = null;
|
||||
|
||||
while (true)
|
||||
{
|
||||
HttpGenerator.Result result = generator.generateRequest(info, header, chunk, content, lastContent);
|
||||
HttpGenerator.Result result = generator.generateRequest(requestInfo, header, chunk, contentInfo.content, contentInfo.lastContent);
|
||||
switch (result)
|
||||
{
|
||||
case NEED_INFO:
|
||||
{
|
||||
info = new HttpGenerator.RequestInfo(request.version(), request.headers(), contentLength, request.method().asString(), request.path());
|
||||
ContentProvider content = request.content();
|
||||
long contentLength = content == null ? -1 : content.length();
|
||||
requestInfo = new HttpGenerator.RequestInfo(request.version(), request.headers(), contentLength, request.method().asString(), request.path());
|
||||
break;
|
||||
}
|
||||
case NEED_HEADER:
|
||||
{
|
||||
header = byteBufferPool.acquire(client.getRequestBufferSize(), false);
|
||||
header = bufferPool.acquire(client.getRequestBufferSize(), false);
|
||||
break;
|
||||
}
|
||||
case NEED_CHUNK:
|
||||
{
|
||||
chunk = byteBufferPool.acquire(HttpGenerator.CHUNK_SIZE, false);
|
||||
chunk = bufferPool.acquire(HttpGenerator.CHUNK_SIZE, false);
|
||||
break;
|
||||
}
|
||||
case FLUSH:
|
||||
|
@ -119,9 +150,20 @@ public class HttpSender
|
|||
@Override
|
||||
protected void pendingCompleted()
|
||||
{
|
||||
LOG.debug("Write completed for {}", request);
|
||||
|
||||
if (!committed)
|
||||
committed(request);
|
||||
send();
|
||||
|
||||
if (expectedContent == null)
|
||||
{
|
||||
send();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.debug("Expecting 100 Continue for {}", request);
|
||||
expectedContent.ready();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -130,22 +172,37 @@ public class HttpSender
|
|||
fail(x);
|
||||
}
|
||||
};
|
||||
if (header == null)
|
||||
header = BufferUtil.EMPTY_BUFFER;
|
||||
if (chunk == null)
|
||||
chunk = BufferUtil.EMPTY_BUFFER;
|
||||
endPoint.write(null, callback, header, chunk, content);
|
||||
|
||||
if (expect100)
|
||||
{
|
||||
// Save the expected content waiting for the 100 Continue response
|
||||
expectedContent = contentInfo;
|
||||
}
|
||||
|
||||
write(callback, header, chunk, expect100 ? null : contentInfo.content);
|
||||
|
||||
if (callback.pending())
|
||||
{
|
||||
LOG.debug("Write pending for {}", request);
|
||||
return;
|
||||
}
|
||||
|
||||
if (callback.completed())
|
||||
{
|
||||
if (!committed)
|
||||
committed(request);
|
||||
|
||||
releaseBuffers();
|
||||
content = contentChunks.hasNext() ? contentChunks.next() : BufferUtil.EMPTY_BUFFER;
|
||||
lastContent = !contentChunks.hasNext();
|
||||
if (expect100)
|
||||
{
|
||||
LOG.debug("Expecting 100 Continue for {}", request);
|
||||
expectedContent.ready();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send further content
|
||||
contentInfo = new ContentInfo(contentIterator);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -179,7 +236,49 @@ public class HttpSender
|
|||
}
|
||||
finally
|
||||
{
|
||||
releaseBuffers();
|
||||
releaseBuffers(bufferPool, header, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
private void write(Callback<Void> callback, ByteBuffer header, ByteBuffer chunk, ByteBuffer content)
|
||||
{
|
||||
int mask = 0;
|
||||
if (header != null)
|
||||
mask += 1;
|
||||
if (chunk != null)
|
||||
mask += 2;
|
||||
if (content != null)
|
||||
mask += 4;
|
||||
|
||||
EndPoint endPoint = connection.getEndPoint();
|
||||
switch (mask)
|
||||
{
|
||||
case 0:
|
||||
endPoint.write(null, callback, BufferUtil.EMPTY_BUFFER);
|
||||
break;
|
||||
case 1:
|
||||
endPoint.write(null, callback, header);
|
||||
break;
|
||||
case 2:
|
||||
endPoint.write(null, callback, chunk);
|
||||
break;
|
||||
case 3:
|
||||
endPoint.write(null, callback, header, chunk);
|
||||
break;
|
||||
case 4:
|
||||
endPoint.write(null, callback, content);
|
||||
break;
|
||||
case 5:
|
||||
endPoint.write(null, callback, header, content);
|
||||
break;
|
||||
case 6:
|
||||
endPoint.write(null, callback, chunk, content);
|
||||
break;
|
||||
case 7:
|
||||
endPoint.write(null, callback, header, chunk, content);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,9 +315,6 @@ public class HttpSender
|
|||
protected void fail(Throwable failure)
|
||||
{
|
||||
// Cleanup first
|
||||
BufferUtil.clear(header);
|
||||
BufferUtil.clear(chunk);
|
||||
releaseBuffers();
|
||||
generator.abort();
|
||||
failed = true;
|
||||
|
||||
|
@ -245,19 +341,12 @@ public class HttpSender
|
|||
}
|
||||
}
|
||||
|
||||
private void releaseBuffers()
|
||||
private void releaseBuffers(ByteBufferPool bufferPool, ByteBuffer header, ByteBuffer chunk)
|
||||
{
|
||||
ByteBufferPool bufferPool = connection.getHttpClient().getByteBufferPool();
|
||||
if (!BufferUtil.hasContent(header))
|
||||
{
|
||||
bufferPool.release(header);
|
||||
header = null;
|
||||
}
|
||||
if (!BufferUtil.hasContent(chunk))
|
||||
{
|
||||
bufferPool.release(chunk);
|
||||
chunk = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static abstract class StatefulExecutorCallback implements Callback<Void>, Runnable
|
||||
|
@ -341,4 +430,34 @@ public class HttpSender
|
|||
INCOMPLETE, PENDING, COMPLETE, FAILED
|
||||
}
|
||||
}
|
||||
|
||||
private class ContentInfo
|
||||
{
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
public final boolean lastContent;
|
||||
public final ByteBuffer content;
|
||||
|
||||
public ContentInfo(Iterator<ByteBuffer> contentIterator)
|
||||
{
|
||||
lastContent = !contentIterator.hasNext();
|
||||
content = lastContent ? BufferUtil.EMPTY_BUFFER : contentIterator.next();
|
||||
}
|
||||
|
||||
public void ready()
|
||||
{
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
public void await()
|
||||
{
|
||||
try
|
||||
{
|
||||
latch.await();
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
throw new IllegalStateException(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,12 +28,13 @@ public class RedirectProtocolHandler extends Response.Listener.Empty implements
|
|||
{
|
||||
private static final String ATTRIBUTE = RedirectProtocolHandler.class.getName() + ".redirect";
|
||||
|
||||
private final ResponseNotifier notifier = new ResponseNotifier();
|
||||
private final HttpClient client;
|
||||
private final ResponseNotifier notifier;
|
||||
|
||||
public RedirectProtocolHandler(HttpClient client)
|
||||
{
|
||||
this.client = client;
|
||||
this.notifier = new ResponseNotifier(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -104,7 +105,7 @@ public class RedirectProtocolHandler extends Response.Listener.Empty implements
|
|||
private void redirect(Result result, HttpMethod method, String location)
|
||||
{
|
||||
Request request = result.getRequest();
|
||||
HttpConversation conversation = client.getConversation(request);
|
||||
HttpConversation conversation = client.getConversation(request.conversation());
|
||||
Integer redirects = (Integer)conversation.getAttribute(ATTRIBUTE);
|
||||
if (redirects == null)
|
||||
redirects = 0;
|
||||
|
@ -114,7 +115,7 @@ public class RedirectProtocolHandler extends Response.Listener.Empty implements
|
|||
++redirects;
|
||||
conversation.setAttribute(ATTRIBUTE, redirects);
|
||||
|
||||
Request redirect = client.newRequest(request.id(), location);
|
||||
Request redirect = client.newRequest(request.conversation(), location);
|
||||
|
||||
// Use given method
|
||||
redirect.method(method);
|
||||
|
@ -140,7 +141,7 @@ public class RedirectProtocolHandler extends Response.Listener.Empty implements
|
|||
{
|
||||
Request request = result.getRequest();
|
||||
Response response = result.getResponse();
|
||||
HttpConversation conversation = client.getConversation(request);
|
||||
HttpConversation conversation = client.getConversation(request.conversation());
|
||||
Response.Listener listener = conversation.exchanges().peekFirst().listener();
|
||||
// TODO: should we reply all event, or just the failure ?
|
||||
notifier.notifyFailure(listener, response, failure);
|
||||
|
|
|
@ -20,6 +20,8 @@ package org.eclipse.jetty.client;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
@ -28,6 +30,12 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
public class ResponseNotifier
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(ResponseNotifier.class);
|
||||
private final HttpClient client;
|
||||
|
||||
public ResponseNotifier(HttpClient client)
|
||||
{
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public void notifyBegin(Response.Listener listener, Response response)
|
||||
{
|
||||
|
@ -106,4 +114,38 @@ public class ResponseNotifier
|
|||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
public void forwardSuccess(Response.Listener listener, Response response)
|
||||
{
|
||||
notifyBegin(listener, response);
|
||||
notifyHeaders(listener, response);
|
||||
if (response instanceof ContentResponse)
|
||||
notifyContent(listener, response, ByteBuffer.wrap(((ContentResponse)response).content()));
|
||||
notifySuccess(listener, response);
|
||||
}
|
||||
|
||||
public void forwardSuccessComplete(Response.Listener listener, Request request, Response response)
|
||||
{
|
||||
HttpConversation conversation = client.getConversation(request.conversation());
|
||||
forwardSuccess(listener, response);
|
||||
conversation.complete();
|
||||
notifyComplete(listener, new Result(request, response));
|
||||
}
|
||||
|
||||
public void forwardFailure(Response.Listener listener, Response response, Throwable failure)
|
||||
{
|
||||
notifyBegin(listener, response);
|
||||
notifyHeaders(listener, response);
|
||||
if (response instanceof ContentResponse)
|
||||
notifyContent(listener, response, ByteBuffer.wrap(((ContentResponse)response).content()));
|
||||
notifyFailure(listener, response, failure);
|
||||
}
|
||||
|
||||
public void forwardFailureComplete(Response.Listener listener, Request request, Throwable requestFailure, Response response, Throwable responseFailure)
|
||||
{
|
||||
HttpConversation conversation = client.getConversation(request.conversation());
|
||||
forwardFailure(listener, response, responseFailure);
|
||||
conversation.complete();
|
||||
notifyComplete(listener, new Result(request, requestFailure, response, responseFailure));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ public interface Request
|
|||
/**
|
||||
* @return the conversation id
|
||||
*/
|
||||
long id();
|
||||
long conversation();
|
||||
|
||||
/**
|
||||
* @return the scheme of this request, such as "http" or "https"
|
||||
|
|
|
@ -36,6 +36,11 @@ import org.eclipse.jetty.http.HttpVersion;
|
|||
*/
|
||||
public interface Response
|
||||
{
|
||||
/**
|
||||
* @return the conversation id
|
||||
*/
|
||||
long conversation();
|
||||
|
||||
/**
|
||||
* @return the response listener passed to {@link Request#send(Listener)}
|
||||
*/
|
||||
|
|
|
@ -23,16 +23,17 @@ import java.util.Collection;
|
|||
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.NetworkConnector;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.toolchain.test.TestTracker;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.rules.TestWatchman;
|
||||
import org.junit.rules.TestWatcher;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public abstract class AbstractHttpClientServerTest
|
||||
|
@ -44,17 +45,7 @@ public abstract class AbstractHttpClientServerTest
|
|||
}
|
||||
|
||||
@Rule
|
||||
public final TestWatchman testName = new TestWatchman()
|
||||
{
|
||||
@Override
|
||||
public void starting(FrameworkMethod method)
|
||||
{
|
||||
super.starting(method);
|
||||
System.err.printf("Running %s.%s()%n",
|
||||
method.getMethod().getDeclaringClass().getName(),
|
||||
method.getName());
|
||||
}
|
||||
};
|
||||
public final TestTracker tracker = new TestTracker();
|
||||
|
||||
protected SslContextFactory sslContextFactory;
|
||||
protected String scheme;
|
||||
|
|
|
@ -0,0 +1,440 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2012 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.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.client;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
||||
import org.eclipse.jetty.client.util.BytesContentProvider;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.toolchain.test.annotation.Slow;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HttpClientContinueTest extends AbstractHttpClientServerTest
|
||||
{
|
||||
public HttpClientContinueTest(SslContextFactory sslContextFactory)
|
||||
{
|
||||
super(sslContextFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Expect100Continue_WithOneContent_Respond100Continue() throws Exception
|
||||
{
|
||||
test_Expect100Continue_Respond100Continue("data1".getBytes("UTF-8"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Expect100Continue_WithMultipleContents_Respond100Continue() throws Exception
|
||||
{
|
||||
test_Expect100Continue_Respond100Continue("data1".getBytes("UTF-8"), "data2".getBytes("UTF-8"), "data3".getBytes("UTF-8"));
|
||||
}
|
||||
|
||||
private void test_Expect100Continue_Respond100Continue(byte[]... contents) throws Exception
|
||||
{
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
// Send 100-Continue and copy the content back
|
||||
IO.copy(request.getInputStream(), response.getOutputStream());
|
||||
}
|
||||
});
|
||||
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.header(HttpHeader.EXPECT.asString(), HttpHeaderValue.CONTINUE.asString())
|
||||
.content(new BytesContentProvider(contents))
|
||||
.send()
|
||||
.get(5, TimeUnit.SECONDS);
|
||||
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.status());
|
||||
|
||||
int index = 0;
|
||||
byte[] responseContent = response.content();
|
||||
for (byte[] content : contents)
|
||||
{
|
||||
for (byte b : content)
|
||||
{
|
||||
Assert.assertEquals(b, responseContent[index++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Expect100Continue_WithChunkedContent_Respond100Continue() throws Exception
|
||||
{
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
// Send 100-Continue and copy the content back
|
||||
ServletInputStream input = request.getInputStream();
|
||||
// Make sure we chunk the response too
|
||||
response.flushBuffer();
|
||||
IO.copy(input, response.getOutputStream());
|
||||
}
|
||||
});
|
||||
|
||||
byte[] content1 = new byte[10240];
|
||||
byte[] content2 = new byte[16384];
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.header(HttpHeader.EXPECT.asString(), HttpHeaderValue.CONTINUE.asString())
|
||||
.content(new BytesContentProvider(content1, content2)
|
||||
{
|
||||
@Override
|
||||
public long length()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
})
|
||||
.send()
|
||||
.get(5, TimeUnit.SECONDS);
|
||||
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.status());
|
||||
|
||||
int index = 0;
|
||||
byte[] responseContent = response.content();
|
||||
for (byte b : content1)
|
||||
Assert.assertEquals(b, responseContent[index++]);
|
||||
for (byte b : content2)
|
||||
Assert.assertEquals(b, responseContent[index++]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Expect100Continue_WithContent_Respond417ExpectationFailed() throws Exception
|
||||
{
|
||||
test_Expect100Continue_WithContent_RespondError(417);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Expect100Continue_WithContent_Respond413RequestEntityTooLarge() throws Exception
|
||||
{
|
||||
test_Expect100Continue_WithContent_RespondError(413);
|
||||
}
|
||||
|
||||
private void test_Expect100Continue_WithContent_RespondError(final int error) throws Exception
|
||||
{
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
response.sendError(error);
|
||||
}
|
||||
});
|
||||
|
||||
byte[] content1 = new byte[10240];
|
||||
byte[] content2 = new byte[16384];
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.header(HttpHeader.EXPECT.asString(), HttpHeaderValue.CONTINUE.asString())
|
||||
.content(new BytesContentProvider(content1, content2))
|
||||
.send(new BufferingResponseListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertTrue(result.isFailed());
|
||||
Assert.assertNotNull(result.getRequestFailure());
|
||||
Assert.assertNull(result.getResponseFailure());
|
||||
byte[] content = getContent();
|
||||
Assert.assertNotNull(content);
|
||||
Assert.assertTrue(content.length > 0);
|
||||
Assert.assertEquals(error, result.getResponse().status());
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Expect100Continue_WithContent_WithRedirect() throws Exception
|
||||
{
|
||||
final String data = "success";
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
if (request.getRequestURI().endsWith("/done"))
|
||||
{
|
||||
response.getOutputStream().print(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send 100-Continue and consume the content
|
||||
IO.copy(request.getInputStream(), new ByteArrayOutputStream());
|
||||
// Send a redirect
|
||||
response.sendRedirect("/done");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
byte[] content = new byte[10240];
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.method(HttpMethod.POST)
|
||||
.path("/continue")
|
||||
.header(HttpHeader.EXPECT.asString(), HttpHeaderValue.CONTINUE.asString())
|
||||
.content(new BytesContentProvider(content))
|
||||
.send(new BufferingResponseListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertFalse(result.isFailed());
|
||||
Assert.assertEquals(200, result.getResponse().status());
|
||||
Assert.assertEquals(data, getContentAsString());
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Redirect_WithExpect100Continue_WithContent() throws Exception
|
||||
{
|
||||
// A request with Expect: 100-Continue cannot receive non-final responses like 3xx
|
||||
|
||||
final String data = "success";
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
if (request.getRequestURI().endsWith("/done"))
|
||||
{
|
||||
// Send 100-Continue and consume the content
|
||||
IO.copy(request.getInputStream(), new ByteArrayOutputStream());
|
||||
response.getOutputStream().print(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send a redirect
|
||||
response.sendRedirect("/done");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
byte[] content = new byte[10240];
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.method(HttpMethod.POST)
|
||||
.path("/redirect")
|
||||
.header(HttpHeader.EXPECT.asString(), HttpHeaderValue.CONTINUE.asString())
|
||||
.content(new BytesContentProvider(content))
|
||||
.send(new BufferingResponseListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertTrue(result.isFailed());
|
||||
Assert.assertNotNull(result.getRequestFailure());
|
||||
Assert.assertNull(result.getResponseFailure());
|
||||
Assert.assertEquals(302, result.getResponse().status());
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Slow
|
||||
@Test
|
||||
public void test_Expect100Continue_WithContent_WithResponseFailure_Before100Continue() throws Exception
|
||||
{
|
||||
final long idleTimeout = 1000;
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
try
|
||||
{
|
||||
TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
throw new ServletException(x);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
client.setIdleTimeout(idleTimeout);
|
||||
|
||||
byte[] content = new byte[1024];
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.header(HttpHeader.EXPECT.asString(), HttpHeaderValue.CONTINUE.asString())
|
||||
.content(new BytesContentProvider(content))
|
||||
.send(new BufferingResponseListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertTrue(result.isFailed());
|
||||
Assert.assertNotNull(result.getRequestFailure());
|
||||
Assert.assertNotNull(result.getResponseFailure());
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(latch.await(3 * idleTimeout, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
@Slow
|
||||
@Test
|
||||
public void test_Expect100Continue_WithContent_WithResponseFailure_After100Continue() throws Exception
|
||||
{
|
||||
final long idleTimeout = 1000;
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
// Send 100-Continue and consume the content
|
||||
IO.copy(request.getInputStream(), new ByteArrayOutputStream());
|
||||
try
|
||||
{
|
||||
TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
throw new ServletException(x);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
client.setIdleTimeout(idleTimeout);
|
||||
|
||||
byte[] content = new byte[1024];
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.header(HttpHeader.EXPECT.asString(), HttpHeaderValue.CONTINUE.asString())
|
||||
.content(new BytesContentProvider(content))
|
||||
.send(new BufferingResponseListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertTrue(result.isFailed());
|
||||
Assert.assertNull(result.getRequestFailure());
|
||||
Assert.assertNotNull(result.getResponseFailure());
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(latch.await(3 * idleTimeout, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Expect100Continue_WithContent_WithResponseFailure_During100Continue() throws Exception
|
||||
{
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
// Send 100-Continue and consume the content
|
||||
IO.copy(request.getInputStream(), new ByteArrayOutputStream());
|
||||
}
|
||||
});
|
||||
|
||||
client.getProtocolHandlers().clear();
|
||||
client.getProtocolHandlers().add(new ContinueProtocolHandler(client)
|
||||
{
|
||||
@Override
|
||||
public Response.Listener getResponseListener()
|
||||
{
|
||||
final Response.Listener listener = super.getResponseListener();
|
||||
return new Response.Listener.Empty()
|
||||
{
|
||||
@Override
|
||||
public void onBegin(Response response)
|
||||
{
|
||||
response.abort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Response response, Throwable failure)
|
||||
{
|
||||
listener.onFailure(response, failure);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
byte[] content = new byte[1024];
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.header(HttpHeader.EXPECT.asString(), HttpHeaderValue.CONTINUE.asString())
|
||||
.content(new BytesContentProvider(content))
|
||||
.send(new BufferingResponseListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertTrue(result.isFailed());
|
||||
Assert.assertNotNull(result.getRequestFailure());
|
||||
Assert.assertNotNull(result.getResponseFailure());
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
|
@ -65,6 +65,7 @@ public class HttpClientLoadTest extends AbstractHttpClientServerTest
|
|||
|
||||
client.setMaxConnectionsPerAddress(32768);
|
||||
client.setMaxQueueSizePerAddress(1024 * 1024);
|
||||
client.setDispatchIO(false);
|
||||
|
||||
Random random = new Random();
|
||||
int iterations = 200;
|
||||
|
|
|
@ -29,7 +29,9 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
import javax.servlet.ServletException;
|
||||
|
@ -573,4 +575,50 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
|||
Assert.assertEquals(200, response.status());
|
||||
Assert.assertArrayEquals(data, response.content());
|
||||
}
|
||||
|
||||
@Slow
|
||||
@Test
|
||||
public void test_Request_IdleTimeout() throws Exception
|
||||
{
|
||||
final long idleTimeout = 1000;
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
try
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
throw new ServletException(x);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
final String host = "localhost";
|
||||
final int port = connector.getLocalPort();
|
||||
try
|
||||
{
|
||||
client.newRequest(host, port)
|
||||
.scheme(scheme)
|
||||
.idleTimeout(idleTimeout)
|
||||
.send().get(3 * idleTimeout, TimeUnit.MILLISECONDS);
|
||||
Assert.fail();
|
||||
}
|
||||
catch (ExecutionException expected)
|
||||
{
|
||||
Assert.assertTrue(expected.getCause() instanceof TimeoutException);
|
||||
}
|
||||
|
||||
// Make another request without specifying the idle timeout, should not fail
|
||||
ContentResponse response = client.newRequest(host, port)
|
||||
.scheme(scheme)
|
||||
.send().get(3 * idleTimeout, TimeUnit.MILLISECONDS);
|
||||
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.status());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.eclipse.jetty.client.api.Result;
|
|||
import org.eclipse.jetty.client.util.ByteBufferContentProvider;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.toolchain.test.annotation.Slow;
|
||||
import org.eclipse.jetty.util.log.StdErrLog;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.junit.Assert;
|
||||
|
@ -212,6 +213,79 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
Assert.assertEquals(0, activeConnections.size());
|
||||
}
|
||||
|
||||
@Slow
|
||||
@Test
|
||||
public void test_BadRequest_WithSlowRequest_RemovesConnection() throws Exception
|
||||
{
|
||||
start(new EmptyServerHandler());
|
||||
|
||||
String host = "localhost";
|
||||
int port = connector.getLocalPort();
|
||||
HttpDestination destination = (HttpDestination)client.getDestination(scheme, host, port);
|
||||
|
||||
final BlockingQueue<Connection> idleConnections = destination.getIdleConnections();
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
|
||||
final BlockingQueue<Connection> activeConnections = destination.getActiveConnections();
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
|
||||
final long delay = 1000;
|
||||
final CountDownLatch successLatch = new CountDownLatch(3);
|
||||
client.newRequest(host, port)
|
||||
.scheme(scheme)
|
||||
.listener(new Request.Listener.Empty()
|
||||
{
|
||||
@Override
|
||||
public void onBegin(Request request)
|
||||
{
|
||||
// Remove the host header, this will make the request invalid
|
||||
request.header(HttpHeader.HOST.asString(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeaders(Request request)
|
||||
{
|
||||
try
|
||||
{
|
||||
TimeUnit.MILLISECONDS.sleep(delay);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Request request)
|
||||
{
|
||||
successLatch.countDown();
|
||||
}
|
||||
})
|
||||
.send(new Response.Listener.Empty()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(Response response)
|
||||
{
|
||||
Assert.assertEquals(400, response.status());
|
||||
// 400 response also come with a Connection: close,
|
||||
// so the connection is closed and removed
|
||||
successLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertFalse(result.isFailed());
|
||||
successLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(successLatch.await(delay * 5, TimeUnit.MILLISECONDS));
|
||||
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ConnectionFailure_RemovesConnection() throws Exception
|
||||
{
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.client;
|
|||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
@ -67,7 +68,8 @@ public class HttpReceiverTest
|
|||
|
||||
protected HttpExchange newExchange(Response.Listener listener)
|
||||
{
|
||||
HttpExchange exchange = new HttpExchange(conversation, connection, null, listener);
|
||||
HttpRequest request = new HttpRequest(client, URI.create("http://localhost"));
|
||||
HttpExchange exchange = new HttpExchange(conversation, connection, request, listener);
|
||||
conversation.exchanges().offer(exchange);
|
||||
connection.setExchange(exchange);
|
||||
exchange.requestComplete(null);
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
#org.eclipse.jetty.LEVEL=DEBUG
|
||||
org.eclipse.jetty.client.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.client.LEVEL=DEBUG
|
||||
|
|
|
@ -198,15 +198,26 @@ public class HttpGenerator
|
|||
else
|
||||
generateHeaders(info,header,content,last);
|
||||
|
||||
// handle the content.
|
||||
int len = BufferUtil.length(content);
|
||||
if (len>0)
|
||||
boolean expect100 = info.getHttpFields().contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString());
|
||||
|
||||
if (expect100)
|
||||
{
|
||||
_contentPrepared+=len;
|
||||
if (isChunking())
|
||||
prepareChunk(header,len);
|
||||
_state = State.COMMITTED;
|
||||
}
|
||||
_state = last?State.COMPLETING:State.COMMITTED;
|
||||
else
|
||||
{
|
||||
// handle the content.
|
||||
int len = BufferUtil.length(content);
|
||||
if (len>0)
|
||||
{
|
||||
_contentPrepared+=len;
|
||||
if (isChunking())
|
||||
prepareChunk(header,len);
|
||||
}
|
||||
_state = last?State.COMPLETING:State.COMMITTED;
|
||||
}
|
||||
|
||||
return Result.FLUSH;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
|
@ -217,8 +228,6 @@ public class HttpGenerator
|
|||
{
|
||||
BufferUtil.flipToFlush(header,pos);
|
||||
}
|
||||
|
||||
return Result.FLUSH;
|
||||
}
|
||||
|
||||
case COMMITTED:
|
||||
|
|
|
@ -46,21 +46,30 @@ public abstract class AbstractConnection implements Connection
|
|||
private final EndPoint _endPoint;
|
||||
private final Executor _executor;
|
||||
private final Callback<Void> _readCallback;
|
||||
private int _inputBufferSize=8192;
|
||||
private int _inputBufferSize=2048;
|
||||
|
||||
public AbstractConnection(EndPoint endp, Executor executor)
|
||||
{
|
||||
this(endp, executor, true);
|
||||
this(endp,executor,true);
|
||||
}
|
||||
|
||||
public AbstractConnection(EndPoint endp, Executor executor, final boolean dispatchCompletion)
|
||||
|
||||
public AbstractConnection(EndPoint endp, Executor executor, final boolean executeOnfillable)
|
||||
{
|
||||
if (executor == null)
|
||||
throw new IllegalArgumentException("Executor must not be null!");
|
||||
_endPoint = endp;
|
||||
_executor = executor;
|
||||
_readCallback = new ExecutorCallback<Void>(executor)
|
||||
_readCallback = new ExecutorCallback<Void>(executor,0)
|
||||
{
|
||||
@Override
|
||||
public void completed(Void context)
|
||||
{
|
||||
if (executeOnfillable)
|
||||
super.completed(context);
|
||||
else
|
||||
onCompleted(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCompleted(Void context)
|
||||
{
|
||||
|
@ -107,16 +116,10 @@ public abstract class AbstractConnection implements Connection
|
|||
onFillInterestedFailed(x);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean alwaysDispatchCompletion()
|
||||
{
|
||||
return dispatchCompletion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("AC.ReadCB@%x", AbstractConnection.this.hashCode());
|
||||
return String.format("AC.ExReadCB@%x", AbstractConnection.this.hashCode());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -137,11 +140,11 @@ public abstract class AbstractConnection implements Connection
|
|||
_inputBufferSize = inputBufferSize;
|
||||
}
|
||||
|
||||
public Executor getExecutor()
|
||||
protected Executor getExecutor()
|
||||
{
|
||||
return _executor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Utility method to be called to register read interest.</p>
|
||||
* <p>After a call to this method, {@link #onFillable()} or {@link #onFillInterestedFailed(Throwable)}
|
||||
|
@ -186,7 +189,7 @@ public abstract class AbstractConnection implements Connection
|
|||
* <p>Callback method invoked when the endpoint failed to be ready to be read.</p>
|
||||
* @param cause the exception that caused the failure
|
||||
*/
|
||||
public void onFillInterestedFailed(Throwable cause)
|
||||
protected void onFillInterestedFailed(Throwable cause)
|
||||
{
|
||||
LOG.debug("{} onFillInterestedFailed {}", this, cause);
|
||||
if (_endPoint.isOpen())
|
||||
|
|
|
@ -202,11 +202,12 @@ public abstract class AbstractEndPoint implements EndPoint
|
|||
{
|
||||
LOG.debug("{} idle timeout expired", this);
|
||||
|
||||
boolean output_shutdown=isOutputShutdown();
|
||||
TimeoutException timeout = new TimeoutException("Idle timeout expired: " + idleElapsed + "/" + idleTimeout + " ms");
|
||||
_fillInterest.onFail(timeout);
|
||||
_writeFlusher.onFail(timeout);
|
||||
|
||||
if (isOutputShutdown())
|
||||
if (output_shutdown)
|
||||
close();
|
||||
notIdle();
|
||||
}
|
||||
|
|
|
@ -75,11 +75,14 @@ public class ArrayByteBufferPool implements ByteBufferPool
|
|||
@Override
|
||||
public void release(ByteBuffer buffer)
|
||||
{
|
||||
Bucket bucket = bucketFor(buffer.capacity(),buffer.isDirect());
|
||||
if (bucket!=null)
|
||||
{
|
||||
BufferUtil.clear(buffer);
|
||||
bucket._queue.offer(buffer);
|
||||
if (buffer!=null)
|
||||
{
|
||||
Bucket bucket = bucketFor(buffer.capacity(),buffer.isDirect());
|
||||
if (bucket!=null)
|
||||
{
|
||||
BufferUtil.clear(buffer);
|
||||
bucket._queue.offer(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,511 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2012 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.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.io;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.annotation.Name;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/**
|
||||
* TODO this class is still experimental
|
||||
*/
|
||||
public class FilterConnection extends AbstractConnection
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(FilterConnection.class);
|
||||
private static final boolean DEBUG = LOG.isDebugEnabled(); // Easy for the compiler to remove the code if DEBUG==false
|
||||
|
||||
public interface Filter
|
||||
{
|
||||
/**
|
||||
* <p>Callback method invoked when a connection from a remote client has been accepted.</p>
|
||||
* <p>The {@code socket} parameter can be used to extract socket address information of
|
||||
* the remote client.</p>
|
||||
*
|
||||
* @param endpoint the socket associated with the remote client
|
||||
*/
|
||||
public void opened(EndPoint endpoint);
|
||||
|
||||
/**
|
||||
* <p>Callback method invoked when bytes sent by a remote client arrived on the server.</p>
|
||||
*
|
||||
* @param endPoint the socket associated with the remote client
|
||||
* @param bytes the read-only buffer containing the incoming bytes
|
||||
*/
|
||||
public void incoming(EndPoint endPoint, ByteBuffer bytes);
|
||||
|
||||
/**
|
||||
* <p>Callback method invoked when bytes are sent to a remote client from the server.</p>
|
||||
* <p>This method is invoked after the bytes have been actually written to the remote client.</p>
|
||||
*
|
||||
* @param endPoint the socket associated with the remote client
|
||||
* @param bytes the read-only buffer containing the outgoing bytes
|
||||
*/
|
||||
public void outgoing(EndPoint endPoint, ByteBuffer bytes);
|
||||
|
||||
/**
|
||||
* <p>Callback method invoked when a connection to a remote client has been closed.</p>
|
||||
* <p>The {@code socket} parameter is already closed when this method is called, so it
|
||||
* cannot be queried for socket address information of the remote client.<br />
|
||||
* However, the {@code socket} parameter is the same object passed to {@link #opened(Socket)},
|
||||
* so it is possible to map socket information in {@link #opened(Socket)} and retrieve it
|
||||
* in this method.
|
||||
*
|
||||
* @param endpoint the (closed) socket associated with the remote client
|
||||
*/
|
||||
public void closed(EndPoint endpoint);
|
||||
}
|
||||
|
||||
public static class DebugFilter implements Filter
|
||||
{
|
||||
public DebugFilter()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void opened(EndPoint endpoint)
|
||||
{
|
||||
if (DEBUG)
|
||||
LOG.debug("{}@{} opened%n",endpoint.getClass().getSimpleName(),Integer.toString(endpoint.hashCode(),16));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incoming(EndPoint endpoint, ByteBuffer bytes)
|
||||
{
|
||||
if (DEBUG)
|
||||
LOG.debug("{}@{} >>> {}%n",endpoint.getClass().getSimpleName(),Integer.toString(endpoint.hashCode(),16),BufferUtil.toDetailString(bytes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void outgoing(EndPoint endpoint, ByteBuffer bytes)
|
||||
{
|
||||
if (DEBUG)
|
||||
LOG.debug("{}@{} <<< {}%n",endpoint.getClass().getSimpleName(),Integer.toString(endpoint.hashCode(),16),BufferUtil.toDetailString(bytes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closed(EndPoint endpoint)
|
||||
{
|
||||
if (DEBUG)
|
||||
LOG.debug("{}@{} closed%n",endpoint.getClass().getSimpleName(),Integer.toString(endpoint.hashCode(),16));
|
||||
}
|
||||
}
|
||||
|
||||
public static class DumpToFileFilter implements Filter
|
||||
{
|
||||
final ConcurrentHashMap<EndPoint,OutputStream> _in = new ConcurrentHashMap<>();
|
||||
final ConcurrentHashMap<EndPoint,OutputStream> _out = new ConcurrentHashMap<>();
|
||||
final File _directory;
|
||||
final String _prefix;
|
||||
final boolean _deleteOnExit;
|
||||
|
||||
public DumpToFileFilter()
|
||||
{
|
||||
this(new File(System.getProperty("java.io.tmpdir")+File.separator+"FilterConnection"),true);
|
||||
}
|
||||
|
||||
public DumpToFileFilter(File directory, boolean deleteOnExit)
|
||||
{
|
||||
this(directory,"dump-",deleteOnExit);
|
||||
}
|
||||
|
||||
public DumpToFileFilter(String prefix)
|
||||
{
|
||||
this(new File(System.getProperty("java.io.tmpdir")+File.separator+"FilterConnection"),prefix,true);
|
||||
}
|
||||
|
||||
public DumpToFileFilter(
|
||||
@Name("directory") File directory,
|
||||
@Name("prefix") String prefix,
|
||||
@Name("deleteOnExit") boolean deleteOnExit)
|
||||
{
|
||||
_directory=directory;
|
||||
_prefix=prefix;
|
||||
_deleteOnExit=deleteOnExit;
|
||||
if (!_directory.exists() && !_directory.mkdirs())
|
||||
throw new IllegalArgumentException("cannot create "+directory);
|
||||
if (!_directory.isDirectory())
|
||||
throw new IllegalArgumentException("not directory "+directory);
|
||||
if (!_directory.canWrite())
|
||||
throw new IllegalArgumentException("cannot write "+directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void opened(EndPoint endpoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
File in = new File(_directory,_prefix+Integer.toHexString(endpoint.hashCode())+".in");
|
||||
File out = new File(_directory,_prefix+Integer.toHexString(endpoint.hashCode())+".out");
|
||||
if (_deleteOnExit)
|
||||
{
|
||||
in.deleteOnExit();
|
||||
out.deleteOnExit();
|
||||
}
|
||||
_in.put(endpoint,new FileOutputStream(in));
|
||||
_out.put(endpoint,new FileOutputStream(out));
|
||||
}
|
||||
catch (FileNotFoundException e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incoming(EndPoint endpoint, ByteBuffer bytes)
|
||||
{
|
||||
try
|
||||
{
|
||||
OutputStream out=_in.get(endpoint);
|
||||
if (out!=null)
|
||||
out.write(BufferUtil.toArray(bytes));
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void outgoing(EndPoint endpoint, ByteBuffer bytes)
|
||||
{
|
||||
try
|
||||
{
|
||||
OutputStream out=_out.get(endpoint);
|
||||
if (out!=null)
|
||||
out.write(BufferUtil.toArray(bytes));
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closed(EndPoint endpoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
OutputStream out=_in.remove(endpoint);
|
||||
if (out!=null)
|
||||
out.close();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
try
|
||||
{
|
||||
OutputStream out=_out.remove(endpoint);
|
||||
if (out!=null)
|
||||
out.close();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
private final ByteBufferPool _bufferPool;
|
||||
private final FilteredEndPoint _filterEndPoint;
|
||||
private final int _outputBufferSize;
|
||||
private final List<Filter> _filters = new CopyOnWriteArrayList<>();
|
||||
|
||||
public FilterConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, int outputBufferSize)
|
||||
{
|
||||
super(endPoint, executor, false);
|
||||
_bufferPool = byteBufferPool;
|
||||
_filterEndPoint = newFilterEndPoint();
|
||||
_outputBufferSize=outputBufferSize;
|
||||
}
|
||||
|
||||
protected FilteredEndPoint newFilterEndPoint()
|
||||
{
|
||||
return new FilteredEndPoint();
|
||||
}
|
||||
|
||||
public FilteredEndPoint getFilterEndPoint()
|
||||
{
|
||||
return _filterEndPoint;
|
||||
}
|
||||
|
||||
public void addFilter(Filter filter)
|
||||
{
|
||||
_filters.add(filter);
|
||||
}
|
||||
|
||||
public boolean removeFilter(Filter listener)
|
||||
{
|
||||
return _filters.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen()
|
||||
{
|
||||
super.onOpen();
|
||||
for (Filter filter: _filters)
|
||||
filter.opened(getEndPoint());
|
||||
getFilterEndPoint().getConnection().onOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose()
|
||||
{
|
||||
for (Filter filter: _filters)
|
||||
filter.closed(getEndPoint());
|
||||
_filterEndPoint.getConnection().onClose();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMessagesIn()
|
||||
{
|
||||
return _filterEndPoint.getConnection().getMessagesIn();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMessagesOut()
|
||||
{
|
||||
return _filterEndPoint.getConnection().getMessagesOut();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
getFilterEndPoint().getConnection().close();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void onFillable()
|
||||
{
|
||||
if (DEBUG)
|
||||
LOG.debug("onFillable enter {}", getEndPoint());
|
||||
|
||||
// wake up whoever is doing the fill or the flush so they can
|
||||
// do all the filling, unwrapping, wrapping and flushing
|
||||
_filterEndPoint.getFillInterest().fillable();
|
||||
|
||||
if (DEBUG)
|
||||
LOG.debug("onFillable exit {}", getEndPoint());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void onFillInterestedFailed(Throwable cause)
|
||||
{
|
||||
_filterEndPoint.getFillInterest().onFail(cause);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s@%x -> %s",
|
||||
FilterConnection.class.getSimpleName(),
|
||||
hashCode(),
|
||||
_filterEndPoint.getConnection());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public class FilteredEndPoint extends AbstractEndPoint
|
||||
{
|
||||
private final Callback<Void> _writeCB = new Callback<Void>()
|
||||
{
|
||||
@Override
|
||||
public void completed(Void context)
|
||||
{
|
||||
if (BufferUtil.isEmpty(_outBuffer))
|
||||
{
|
||||
_bufferPool.release(_outBuffer);
|
||||
_outBuffer=null;
|
||||
}
|
||||
getWriteFlusher().completeWrite();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Void context, Throwable x)
|
||||
{
|
||||
if (BufferUtil.isEmpty(_outBuffer))
|
||||
{
|
||||
_bufferPool.release(_outBuffer);
|
||||
_outBuffer=null;
|
||||
}
|
||||
getWriteFlusher().onFail(x);
|
||||
}
|
||||
};
|
||||
|
||||
private ByteBuffer _outBuffer;
|
||||
|
||||
public FilteredEndPoint()
|
||||
{
|
||||
super(null,getEndPoint().getLocalAddress(), getEndPoint().getRemoteAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onIncompleteFlush()
|
||||
{
|
||||
if (BufferUtil.isEmpty(_outBuffer))
|
||||
{
|
||||
_bufferPool.release(_outBuffer);
|
||||
_outBuffer=null;
|
||||
getWriteFlusher().completeWrite();
|
||||
}
|
||||
else
|
||||
getEndPoint().write(null,_writeCB,_outBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean needsFill() throws IOException
|
||||
{
|
||||
FilterConnection.this.fillInterested();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized int fill(ByteBuffer buffer) throws IOException
|
||||
{
|
||||
if (DEBUG)
|
||||
LOG.debug("{} fill enter", FilterConnection.this);
|
||||
|
||||
int orig=buffer.remaining();
|
||||
|
||||
int filled = getEndPoint().fill(buffer);
|
||||
|
||||
if (orig>0)
|
||||
buffer.position(buffer.position()+orig);
|
||||
for (Filter filter: _filters)
|
||||
filter.incoming(getEndPoint() ,buffer);
|
||||
if (orig>0)
|
||||
buffer.position(buffer.position()-orig);
|
||||
|
||||
if (DEBUG)
|
||||
LOG.debug("{} fill {} exit", FilterConnection.this,filled);
|
||||
return filled;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean flush(ByteBuffer... buffers) throws IOException
|
||||
{
|
||||
if (DEBUG)
|
||||
LOG.debug("{} flush enter {}", FilterConnection.this, Arrays.toString(buffers));
|
||||
|
||||
if (BufferUtil.hasContent(_outBuffer))
|
||||
return false;
|
||||
|
||||
if (_outBuffer==null)
|
||||
_outBuffer=_bufferPool.acquire(_outputBufferSize,true);
|
||||
|
||||
// Take as much data as we can
|
||||
boolean all_taken=true;
|
||||
for (ByteBuffer buffer : buffers)
|
||||
{
|
||||
if (buffer==null)
|
||||
continue;
|
||||
BufferUtil.flipPutFlip(buffer,_outBuffer);
|
||||
|
||||
if (BufferUtil.hasContent(buffer))
|
||||
{
|
||||
all_taken=false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (Filter filter: _filters)
|
||||
filter.outgoing(getEndPoint() ,_outBuffer);
|
||||
|
||||
boolean flushed = getEndPoint().flush(_outBuffer);
|
||||
if (BufferUtil.isEmpty(_outBuffer))
|
||||
{
|
||||
_bufferPool.release(_outBuffer);
|
||||
_outBuffer=null;
|
||||
}
|
||||
|
||||
if (DEBUG)
|
||||
LOG.debug("{} flush exit, consumed {}", FilterConnection.this, flushed);
|
||||
|
||||
return all_taken && flushed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownOutput()
|
||||
{
|
||||
getEndPoint().shutdownOutput();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutputShutdown()
|
||||
{
|
||||
return getEndPoint().isOutputShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
getEndPoint().close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen()
|
||||
{
|
||||
return getEndPoint().isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTransport()
|
||||
{
|
||||
return getEndPoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInputShutdown()
|
||||
{
|
||||
return getEndPoint().isInputShutdown();
|
||||
}
|
||||
|
||||
public EndPoint getWrappedEndPoint()
|
||||
{
|
||||
return getEndPoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return super.toString()+"->"+getEndPoint().toString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -58,9 +58,8 @@ public class MappedByteBufferPool implements ByteBufferPool
|
|||
int capacity = bucket * factor;
|
||||
result = direct ? BufferUtil.allocateDirect(capacity) : BufferUtil.allocate(capacity);
|
||||
}
|
||||
else
|
||||
BufferUtil.clear(result);
|
||||
|
||||
BufferUtil.clear(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.io.IOException;
|
|||
import java.net.ConnectException;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.nio.channels.CancelledKeyException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.SelectionKey;
|
||||
|
@ -35,16 +36,18 @@ import java.util.Queue;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.eclipse.jetty.util.ForkInvoker;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.annotation.Name;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
|
||||
/**
|
||||
* <p>{@link SelectorManager} manages a number of {@link ManagedSelector}s that
|
||||
|
@ -56,25 +59,53 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
{
|
||||
protected static final Logger LOG = Log.getLogger(SelectorManager.class);
|
||||
|
||||
private final Executor executor;
|
||||
private final Scheduler scheduler;
|
||||
private final ManagedSelector[] _selectors;
|
||||
private volatile long _selectorIndex;
|
||||
private long _connectTimeout = 15000;
|
||||
private long _selectorIndex;
|
||||
|
||||
protected SelectorManager()
|
||||
protected SelectorManager(Executor executor, Scheduler scheduler)
|
||||
{
|
||||
this((Runtime.getRuntime().availableProcessors() + 1) / 2);
|
||||
this(executor, scheduler, (Runtime.getRuntime().availableProcessors() + 1) / 2);
|
||||
}
|
||||
|
||||
protected SelectorManager(@Name(value="selectors") int selectors)
|
||||
protected SelectorManager(Executor executor, Scheduler scheduler, int selectors)
|
||||
{
|
||||
this.executor = executor;
|
||||
this.scheduler = scheduler;
|
||||
_selectors = new ManagedSelector[selectors];
|
||||
}
|
||||
|
||||
public Executor getExecutor()
|
||||
{
|
||||
return executor;
|
||||
}
|
||||
|
||||
public Scheduler getScheduler()
|
||||
{
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
public long getConnectTimeout()
|
||||
{
|
||||
return _connectTimeout;
|
||||
}
|
||||
|
||||
public void setConnectTimeout(long connectTimeout)
|
||||
{
|
||||
_connectTimeout = connectTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given task in a different thread.
|
||||
*
|
||||
* @param task the task to execute
|
||||
*/
|
||||
protected abstract void execute(Runnable task);
|
||||
protected void execute(Runnable task)
|
||||
{
|
||||
executor.execute(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of selectors in use
|
||||
|
@ -207,6 +238,11 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
}
|
||||
}
|
||||
|
||||
protected boolean finishConnect(SocketChannel channel) throws IOException
|
||||
{
|
||||
return channel.finishConnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Callback method invoked when a non-blocking connect cannot be completed.</p>
|
||||
* <p>By default it just logs with level warning.</p>
|
||||
|
@ -416,27 +452,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
}
|
||||
else if (key.isConnectable())
|
||||
{
|
||||
// Complete a connection of a registered channel
|
||||
SocketChannel channel = (SocketChannel)key.channel();
|
||||
try
|
||||
{
|
||||
boolean connected = channel.finishConnect();
|
||||
if (connected)
|
||||
{
|
||||
key.interestOps(0);
|
||||
EndPoint endpoint = createEndPoint(channel, key);
|
||||
key.attach(endpoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ConnectException();
|
||||
}
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
connectionFailed(channel, x, attachment);
|
||||
closeNoExceptions(channel);
|
||||
}
|
||||
processConnect(key, (Connect)attachment);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -457,6 +473,32 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
}
|
||||
}
|
||||
|
||||
private void processConnect(SelectionKey key, Connect connect)
|
||||
{
|
||||
key.attach(connect.attachment);
|
||||
SocketChannel channel = (SocketChannel)key.channel();
|
||||
try
|
||||
{
|
||||
boolean connected = finishConnect(channel);
|
||||
if (connected)
|
||||
{
|
||||
connect.timeout.cancel();
|
||||
key.interestOps(0);
|
||||
EndPoint endpoint = createEndPoint(channel, key);
|
||||
key.attach(endpoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ConnectException();
|
||||
}
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
connect.failed(x);
|
||||
closeNoExceptions(channel);
|
||||
}
|
||||
}
|
||||
|
||||
private void closeNoExceptions(Closeable closeable)
|
||||
{
|
||||
try
|
||||
|
@ -469,11 +511,6 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
}
|
||||
}
|
||||
|
||||
public SelectorManager getSelectorManager()
|
||||
{
|
||||
return SelectorManager.this;
|
||||
}
|
||||
|
||||
public void wakeup()
|
||||
{
|
||||
_selector.wakeup();
|
||||
|
@ -653,13 +690,16 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
|
||||
private class Connect implements Runnable
|
||||
{
|
||||
private final AtomicBoolean failed = new AtomicBoolean();
|
||||
private final SocketChannel channel;
|
||||
private final Object attachment;
|
||||
private final Scheduler.Task timeout;
|
||||
|
||||
public Connect(SocketChannel channel, Object attachment)
|
||||
{
|
||||
this.channel = channel;
|
||||
this.attachment = attachment;
|
||||
this.timeout = scheduler.schedule(new ConnectTimeout(this), getConnectTimeout(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -667,13 +707,52 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
{
|
||||
try
|
||||
{
|
||||
channel.register(_selector, SelectionKey.OP_CONNECT, attachment);
|
||||
channel.register(_selector, SelectionKey.OP_CONNECT, this);
|
||||
}
|
||||
catch (ClosedChannelException x)
|
||||
{
|
||||
LOG.debug(x);
|
||||
}
|
||||
}
|
||||
|
||||
protected void failed(Throwable failure)
|
||||
{
|
||||
if (failed.compareAndSet(false, true))
|
||||
connectionFailed(channel, failure, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
private class ConnectTimeout implements Runnable
|
||||
{
|
||||
private final Connect connect;
|
||||
|
||||
private ConnectTimeout(Connect connect)
|
||||
{
|
||||
this.connect = connect;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
SocketChannel channel = connect.channel;
|
||||
if (channel.isConnectionPending())
|
||||
{
|
||||
LOG.debug("Channel {} timed out while connecting, closing it", channel);
|
||||
try
|
||||
{
|
||||
// This will unregister the channel from the selector
|
||||
channel.close();
|
||||
}
|
||||
catch (IOException x)
|
||||
{
|
||||
LOG.ignore(x);
|
||||
}
|
||||
finally
|
||||
{
|
||||
connect.failed(new SocketTimeoutException());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Stop implements Runnable
|
||||
|
|
|
@ -327,18 +327,19 @@ public class SslConnection extends AbstractConnection
|
|||
// TODO does this need idle timeouts
|
||||
super(null,getEndPoint().getLocalAddress(), getEndPoint().getRemoteAddress());
|
||||
}
|
||||
|
||||
public EndPoint getEncryptedEndPoint()
|
||||
{
|
||||
return getEndPoint();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected FillInterest getFillInterest()
|
||||
{
|
||||
return super.getFillInterest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIdleTimeout(long idleTimeout)
|
||||
{
|
||||
super.setIdleTimeout(idleTimeout);
|
||||
getEndPoint().setIdleTimeout(idleTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WriteFlusher getWriteFlusher()
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2012 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.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.io;
|
||||
|
||||
import java.nio.channels.SocketChannel;
|
||||
|
||||
import org.junit.Ignore;
|
||||
|
||||
// TODO ignore this test as it creates too many /tmp files and is not portable and does not even check the contents!
|
||||
@Ignore
|
||||
public class FilteredSelectChannelEndPointTest extends SelectChannelEndPointTest
|
||||
{
|
||||
public FilteredSelectChannelEndPointTest()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Connection newConnection(SocketChannel channel, EndPoint endpoint)
|
||||
{
|
||||
FilterConnection filter = new FilterConnection(new MappedByteBufferPool(),_threadPool,endpoint,8192);
|
||||
filter.addFilter(new FilterConnection.DumpToFileFilter());
|
||||
Connection connection= super.newConnection(null,filter.getFilterEndPoint());
|
||||
filter.getFilterEndPoint().setConnection(connection);
|
||||
return filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testBlockedReadIdle() throws Exception
|
||||
{
|
||||
super.testBlockedReadIdle();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -60,18 +60,12 @@ public class SelectChannelEndPointInterestsTest
|
|||
connector = ServerSocketChannel.open();
|
||||
connector.bind(new InetSocketAddress("localhost", 0));
|
||||
|
||||
selectorManager = new SelectorManager()
|
||||
selectorManager = new SelectorManager(threadPool, scheduler)
|
||||
{
|
||||
@Override
|
||||
protected void execute(Runnable task)
|
||||
{
|
||||
threadPool.execute(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EndPoint newEndPoint(SocketChannel channel, ManagedSelector selector, SelectionKey selectionKey) throws IOException
|
||||
{
|
||||
return new SelectChannelEndPoint(channel, selector, selectionKey, scheduler, 60000)
|
||||
return new SelectChannelEndPoint(channel, selector, selectionKey, getScheduler(), 60000)
|
||||
{
|
||||
@Override
|
||||
protected void onIncompleteFlush()
|
||||
|
@ -85,7 +79,7 @@ public class SelectChannelEndPointInterestsTest
|
|||
@Override
|
||||
public Connection newConnection(SocketChannel channel, final EndPoint endPoint, Object attachment)
|
||||
{
|
||||
return new AbstractConnection(endPoint, threadPool)
|
||||
return new AbstractConnection(endPoint, getExecutor())
|
||||
{
|
||||
@Override
|
||||
public void onOpen()
|
||||
|
|
|
@ -60,14 +60,8 @@ public class SelectChannelEndPointTest
|
|||
protected ServerSocketChannel _connector;
|
||||
protected QueuedThreadPool _threadPool = new QueuedThreadPool();
|
||||
protected Scheduler _scheduler = new TimerScheduler();
|
||||
protected SelectorManager _manager = new SelectorManager()
|
||||
protected SelectorManager _manager = new SelectorManager(_threadPool, _scheduler)
|
||||
{
|
||||
@Override
|
||||
protected void execute(Runnable task)
|
||||
{
|
||||
_threadPool.execute(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection newConnection(SocketChannel channel, EndPoint endpoint, Object attachment)
|
||||
{
|
||||
|
@ -77,7 +71,7 @@ public class SelectChannelEndPointTest
|
|||
@Override
|
||||
protected SelectChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey selectionKey) throws IOException
|
||||
{
|
||||
SelectChannelEndPoint endp = new SelectChannelEndPoint(channel, selectSet, selectionKey, _scheduler, 60000);
|
||||
SelectChannelEndPoint endp = new SelectChannelEndPoint(channel, selectSet, selectionKey, getScheduler(), 60000);
|
||||
_lastEndPoint = endp;
|
||||
_lastEndPointLatch.countDown();
|
||||
return endp;
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2012 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.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.annotation.Slow;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.eclipse.jetty.util.thread.TimerScheduler;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SelectorManagerTest
|
||||
{
|
||||
private QueuedThreadPool executor = new QueuedThreadPool();
|
||||
private TimerScheduler scheduler = new TimerScheduler();
|
||||
|
||||
@Before
|
||||
public void prepare() throws Exception
|
||||
{
|
||||
executor.start();
|
||||
scheduler.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void dispose() throws Exception
|
||||
{
|
||||
scheduler.stop();
|
||||
executor.stop();
|
||||
}
|
||||
|
||||
@Slow
|
||||
@Test
|
||||
public void testConnectTimeoutBeforeSuccessfulConnect() throws Exception
|
||||
{
|
||||
ServerSocketChannel server = ServerSocketChannel.open();
|
||||
server.bind(new InetSocketAddress("localhost", 0));
|
||||
SocketAddress address = server.getLocalAddress();
|
||||
|
||||
SocketChannel client = SocketChannel.open();
|
||||
client.configureBlocking(false);
|
||||
client.connect(address);
|
||||
|
||||
final long connectTimeout = 1000;
|
||||
SelectorManager selectorManager = new SelectorManager(executor, scheduler)
|
||||
{
|
||||
@Override
|
||||
protected EndPoint newEndPoint(SocketChannel channel, ManagedSelector selector, SelectionKey selectionKey) throws IOException
|
||||
{
|
||||
return new SelectChannelEndPoint(channel, selector, selectionKey, getScheduler(), connectTimeout / 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean finishConnect(SocketChannel channel) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
TimeUnit.MILLISECONDS.sleep(connectTimeout * 2);
|
||||
return super.finishConnect(channel);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection newConnection(SocketChannel channel, EndPoint endpoint, Object attachment) throws IOException
|
||||
{
|
||||
return new AbstractConnection(endpoint, executor)
|
||||
{
|
||||
@Override
|
||||
public void onFillable()
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void connectionFailed(SocketChannel channel, Throwable ex, Object attachment)
|
||||
{
|
||||
((Callback<Void>)attachment).failed(null, ex);
|
||||
}
|
||||
};
|
||||
selectorManager.setConnectTimeout(connectTimeout);
|
||||
selectorManager.start();
|
||||
|
||||
try
|
||||
{
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
selectorManager.connect(client, new Callback.Empty<Void>()
|
||||
{
|
||||
@Override
|
||||
public void failed(Void context, Throwable x)
|
||||
{
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(latch.await(connectTimeout * 3, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
finally
|
||||
{
|
||||
selectorManager.stop();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ import java.nio.channels.ServerSocketChannel;
|
|||
import java.nio.channels.SocketChannel;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
|
||||
|
@ -57,22 +58,27 @@ public class SslConnectionTest
|
|||
private volatile boolean _testFill=true;
|
||||
private volatile FutureCallback<Void> _writeCallback;
|
||||
protected ServerSocketChannel _connector;
|
||||
protected QueuedThreadPool _threadPool = new QueuedThreadPool();
|
||||
protected Scheduler _scheduler = new TimerScheduler();
|
||||
protected SelectorManager _manager = new SelectorManager()
|
||||
final AtomicInteger _dispatches = new AtomicInteger();
|
||||
protected QueuedThreadPool _threadPool = new QueuedThreadPool()
|
||||
{
|
||||
|
||||
@Override
|
||||
protected void execute(Runnable task)
|
||||
public boolean dispatch(Runnable job)
|
||||
{
|
||||
_threadPool.execute(task);
|
||||
_dispatches.incrementAndGet();
|
||||
return super.dispatch(job);
|
||||
}
|
||||
|
||||
};
|
||||
protected Scheduler _scheduler = new TimerScheduler();
|
||||
protected SelectorManager _manager = new SelectorManager(_threadPool, _scheduler)
|
||||
{
|
||||
@Override
|
||||
public Connection newConnection(SocketChannel channel, EndPoint endpoint, Object attachment)
|
||||
{
|
||||
SSLEngine engine = __sslCtxFactory.newSSLEngine();
|
||||
engine.setUseClientMode(false);
|
||||
SslConnection sslConnection = new SslConnection(__byteBufferPool, _threadPool, endpoint, engine);
|
||||
SslConnection sslConnection = new SslConnection(__byteBufferPool, getExecutor(), endpoint, engine);
|
||||
|
||||
Connection appConnection = new TestConnection(sslConnection.getDecryptedEndPoint());
|
||||
sslConnection.getDecryptedEndPoint().setConnection(appConnection);
|
||||
|
@ -83,7 +89,7 @@ public class SslConnectionTest
|
|||
@Override
|
||||
protected SelectChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey selectionKey) throws IOException
|
||||
{
|
||||
SelectChannelEndPoint endp = new SelectChannelEndPoint(channel,selectSet, selectionKey, _scheduler, 60000);
|
||||
SelectChannelEndPoint endp = new SelectChannelEndPoint(channel,selectSet, selectionKey, getScheduler(), 60000);
|
||||
_lastEndp=endp;
|
||||
return endp;
|
||||
}
|
||||
|
@ -113,6 +119,7 @@ public class SslConnectionTest
|
|||
_threadPool.start();
|
||||
_scheduler.start();
|
||||
_manager.start();
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -132,7 +139,7 @@ public class SslConnectionTest
|
|||
|
||||
public TestConnection(EndPoint endp)
|
||||
{
|
||||
super(endp, _threadPool);
|
||||
super(endp, _threadPool,false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -228,11 +235,18 @@ public class SslConnectionTest
|
|||
server.configureBlocking(false);
|
||||
_manager.accept(server);
|
||||
|
||||
client.getOutputStream().write("HelloWorld".getBytes("UTF-8"));
|
||||
client.getOutputStream().write("Hello".getBytes("UTF-8"));
|
||||
byte[] buffer = new byte[1024];
|
||||
int len=client.getInputStream().read(buffer);
|
||||
Assert.assertEquals(10, len);
|
||||
Assert.assertEquals("HelloWorld",new String(buffer,0,len,StringUtil.__UTF8_CHARSET));
|
||||
Assert.assertEquals(5, len);
|
||||
Assert.assertEquals("Hello",new String(buffer,0,len,StringUtil.__UTF8_CHARSET));
|
||||
|
||||
_dispatches.set(0);
|
||||
client.getOutputStream().write("World".getBytes("UTF-8"));
|
||||
len=5;
|
||||
while(len>0)
|
||||
len-=client.getInputStream().read(buffer);
|
||||
Assert.assertEquals(1, _dispatches.get());
|
||||
|
||||
client.close();
|
||||
}
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
org.eclipse.jetty.LEVEL=INFO
|
||||
|
||||
|
|
|
@ -33,6 +33,10 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
|
|||
{
|
||||
|
||||
private static boolean identifiedOsgiImpl = false;
|
||||
|
||||
private static Class BundleWiringClass = null;
|
||||
private static Method BundleWiringClass_getClassLoader_method = null;
|
||||
private static Method BundleClass_adapt_method = null;
|
||||
|
||||
private static boolean isEquinox = false;
|
||||
|
||||
|
@ -41,13 +45,34 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
|
|||
private static void init(Bundle bundle)
|
||||
{
|
||||
identifiedOsgiImpl = true;
|
||||
|
||||
try
|
||||
{
|
||||
isEquinox = bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.framework.internal.core.BundleHost") != null;
|
||||
BundleWiringClass = bundle.getClass().getClassLoader().loadClass("org.osgi.framework.wiring.BundleWiring");
|
||||
if (BundleWiringClass != null)
|
||||
{
|
||||
BundleWiringClass_getClassLoader_method = BundleWiringClass.getDeclaredMethod("getClassLoader", new Class[] {});
|
||||
BundleClass_adapt_method = bundle.getClass().getDeclaredMethod("adapt", new Class[] { Class.class });
|
||||
BundleClass_adapt_method.setAccessible(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
isEquinox = false;
|
||||
//nevermind: an older version of OSGi where BundleWiring is not availble
|
||||
//t.printStackTrace();
|
||||
}
|
||||
|
||||
if (!bundle.getClass().getName().startsWith("org.apache.felix"))
|
||||
{
|
||||
try
|
||||
{
|
||||
isEquinox = bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.framework.internal.core.BundleHost") != null;
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
isEquinox = false;
|
||||
}
|
||||
}
|
||||
if (!isEquinox)
|
||||
{
|
||||
|
@ -70,6 +95,7 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
|
|||
*/
|
||||
public ClassLoader getBundleClassLoader(Bundle bundle)
|
||||
{
|
||||
//Older OSGi implementations:
|
||||
String bundleActivator = (String) bundle.getHeaders().get("Bundle-Activator");
|
||||
if (bundleActivator == null)
|
||||
{
|
||||
|
@ -93,6 +119,22 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
|
|||
{
|
||||
init(bundle);
|
||||
}
|
||||
//This works for OSGi 4.2 and more recent. Aka version 1.6
|
||||
//It is using ava reflection to execute:
|
||||
//(BundleClassLoader) bundle.adapt(BundleWiring.class).getClassLoader()
|
||||
if (BundleClass_adapt_method != null && BundleWiringClass_getClassLoader_method != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Object bundleWiring = BundleClass_adapt_method.invoke(bundle, BundleWiringClass);
|
||||
return (ClassLoader)BundleWiringClass_getClassLoader_method.invoke(bundleWiring, new Object[] {});
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
t.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (isEquinox)
|
||||
{
|
||||
return internalGetEquinoxBundleClassLoader(bundle);
|
||||
|
@ -135,13 +177,16 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
|
|||
private static Field Felix_BundleImpl_m_modules_field;
|
||||
|
||||
private static Field Felix_ModuleImpl_m_classLoader_field;
|
||||
|
||||
private static Field Felix_BundleImpl_m_revisions_field;
|
||||
|
||||
|
||||
private static ClassLoader internalGetFelixBundleClassLoader(Bundle bundle)
|
||||
{
|
||||
// assume felix:
|
||||
try
|
||||
{
|
||||
// now get the current module from the bundle.
|
||||
// now get the current module from the bundle.
|
||||
// and return the private field m_classLoader of ModuleImpl
|
||||
if (Felix_BundleImpl_m_modules_field == null)
|
||||
{
|
||||
|
|
|
@ -36,8 +36,8 @@ import org.eclipse.jetty.util.resource.FileResource;
|
|||
import org.osgi.framework.Bundle;
|
||||
|
||||
/**
|
||||
* From a bundle to its location on the filesystem. Assumes the bundle is not a
|
||||
* jar.
|
||||
* From a bundle to its location on the filesystem.
|
||||
* Often assumes the bundle is not a jar.
|
||||
*
|
||||
* @author hmalphettes
|
||||
*/
|
||||
|
@ -150,7 +150,6 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
|
|||
{
|
||||
// observed this on felix-2.0.0
|
||||
String location = bundle.getLocation();
|
||||
// System.err.println("location " + location);
|
||||
if (location.startsWith("file:/"))
|
||||
{
|
||||
URI uri = new URI(URIUtil.encodePath(location));
|
||||
|
@ -183,10 +182,16 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
|
|||
File file = new File(location.substring("file:".length()));
|
||||
return file;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Resort to introspection on felix:
|
||||
return getBundleInstallLocationInFelix(bundle);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Locate a file inside a bundle.
|
||||
*
|
||||
|
@ -357,4 +362,85 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
|
|||
return url;
|
||||
}
|
||||
|
||||
// Felix introspection
|
||||
private static Method Felix_BundleImpl_getArchive_method;
|
||||
private static Method Felix_BundleArchive_getCurrentRevision_method;
|
||||
private static Method Felix_BundleRevision_getRevisionRootDir_method;
|
||||
|
||||
private static boolean felixIntroSpectionDone = false;
|
||||
|
||||
/**
|
||||
* Introspection of the implementation classes of Felix-3.x and Felix-4.x.
|
||||
* <p>
|
||||
* See org.apache.felix.framework.cache
|
||||
* In pseudo code:
|
||||
* <code>
|
||||
* File revRootDir = BundleImpl.getArchive().getCurrentRevision().getRevisionRootDir();
|
||||
* return new File(revRootDir, bundle.jar) if it exists?
|
||||
* else return revRootDir
|
||||
* </p>
|
||||
* @param bundle
|
||||
* @return The File or null if we failed to find it.
|
||||
*/
|
||||
private static File getBundleInstallLocationInFelix(Bundle bundle)
|
||||
{
|
||||
if (Felix_BundleImpl_getArchive_method == null) {
|
||||
if (felixIntroSpectionDone)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
felixIntroSpectionDone = true;
|
||||
try
|
||||
{
|
||||
Felix_BundleImpl_getArchive_method = bundle.getClass().getDeclaredMethod("getArchive", new Class[] {});
|
||||
Felix_BundleImpl_getArchive_method.setAccessible(true);
|
||||
Object archive = Felix_BundleImpl_getArchive_method.invoke(bundle);
|
||||
Class bundleArchiveClass = archive.getClass();
|
||||
Felix_BundleArchive_getCurrentRevision_method = bundleArchiveClass.getDeclaredMethod("getCurrentRevision", new Class[] {});
|
||||
Felix_BundleArchive_getCurrentRevision_method.setAccessible(true);
|
||||
Object revision = Felix_BundleArchive_getCurrentRevision_method.invoke(archive);
|
||||
Class bundleRevisionClass = revision.getClass();
|
||||
Felix_BundleRevision_getRevisionRootDir_method = bundleRevisionClass.getMethod("getRevisionRootDir", new Class[] {});
|
||||
Felix_BundleRevision_getRevisionRootDir_method.setAccessible(true);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
//nevermind?
|
||||
//t.printStackTrace();
|
||||
Felix_BundleImpl_getArchive_method = null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (Felix_BundleImpl_getArchive_method != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Object archive = Felix_BundleImpl_getArchive_method.invoke(bundle);
|
||||
Object revision = Felix_BundleArchive_getCurrentRevision_method.invoke(archive);
|
||||
File revRootDir = (File)Felix_BundleRevision_getRevisionRootDir_method.invoke(revision);
|
||||
//System.err.println("Got the archive revision root dir " + revRootDir.getAbsolutePath());
|
||||
File bundleJar = new File(revRootDir, "bundle.jar");
|
||||
if (bundleJar.exists())
|
||||
{
|
||||
//bundle.jar is hardcoded in org.apache.felix.framework.cache.JarRevision
|
||||
//when it is not a bundle.jar, then the bundle location starts with 'file:' and we have already
|
||||
//taken care if that scheme earlier.
|
||||
return bundleJar;
|
||||
}
|
||||
else //sanity check?: if (new File(revRootDir, "META-INF/MANIFEST.MF").exists())
|
||||
{
|
||||
//this is a DirectoryRevision
|
||||
return revRootDir;
|
||||
}
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
//best effort: nevermind
|
||||
//t.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// -- end Felix introspection
|
||||
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<Call name="addServlet">
|
||||
<Arg>org.eclipse.equinox.http.servlet.HttpServiceServlet</Arg>
|
||||
<Arg>/*</Arg>
|
||||
<Set name="InitOrder">0</Set>
|
||||
<Set name="InitOrder">1</Set>
|
||||
</Call>
|
||||
<!-- custom pluggable error handler -->
|
||||
<Set name="ErrorHandler">
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
<osgi-services-version>3.2.100.v20100503</osgi-services-version>
|
||||
<equinox-http-servlet-version>1.0.0-v20070606</equinox-http-servlet-version>
|
||||
<!--equinox-servletbridge-version>1.0.0-v20070523</equinox-servletbridge-version-->
|
||||
<logback-version>0.9.18</logback-version>
|
||||
<slf4j-version>1.5.11</slf4j-version>
|
||||
<logback-version>0.9.29</logback-version>
|
||||
<slf4j-version>1.6.1</slf4j-version>
|
||||
</properties>
|
||||
<modules>
|
||||
<module>jetty-osgi-boot</module>
|
||||
|
|
|
@ -8,28 +8,157 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>test-jetty-osgi</artifactId>
|
||||
<name>Jetty :: OSGi :: Test</name>
|
||||
<description>Jetty OSGi Integration test</description>
|
||||
<description>Jetty OSGi Integration tests</description>
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.boot.test</bundle-symbolic-name>
|
||||
<bundle-symbolic-name>${project.groupId}.boot.test.spdy</bundle-symbolic-name>
|
||||
<jetty-orbit-url>http://download.eclipse.org/jetty/orbit/</jetty-orbit-url>
|
||||
<assembly-directory>target/distribution</assembly-directory>
|
||||
<paxexam-version-old>1.2.0</paxexam-version-old>
|
||||
<paxexam-version>1.2.4</paxexam-version>
|
||||
<exam.version>2.6.0</exam.version>
|
||||
<url.version>1.4.0</url.version>
|
||||
<paxswissbox.version>1.5.1</paxswissbox.version>
|
||||
<felixversion>4.0.3</felixversion>
|
||||
<injection.bundle.version>1.0</injection.bundle.version>
|
||||
<runner.version>1.7.6</runner.version>
|
||||
<npn-version>1.1.0.v20120525</npn-version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit-dep</artifactId>
|
||||
<version>4.10</version>
|
||||
</dependency>
|
||||
<!-- Pax Exam Dependencies -->
|
||||
<!-- OPS4J Swissbox Dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.swissbox</groupId>
|
||||
<artifactId>pax-swissbox-core</artifactId>
|
||||
<version>${paxswissbox.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.swissbox</groupId>
|
||||
<artifactId>pax-swissbox-extender</artifactId>
|
||||
<version>${paxswissbox.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.swissbox</groupId>
|
||||
<artifactId>pax-swissbox-lifecycle</artifactId>
|
||||
<version>${paxswissbox.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.swissbox</groupId>
|
||||
<artifactId>pax-swissbox-framework</artifactId>
|
||||
<version>${paxswissbox.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.exam</groupId>
|
||||
<artifactId>pax-exam</artifactId>
|
||||
<version>${exam.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.geronimo.specs</groupId>
|
||||
<artifactId>geronimo-atinject_1.0_spec</artifactId>
|
||||
<version>${injection.bundle.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.exam</groupId>
|
||||
<artifactId>pax-exam-inject</artifactId>
|
||||
<version>${exam.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Don't use the native container for now. Observed limitations:
|
||||
- single test with a single configuration
|
||||
- does not read the versions of the dependencies from the pom.xml
|
||||
and hence hardcode the bundles versions in the source code instead
|
||||
- no support for most configuration options for the OSGi container. -->
|
||||
<!--dependency>
|
||||
<groupId>org.ops4j.pax.exam</groupId>
|
||||
<artifactId>pax-exam-container-native</artifactId>
|
||||
<version>${exam.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency-->
|
||||
|
||||
<!-- container is not bad but not enough config parameters yet
|
||||
can't pass the VMOption for npn-boot
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.exam</groupId>
|
||||
<artifactId>pax-exam-container-forked</artifactId>
|
||||
<version>${exam.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.exam</groupId>
|
||||
<artifactId>pax-exam-container-paxrunner</artifactId>
|
||||
<version>${exam.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.runner</groupId>
|
||||
<artifactId>pax-runner-no-jcl</artifactId>
|
||||
<version>${runner.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.exam</groupId>
|
||||
<artifactId>pax-exam-junit4</artifactId>
|
||||
<version>${exam.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.exam</groupId>
|
||||
<artifactId>pax-exam-link-mvn</artifactId>
|
||||
<version>${exam.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.url</groupId>
|
||||
<artifactId>pax-url-aether</artifactId>
|
||||
<version>${url.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- OSGi R4 frameworks -->
|
||||
<dependency>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>org.apache.felix.framework</artifactId>
|
||||
<version>${felixversion}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.exam</groupId>
|
||||
<artifactId>pax-exam-testforge</artifactId>
|
||||
<version>${exam.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- For sane logging -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>1.6.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- Orbit Servlet Deps -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Orbit JSP Deps -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-jsp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- OSGi Deps -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
|
@ -49,7 +178,6 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Jetty Deps -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
|
@ -123,13 +251,44 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.spdy</groupId>
|
||||
<artifactId>spdy-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.spdy</groupId>
|
||||
<artifactId>spdy-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.spdy</groupId>
|
||||
<artifactId>spdy-http-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.spdy</groupId>
|
||||
<artifactId>spdy-client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty.npn</groupId>
|
||||
<artifactId>npn-boot</artifactId>
|
||||
<version>${npn-version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-plus</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Eclipse OSGi Deps -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.osgi</groupId>
|
||||
|
@ -153,7 +312,6 @@
|
|||
<artifactId>servlet</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>test-jetty-webapp</artifactId>
|
||||
|
@ -161,139 +319,40 @@
|
|||
<classifier>webbundle</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.exam</groupId>
|
||||
<artifactId>pax-exam</artifactId>
|
||||
<version>${paxexam-version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.exam</groupId>
|
||||
<artifactId>pax-exam-junit</artifactId>
|
||||
<version>${paxexam-version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<!-- we use junit-dep -->
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.exam</groupId>
|
||||
<artifactId>pax-exam-container-default</artifactId>
|
||||
<version>${paxexam-version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||
<artifactId>jetty-test-helper</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>${slf4j-version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.ops4j.pax.exam</groupId>
|
||||
<artifactId>maven-paxexam-plugin</artifactId>
|
||||
<version>${paxexam-version}</version>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<!-- No point defining -Xbootclasspath as the actual OSGi VM is run as a forked process by pax-exam -->
|
||||
<!--argLine>-Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn-version}/npn-boot-${npn-version}.jar</argLine-->
|
||||
<!-- But we do pass the sys property of the npn-boot jar -->
|
||||
<argLine>-Dmortbay-npn-boot=${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn-version}/npn-boot-${npn-version}.jar</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.5.1</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.servicemix.tooling</groupId>
|
||||
<artifactId>depends-maven-plugin</artifactId>
|
||||
<version>1.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>generate-config</id>
|
||||
<id>generate-depends-file</id>
|
||||
<goals>
|
||||
<goal>generate-depends-file</goal>
|
||||
<goal>generate-config</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<options>
|
||||
<workingDirectory>${project.build.directory}/paxexam</workingDirectory>
|
||||
</options>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-orbit-servlet-api-deps</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
<version>${orbit-servlet-api-version}</version>
|
||||
<overWrite>true</overWrite>
|
||||
<outputDirectory>${assembly-directory}/lib</outputDirectory>
|
||||
<destFileName>servlet-api-3.0.jar</destFileName>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-orbit-lib-jndi-deps</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includeGroupIds>org.eclipse.jetty.orbit</includeGroupIds>
|
||||
<includeArtifactIds>javax.mail.glassfish,javax.activation</includeArtifactIds>
|
||||
<includeTypes>jar</includeTypes>
|
||||
<outputDirectory>${assembly-directory}/lib/jndi</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-orbit-lib-jsp-deps</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includeGroupIds>org.eclipse.jetty.orbit</includeGroupIds>
|
||||
<includeArtifactIds>com.sun.el,javax.el,javax.servlet.jsp,javax.servlet.jsp.jstl,org.apache.jasper.glassfish,org.apache.taglibs.standard.glassfish,org.eclipse.jdt.core</includeArtifactIds>
|
||||
<includeTypes>jar</includeTypes>
|
||||
<outputDirectory>${assembly-directory}/lib/jsp</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-eclipse-plugin</artifactId>
|
||||
<version>2.8</version>
|
||||
<configuration>
|
||||
<manifest>prevent/overwriting/by/pointing/to/nonexisting/MANIFEST.MF</manifest>
|
||||
<pde>false</pde>
|
||||
<downloadSources>true</downloadSources>
|
||||
<sourceExcludes>
|
||||
<sourceExclude>**/.svn/**</sourceExclude>
|
||||
</sourceExcludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
</project>
|
|
@ -0,0 +1,2 @@
|
|||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
org.eclipse.jetty.spdy.LEVEL=WARN
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,133 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
|
||||
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- HttpChannel Configuration -->
|
||||
<!-- =========================================================== -->
|
||||
<New id="httpConfig" class="org.eclipse.jetty.server.HttpChannelConfig">
|
||||
<Set name="secureScheme">https</Set>
|
||||
<Set name="securePort"><SystemProperty name="jetty.spdy.port" default="8443"/></Set>
|
||||
<Set name="outputBufferSize">32768</Set>
|
||||
<Set name="requestHeaderSize">8192</Set>
|
||||
<Set name="responseHeaderSize">8192</Set>
|
||||
<Call name="addCustomizer">
|
||||
<Arg><New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/></Arg>
|
||||
</Call>
|
||||
</New>
|
||||
|
||||
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Setup a SSL Context factory -->
|
||||
<!-- =========================================================== -->
|
||||
<New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
|
||||
<Set name="KeyStorePath"><Property name="jetty.home" default="."/>/etc/keystore
|
||||
</Set>
|
||||
<Set name="KeyStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
|
||||
<Set name="KeyManagerPassword">OBF:1u2u1wml1z7s1z7a1wnl1u2g</Set>
|
||||
<Set name="TrustStorePath"><Property name="jetty.home" default="."/>/etc/keystore
|
||||
</Set>
|
||||
<Set name="TrustStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
|
||||
</New>
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Add HTTP Customizer for Secure request -->
|
||||
<!-- =========================================================== -->
|
||||
<Ref id="httpConfig">
|
||||
<Call name="addCustomizer">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.server.SecureRequestCustomizer"/>
|
||||
</Arg>
|
||||
</Call>
|
||||
</Ref>
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Create a push strategy -->
|
||||
<!-- =========================================================== -->
|
||||
<New id="pushStrategy" class="org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy">
|
||||
<Arg type="List">
|
||||
<Array type="String">
|
||||
<Item>.*\.css</Item>
|
||||
<Item>.*\.js</Item>
|
||||
<Item>.*\.png</Item>
|
||||
<Item>.*\.jpg</Item>
|
||||
<Item>.*\.gif</Item>
|
||||
</Array>
|
||||
</Arg>
|
||||
</New>
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Set connectors -->
|
||||
<!-- =========================================================== -->
|
||||
<Call id="sslConnector" name="addConnector">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.server.ServerConnector">
|
||||
<Arg name="server">
|
||||
<Ref id="Server"/>
|
||||
</Arg>
|
||||
<Arg name="factories">
|
||||
<Array type="org.eclipse.jetty.server.ConnectionFactory">
|
||||
<Item>
|
||||
<New class="org.eclipse.jetty.server.SslConnectionFactory">
|
||||
<Arg name="next">npn</Arg>
|
||||
<Arg name="sslContextFactory">
|
||||
<Ref id="sslContextFactory"/>
|
||||
</Arg>
|
||||
</New>
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<New class="org.eclipse.jetty.spdy.server.NPNServerConnectionFactory">
|
||||
<Arg name="protocols">
|
||||
<Array type="String">
|
||||
<Item>spdy/3</Item>
|
||||
<Item>spdy/2</Item>
|
||||
<Item>http/1.1</Item>
|
||||
</Array>
|
||||
</Arg>
|
||||
<Set name="defaultProtocol">http/1.1</Set>
|
||||
</New>
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<New class="org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory">
|
||||
<Arg name="version" type="int">3</Arg>
|
||||
<Arg name="config">
|
||||
<Ref id="httpConfig"/>
|
||||
</Arg>
|
||||
<!-- <Arg name="pushStrategy"><Ref id="pushStrategy"/></Arg> -->
|
||||
</New>
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<New class="org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory">
|
||||
<Arg name="version" type="int">2</Arg>
|
||||
<Arg name="config">
|
||||
<Ref id="httpConfig"/>
|
||||
</Arg>
|
||||
</New>
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<New class="org.eclipse.jetty.server.HttpConnectionFactory">
|
||||
<Arg name="config">
|
||||
<Ref id="httpConfig"/>
|
||||
</Arg>
|
||||
</New>
|
||||
</Item>
|
||||
</Array>
|
||||
</Arg>
|
||||
<Set name="host">
|
||||
<Property name="jetty.host"/>
|
||||
</Set>
|
||||
<Set name="port">
|
||||
<SystemProperty name="jetty.spdy.port" default="8443"/>
|
||||
</Set>
|
||||
<Set name="idleTimeout">30000</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
|
||||
</Configure>
|
Binary file not shown.
|
@ -1,197 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2012 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.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.osgi.boot;
|
||||
|
||||
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
|
||||
import static org.ops4j.pax.exam.CoreOptions.options;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.HttpContentResponse;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHomeHelper;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.ops4j.pax.exam.CoreOptions;
|
||||
import org.ops4j.pax.exam.Inject;
|
||||
import org.ops4j.pax.exam.Option;
|
||||
import org.ops4j.pax.exam.container.def.PaxRunnerOptions;
|
||||
import org.ops4j.pax.exam.junit.Configuration;
|
||||
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
import org.osgi.service.http.HttpService;
|
||||
|
||||
/**
|
||||
* Pax-Exam to make sure the jetty-osgi-boot can be started along with the httpservice web-bundle.
|
||||
* Then make sure we can deploy an OSGi service on the top of this.
|
||||
*/
|
||||
@RunWith( JUnit4TestRunner.class )
|
||||
public class TestJettyOSGiBootCore
|
||||
{
|
||||
/**
|
||||
* Jetty-osgi including webapp support and also jetty-client.
|
||||
* Sets the system property jetty.home.bunde=org.eclipse.jetty.osgi.boot
|
||||
* to use the jetty server configuration embedded in
|
||||
*
|
||||
* @return list of options
|
||||
*/
|
||||
public static List<Option> provisionCoreJetty()
|
||||
{
|
||||
List<Option> res = new ArrayList<Option>();
|
||||
// get the jetty home config from the osgi boot bundle.
|
||||
res.add(PaxRunnerOptions.vmOptions("-Djetty.port=9876 -D" + DefaultJettyAtJettyHomeHelper.SYS_PROP_JETTY_HOME_BUNDLE + "=org.eclipse.jetty.osgi.boot"));
|
||||
res.addAll(coreJettyDependencies());
|
||||
return res;
|
||||
}
|
||||
|
||||
public static List<Option> coreJettyDependencies() {
|
||||
List<Option> res = new ArrayList<Option>();
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.osgi" ).artifactId( "org.eclipse.osgi.services" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-deploy" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-server" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-servlet" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-util" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-http" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-xml" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-webapp" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-io" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-continuation" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-security" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-servlets" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-client" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-core" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-server" ).versionAsInProject().noStart());
|
||||
//optional:
|
||||
//res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-client" ).versionAsInProject().noStart());
|
||||
return res;
|
||||
}
|
||||
|
||||
public static List<Option> websocketDependencies() {
|
||||
List<Option> res = new ArrayList<Option>();
|
||||
return res;
|
||||
}
|
||||
|
||||
@Inject
|
||||
BundleContext bundleContext = null;
|
||||
|
||||
|
||||
@Configuration
|
||||
public static Option[] configure()
|
||||
{
|
||||
ArrayList<Option> options = new ArrayList<Option>();
|
||||
options.addAll(provisionCoreJetty());
|
||||
options.addAll(Arrays.asList(options(
|
||||
// install log service using pax runners profile abstraction (there are more profiles, like DS)
|
||||
//logProfile(),
|
||||
// this is how you set the default log level when using pax logging (logProfile)
|
||||
CoreOptions.systemProperty( "org.ops4j.pax.logging.DefaultServiceLog.level" ).value( "INFO" ),
|
||||
|
||||
// CoreOptions.equinox(), CoreOptions.felix(),//.version("3.0.0"),
|
||||
|
||||
mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "jetty-osgi-boot" ).versionAsInProject().start(),
|
||||
mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "jetty-httpservice" ).versionAsInProject().start(),
|
||||
|
||||
mavenBundle().groupId( "org.eclipse.equinox.http" ).artifactId( "servlet" ).versionAsInProject().start()
|
||||
)));
|
||||
return options.toArray(new Option[options.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* You will get a list of bundles installed by default
|
||||
* plus your testcase, wrapped into a bundle called pax-exam-probe
|
||||
*/
|
||||
@Test
|
||||
public void testHttpService() throws Exception
|
||||
{
|
||||
// ServletContextHandler sch = null;
|
||||
// sch.addServlet("className", "pathSpec").setInitOrder("0");
|
||||
|
||||
|
||||
Map<String,Bundle> bundlesIndexedBySymbolicName = new HashMap<String, Bundle>();
|
||||
for( Bundle b : bundleContext.getBundles() )
|
||||
{
|
||||
bundlesIndexedBySymbolicName.put(b.getSymbolicName(), b);
|
||||
//System.err.println("got " + b.getSymbolicName());
|
||||
}
|
||||
Bundle osgiBoot = bundlesIndexedBySymbolicName.get("org.eclipse.jetty.osgi.boot");
|
||||
Assert.assertNotNull("Could not find the org.eclipse.jetty.osgi.boot bundle", osgiBoot);
|
||||
Assert.assertTrue(osgiBoot.getState() == Bundle.ACTIVE);
|
||||
|
||||
Bundle httpServiceOSGiBundle = bundlesIndexedBySymbolicName.get("org.eclipse.jetty.osgi.httpservice");
|
||||
Assert.assertNotNull(httpServiceOSGiBundle);
|
||||
Assert.assertTrue(httpServiceOSGiBundle.getState() == Bundle.ACTIVE);
|
||||
|
||||
Bundle equinoxServlet = bundlesIndexedBySymbolicName.get("org.eclipse.equinox.http.servlet");
|
||||
Assert.assertNotNull(equinoxServlet);
|
||||
//interestingly with equinox the bundle is not started. probably a difference in pax-exam and
|
||||
//the way the bundles are activated. the rest of the test goes fine.
|
||||
Assert.assertTrue(equinoxServlet.getState() == Bundle.ACTIVE);
|
||||
|
||||
//in the OSGi world this would be bad code and we should use a bundle tracker.
|
||||
//here we purposely want to make sure that the httpService is actually ready.
|
||||
ServiceReference sr = bundleContext.getServiceReference(HttpService.class.getName());
|
||||
Assert.assertNotNull("The httpServiceOSGiBundle is started and should have deployed a service reference for HttpService" ,sr);
|
||||
HttpService http = (HttpService)bundleContext.getService(sr);
|
||||
http.registerServlet("/greetings", new HttpServlet() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req,
|
||||
HttpServletResponse resp) throws ServletException,
|
||||
IOException {
|
||||
resp.getWriter().write("Hello");
|
||||
}
|
||||
}, null, null);
|
||||
|
||||
//now test the servlet
|
||||
HttpClient client = new HttpClient();
|
||||
try
|
||||
{
|
||||
client.start();
|
||||
Response response = client.GET("http://127.0.0.1:9876/greetings").get(5, TimeUnit.SECONDS);;
|
||||
Assert.assertEquals(HttpStatus.OK_200, response.status());
|
||||
|
||||
String content = new String(((HttpContentResponse)response).content());
|
||||
Assert.assertEquals("Hello", content);
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2012 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.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.osgi.boot;
|
||||
|
||||
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
|
||||
import static org.ops4j.pax.exam.CoreOptions.options;
|
||||
import static org.ops4j.pax.exam.CoreOptions.systemProperty;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.HttpContentResponse;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.ops4j.pax.exam.CoreOptions;
|
||||
import org.ops4j.pax.exam.Inject;
|
||||
import org.ops4j.pax.exam.Option;
|
||||
import org.ops4j.pax.exam.container.def.PaxRunnerOptions;
|
||||
import org.ops4j.pax.exam.junit.Configuration;
|
||||
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleContext;
|
||||
|
||||
/**
|
||||
* Pax-Exam to make sure the jetty-osgi-boot can be started along with the httpservice web-bundle.
|
||||
* Then make sure we can deploy an OSGi service on the top of this.
|
||||
*/
|
||||
@RunWith( JUnit4TestRunner.class )
|
||||
public class TestJettyOSGiBootWithJsp
|
||||
{
|
||||
private static final boolean LOGGING_ENABLED = true;
|
||||
private static final boolean REMOTE_DEBUGGING = false;
|
||||
|
||||
@Inject
|
||||
BundleContext bundleContext = null;
|
||||
|
||||
@Configuration
|
||||
public static Option[] configure()
|
||||
{
|
||||
File etcFolder = new File("src/test/config/etc");
|
||||
String etc = "file://" + etcFolder.getAbsolutePath();
|
||||
|
||||
ArrayList<Option> options = new ArrayList<Option>();
|
||||
options.add(PaxRunnerOptions.vmOption("-Djetty.port=9876 -Djetty.home="+ etcFolder.getParentFile().getAbsolutePath() +
|
||||
" -D" + OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS +
|
||||
"="+etc+"/jetty.xml;"+etc+"/jetty-selector.xml;"+etc+"/jetty-deployer.xml;" + etc + "/jetty-testrealm.xml"));
|
||||
options.add(CoreOptions.equinox());
|
||||
options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*",
|
||||
"org.w3c.*", "javax.xml.*"));
|
||||
options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
|
||||
|
||||
// Enable Logging
|
||||
if(LOGGING_ENABLED) {
|
||||
options.addAll(Arrays.asList(options(
|
||||
// install log service using pax runners profile abstraction (there are more profiles, like DS)
|
||||
// logProfile(),
|
||||
// this is how you set the default log level when using pax logging (logProfile)
|
||||
systemProperty( "org.ops4j.pax.logging.DefaultServiceLog.level" ).value( "INFO" )
|
||||
)));
|
||||
}
|
||||
|
||||
options.addAll(jspDependencies());
|
||||
|
||||
// Remote JDWP Debugging
|
||||
if(REMOTE_DEBUGGING) {
|
||||
options.addAll(Arrays.asList(options(
|
||||
// this just adds all what you write here to java vm argumenents of the (new) osgi process.
|
||||
PaxRunnerOptions.vmOption( "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5006" )
|
||||
)));
|
||||
}
|
||||
|
||||
return options.toArray(new Option[options.size()]);
|
||||
}
|
||||
|
||||
public static List<Option> jspDependencies() {
|
||||
List<Option> res = new ArrayList<Option>();
|
||||
/* orbit deps */
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet.jsp" ).versionAsInProject());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet.jsp.jstl" ).versionAsInProject());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.el" ).versionAsInProject());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "com.sun.el" ).versionAsInProject());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "org.apache.jasper.glassfish" ).versionAsInProject());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "org.apache.taglibs.standard.glassfish" ).versionAsInProject());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "org.eclipse.jdt.core" ).versionAsInProject());
|
||||
|
||||
/* jetty-osgi deps */
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "jetty-osgi-boot-jsp" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "jetty-osgi-boot" ).versionAsInProject().start());
|
||||
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "test-jetty-webapp" ).classifier("webbundle").versionAsInProject());
|
||||
|
||||
res.add(mavenBundle().groupId( "org.eclipse.equinox.http" ).artifactId( "servlet" ).versionAsInProject().start());
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* You will get a list of bundles installed by default
|
||||
* plus your testcase, wrapped into a bundle called pax-exam-probe
|
||||
*/
|
||||
@Test
|
||||
public void listBundles() throws Exception
|
||||
{
|
||||
Map<String,Bundle> bundlesIndexedBySymbolicName = new HashMap<String, Bundle>();
|
||||
for( Bundle b : bundleContext.getBundles() )
|
||||
{
|
||||
bundlesIndexedBySymbolicName.put(b.getSymbolicName(), b);
|
||||
}
|
||||
Bundle websocketServer = bundlesIndexedBySymbolicName.get("org.eclipse.jetty.websocket.server");
|
||||
Assert.assertNotNull("Could not find the org.eclipse.jetty.websocket.server bundle", websocketServer);
|
||||
Assert.assertEquals(Bundle.RESOLVED, websocketServer.getState());
|
||||
|
||||
Bundle osgiBoot = bundlesIndexedBySymbolicName.get("org.eclipse.jetty.osgi.boot");
|
||||
Assert.assertNotNull("Could not find the org.eclipse.jetty.osgi.boot bundle", osgiBoot);
|
||||
Assert.assertEquals(Bundle.ACTIVE, osgiBoot.getState());
|
||||
|
||||
Bundle osgiBootJsp = bundlesIndexedBySymbolicName.get("org.eclipse.jetty.osgi.boot.jsp");
|
||||
Assert.assertNotNull("Could not find the org.eclipse.jetty.osgi.boot.jsp bundle", osgiBootJsp);
|
||||
Assert.assertEquals("The fragment jsp is not correctly resolved.",
|
||||
Bundle.RESOLVED, osgiBootJsp.getState());
|
||||
|
||||
Bundle testWebBundle = bundlesIndexedBySymbolicName.get("org.eclipse.jetty.test-jetty-webapp");
|
||||
Assert.assertNotNull("Could not find the org.eclipse.jetty.test-jetty-webappp bundle", osgiBootJsp);
|
||||
Assert.assertEquals("The test-jetty-webapp is not correctly resolved", Bundle.ACTIVE, testWebBundle.getState());
|
||||
|
||||
//now test the jsp/dump.jsp
|
||||
HttpClient client = new HttpClient();
|
||||
try
|
||||
{
|
||||
client.start();
|
||||
Response response = client.GET("http://127.0.0.1:9876/jsp/dump.jsp").get(5, TimeUnit.SECONDS);
|
||||
Assert.assertEquals(HttpStatus.OK_200, response.status());
|
||||
|
||||
String content = new String(((HttpContentResponse)response).content());
|
||||
//System.err.println("content: " + content);
|
||||
Assert.assertTrue(content.indexOf("<tr><th>ServletPath:</th><td>/jsp/dump.jsp</td></tr>") != -1);
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2012 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.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.osgi.test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.HttpContentResponse;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.junit.Assert;
|
||||
import org.ops4j.pax.exam.CoreOptions;
|
||||
import org.ops4j.pax.exam.Option;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
import org.osgi.service.http.HttpService;
|
||||
|
||||
/**
|
||||
* Helper methods for pax-exam tests
|
||||
*/
|
||||
public class AbstractTestOSGi
|
||||
{
|
||||
|
||||
private Map<String,Bundle> _bundles;
|
||||
|
||||
/**
|
||||
* Note: this will run many more tests.
|
||||
* TODO: find a better way to control this and use non-deprecated methods.
|
||||
* @param options
|
||||
*/
|
||||
protected static void addMoreOSGiContainers(List<Option> options)
|
||||
{
|
||||
options.add(CoreOptions.equinox().version("3.6.1"));
|
||||
options.add(CoreOptions.equinox().version("3.7.1"));
|
||||
options.add(CoreOptions.felix().version("3.2.2"));
|
||||
options.add(CoreOptions.felix().version("4.0.2"));
|
||||
}
|
||||
|
||||
protected Bundle getBundle(BundleContext bundleContext, String symbolicName)
|
||||
{
|
||||
if (_bundles == null)
|
||||
{
|
||||
_bundles = new HashMap<String,Bundle>();
|
||||
for (Bundle b : bundleContext.getBundles())
|
||||
{
|
||||
Bundle prevBundle = _bundles.put(b.getSymbolicName(), b);
|
||||
String err = prevBundle != null ? "2 versions of the bundle " + b.getSymbolicName() +
|
||||
" " + b.getHeaders().get("Bundle-Version") +
|
||||
" and " + prevBundle.getHeaders().get("Bundle-Version")
|
||||
: "";
|
||||
Assert.assertNull(err, prevBundle);
|
||||
}
|
||||
}
|
||||
return _bundles.get(symbolicName);
|
||||
}
|
||||
|
||||
protected void assertActiveBundle(BundleContext bundleContext, String symbolicName) throws Exception
|
||||
{
|
||||
Bundle b = getBundle(bundleContext, symbolicName);
|
||||
Assert.assertNotNull(b);
|
||||
Assert.assertEquals(b.getSymbolicName()+" must be active.", Bundle.ACTIVE, b.getState());
|
||||
}
|
||||
|
||||
protected void assertActiveOrResolvedBundle(BundleContext bundleContext, String symbolicName) throws Exception
|
||||
{
|
||||
Bundle b = getBundle(bundleContext, symbolicName);
|
||||
Assert.assertNotNull(b);
|
||||
if (b.getHeaders().get("Fragment-Host") == null) diagnoseNonActiveOrNonResolvedBundle(b);
|
||||
Assert.assertTrue(b.getSymbolicName()+" must be active or resolved. It was "+b.getState(),
|
||||
b.getState() == Bundle.ACTIVE || b.getState() == Bundle.RESOLVED);
|
||||
}
|
||||
|
||||
protected void assertAllBundlesActiveOrResolved(BundleContext bundleContext)
|
||||
{
|
||||
for (Bundle b : bundleContext.getBundles())
|
||||
{
|
||||
if (b.getState() == Bundle.INSTALLED)
|
||||
{
|
||||
diagnoseNonActiveOrNonResolvedBundle(b);
|
||||
}
|
||||
Assert.assertTrue(b.getState() == Bundle.ACTIVE || b.getState() == Bundle.RESOLVED);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean diagnoseNonActiveOrNonResolvedBundle(Bundle b)
|
||||
{
|
||||
if (b.getState() != Bundle.ACTIVE && b.getHeaders().get("Fragment-Host") == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
System.err.println("Trying to start the bundle "+b.getSymbolicName()+
|
||||
" that was supposed to be active or resolved.");
|
||||
b.start();
|
||||
System.err.println(b.getSymbolicName() + " did start");
|
||||
return true;
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
System.err.println(b.getSymbolicName() + " failed to start");
|
||||
t.printStackTrace(System.err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
System.err.println(b.getSymbolicName() + " was already started");
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void debugBundles(BundleContext bundleContext)
|
||||
{
|
||||
Map<String,Bundle> bundlesIndexedBySymbolicName = new HashMap<String, Bundle>();
|
||||
System.err.println("Active " + Bundle.ACTIVE);
|
||||
System.err.println("RESOLVED " + Bundle.RESOLVED);
|
||||
System.err.println("INSTALLED " + Bundle.INSTALLED);
|
||||
for( Bundle b : bundleContext.getBundles() )
|
||||
{
|
||||
bundlesIndexedBySymbolicName.put(b.getSymbolicName(), b);
|
||||
System.err.println(" " + b.getSymbolicName() + " " + b.getState());
|
||||
}
|
||||
}
|
||||
|
||||
protected SslContextFactory getSslContextFactory()
|
||||
{
|
||||
return new SslContextFactory(true);
|
||||
}
|
||||
|
||||
protected void testHttpServiceGreetings(BundleContext bundleContext, String protocol, int port) throws Exception
|
||||
{
|
||||
assertActiveBundle(bundleContext, "org.eclipse.jetty.osgi.boot");
|
||||
|
||||
assertActiveBundle(bundleContext, "org.eclipse.jetty.osgi.httpservice");
|
||||
assertActiveBundle(bundleContext, "org.eclipse.equinox.http.servlet");
|
||||
|
||||
//in the OSGi world this would be bad code and we should use a bundle tracker.
|
||||
//here we purposely want to make sure that the httpService is actually ready.
|
||||
ServiceReference sr = bundleContext.getServiceReference(HttpService.class.getName());
|
||||
Assert.assertNotNull("The httpServiceOSGiBundle is started and should " +
|
||||
"have deployed a service reference for HttpService" ,sr);
|
||||
HttpService http = (HttpService)bundleContext.getService(sr);
|
||||
http.registerServlet("/greetings", new HttpServlet() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req,
|
||||
HttpServletResponse resp) throws ServletException,
|
||||
IOException {
|
||||
resp.getWriter().write("Hello");
|
||||
}
|
||||
}, null, null);
|
||||
|
||||
//now test the servlet
|
||||
HttpClient client = protocol.equals("https") ? new HttpClient(getSslContextFactory()) : new HttpClient();
|
||||
try
|
||||
{
|
||||
client.start();
|
||||
Response response = client.GET(protocol+"://127.0.0.1:"+port+"/greetings").get(5, TimeUnit.SECONDS);;
|
||||
Assert.assertEquals(HttpStatus.OK_200, response.status());
|
||||
|
||||
String content = new String(((HttpContentResponse)response).content());
|
||||
Assert.assertEquals("Hello", content);
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2012 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.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.osgi.test;
|
||||
|
||||
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.ops4j.pax.exam.CoreOptions;
|
||||
import org.ops4j.pax.exam.Option;
|
||||
import org.ops4j.pax.exam.junit.Configuration;
|
||||
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
|
||||
import org.osgi.framework.BundleContext;
|
||||
|
||||
|
||||
/**
|
||||
* Default OSGi setup integration test
|
||||
*/
|
||||
@RunWith( JUnit4TestRunner.class )
|
||||
public class TestJettyOSGiBootCore extends AbstractTestOSGi {
|
||||
|
||||
public static int DEFAULT_JETTY_HTTP_PORT = 9876;
|
||||
|
||||
@Inject
|
||||
private BundleContext bundleContext;
|
||||
|
||||
@Configuration
|
||||
public Option[] config()
|
||||
{
|
||||
ArrayList<Option> options = new ArrayList<Option>();
|
||||
addMoreOSGiContainers(options);
|
||||
options.addAll(provisionCoreJetty());
|
||||
options.add(CoreOptions.junitBundles());
|
||||
options.addAll(httpServiceJetty());
|
||||
return options.toArray(new Option[options.size()]);
|
||||
}
|
||||
|
||||
public static List<Option> provisionCoreJetty()
|
||||
{
|
||||
List<Option> res = new ArrayList<Option>();
|
||||
// get the jetty home config from the osgi boot bundle.
|
||||
res.add(CoreOptions.systemProperty("jetty.port").value(String.valueOf(DEFAULT_JETTY_HTTP_PORT)));
|
||||
res.add(CoreOptions.systemProperty("jetty.home.bundle").value("org.eclipse.jetty.osgi.boot"));
|
||||
res.addAll(coreJettyDependencies());
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
public static List<Option> coreJettyDependencies()
|
||||
{
|
||||
List<Option> res = new ArrayList<Option>();
|
||||
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "jetty-osgi-boot" ).versionAsInProject().start());
|
||||
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-deploy" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-server" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-servlet" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-util" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-http" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-xml" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-webapp" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-io" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-continuation" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-security" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-servlets" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-client" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-core" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-server" ).versionAsInProject().noStart());
|
||||
//optional:
|
||||
//res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-client" ).versionAsInProject().noStart());
|
||||
return res;
|
||||
}
|
||||
|
||||
public static List<Option> httpServiceJetty()
|
||||
{
|
||||
List<Option> res = new ArrayList<Option>();
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "jetty-httpservice" ).versionAsInProject().start());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.equinox.http" ).artifactId( "servlet" ).versionAsInProject().start());
|
||||
return res;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assertAllBundlesActiveOrResolved()
|
||||
{
|
||||
assertAllBundlesActiveOrResolved(bundleContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* You will get a list of bundles installed by default
|
||||
* plus your testcase, wrapped into a bundle called pax-exam-probe
|
||||
*/
|
||||
@Test
|
||||
public void testHttpService() throws Exception
|
||||
{
|
||||
testHttpServiceGreetings(bundleContext, "http", DEFAULT_JETTY_HTTP_PORT);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2012 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.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.osgi.test;
|
||||
|
||||
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.ops4j.pax.exam.CoreOptions;
|
||||
import org.ops4j.pax.exam.Option;
|
||||
import org.ops4j.pax.exam.junit.Configuration;
|
||||
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
|
||||
import org.osgi.framework.BundleContext;
|
||||
|
||||
/**
|
||||
* SPDY setup.
|
||||
*/
|
||||
@RunWith( JUnit4TestRunner.class )
|
||||
public class TestJettyOSGiBootSpdy extends AbstractTestOSGi {
|
||||
|
||||
private static final String JETTY_SPDY_PORT = "jetty.spdy.port";
|
||||
private static final int DEFAULT_JETTY_SPDY_PORT = 9877;
|
||||
|
||||
@Inject
|
||||
private BundleContext bundleContext;
|
||||
|
||||
@Configuration
|
||||
public Option[] config()
|
||||
{
|
||||
ArrayList<Option> options = new ArrayList<Option>();
|
||||
|
||||
addMoreOSGiContainers(options);
|
||||
|
||||
options.addAll(TestJettyOSGiBootCore.provisionCoreJetty());
|
||||
options.addAll(TestJettyOSGiBootWithJsp.configureJettyHomeAndPort("jetty-spdy.xml"));
|
||||
options.add(CoreOptions.junitBundles());
|
||||
options.addAll(TestJettyOSGiBootCore.httpServiceJetty());
|
||||
options.addAll(spdyJettyDependencies());
|
||||
return options.toArray(new Option[options.size()]);
|
||||
}
|
||||
|
||||
public static List<Option> spdyJettyDependencies()
|
||||
{
|
||||
List<Option> res = new ArrayList<Option>();
|
||||
res.add(CoreOptions.systemProperty(JETTY_SPDY_PORT).value(String.valueOf(DEFAULT_JETTY_SPDY_PORT)));
|
||||
//java -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn-version}/npn-boot-${npn-version}.jar
|
||||
// res.add(CoreOptions.vmOptions("-Xbootclasspath/p:"+System.getenv("HOME")+"/.m2/repository/org/mortbay/jetty/npn/npn-boot/"+npnBootVersion+"/npn-boot-"+npnBootVersion+".jar"));
|
||||
String npnBoot = System.getProperty("mortbay-npn-boot");
|
||||
if (npnBoot == null)
|
||||
{
|
||||
throw new IllegalStateException("Please define the path to the npn boot jar as the sys property -Dmortbay-npn-boot");
|
||||
//are we trying to be too nice? this kinds of work outside of maven maybe
|
||||
// String npnBootUrl = mavenBundle().groupId( "org.mortbay.jetty.npn" ).artifactId( "npn-boot" ).versionAsInProject().getURL();
|
||||
// String npnBootVersion = npnBootUrl.split("\\/")[2];
|
||||
// if (!Character.isDigit(npnBootVersion.charAt(0)))
|
||||
// {
|
||||
// throw new IllegalArgumentException(npnBootUrl + " - " + npnBootVersion);
|
||||
// }
|
||||
// npnBoot = System.getenv("HOME")+"/.m2/repository/org/mortbay/jetty/npn/npn-boot/"+npnBootVersion+"/npn-boot-"+npnBootVersion+".jar";
|
||||
}
|
||||
File checkNpnBoot = new File(npnBoot);
|
||||
if (!checkNpnBoot.exists())
|
||||
{
|
||||
throw new IllegalStateException("Unable to find the npn boot jar here: " + npnBoot);
|
||||
}
|
||||
|
||||
res.add(CoreOptions.vmOptions("-Xbootclasspath/p:"+npnBoot));
|
||||
res.add(CoreOptions.bootDelegationPackages("org.eclipse.jetty.npn"));
|
||||
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.spdy" ).artifactId( "spdy-core" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.spdy" ).artifactId( "spdy-server" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.spdy" ).artifactId( "spdy-http-server" ).versionAsInProject().noStart());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.spdy" ).artifactId( "spdy-client" ).versionAsInProject().noStart());
|
||||
return res;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkNpnBootOnBootstrapClasspath() throws Exception
|
||||
{
|
||||
Class<?> npn = Thread.currentThread().getContextClassLoader()
|
||||
.loadClass("org.eclipse.jetty.npn.NextProtoNego");
|
||||
Assert.assertNotNull(npn);
|
||||
Assert.assertNull(npn.getClassLoader());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assertAllBundlesActiveOrResolved()
|
||||
{
|
||||
assertAllBundlesActiveOrResolved(bundleContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpdyOnHttpService() throws Exception
|
||||
{
|
||||
testHttpServiceGreetings(bundleContext, "https", DEFAULT_JETTY_SPDY_PORT);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2012 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.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.osgi.test;
|
||||
|
||||
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
|
||||
import static org.ops4j.pax.exam.CoreOptions.options;
|
||||
import static org.ops4j.pax.exam.CoreOptions.systemProperty;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.HttpContentResponse;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.ops4j.pax.exam.CoreOptions;
|
||||
import org.ops4j.pax.exam.Option;
|
||||
import org.ops4j.pax.exam.junit.Configuration;
|
||||
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
|
||||
import org.osgi.framework.BundleContext;
|
||||
|
||||
/**
|
||||
* Pax-Exam to make sure the jetty-osgi-boot can be started along with the httpservice web-bundle.
|
||||
* Then make sure we can deploy an OSGi service on the top of this.
|
||||
*/
|
||||
@RunWith( JUnit4TestRunner.class )
|
||||
public class TestJettyOSGiBootWithJsp extends AbstractTestOSGi
|
||||
{
|
||||
private static final boolean LOGGING_ENABLED = true;
|
||||
private static final boolean REMOTE_DEBUGGING = false;
|
||||
|
||||
@Inject
|
||||
BundleContext bundleContext = null;
|
||||
|
||||
@Configuration
|
||||
public static Option[] configure()
|
||||
{
|
||||
|
||||
ArrayList<Option> options = new ArrayList<Option>();
|
||||
|
||||
addMoreOSGiContainers(options);
|
||||
|
||||
options.add(CoreOptions.junitBundles());
|
||||
options.addAll(configureJettyHomeAndPort("jetty-selector.xml"));
|
||||
options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*",
|
||||
"org.w3c.*", "javax.xml.*"));
|
||||
options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
|
||||
|
||||
// Enable Logging
|
||||
if(LOGGING_ENABLED) {
|
||||
options.addAll(Arrays.asList(options(
|
||||
// install log service using pax runners profile abstraction (there are more profiles, like DS)
|
||||
// logProfile(),
|
||||
// this is how you set the default log level when using pax logging (logProfile)
|
||||
systemProperty( "org.ops4j.pax.logging.DefaultServiceLog.level" ).value( "INFO" )
|
||||
)));
|
||||
}
|
||||
|
||||
options.addAll(jspDependencies());
|
||||
|
||||
// Remote JDWP Debugging, this won't work with the forked container.
|
||||
// if(REMOTE_DEBUGGING) {
|
||||
// options.addAll(Arrays.asList(options(
|
||||
// // this just adds all what you write here to java vm argumenents of the (new) osgi process.
|
||||
// PaxRunnerOptions.vmOption( "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5006" )
|
||||
// )));
|
||||
// }
|
||||
|
||||
//bug at the moment: this would make the httpservice catch all
|
||||
//requests and prevent the webapp at the root context to catch any of them.
|
||||
//options.addAll(TestJettyOSGiBootCore.httpServiceJetty());
|
||||
|
||||
return options.toArray(new Option[options.size()]);
|
||||
}
|
||||
|
||||
public static List<Option> configureJettyHomeAndPort(String jettySelectorFileName)
|
||||
{
|
||||
File etcFolder = new File("src/test/config/etc");
|
||||
String etc = "file://" + etcFolder.getAbsolutePath();
|
||||
List<Option> options = new ArrayList<Option>();
|
||||
options.add(systemProperty(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS)
|
||||
.value(etc + "/jetty.xml;" +
|
||||
etc + "/" + jettySelectorFileName + ";" +
|
||||
etc + "/jetty-deployer.xml;" +
|
||||
etc + "/jetty-testrealm.xml"));
|
||||
options.add(systemProperty("jetty.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT)));
|
||||
options.add(systemProperty("jetty.home").value(etcFolder.getParentFile().getAbsolutePath()));
|
||||
return options;
|
||||
}
|
||||
|
||||
public static List<Option> jspDependencies() {
|
||||
List<Option> res = new ArrayList<Option>();
|
||||
/* orbit deps */
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet.jsp" ).versionAsInProject());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet.jsp.jstl" ).versionAsInProject());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.el" ).versionAsInProject());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "com.sun.el" ).versionAsInProject());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "org.apache.jasper.glassfish" ).versionAsInProject());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "org.apache.taglibs.standard.glassfish" ).versionAsInProject());
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "org.eclipse.jdt.core" ).versionAsInProject());
|
||||
|
||||
/* jetty-osgi deps */
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "jetty-osgi-boot-jsp" ).versionAsInProject().noStart());
|
||||
|
||||
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "test-jetty-webapp" ).classifier("webbundle").versionAsInProject());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assertAllBundlesActiveOrResolved()
|
||||
{
|
||||
assertAllBundlesActiveOrResolved(bundleContext);
|
||||
}
|
||||
|
||||
//at the moment can't run httpservice with jsp at the same time.
|
||||
//that is a regression in jetty-9
|
||||
@Ignore
|
||||
@Test
|
||||
public void testHttpService() throws Exception
|
||||
{
|
||||
super.testHttpServiceGreetings(bundleContext, "http", TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJspDump() throws Exception
|
||||
{
|
||||
|
||||
//System.err.println("http://127.0.0.1:9876/jsp/dump.jsp sleeping....");
|
||||
//Thread.currentThread().sleep(5000000);
|
||||
//now test the jsp/dump.jsp
|
||||
HttpClient client = new HttpClient();
|
||||
try
|
||||
{
|
||||
client.start();
|
||||
Response response = client.GET("http://127.0.0.1:"+
|
||||
TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT+"/jsp/dump.jsp").get(5, TimeUnit.SECONDS);
|
||||
Assert.assertEquals(HttpStatus.OK_200, response.status());
|
||||
|
||||
String content = new String(((HttpContentResponse)response).content());
|
||||
//System.err.println("content: " + content);
|
||||
Assert.assertTrue(content.indexOf("<tr><th>ServletPath:</th><td>/jsp/dump.jsp</td></tr>") != -1);
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -28,7 +28,9 @@
|
|||
<configuration>
|
||||
<instructions>
|
||||
<_versionpolicy> </_versionpolicy>
|
||||
<Import-Package>javax.sql.*,javax.security.*,javax.naming.*,javax.servlet.*;version="2.6.0",javax.transaction.*;version="[1.1,1.2)",*</Import-Package>
|
||||
<Import-Package>javax.sql.*,javax.security.*,javax.naming.*,
|
||||
javax.servlet.*;version="2.6.0",javax.transaction.*;version="[1.1,1.2)",
|
||||
*</Import-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
|
|
@ -49,7 +49,7 @@ public class EncodingHttpWriter extends HttpWriter
|
|||
public void write (char[] s,int offset, int length) throws IOException
|
||||
{
|
||||
if (length==0)
|
||||
_out.checkAllWritten();
|
||||
_out.closeIfAllContentWritten();
|
||||
|
||||
while (length > 0)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2012 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.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.io.FilterConnection;
|
||||
import org.eclipse.jetty.util.annotation.Name;
|
||||
|
||||
public class FilterConnectionFactory extends AbstractConnectionFactory
|
||||
{
|
||||
private final String _nextProtocol;
|
||||
private final int _outputBufferSize;
|
||||
private final CopyOnWriteArrayList<FilterConnection.Filter> _filters = new CopyOnWriteArrayList<>();
|
||||
|
||||
public FilterConnectionFactory()
|
||||
{
|
||||
this(HttpVersion.HTTP_1_1.asString());
|
||||
}
|
||||
|
||||
public FilterConnectionFactory(String nextProtocol)
|
||||
{
|
||||
this(nextProtocol,16*1024);
|
||||
}
|
||||
|
||||
public FilterConnectionFactory(@Name("nextProtocol") String nextProtocol,@Name("outputBufferSize") int outputBufferSize)
|
||||
{
|
||||
super("filter-"+nextProtocol);
|
||||
_nextProtocol=nextProtocol;
|
||||
_outputBufferSize=outputBufferSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection newConnection(Connector connector, EndPoint endPoint)
|
||||
{
|
||||
FilterConnection filteredConnection = new FilterConnection(connector.getByteBufferPool(),connector.getExecutor(),endPoint,_outputBufferSize);
|
||||
|
||||
configure(filteredConnection, connector, endPoint);
|
||||
addFilters(connector, filteredConnection);
|
||||
|
||||
ConnectionFactory next = connector.getConnectionFactory(_nextProtocol);
|
||||
EndPoint filteredEndPoint = filteredConnection.getFilterEndPoint();
|
||||
|
||||
Connection connection = next.newConnection(connector, filteredEndPoint);
|
||||
filteredEndPoint.setConnection(connection);
|
||||
|
||||
return filteredConnection;
|
||||
}
|
||||
|
||||
protected void addFilters(Connector connector, FilterConnection filteredConnection)
|
||||
{
|
||||
for (FilterConnection.Filter filter : _filters)
|
||||
filteredConnection.addFilter(filter);
|
||||
}
|
||||
|
||||
public void addFilter(FilterConnection.Filter filter)
|
||||
{
|
||||
addBean(filter);
|
||||
_filters.add(filter);
|
||||
}
|
||||
|
||||
public boolean removeFilter(FilterConnection.Filter filter)
|
||||
{
|
||||
removeBean(filter);
|
||||
return _filters.remove(filter);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -57,6 +57,25 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
private final HttpParser _parser;
|
||||
private volatile ByteBuffer _requestBuffer = null;
|
||||
private volatile ByteBuffer _chunk = null;
|
||||
|
||||
// TODO get rid of this
|
||||
private final Runnable _channelRunner = new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
setCurrentConnection(HttpConnection.this);
|
||||
_channel.run();
|
||||
}
|
||||
finally
|
||||
{
|
||||
setCurrentConnection(null);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
public static HttpConnection getCurrentConnection()
|
||||
{
|
||||
|
@ -75,7 +94,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
|
||||
public HttpConnection(HttpChannelConfig config, Connector connector, EndPoint endPoint)
|
||||
{
|
||||
super(endPoint, connector.getExecutor());
|
||||
// Tell AbstractConnector executeOnFillable==false because we are guaranteeing that onfillable
|
||||
// will never block nor take an excessive amount of CPU. ie it is OK for the selector thread to
|
||||
// be used. In this case the thread that calls onfillable will be asked to do some IO and parsing.
|
||||
super(endPoint, connector.getExecutor(),false);
|
||||
|
||||
_config = config;
|
||||
_connector = connector;
|
||||
|
@ -193,10 +215,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
while (true)
|
||||
{
|
||||
// Can the parser progress (even with an empty buffer)
|
||||
boolean event=_parser.parseNext(_requestBuffer==null?BufferUtil.EMPTY_BUFFER:_requestBuffer);
|
||||
boolean call_channel=_parser.parseNext(_requestBuffer==null?BufferUtil.EMPTY_BUFFER:_requestBuffer);
|
||||
|
||||
// If there is a request buffer, we are re-entering here
|
||||
if (!event && BufferUtil.isEmpty(_requestBuffer))
|
||||
if (!call_channel && BufferUtil.isEmpty(_requestBuffer))
|
||||
{
|
||||
if (_requestBuffer == null)
|
||||
_requestBuffer = _bufferPool.acquire(getInputBufferSize(), false);
|
||||
|
@ -232,11 +254,11 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
}
|
||||
|
||||
// Parse what we have read
|
||||
event=_parser.parseNext(_requestBuffer);
|
||||
call_channel=_parser.parseNext(_requestBuffer);
|
||||
}
|
||||
|
||||
// Parse the buffer
|
||||
if (event)
|
||||
if (call_channel)
|
||||
{
|
||||
// Parse as much content as there is available before calling the channel
|
||||
// this is both efficient (may queue many chunks), will correctly set available for 100 continues
|
||||
|
@ -250,25 +272,8 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
// The parser returned true, which indicates the channel is ready to handle a request.
|
||||
// Call the channel and this will either handle the request/response to completion OR,
|
||||
// if the request suspends, the request/response will be incomplete so the outer loop will exit.
|
||||
_channel.run();
|
||||
|
||||
// Return if the channel is still processing the request
|
||||
if (_channel.getState().isSuspending())
|
||||
{
|
||||
// release buffer if no input being held.
|
||||
// This is needed here to handle the case of no request input. If there
|
||||
// is request input, then the release is handled by Input@onAllContentConsumed()
|
||||
if (_channel.getRequest().getHttpInput().available()==0)
|
||||
releaseRequestBuffer();
|
||||
return;
|
||||
}
|
||||
|
||||
// return if the connection has been changed
|
||||
if (getEndPoint().getConnection()!=this)
|
||||
{
|
||||
releaseRequestBuffer();
|
||||
return;
|
||||
}
|
||||
getExecutor().execute(_channelRunner);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -452,54 +457,52 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
onClose();
|
||||
getEndPoint().setConnection(connection);
|
||||
connection.onOpen();
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
reset();
|
||||
|
||||
// Is this thread dispatched from a resume ?
|
||||
if (getCurrentConnection() != HttpConnection.this)
|
||||
if (_parser.isStart())
|
||||
{
|
||||
if (_parser.isStart())
|
||||
// it wants to eat more
|
||||
if (_requestBuffer == null)
|
||||
{
|
||||
// it wants to eat more
|
||||
if (_requestBuffer == null)
|
||||
{
|
||||
fillInterested();
|
||||
}
|
||||
else if (getConnector().isStarted())
|
||||
{
|
||||
LOG.debug("{} pipelined", this);
|
||||
fillInterested();
|
||||
}
|
||||
else if (getConnector().isStarted())
|
||||
{
|
||||
LOG.debug("{} pipelined", this);
|
||||
|
||||
try
|
||||
{
|
||||
getExecutor().execute(this);
|
||||
}
|
||||
catch (RejectedExecutionException e)
|
||||
{
|
||||
if (getConnector().isStarted())
|
||||
LOG.warn(e);
|
||||
else
|
||||
LOG.ignore(e);
|
||||
getEndPoint().close();
|
||||
}
|
||||
}
|
||||
else
|
||||
try
|
||||
{
|
||||
getExecutor().execute(this);
|
||||
}
|
||||
catch (RejectedExecutionException e)
|
||||
{
|
||||
if (getConnector().isStarted())
|
||||
LOG.warn(e);
|
||||
else
|
||||
LOG.ignore(e);
|
||||
getEndPoint().close();
|
||||
}
|
||||
}
|
||||
|
||||
if (_parser.isClosed() && !getEndPoint().isOutputShutdown())
|
||||
else
|
||||
{
|
||||
// TODO This is a catch all indicating some protocol handling failure
|
||||
// Currently needed for requests saying they are HTTP/2.0.
|
||||
// This should be removed once better error handling is in place
|
||||
LOG.warn("Endpoint output not shutdown when seeking EOF");
|
||||
getEndPoint().shutdownOutput();
|
||||
getEndPoint().close();
|
||||
}
|
||||
}
|
||||
|
||||
if (_parser.isClosed() && !getEndPoint().isOutputShutdown())
|
||||
{
|
||||
// TODO This is a catch all indicating some protocol handling failure
|
||||
// Currently needed for requests saying they are HTTP/2.0.
|
||||
// This should be removed once better error handling is in place
|
||||
LOG.warn("Endpoint output not shutdown when seeking EOF");
|
||||
getEndPoint().shutdownOutput();
|
||||
}
|
||||
|
||||
// make sure that an oshut connection is driven towards close
|
||||
// TODO this is a little ugly
|
||||
if (getEndPoint().isOpen() && getEndPoint().isOutputShutdown())
|
||||
|
@ -537,7 +540,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
|
||||
// Do we have content ready to parse?
|
||||
if (BufferUtil.isEmpty(_requestBuffer))
|
||||
{
|
||||
{
|
||||
// If no more input
|
||||
if (getEndPoint().isInputShutdown())
|
||||
{
|
||||
|
@ -553,7 +556,13 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
|
||||
// We will need a buffer to read into
|
||||
if (_requestBuffer==null)
|
||||
_requestBuffer=_bufferPool.acquire(getInputBufferSize(),false);
|
||||
{
|
||||
long content_length=_channel.getRequest().getContentLength();
|
||||
int size=getInputBufferSize();
|
||||
if (size<content_length)
|
||||
size=size*4; // TODO tune this
|
||||
_requestBuffer=_bufferPool.acquire(size,false);
|
||||
}
|
||||
|
||||
// read some data
|
||||
int filled=getEndPoint().fill(_requestBuffer);
|
||||
|
|
|
@ -46,13 +46,13 @@ import org.eclipse.jetty.util.resource.Resource;
|
|||
*/
|
||||
public class HttpOutput extends ServletOutputStream
|
||||
{
|
||||
private final HttpChannel _channel;
|
||||
private final HttpChannel<?> _channel;
|
||||
private boolean _closed;
|
||||
private long _written;
|
||||
private ByteBuffer _aggregate;
|
||||
private int _bufferSize;
|
||||
|
||||
public HttpOutput(HttpChannel channel)
|
||||
public HttpOutput(HttpChannel<?> channel)
|
||||
{
|
||||
_channel = channel;
|
||||
_bufferSize = _channel.getHttpChannelConfig().getOutputBufferSize();
|
||||
|
@ -114,9 +114,9 @@ public class HttpOutput extends ServletOutputStream
|
|||
_channel.write(BufferUtil.EMPTY_BUFFER, false);
|
||||
}
|
||||
|
||||
public boolean checkAllWritten() throws IOException
|
||||
public boolean closeIfAllContentWritten() throws IOException
|
||||
{
|
||||
return _channel.getResponse().checkAllContentWritten(_written);
|
||||
return _channel.getResponse().closeIfAllContentWritten(_written);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -169,10 +169,8 @@ public class HttpOutput extends ServletOutputStream
|
|||
_written += len;
|
||||
|
||||
// Check if all written or full
|
||||
if (!checkAllWritten() && BufferUtil.isFull(_aggregate))
|
||||
{
|
||||
if (!closeIfAllContentWritten() && BufferUtil.isFull(_aggregate))
|
||||
_channel.write(_aggregate, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -188,7 +186,7 @@ public class HttpOutput extends ServletOutputStream
|
|||
_written++;
|
||||
|
||||
// Check if all written or full
|
||||
if (!checkAllWritten() && BufferUtil.isFull(_aggregate))
|
||||
if (!closeIfAllContentWritten() && BufferUtil.isFull(_aggregate))
|
||||
_channel.write(_aggregate, false);
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ public class Iso88591HttpWriter extends HttpWriter
|
|||
{
|
||||
HttpOutput out = _out;
|
||||
if (length==0)
|
||||
out.checkAllWritten();
|
||||
out.closeIfAllContentWritten();
|
||||
|
||||
while (length > 0)
|
||||
{
|
||||
|
|
|
@ -705,7 +705,7 @@ public class Response implements HttpServletResponse
|
|||
{
|
||||
try
|
||||
{
|
||||
checkAllContentWritten(written);
|
||||
closeIfAllContentWritten(written);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
|
@ -714,7 +714,7 @@ public class Response implements HttpServletResponse
|
|||
}
|
||||
}
|
||||
|
||||
public boolean checkAllContentWritten(long written) throws IOException
|
||||
public boolean closeIfAllContentWritten(long written) throws IOException
|
||||
{
|
||||
if (_contentLength >= 0 && written >= _contentLength)
|
||||
{
|
||||
|
|
|
@ -133,7 +133,7 @@ public class ServerConnector extends AbstractNetworkConnector
|
|||
@Name("factories") ConnectionFactory... factories)
|
||||
{
|
||||
super(server,executor,scheduler,pool,acceptors,factories);
|
||||
_manager = new ServerConnectorManager(selectors > 0 ? selectors : Runtime.getRuntime().availableProcessors());
|
||||
_manager = new ServerConnectorManager(getExecutor(), getScheduler(), selectors > 0 ? selectors : Runtime.getRuntime().availableProcessors());
|
||||
addBean(_manager, true);
|
||||
}
|
||||
|
||||
|
@ -346,31 +346,11 @@ public class ServerConnector extends AbstractNetworkConnector
|
|||
|
||||
private final class ServerConnectorManager extends SelectorManager
|
||||
{
|
||||
private ServerConnectorManager(int selectors)
|
||||
private ServerConnectorManager(Executor executor, Scheduler scheduler, int selectors)
|
||||
{
|
||||
super(selectors);
|
||||
super(executor, scheduler, selectors);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(Runnable task)
|
||||
{
|
||||
getExecutor().execute(task);
|
||||
}
|
||||
|
||||
// TODO
|
||||
// @Override
|
||||
// public void connectionOpened(Connection connection)
|
||||
// {
|
||||
// ServerConnector.this.connectionOpened(connection);
|
||||
// }
|
||||
|
||||
// TODO
|
||||
// @Override
|
||||
// public void connectionClosed(Connection connection)
|
||||
// {
|
||||
// ServerConnector.this.connectionClosed(connection);
|
||||
// }
|
||||
|
||||
@Override
|
||||
protected SelectChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey selectionKey) throws IOException
|
||||
{
|
||||
|
|
|
@ -44,7 +44,7 @@ public class Utf8HttpWriter extends HttpWriter
|
|||
{
|
||||
HttpOutput out = _out;
|
||||
if (length==0)
|
||||
out.checkAllWritten();
|
||||
out.closeIfAllContentWritten();
|
||||
|
||||
while (length > 0)
|
||||
{
|
||||
|
|
|
@ -37,7 +37,8 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
import org.eclipse.jetty.toolchain.test.Stress;
|
||||
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
|
||||
import org.eclipse.jetty.toolchain.test.annotation.Stress;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
@ -46,7 +47,9 @@ import org.junit.After;
|
|||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AdvancedRunner.class)
|
||||
public class AsyncStressTest
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(AsyncStressTest.class);
|
||||
|
@ -90,17 +93,10 @@ public class AsyncStressTest
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
@Stress("High connection count")
|
||||
public void testAsync() throws Throwable
|
||||
{
|
||||
if (Stress.isEnabled())
|
||||
{
|
||||
doConnections(1600,240);
|
||||
}
|
||||
else
|
||||
{
|
||||
doConnections(80,80);
|
||||
}
|
||||
doConnections(1600,240);
|
||||
}
|
||||
|
||||
private void doConnections(int connections,final int loops) throws Throwable
|
||||
|
|
|
@ -34,16 +34,14 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
import org.eclipse.jetty.toolchain.test.Stress;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
|
||||
public class HttpServerTestFixture
|
||||
{ // Useful constants
|
||||
protected static final long PAUSE=10L;
|
||||
protected static final int LOOPS=Stress.isEnabled()?250:50;
|
||||
protected static final int LOOPS=50;
|
||||
protected static final String HOST="localhost";
|
||||
|
||||
protected Server _server;
|
||||
|
|
|
@ -34,8 +34,9 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
|
||||
import org.eclipse.jetty.toolchain.test.OS;
|
||||
import org.eclipse.jetty.toolchain.test.Stress;
|
||||
import org.eclipse.jetty.toolchain.test.annotation.Stress;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
@ -43,10 +44,10 @@ import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
|||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@Ignore
|
||||
@RunWith(AdvancedRunner.class)
|
||||
public class StressTest
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(StressTest.class);
|
||||
|
@ -122,45 +123,42 @@ public class StressTest
|
|||
|
||||
|
||||
@Test
|
||||
@Stress("Hey, its called StressTest for a reason")
|
||||
public void testMinNonPersistent() throws Throwable
|
||||
{
|
||||
doThreads(2,2,false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Stress("Much threading")
|
||||
public void testNonPersistent() throws Throwable
|
||||
{
|
||||
// TODO needs to be further investigated
|
||||
assumeTrue(!OS.IS_OSX || Stress.isEnabled());
|
||||
assumeTrue(!OS.IS_OSX);
|
||||
|
||||
doThreads(10,10,false);
|
||||
Thread.sleep(1000);
|
||||
doThreads(20,20,false);
|
||||
if (Stress.isEnabled())
|
||||
{
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,10,false);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,200,false);
|
||||
}
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,10,false);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,200,false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Stress("Much threading")
|
||||
public void testPersistent() throws Throwable
|
||||
{
|
||||
// TODO needs to be further investigated
|
||||
assumeTrue(!OS.IS_OSX || Stress.isEnabled());
|
||||
assumeTrue(!OS.IS_OSX);
|
||||
|
||||
doThreads(10,10,true);
|
||||
Thread.sleep(1000);
|
||||
doThreads(40,40,true);
|
||||
if (Stress.isEnabled())
|
||||
{
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,10,true);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,200,true);
|
||||
}
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,10,true);
|
||||
Thread.sleep(1000);
|
||||
doThreads(200,200,true);
|
||||
}
|
||||
|
||||
private void doThreads(int threadCount, final int loops, final boolean persistent) throws Throwable
|
||||
|
@ -293,33 +291,30 @@ public class StressTest
|
|||
}
|
||||
}
|
||||
|
||||
if(Stress.isEnabled())
|
||||
System.out.println(" stage:\tbind\twrite\trecv\tdispatch\twrote\ttotal");
|
||||
for (int q=0;q<quantums;q++)
|
||||
{
|
||||
System.out.println(" stage:\tbind\twrite\trecv\tdispatch\twrote\ttotal");
|
||||
for (int q=0;q<quantums;q++)
|
||||
{
|
||||
System.out.printf("%02d00<=l<%02d00",q,(q+1));
|
||||
for (int i=0;i<_latencies.length;i++)
|
||||
System.out.print("\t"+count[i][q]);
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
System.out.print("other ");
|
||||
System.out.printf("%02d00<=l<%02d00",q,(q+1));
|
||||
for (int i=0;i<_latencies.length;i++)
|
||||
System.out.print("\t"+other[i]);
|
||||
System.out.print("\t"+count[i][q]);
|
||||
System.out.println();
|
||||
|
||||
System.out.print("HANDLED ");
|
||||
for (int i=0;i<_latencies.length;i++)
|
||||
System.out.print("\t"+_handled.get());
|
||||
System.out.println();
|
||||
System.out.print("TOTAL ");
|
||||
for (int i=0;i<_latencies.length;i++)
|
||||
System.out.print("\t"+length[i]);
|
||||
System.out.println();
|
||||
long ave=total/_latencies[4].size();
|
||||
System.out.println("ave="+ave);
|
||||
}
|
||||
|
||||
System.out.print("other ");
|
||||
for (int i=0;i<_latencies.length;i++)
|
||||
System.out.print("\t"+other[i]);
|
||||
System.out.println();
|
||||
|
||||
System.out.print("HANDLED ");
|
||||
for (int i=0;i<_latencies.length;i++)
|
||||
System.out.print("\t"+_handled.get());
|
||||
System.out.println();
|
||||
System.out.print("TOTAL ");
|
||||
for (int i=0;i<_latencies.length;i++)
|
||||
System.out.print("\t"+length[i]);
|
||||
System.out.println();
|
||||
long ave=total/_latencies[4].size();
|
||||
System.out.println("ave="+ave);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -260,13 +260,6 @@ public class Holder<T> extends AbstractLifeCycle implements Dumpable
|
|||
return _asyncSupported;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected void illegalStateIfContextStarted()
|
||||
{
|
||||
|
@ -282,7 +275,7 @@ public class Holder<T> extends AbstractLifeCycle implements Dumpable
|
|||
@Override
|
||||
public void dump(Appendable out, String indent) throws IOException
|
||||
{
|
||||
out.append(_name).append("==").append(_className)
|
||||
out.append(toString())
|
||||
.append(" - ").append(AbstractLifeCycle.getState(this)).append("\n");
|
||||
ContainerLifeCycle.dump(out,indent,_initParams.entrySet());
|
||||
}
|
||||
|
@ -294,6 +287,13 @@ public class Holder<T> extends AbstractLifeCycle implements Dumpable
|
|||
return ContainerLifeCycle.dump(this);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s@%x==%s",_name,hashCode(),_className);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -178,15 +178,10 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
*/
|
||||
public void setInitOrder(int order)
|
||||
{
|
||||
_initOnStartup=true;
|
||||
_initOnStartup=order>0;
|
||||
_initOrder = order;
|
||||
}
|
||||
|
||||
public boolean isSetInitOrder()
|
||||
{
|
||||
return _initOnStartup;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Comparitor by init order.
|
||||
*/
|
||||
|
@ -931,4 +926,12 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
throw se;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s@%x==%s,%d,%b",_name,hashCode(),_className,_initOrder,_servlet!=null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,8 +119,6 @@ public class ServletContextHandlerTest
|
|||
|
||||
assertEquals(2,__testServlets.get());
|
||||
|
||||
|
||||
|
||||
assertThat(holder0.getServletInstance(),nullValue());
|
||||
response =_connector.getResponses("GET /test0 HTTP/1.0\r\n\r\n");
|
||||
assertThat(response,containsString("200 OK"));
|
||||
|
@ -129,6 +127,8 @@ public class ServletContextHandlerTest
|
|||
|
||||
_server.stop();
|
||||
assertEquals(0,__testServlets.get());
|
||||
|
||||
holder0.setInitOrder(0);
|
||||
_server.start();
|
||||
assertEquals(2,__testServlets.get());
|
||||
assertThat(holder0.getServletInstance(),nullValue());
|
||||
|
|
|
@ -79,6 +79,7 @@
|
|||
<instructions>
|
||||
<Export-Package>org.eclipse.jetty.spdy.*;version="9.0"</Export-Package>
|
||||
<Import-Package>org.eclipse.jetty.*;version="[9.0,10.0)",*</Import-Package>
|
||||
<_nouses>true</_nouses>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
<argLine>-Xbootclasspath/p:${project.build.directory}/npn/npn-boot-${npn.version}.jar</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- <plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
|
@ -57,21 +57,13 @@
|
|||
</goals>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Export-Package>*</Export-Package>
|
||||
<Export-Package>org.eclipse.jetty.spdy.client;version="9.0"</Export-Package>
|
||||
<Import-Package>!org.eclipse.jetty.npn,org.eclipse.jetty.*;version="[9.0,10.0)",*</Import-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>-->
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
|
||||
public class NextProtoNegoClientConnection extends AbstractConnection implements NextProtoNego.ClientProvider
|
||||
{
|
||||
private final Logger logger = Log.getLogger(getClass());
|
||||
private final Logger LOG = Log.getLogger(getClass());
|
||||
private final SocketChannel channel;
|
||||
private final Object attachment;
|
||||
private final SPDYClient client;
|
||||
|
@ -49,7 +49,7 @@ public class NextProtoNegoClientConnection extends AbstractConnection implements
|
|||
this.channel = channel;
|
||||
this.attachment = attachment;
|
||||
this.client = client;
|
||||
this.engine=endPoint.getSslConnection().getSSLEngine();
|
||||
this.engine = endPoint.getSslConnection().getSSLEngine();
|
||||
NextProtoNego.put(engine, this);
|
||||
}
|
||||
|
||||
|
@ -60,12 +60,12 @@ public class NextProtoNegoClientConnection extends AbstractConnection implements
|
|||
try
|
||||
{
|
||||
getEndPoint().flush(BufferUtil.EMPTY_BUFFER);
|
||||
fillInterested();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
fillInterested();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -76,9 +76,11 @@ public class NextProtoNegoClientConnection extends AbstractConnection implements
|
|||
int filled = fill();
|
||||
if (filled == 0 && !completed)
|
||||
fillInterested();
|
||||
if (filled <= 0)
|
||||
if (filled <= 0 || completed)
|
||||
break;
|
||||
}
|
||||
if (completed)
|
||||
replaceConnection();
|
||||
}
|
||||
|
||||
private int fill()
|
||||
|
@ -89,7 +91,7 @@ public class NextProtoNegoClientConnection extends AbstractConnection implements
|
|||
}
|
||||
catch (IOException x)
|
||||
{
|
||||
logger.debug(x);
|
||||
LOG.debug(x);
|
||||
getEndPoint().close();
|
||||
return -1;
|
||||
}
|
||||
|
@ -105,10 +107,6 @@ public class NextProtoNegoClientConnection extends AbstractConnection implements
|
|||
public void unsupported()
|
||||
{
|
||||
NextProtoNego.remove(engine);
|
||||
// Server does not support NPN, but this is a SPDY client, so hardcode SPDY
|
||||
EndPoint endPoint = getEndPoint();
|
||||
Connection connection = client.getConnectionFactory().newConnection(channel, endPoint, attachment);
|
||||
client.replaceConnection(endPoint, connection);
|
||||
completed = true;
|
||||
}
|
||||
|
||||
|
@ -116,14 +114,18 @@ public class NextProtoNegoClientConnection extends AbstractConnection implements
|
|||
public String selectProtocol(List<String> protocols)
|
||||
{
|
||||
NextProtoNego.remove(engine);
|
||||
String protocol = client.selectProtocol(protocols);
|
||||
if (protocol == null)
|
||||
return null;
|
||||
EndPoint endPoint = getEndPoint();
|
||||
Connection connection = client.getConnectionFactory().newConnection(channel, endPoint, attachment);
|
||||
client.replaceConnection(endPoint, connection);
|
||||
completed = true;
|
||||
return protocol;
|
||||
String protocol = client.selectProtocol(protocols);
|
||||
return protocol == null ? null : protocol;
|
||||
}
|
||||
|
||||
private void replaceConnection()
|
||||
{
|
||||
EndPoint endPoint = getEndPoint();
|
||||
Connection connection = client.getConnectionFactory().newConnection(channel, endPoint, attachment);
|
||||
endPoint.getConnection().onClose();
|
||||
endPoint.setConnection(connection);
|
||||
connection.onOpen();
|
||||
completed = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -153,22 +153,16 @@ public class SPDYClient
|
|||
return FlowControlStrategyFactory.newFlowControlStrategy(version);
|
||||
}
|
||||
|
||||
public void replaceConnection(EndPoint endPoint, Connection connection)
|
||||
{
|
||||
endPoint.getConnection().onClose();
|
||||
endPoint.setConnection(connection);
|
||||
connection.onOpen();
|
||||
}
|
||||
|
||||
public static class Factory extends ContainerLifeCycle
|
||||
{
|
||||
private final Queue<Session> sessions = new ConcurrentLinkedQueue<>();
|
||||
final ByteBufferPool bufferPool = new MappedByteBufferPool();
|
||||
final Scheduler scheduler = new TimerScheduler();
|
||||
final Executor executor;
|
||||
private final ByteBufferPool bufferPool = new MappedByteBufferPool();
|
||||
private final Scheduler scheduler;
|
||||
private final Executor executor;
|
||||
private final SslContextFactory sslContextFactory;
|
||||
private final SelectorManager selector;
|
||||
private final long idleTimeout;
|
||||
private long connectTimeout = 15000;
|
||||
|
||||
public Factory()
|
||||
{
|
||||
|
@ -177,7 +171,7 @@ public class SPDYClient
|
|||
|
||||
public Factory(SslContextFactory sslContextFactory)
|
||||
{
|
||||
this(null, sslContextFactory);
|
||||
this(null, null, sslContextFactory);
|
||||
}
|
||||
|
||||
public Factory(Executor executor)
|
||||
|
@ -185,29 +179,62 @@ public class SPDYClient
|
|||
this(executor, null);
|
||||
}
|
||||
|
||||
public Factory(Executor executor, SslContextFactory sslContextFactory)
|
||||
public Factory(Executor executor, Scheduler scheduler)
|
||||
{
|
||||
this(executor, sslContextFactory, 30000);
|
||||
this(executor, scheduler, null);
|
||||
}
|
||||
|
||||
public Factory(Executor executor, SslContextFactory sslContextFactory, long idleTimeout)
|
||||
public Factory(Executor executor, Scheduler scheduler, SslContextFactory sslContextFactory)
|
||||
{
|
||||
// TODO make this injectable
|
||||
addBean(scheduler);
|
||||
this(executor, scheduler, sslContextFactory, 30000);
|
||||
}
|
||||
|
||||
public Factory(Executor executor, Scheduler scheduler, SslContextFactory sslContextFactory, long idleTimeout)
|
||||
{
|
||||
this.idleTimeout = idleTimeout;
|
||||
|
||||
if (executor == null)
|
||||
executor = new QueuedThreadPool();
|
||||
this.executor = executor;
|
||||
addBean(executor);
|
||||
|
||||
if (scheduler == null)
|
||||
scheduler = new TimerScheduler();
|
||||
this.scheduler = scheduler;
|
||||
addBean(scheduler);
|
||||
|
||||
this.sslContextFactory = sslContextFactory;
|
||||
if (sslContextFactory != null)
|
||||
addBean(sslContextFactory);
|
||||
|
||||
selector = new ClientSelectorManager();
|
||||
selector = new ClientSelectorManager(executor, scheduler);
|
||||
selector.setConnectTimeout(getConnectTimeout());
|
||||
addBean(selector);
|
||||
}
|
||||
|
||||
public ByteBufferPool getByteBufferPool()
|
||||
{
|
||||
return bufferPool;
|
||||
}
|
||||
|
||||
public Scheduler getScheduler()
|
||||
{
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
public Executor getExecutor()
|
||||
{
|
||||
return executor;
|
||||
}
|
||||
|
||||
public long getConnectTimeout()
|
||||
{
|
||||
return connectTimeout;
|
||||
}
|
||||
|
||||
public void setConnectTimeout(long connectTimeout)
|
||||
{
|
||||
this.connectTimeout = connectTimeout;
|
||||
}
|
||||
|
||||
public SPDYClient newSPDYClient(short version)
|
||||
|
@ -249,6 +276,11 @@ public class SPDYClient
|
|||
|
||||
private class ClientSelectorManager extends SelectorManager
|
||||
{
|
||||
private ClientSelectorManager(Executor executor, Scheduler scheduler)
|
||||
{
|
||||
super(executor, scheduler);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException
|
||||
{
|
||||
|
@ -258,13 +290,7 @@ public class SPDYClient
|
|||
if (clientIdleTimeout < 0)
|
||||
clientIdleTimeout = idleTimeout;
|
||||
|
||||
return new SelectChannelEndPoint(channel, selectSet, key, scheduler, clientIdleTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(Runnable task)
|
||||
{
|
||||
executor.execute(task);
|
||||
return new SelectChannelEndPoint(channel, selectSet, key, getScheduler(), clientIdleTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -278,9 +304,9 @@ public class SPDYClient
|
|||
if (sslContextFactory != null)
|
||||
{
|
||||
final SSLEngine engine = client.newSSLEngine(sslContextFactory, channel);
|
||||
SslConnection sslConnection = new SslConnection(bufferPool, executor, endPoint, engine);
|
||||
SslConnection sslConnection = new SslConnection(bufferPool, getExecutor(), endPoint, engine);
|
||||
DecryptedEndPoint sslEndPoint = sslConnection.getDecryptedEndPoint();
|
||||
NextProtoNegoClientConnection connection = new NextProtoNegoClientConnection(channel, sslEndPoint, attachment, client.factory.executor, client);
|
||||
NextProtoNegoClientConnection connection = new NextProtoNegoClientConnection(channel, sslEndPoint, attachment, getExecutor(), client);
|
||||
sslEndPoint.setConnection(connection);
|
||||
return sslConnection;
|
||||
}
|
||||
|
|
|
@ -39,16 +39,17 @@ public class SPDYClientConnectionFactory
|
|||
SessionPromise sessionPromise = (SessionPromise)attachment;
|
||||
SPDYClient client = sessionPromise.client;
|
||||
Factory factory = client.factory;
|
||||
ByteBufferPool bufferPool = factory.getByteBufferPool();
|
||||
|
||||
CompressionFactory compressionFactory = new StandardCompressionFactory();
|
||||
Parser parser = new Parser(compressionFactory.newDecompressor());
|
||||
Generator generator = new Generator(factory.bufferPool, compressionFactory.newCompressor());
|
||||
Generator generator = new Generator(bufferPool, compressionFactory.newCompressor());
|
||||
|
||||
SPDYConnection connection = new ClientSPDYConnection(endPoint, factory.bufferPool, parser, factory);
|
||||
SPDYConnection connection = new ClientSPDYConnection(endPoint, bufferPool, parser, factory);
|
||||
|
||||
FlowControlStrategy flowControlStrategy = client.newFlowControlStrategy();
|
||||
|
||||
StandardSession session = new StandardSession(client.version, factory.bufferPool, factory.executor, factory.scheduler, connection, connection, 1, sessionPromise.listener, generator, flowControlStrategy);
|
||||
StandardSession session = new StandardSession(client.version, bufferPool, factory.getExecutor(), factory.getScheduler(), connection, connection, 1, sessionPromise.listener, generator, flowControlStrategy);
|
||||
session.setWindowSize(client.getInitialWindowSize());
|
||||
parser.addListener(session);
|
||||
sessionPromise.completed(session);
|
||||
|
@ -65,7 +66,7 @@ public class SPDYClientConnectionFactory
|
|||
|
||||
public ClientSPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Factory factory)
|
||||
{
|
||||
super(endPoint, bufferPool, parser, factory.executor);
|
||||
super(endPoint, bufferPool, parser, factory.getExecutor());
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,8 @@ public class SPDYConnection extends AbstractConnection implements Controller<Sta
|
|||
|
||||
public SPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Executor executor,int bufferSize)
|
||||
{
|
||||
super(endPoint, executor);
|
||||
// TODO explain why we are passing false here
|
||||
super(endPoint, executor, false);
|
||||
this.bufferPool = bufferPool;
|
||||
this.parser = parser;
|
||||
onIdle(true);
|
||||
|
|
|
@ -14,38 +14,6 @@
|
|||
<bundle-symbolic-name>${project.groupId}.core</bundle-symbolic-name>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- <plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>manifest</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Import-Package>javax.net.*,*</Import-Package>
|
||||
<Export-Package>*</Export-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>-->
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
|
|
|
@ -18,85 +18,9 @@
|
|||
|
||||
package org.eclipse.jetty.spdy;
|
||||
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import org.eclipse.jetty.util.FutureCallback;
|
||||
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
||||
/**
|
||||
* <p>A {@link Promise} is a {@link Future} that allows a result or a failure to be set,
|
||||
* so that the {@link Future} will be {@link #isDone() done}.</p>
|
||||
*
|
||||
* @param <T> the type of the result object
|
||||
*/
|
||||
public class Promise<T> implements Callback<T>, Future<T>
|
||||
@Deprecated
|
||||
public class Promise<T> extends FutureCallback<T>
|
||||
{
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
private boolean cancelled;
|
||||
private Throwable failure;
|
||||
private T promise;
|
||||
|
||||
@Override
|
||||
public void completed(T result)
|
||||
{
|
||||
this.promise = result;
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(T context, Throwable x)
|
||||
{
|
||||
this.failure = x;
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning)
|
||||
{
|
||||
cancelled = true;
|
||||
latch.countDown();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled()
|
||||
{
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone()
|
||||
{
|
||||
return cancelled || latch.getCount() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() throws InterruptedException, ExecutionException
|
||||
{
|
||||
latch.await();
|
||||
return result();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
boolean elapsed = !latch.await(timeout, unit);
|
||||
if (elapsed)
|
||||
throw new TimeoutException();
|
||||
return result();
|
||||
}
|
||||
|
||||
private T result() throws ExecutionException
|
||||
{
|
||||
if (isCancelled())
|
||||
throw new CancellationException();
|
||||
Throwable failure = this.failure;
|
||||
if (failure != null)
|
||||
throw new ExecutionException(failure);
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,15 +45,21 @@
|
|||
<argLine>-Xbootclasspath/p:${project.build.directory}/npn/npn-boot-${npn.version}.jar</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>manifest</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Import-Package>org.eclipse.jetty.npn;version="0";resolution:=optional,*</Import-Package>
|
||||
<Export-Package>*</Export-Package>
|
||||
<Export-Package>org.eclipse.jetty.spdy.server.http;version="9.0",
|
||||
org.eclipse.jetty.spdy.server.proxy;version="9.0"</Export-Package>
|
||||
<Import-Package>!org.eclipse.jetty.npn,org.eclipse.jetty.*;version="[9.0,10.0)",*</Import-Package>
|
||||
<_nouses>true</_nouses>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
|
|
@ -34,10 +34,10 @@ import org.eclipse.jetty.spdy.client.SPDYClient;
|
|||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.rules.TestWatchman;
|
||||
import org.junit.rules.TestWatcher;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public abstract class AbstractHTTPSPDYTest
|
||||
|
@ -49,15 +49,16 @@ public abstract class AbstractHTTPSPDYTest
|
|||
}
|
||||
|
||||
@Rule
|
||||
public final TestWatchman testName = new TestWatchman()
|
||||
public final TestWatcher testName = new TestWatcher()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void starting(FrameworkMethod method)
|
||||
public void starting(Description description)
|
||||
{
|
||||
super.starting(method);
|
||||
super.starting(description);
|
||||
System.err.printf("Running %s.%s()%n",
|
||||
method.getMethod().getDeclaringClass().getName(),
|
||||
method.getName());
|
||||
description.getClassName(),
|
||||
description.getMethodName());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -114,7 +115,7 @@ public abstract class AbstractHTTPSPDYTest
|
|||
|
||||
protected SPDYClient.Factory newSPDYClientFactory(Executor threadPool)
|
||||
{
|
||||
return new SPDYClient.Factory(threadPool, null, connector.getIdleTimeout());
|
||||
return new SPDYClient.Factory(threadPool, null, null, connector.getIdleTimeout());
|
||||
}
|
||||
|
||||
@After
|
||||
|
|
|
@ -36,21 +36,22 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestWatchman;
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.junit.rules.TestWatcher;
|
||||
import org.junit.runner.Description;
|
||||
|
||||
public class ProtocolNegotiationTest
|
||||
{
|
||||
@Rule
|
||||
public final TestWatchman testName = new TestWatchman()
|
||||
public final TestWatcher testName = new TestWatcher()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void starting(FrameworkMethod method)
|
||||
public void starting(Description description)
|
||||
{
|
||||
super.starting(method);
|
||||
super.starting(description);
|
||||
System.err.printf("Running %s.%s()%n",
|
||||
method.getMethod().getDeclaringClass().getName(),
|
||||
method.getName());
|
||||
description.getClassName(),
|
||||
description.getMethodName());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ public class SSLExternalServerTest extends AbstractHTTPSPDYTest
|
|||
SslContextFactory sslContextFactory = new SslContextFactory();
|
||||
// Force TLSv1
|
||||
sslContextFactory.setIncludeProtocols("TLSv1");
|
||||
return new SPDYClient.Factory(threadPool, sslContextFactory, 30000);
|
||||
return new SPDYClient.Factory(threadPool, null, sslContextFactory, 30000);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -57,25 +57,26 @@ import org.junit.Before;
|
|||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestWatchman;
|
||||
import org.junit.rules.TestWatcher;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
|
||||
@Ignore // TODO: make these tests pass
|
||||
@RunWith(value = Parameterized.class)
|
||||
public class ProxyHTTPSPDYTest
|
||||
{
|
||||
@Rule
|
||||
public final TestWatchman testName = new TestWatchman()
|
||||
public final TestWatcher testName = new TestWatcher()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void starting(FrameworkMethod method)
|
||||
public void starting(Description description)
|
||||
{
|
||||
super.starting(method);
|
||||
super.starting(description);
|
||||
System.err.printf("Running %s.%s()%n",
|
||||
method.getMethod().getDeclaringClass().getName(),
|
||||
method.getName());
|
||||
description.getClassName(),
|
||||
description.getMethodName());
|
||||
}
|
||||
};
|
||||
private final short version;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
#org.eclipse.jetty.spdy.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.server.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.io.ssl.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.spdy.server.LEVEL=DEBUG
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
<argLine>-Xbootclasspath/p:${project.build.directory}/npn/npn-boot-${npn.version}.jar</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- <plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
|
@ -57,22 +57,14 @@
|
|||
</goals>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Import-Package>javax.net.ssl.*,*</Import-Package>
|
||||
<Export-Package>*</Export-Package>
|
||||
<Export-Package>org.eclipse.jetty.spdy.server;version="9.0"</Export-Package>
|
||||
<Import-Package>!org.eclipse.jetty.npn,org.eclipse.jetty.*;version="[9.0,10.0)",*</Import-Package>
|
||||
<_nouses>true</_nouses>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin> -->
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
|
|
@ -23,8 +23,12 @@ import java.util.Arrays;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.io.FilterConnection;
|
||||
import org.eclipse.jetty.io.ssl.SslConnection;
|
||||
import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint;
|
||||
import org.eclipse.jetty.npn.NextProtoNego;
|
||||
import org.eclipse.jetty.server.AbstractConnectionFactory;
|
||||
|
@ -96,8 +100,20 @@ public class NPNServerConnectionFactory extends AbstractConnectionFactory
|
|||
String dft=_defaultProtocol;
|
||||
if (dft==null)
|
||||
dft=_protocols.get(0);
|
||||
|
||||
SSLEngine engine=null;
|
||||
EndPoint ep=endPoint;
|
||||
while(engine==null && ep!=null)
|
||||
{
|
||||
if (ep instanceof SslConnection.DecryptedEndPoint)
|
||||
engine=((SslConnection.DecryptedEndPoint)ep).getSslConnection().getSSLEngine();
|
||||
if (ep instanceof FilterConnection.FilteredEndPoint) // TODO make more generic
|
||||
ep=((FilterConnection.FilteredEndPoint)ep).getWrappedEndPoint();
|
||||
else
|
||||
ep=null;
|
||||
}
|
||||
|
||||
return configure(new NextProtoNegoServerConnection((DecryptedEndPoint)endPoint, connector,protocols,_defaultProtocol),connector,endPoint);
|
||||
return configure(new NextProtoNegoServerConnection(endPoint, engine, connector,protocols,_defaultProtocol),connector,endPoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -35,23 +35,21 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
|
||||
public class NextProtoNegoServerConnection extends AbstractConnection implements NextProtoNego.ServerProvider
|
||||
{
|
||||
private final Logger logger = Log.getLogger(getClass());
|
||||
private final Logger LOG = Log.getLogger(getClass());
|
||||
private final Connector connector;
|
||||
private final SSLEngine engine;
|
||||
private final List<String> protocols;
|
||||
private final String defaultProtocol;
|
||||
private boolean completed; // No need to be volatile: it is modified and read by the same thread
|
||||
private String nextProtocol; // No need to be volatile: it is modified and read by the same thread
|
||||
|
||||
|
||||
public NextProtoNegoServerConnection(DecryptedEndPoint endPoint, Connector connector, List<String>protocols, String defaultProtocol)
|
||||
public NextProtoNegoServerConnection(EndPoint endPoint, SSLEngine engine, Connector connector, List<String>protocols, String defaultProtocol)
|
||||
{
|
||||
super(endPoint, connector.getExecutor());
|
||||
this.connector = connector;
|
||||
this.protocols = protocols;
|
||||
this.defaultProtocol=defaultProtocol;
|
||||
engine = endPoint.getSslConnection().getSSLEngine();
|
||||
|
||||
NextProtoNego.put(engine,this);
|
||||
this.defaultProtocol = defaultProtocol;
|
||||
this.engine = engine;
|
||||
NextProtoNego.put(engine, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -61,23 +59,29 @@ public class NextProtoNegoServerConnection extends AbstractConnection implements
|
|||
fillInterested();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose()
|
||||
{
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFillable()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int filled = fill();
|
||||
if (filled == 0 && !completed)
|
||||
if (filled == 0 && nextProtocol == null)
|
||||
fillInterested();
|
||||
if (filled <= 0 || completed)
|
||||
if (filled <= 0 || nextProtocol != null)
|
||||
break;
|
||||
}
|
||||
|
||||
if (nextProtocol != null)
|
||||
{
|
||||
ConnectionFactory connectionFactory = connector.getConnectionFactory(nextProtocol);
|
||||
EndPoint endPoint = getEndPoint();
|
||||
Connection oldConnection = endPoint.getConnection();
|
||||
oldConnection.onClose();
|
||||
Connection connection = connectionFactory.newConnection(connector, endPoint);
|
||||
LOG.debug("{} switching from {} to {}", this, oldConnection, connection);
|
||||
endPoint.setConnection(connection);
|
||||
getEndPoint().getConnection().onOpen();
|
||||
}
|
||||
}
|
||||
|
||||
private int fill()
|
||||
|
@ -88,7 +92,7 @@ public class NextProtoNegoServerConnection extends AbstractConnection implements
|
|||
}
|
||||
catch (IOException x)
|
||||
{
|
||||
logger.debug(x);
|
||||
LOG.debug(x);
|
||||
getEndPoint().close();
|
||||
return -1;
|
||||
}
|
||||
|
@ -109,13 +113,8 @@ public class NextProtoNegoServerConnection extends AbstractConnection implements
|
|||
@Override
|
||||
public void protocolSelected(String protocol)
|
||||
{
|
||||
LOG.debug("{} protocol selected {}", this, protocol);
|
||||
nextProtocol = protocol;
|
||||
NextProtoNego.remove(engine);
|
||||
ConnectionFactory connectionFactory = connector.getConnectionFactory(protocol);
|
||||
EndPoint endPoint = getEndPoint();
|
||||
endPoint.getConnection().onClose();
|
||||
Connection connection = connectionFactory.newConnection(connector, endPoint);
|
||||
endPoint.setConnection(connection);
|
||||
connection.onOpen();
|
||||
completed = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ public class SSLEngineLeakTest extends AbstractTest
|
|||
protected SPDYClient.Factory newSPDYClientFactory(Executor threadPool)
|
||||
{
|
||||
SslContextFactory sslContextFactory = newSslContextFactory();
|
||||
return new SPDYClient.Factory(threadPool, sslContextFactory);
|
||||
return new SPDYClient.Factory(threadPool, null, sslContextFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -41,7 +41,7 @@ public class SSLSynReplyTest extends SynReplyTest
|
|||
protected SPDYClient.Factory newSPDYClientFactory(Executor threadPool)
|
||||
{
|
||||
SslContextFactory sslContextFactory = newSslContextFactory();
|
||||
return new SPDYClient.Factory(threadPool, sslContextFactory);
|
||||
return new SPDYClient.Factory(threadPool, null, sslContextFactory);
|
||||
}
|
||||
|
||||
@Before
|
||||
|
|
|
@ -24,6 +24,14 @@ public abstract class ExecutorCallback<C> implements Callback<C>
|
|||
{
|
||||
private final ForkInvoker<C> _invoker;
|
||||
private final Executor _executor;
|
||||
private final Runnable _onComplete=new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
onCompleted(null);
|
||||
}
|
||||
};
|
||||
|
||||
public ExecutorCallback(Executor executor)
|
||||
{
|
||||
|
@ -33,14 +41,32 @@ public abstract class ExecutorCallback<C> implements Callback<C>
|
|||
public ExecutorCallback(Executor executor, int maxRecursion)
|
||||
{
|
||||
_executor = executor;
|
||||
_invoker = new ExecutorCallbackInvoker(maxRecursion);
|
||||
_invoker = maxRecursion>0?new ExecutorCallbackInvoker(maxRecursion):null;
|
||||
if (_executor==null)
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void completed(final C context)
|
||||
public void completed(final C context)
|
||||
{
|
||||
// Should we execute?
|
||||
if (alwaysDispatchCompletion())
|
||||
if (_invoker==null)
|
||||
{
|
||||
if (context==null)
|
||||
_executor.execute(_onComplete);
|
||||
else
|
||||
{
|
||||
_executor.execute(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
onCompleted(context);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (alwaysDispatchCompletion())
|
||||
{
|
||||
_invoker.fork(context);
|
||||
}
|
||||
|
@ -53,7 +79,7 @@ public abstract class ExecutorCallback<C> implements Callback<C>
|
|||
protected abstract void onCompleted(C context);
|
||||
|
||||
@Override
|
||||
public final void failed(final C context, final Throwable x)
|
||||
public void failed(final C context, final Throwable x)
|
||||
{
|
||||
// Always execute failure
|
||||
Runnable runnable = new Runnable()
|
||||
|
|
|
@ -35,7 +35,7 @@ import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
|||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.eclipse.jetty.util.thread.TimerScheduler;
|
||||
import org.eclipse.jetty.websocket.client.internal.ConnectionManager;
|
||||
import org.eclipse.jetty.websocket.client.internal.IWebSocketClient;
|
||||
import org.eclipse.jetty.websocket.client.internal.DefaultWebSocketClient;
|
||||
import org.eclipse.jetty.websocket.core.api.Extension;
|
||||
import org.eclipse.jetty.websocket.core.api.ExtensionRegistry;
|
||||
import org.eclipse.jetty.websocket.core.api.WebSocketPolicy;
|
||||
|
@ -127,7 +127,7 @@ public class WebSocketClientFactory extends ContainerLifeCycle
|
|||
|
||||
/**
|
||||
* The address to bind local physical (outgoing) TCP Sockets to.
|
||||
*
|
||||
*
|
||||
* @return the address to bind the socket channel to
|
||||
* @see #setBindAddress(SocketAddress)
|
||||
*/
|
||||
|
@ -190,7 +190,7 @@ public class WebSocketClientFactory extends ContainerLifeCycle
|
|||
{
|
||||
LOG.debug("Creating new WebSocket for {}",websocketPojo);
|
||||
EventDriver websocket = eventDriverFactory.wrap(websocketPojo);
|
||||
return new IWebSocketClient(this,websocket);
|
||||
return new DefaultWebSocketClient(this,websocket);
|
||||
}
|
||||
|
||||
public boolean sessionClosed(WebSocketSession session)
|
||||
|
@ -204,17 +204,6 @@ public class WebSocketClientFactory extends ContainerLifeCycle
|
|||
{
|
||||
LOG.debug("Session Opened: {}",session);
|
||||
}
|
||||
// FIXME: what is going on?
|
||||
// if (!isRunning())
|
||||
// {
|
||||
// LOG.debug("Factory.isRunning: {}",this.isRunning());
|
||||
// LOG.debug("Factory.isStarted: {}",this.isStarted());
|
||||
// LOG.debug("Factory.isStarting: {}",this.isStarting());
|
||||
// LOG.debug("Factory.isStopped: {}",this.isStopped());
|
||||
// LOG.debug("Factory.isStopping: {}",this.isStopping());
|
||||
// LOG.warn("Factory is not running");
|
||||
// return false;
|
||||
// }
|
||||
boolean ret = sessions.offer(session);
|
||||
session.onConnect();
|
||||
return ret;
|
||||
|
|
|
@ -19,13 +19,19 @@
|
|||
package org.eclipse.jetty.websocket.client.internal;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jetty.util.B64Code;
|
||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.core.api.UpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.core.protocol.ExtensionConfig;
|
||||
|
||||
|
@ -34,18 +40,58 @@ import org.eclipse.jetty.websocket.core.protocol.ExtensionConfig;
|
|||
*/
|
||||
public class ClientUpgradeRequest implements UpgradeRequest
|
||||
{
|
||||
public static final String COOKIE_DELIM = "\"\\\n\r\t\f\b%+ ;=";
|
||||
private final static Logger LOG = Log.getLogger(ClientUpgradeRequest.class);
|
||||
private static final String HEADER_VALUES_DELIM = "\"\\\n\r\t\f\b%+ ;=";
|
||||
private static final Set<String> FORBIDDEN_HEADERS;
|
||||
|
||||
static
|
||||
{
|
||||
// headers not allowed to be set in ClientUpgradeRequest.headers
|
||||
FORBIDDEN_HEADERS = new HashSet<>();
|
||||
FORBIDDEN_HEADERS.add("cookie");
|
||||
FORBIDDEN_HEADERS.add("upgrade");
|
||||
FORBIDDEN_HEADERS.add("host");
|
||||
FORBIDDEN_HEADERS.add("connection");
|
||||
FORBIDDEN_HEADERS.add("sec-websocket-key");
|
||||
FORBIDDEN_HEADERS.add("sec-websocket-extensions");
|
||||
FORBIDDEN_HEADERS.add("sec-websocket-accept");
|
||||
FORBIDDEN_HEADERS.add("sec-websocket-protocol");
|
||||
FORBIDDEN_HEADERS.add("sec-websocket-version");
|
||||
}
|
||||
|
||||
private final String key;
|
||||
private List<String> subProtocols;
|
||||
private List<ExtensionConfig> extensions;
|
||||
private Map<String, String> cookies;
|
||||
private Map<String, String> headers;
|
||||
private String httpEndPointName;
|
||||
private String host;
|
||||
|
||||
public ClientUpgradeRequest()
|
||||
{
|
||||
byte[] bytes = new byte[16];
|
||||
new Random().nextBytes(bytes);
|
||||
this.key = new String(B64Code.encode(bytes));
|
||||
this.subProtocols = new ArrayList<>();
|
||||
this.extensions = new ArrayList<>();
|
||||
this.cookies = new HashMap<>();
|
||||
this.headers = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addExtensions(String... extConfigs)
|
||||
{
|
||||
for (String extConfig : extConfigs)
|
||||
{
|
||||
extensions.add(ExtensionConfig.parse(extConfig));
|
||||
}
|
||||
}
|
||||
|
||||
public String generate(URI uri)
|
||||
{
|
||||
this.httpEndPointName = uri.toASCIIString();
|
||||
this.host = uri.getHost();
|
||||
|
||||
StringBuilder request = new StringBuilder(512);
|
||||
request.append("GET ");
|
||||
if (StringUtil.isBlank(uri.getPath()))
|
||||
|
@ -62,36 +108,88 @@ public class ClientUpgradeRequest implements UpgradeRequest
|
|||
}
|
||||
request.append(" HTTP/1.1\r\n");
|
||||
|
||||
request.append("Host: ").append(uri.getHost());
|
||||
request.append("Host: ").append(this.host);
|
||||
if (uri.getPort() > 0)
|
||||
{
|
||||
request.append(':').append(uri.getPort());
|
||||
}
|
||||
request.append("\r\n");
|
||||
|
||||
// WebSocket specifics
|
||||
request.append("Upgrade: websocket\r\n");
|
||||
request.append("Connection: Upgrade\r\n");
|
||||
request.append("Sec-WebSocket-Key: ").append(key).append("\r\n");
|
||||
|
||||
if (StringUtil.isNotBlank(getOrigin()))
|
||||
{
|
||||
request.append("Origin: ").append(getOrigin()).append("\r\n");
|
||||
}
|
||||
|
||||
request.append("Sec-WebSocket-Version: 13\r\n"); // RFC-6455 specified version
|
||||
|
||||
Map<String, String> cookies = getCookieMap();
|
||||
if ((cookies != null) && (cookies.size() > 0))
|
||||
// Extensions
|
||||
if (!getExtensions().isEmpty())
|
||||
{
|
||||
for (String cookie : cookies.keySet())
|
||||
request.append("Sec-WebSocket-Extensions: ");
|
||||
boolean needDelim = false;
|
||||
for (ExtensionConfig ext : getExtensions())
|
||||
{
|
||||
request.append("Cookie: ");
|
||||
request.append(QuotedStringTokenizer.quoteIfNeeded(cookie,COOKIE_DELIM));
|
||||
request.append("=");
|
||||
request.append(QuotedStringTokenizer.quoteIfNeeded(cookies.get(cookie),COOKIE_DELIM));
|
||||
request.append("\r\n");
|
||||
if (needDelim)
|
||||
{
|
||||
request.append(", ");
|
||||
}
|
||||
request.append(ext.getParameterizedName());
|
||||
needDelim = true;
|
||||
}
|
||||
request.append("\r\n");
|
||||
}
|
||||
|
||||
// Sub Protocols
|
||||
if (!getSubProtocols().isEmpty())
|
||||
{
|
||||
request.append("Sec-WebSocket-Protocol: ");
|
||||
boolean needDelim = false;
|
||||
for (String protocol : getSubProtocols())
|
||||
{
|
||||
if (needDelim)
|
||||
{
|
||||
request.append(", ");
|
||||
}
|
||||
request.append(protocol);
|
||||
needDelim = true;
|
||||
}
|
||||
request.append("\r\n");
|
||||
}
|
||||
|
||||
// Cookies
|
||||
if (!getCookieMap().isEmpty())
|
||||
{
|
||||
request.append("Cookie: ");
|
||||
boolean needDelim = false;
|
||||
for (String cookie : getCookieMap().keySet())
|
||||
{
|
||||
if (needDelim)
|
||||
{
|
||||
request.append("; ");
|
||||
}
|
||||
request.append(QuotedStringTokenizer.quoteIfNeeded(cookie,HEADER_VALUES_DELIM));
|
||||
request.append("=");
|
||||
String val = cookies.get(cookie);
|
||||
request.append(QuotedStringTokenizer.quoteIfNeeded(val,HEADER_VALUES_DELIM));
|
||||
needDelim = true;
|
||||
}
|
||||
request.append("\r\n");
|
||||
}
|
||||
|
||||
// Other headers
|
||||
for (String key : headers.keySet())
|
||||
{
|
||||
String value = headers.get(key);
|
||||
if (FORBIDDEN_HEADERS.contains(key.toLowerCase()))
|
||||
{
|
||||
LOG.warn("Skipping forbidden header - {}: {}",key,value);
|
||||
continue; // skip
|
||||
}
|
||||
request.append(key).append(": ");
|
||||
request.append(QuotedStringTokenizer.quoteIfNeeded(value,HEADER_VALUES_DELIM));
|
||||
request.append("\r\n");
|
||||
}
|
||||
|
||||
// request header end
|
||||
request.append("\r\n");
|
||||
return request.toString();
|
||||
}
|
||||
|
@ -99,36 +197,31 @@ public class ClientUpgradeRequest implements UpgradeRequest
|
|||
@Override
|
||||
public Map<String, String> getCookieMap()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return cookies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ExtensionConfig> getExtensions()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return extensions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeader(String name)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return headers.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return this.host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHttpEndPointName()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return httpEndPointName;
|
||||
}
|
||||
|
||||
public String getKey()
|
||||
|
@ -139,36 +232,45 @@ public class ClientUpgradeRequest implements UpgradeRequest
|
|||
@Override
|
||||
public String getOrigin()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return getHeader("Origin");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSubProtocols()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return subProtocols;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSubProtocol(String test)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
for (String protocol : subProtocols)
|
||||
{
|
||||
if (protocol.equalsIgnoreCase(test))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOrigin(String test)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
return test.equalsIgnoreCase(getOrigin());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSubProtocols(String string)
|
||||
public void setSubProtocols(String protocols)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
this.subProtocols.clear();
|
||||
if (StringUtil.isBlank(protocols))
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (String protocol : protocols.split("\\s*,\\s*"))
|
||||
{
|
||||
this.subProtocols.add(protocol);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ public class ClientUpgradeResponse implements UpgradeResponse
|
|||
@Override
|
||||
public Iterator<String> getHeaderValues(String name)
|
||||
{
|
||||
List<String> values = headers.getValues(name);
|
||||
List<String> values = headers.getValues(name.toLowerCase());
|
||||
if (values == null)
|
||||
{
|
||||
return Collections.emptyIterator();
|
||||
|
|
|
@ -85,6 +85,7 @@ public class ConnectionManager extends ContainerLifeCycle
|
|||
public ConnectionManager(ByteBufferPool bufferPool, Executor executor, Scheduler scheduler, SslContextFactory sslContextFactory,
|
||||
WebSocketPolicy policy)
|
||||
{
|
||||
// TODO: configure connect timeout
|
||||
selector = new WebSocketClientSelectorManager(bufferPool,executor,scheduler,policy);
|
||||
selector.setSslContextFactory(sslContextFactory);
|
||||
addBean(selector);
|
||||
|
@ -106,7 +107,7 @@ public class ConnectionManager extends ContainerLifeCycle
|
|||
}
|
||||
}
|
||||
|
||||
public FutureCallback<UpgradeResponse> connectPhysical(IWebSocketClient client) throws IOException
|
||||
public FutureCallback<UpgradeResponse> connectPhysical(DefaultWebSocketClient client) throws IOException
|
||||
{
|
||||
SocketChannel channel = SocketChannel.open();
|
||||
SocketAddress bindAddress = client.getFactory().getBindAddress();
|
||||
|
|
|
@ -38,9 +38,9 @@ import org.eclipse.jetty.websocket.core.io.event.EventDriver;
|
|||
/**
|
||||
* WebSocketClient for working with Upgrade (request and response), and establishing connections to the websocket URI of your choice.
|
||||
*/
|
||||
public class IWebSocketClient extends FutureCallback<UpgradeResponse> implements WebSocketClient
|
||||
public class DefaultWebSocketClient extends FutureCallback<UpgradeResponse> implements WebSocketClient
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(IWebSocketClient.class);
|
||||
private static final Logger LOG = Log.getLogger(DefaultWebSocketClient.class);
|
||||
|
||||
private final WebSocketClientFactory factory;
|
||||
private final WebSocketPolicy policy;
|
||||
|
@ -57,7 +57,7 @@ public class IWebSocketClient extends FutureCallback<UpgradeResponse> implements
|
|||
private ClientUpgradeResponse upgradeResponse;
|
||||
private Masker masker;
|
||||
|
||||
public IWebSocketClient(WebSocketClientFactory factory, EventDriver websocket)
|
||||
public DefaultWebSocketClient(WebSocketClientFactory factory, EventDriver websocket)
|
||||
{
|
||||
this.factory = factory;
|
||||
LOG.debug("factory.isRunning(): {}",factory.isRunning());
|
|
@ -38,7 +38,7 @@ import org.eclipse.jetty.util.log.Log;
|
|||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.client.internal.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.internal.ClientUpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.client.internal.IWebSocketClient;
|
||||
import org.eclipse.jetty.websocket.client.internal.DefaultWebSocketClient;
|
||||
import org.eclipse.jetty.websocket.core.api.Extension;
|
||||
import org.eclipse.jetty.websocket.core.api.UpgradeException;
|
||||
import org.eclipse.jetty.websocket.core.api.UpgradeResponse;
|
||||
|
@ -81,11 +81,11 @@ public class UpgradeConnection extends AbstractConnection
|
|||
|
||||
private static final Logger LOG = Log.getLogger(UpgradeConnection.class);
|
||||
private final ByteBufferPool bufferPool;
|
||||
private final IWebSocketClient client;
|
||||
private final DefaultWebSocketClient client;
|
||||
private final HttpResponseHeaderParser parser;
|
||||
private ClientUpgradeRequest request;
|
||||
|
||||
public UpgradeConnection(EndPoint endp, Executor executor, IWebSocketClient client)
|
||||
public UpgradeConnection(EndPoint endp, Executor executor, DefaultWebSocketClient client)
|
||||
{
|
||||
super(endp,executor);
|
||||
this.client = client;
|
||||
|
@ -230,6 +230,9 @@ public class UpgradeConnection extends AbstractConnection
|
|||
// Connect extensions
|
||||
if (extensions != null)
|
||||
{
|
||||
connection.getParser().configureFromExtensions(extensions);
|
||||
connection.getGenerator().configureFromExtensions(extensions);
|
||||
|
||||
Iterator<Extension> extIter;
|
||||
// Connect outgoings
|
||||
extIter = extensions.iterator();
|
||||
|
@ -238,23 +241,6 @@ public class UpgradeConnection extends AbstractConnection
|
|||
Extension ext = extIter.next();
|
||||
ext.setNextOutgoingFrames(outgoing);
|
||||
outgoing = ext;
|
||||
|
||||
// Handle RSV reservations
|
||||
if (ext.useRsv1())
|
||||
{
|
||||
connection.getGenerator().setRsv1InUse(true);
|
||||
connection.getParser().setRsv1InUse(true);
|
||||
}
|
||||
if (ext.useRsv2())
|
||||
{
|
||||
connection.getGenerator().setRsv2InUse(true);
|
||||
connection.getParser().setRsv2InUse(true);
|
||||
}
|
||||
if (ext.useRsv3())
|
||||
{
|
||||
connection.getGenerator().setRsv3InUse(true);
|
||||
connection.getParser().setRsv3InUse(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Connect incomings
|
||||
|
|
|
@ -23,7 +23,7 @@ import java.util.concurrent.Executor;
|
|||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClientFactory;
|
||||
import org.eclipse.jetty.websocket.client.internal.IWebSocketClient;
|
||||
import org.eclipse.jetty.websocket.client.internal.DefaultWebSocketClient;
|
||||
import org.eclipse.jetty.websocket.client.masks.Masker;
|
||||
import org.eclipse.jetty.websocket.core.io.AbstractWebSocketConnection;
|
||||
import org.eclipse.jetty.websocket.core.protocol.WebSocketFrame;
|
||||
|
@ -34,11 +34,11 @@ import org.eclipse.jetty.websocket.core.protocol.WebSocketFrame;
|
|||
public class WebSocketClientConnection extends AbstractWebSocketConnection
|
||||
{
|
||||
private final WebSocketClientFactory factory;
|
||||
private final IWebSocketClient client;
|
||||
private final DefaultWebSocketClient client;
|
||||
private final Masker masker;
|
||||
private boolean connected;
|
||||
|
||||
public WebSocketClientConnection(EndPoint endp, Executor executor, IWebSocketClient client)
|
||||
public WebSocketClientConnection(EndPoint endp, Executor executor, DefaultWebSocketClient client)
|
||||
{
|
||||
super(endp,executor,client.getFactory().getScheduler(),client.getPolicy(),client.getFactory().getBufferPool());
|
||||
this.client = client;
|
||||
|
@ -47,7 +47,7 @@ public class WebSocketClientConnection extends AbstractWebSocketConnection
|
|||
this.masker = client.getMasker();
|
||||
}
|
||||
|
||||
public IWebSocketClient getClient()
|
||||
public DefaultWebSocketClient getClient()
|
||||
{
|
||||
return client;
|
||||
}
|
||||
|
|
|
@ -35,33 +35,23 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClientFactory;
|
||||
import org.eclipse.jetty.websocket.client.internal.IWebSocketClient;
|
||||
import org.eclipse.jetty.websocket.client.internal.DefaultWebSocketClient;
|
||||
import org.eclipse.jetty.websocket.core.api.WebSocketPolicy;
|
||||
|
||||
public class WebSocketClientSelectorManager extends SelectorManager
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(WebSocketClientSelectorManager.class);
|
||||
private final Executor executor;
|
||||
private final Scheduler scheduler;
|
||||
private final WebSocketPolicy policy;
|
||||
private final ByteBufferPool bufferPool;
|
||||
private SslContextFactory sslContextFactory;
|
||||
|
||||
public WebSocketClientSelectorManager(ByteBufferPool bufferPool, Executor executor, Scheduler scheduler, WebSocketPolicy policy)
|
||||
{
|
||||
super();
|
||||
super(executor, scheduler);
|
||||
this.bufferPool = bufferPool;
|
||||
this.executor = executor;
|
||||
this.scheduler = scheduler;
|
||||
this.policy = policy;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(Runnable task)
|
||||
{
|
||||
this.executor.execute(task);
|
||||
}
|
||||
|
||||
public SslContextFactory getSslContextFactory()
|
||||
{
|
||||
return sslContextFactory;
|
||||
|
@ -71,7 +61,7 @@ public class WebSocketClientSelectorManager extends SelectorManager
|
|||
public Connection newConnection(final SocketChannel channel, EndPoint endPoint, final Object attachment) throws IOException
|
||||
{
|
||||
LOG.debug("newConnection({},{},{})",channel,endPoint,attachment);
|
||||
IWebSocketClient client = (IWebSocketClient)attachment;
|
||||
DefaultWebSocketClient client = (DefaultWebSocketClient)attachment;
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -83,7 +73,7 @@ public class WebSocketClientSelectorManager extends SelectorManager
|
|||
if (sslContextFactory != null)
|
||||
{
|
||||
SSLEngine engine = newSSLEngine(sslContextFactory,channel);
|
||||
SslConnection sslConnection = new SslConnection(bufferPool,executor,endPoint,engine);
|
||||
SslConnection sslConnection = new SslConnection(bufferPool,getExecutor(),endPoint,engine);
|
||||
EndPoint sslEndPoint = sslConnection.getDecryptedEndPoint();
|
||||
|
||||
Connection connection = newUpgradeConnection(channel,sslEndPoint,client);
|
||||
|
@ -116,7 +106,7 @@ public class WebSocketClientSelectorManager extends SelectorManager
|
|||
protected EndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey selectionKey) throws IOException
|
||||
{
|
||||
LOG.debug("newEndPoint({}, {}, {})",channel,selectSet,selectionKey);
|
||||
return new SelectChannelEndPoint(channel,selectSet,selectionKey,scheduler,policy.getIdleTimeout());
|
||||
return new SelectChannelEndPoint(channel,selectSet,selectionKey,getScheduler(),policy.getIdleTimeout());
|
||||
}
|
||||
|
||||
public SSLEngine newSSLEngine(SslContextFactory sslContextFactory, SocketChannel channel)
|
||||
|
@ -128,7 +118,7 @@ public class WebSocketClientSelectorManager extends SelectorManager
|
|||
return engine;
|
||||
}
|
||||
|
||||
public UpgradeConnection newUpgradeConnection(SocketChannel channel, EndPoint endPoint, IWebSocketClient client)
|
||||
public UpgradeConnection newUpgradeConnection(SocketChannel channel, EndPoint endPoint, DefaultWebSocketClient client)
|
||||
{
|
||||
WebSocketClientFactory factory = client.getFactory();
|
||||
Executor executor = factory.getExecutor();
|
||||
|
|
|
@ -368,6 +368,8 @@ public class BlockheadServer
|
|||
// Connect extensions
|
||||
if (!extensions.isEmpty())
|
||||
{
|
||||
generator.configureFromExtensions(extensions);
|
||||
|
||||
Iterator<Extension> extIter;
|
||||
// Connect outgoings
|
||||
extIter = extensions.iterator();
|
||||
|
@ -376,20 +378,6 @@ public class BlockheadServer
|
|||
Extension ext = extIter.next();
|
||||
ext.setNextOutgoingFrames(outgoing);
|
||||
outgoing = ext;
|
||||
|
||||
// Handle RSV reservations
|
||||
if (ext.useRsv1())
|
||||
{
|
||||
generator.setRsv1InUse(true);
|
||||
}
|
||||
if (ext.useRsv2())
|
||||
{
|
||||
generator.setRsv2InUse(true);
|
||||
}
|
||||
if (ext.useRsv3())
|
||||
{
|
||||
generator.setRsv3InUse(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Connect incomings
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2012 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.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.client.examples;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.util.FutureCallback;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClientFactory;
|
||||
import org.eclipse.jetty.websocket.core.annotations.OnWebSocketClose;
|
||||
import org.eclipse.jetty.websocket.core.annotations.OnWebSocketConnect;
|
||||
import org.eclipse.jetty.websocket.core.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.core.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.core.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.core.api.WebSocketConnection;
|
||||
|
||||
/**
|
||||
* Example of a simple Echo Client.
|
||||
*/
|
||||
public class SimpleEchoClient
|
||||
{
|
||||
@WebSocket
|
||||
public static class SimpleEchoSocket
|
||||
{
|
||||
private final CountDownLatch closeLatch;
|
||||
@SuppressWarnings("unused")
|
||||
private WebSocketConnection conn;
|
||||
|
||||
public SimpleEchoSocket()
|
||||
{
|
||||
this.closeLatch = new CountDownLatch(1);
|
||||
}
|
||||
|
||||
public boolean awaitClose(int duration, TimeUnit unit) throws InterruptedException
|
||||
{
|
||||
return this.closeLatch.await(duration,unit);
|
||||
}
|
||||
|
||||
@OnWebSocketClose
|
||||
public void onClose(int statusCode, String reason)
|
||||
{
|
||||
System.out.printf("Connection closed: %d - %s%n",statusCode,reason);
|
||||
this.conn = null;
|
||||
this.closeLatch.countDown(); // trigger latch
|
||||
}
|
||||
|
||||
@OnWebSocketConnect
|
||||
public void onConnect(WebSocketConnection conn)
|
||||
{
|
||||
System.out.printf("Got connect: %s%n",conn);
|
||||
this.conn = conn;
|
||||
try
|
||||
{
|
||||
FutureCallback<Void> callback = new FutureCallback<>();
|
||||
conn.write(null,callback,"Hello");
|
||||
callback.get(2,TimeUnit.SECONDS); // wait for send to complete.
|
||||
|
||||
callback = new FutureCallback<>();
|
||||
conn.write(null,callback,"Thanks for the conversation.");
|
||||
callback.get(2,TimeUnit.SECONDS); // wait for send to complete.
|
||||
|
||||
conn.close(StatusCode.NORMAL,"I'm done");
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@OnWebSocketMessage
|
||||
public void onMessage(String msg)
|
||||
{
|
||||
System.out.printf("Got msg: %s%n",msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
String destUri = "ws://echo.websocket.org";
|
||||
if (args.length > 0)
|
||||
{
|
||||
destUri = args[0];
|
||||
}
|
||||
|
||||
WebSocketClientFactory factory = new WebSocketClientFactory();
|
||||
SimpleEchoSocket socket = new SimpleEchoSocket();
|
||||
try
|
||||
{
|
||||
factory.start();
|
||||
WebSocketClient client = factory.newWebSocketClient(socket);
|
||||
URI echoUri = new URI(destUri);
|
||||
System.out.printf("Connecting to : %s%n",echoUri);
|
||||
client.getUpgradeRequest().addExtensions("x-webkit-deflate-frame");
|
||||
client.connect(echoUri);
|
||||
|
||||
// wait for closed socket connection.
|
||||
socket.awaitClose(5,TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
t.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
factory.stop();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
org.eclipse.jetty.LEVEL=WARN
|
||||
# org.eclipse.jetty.io.ChannelEndPoint.LEVEL=INFO
|
||||
org.eclipse.jetty.websocket.LEVEL=WARN
|
||||
# org.eclipse.jetty.websocket.LEVEL=WARN
|
||||
# org.eclipse.jetty.websocket.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.websocket.client.TrackingSocket.LEVEL=DEBUG
|
||||
# Hide the stacktraces
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue