diff --git a/example-jetty-embedded/pom.xml b/example-jetty-embedded/pom.xml index f2408d3a287..03da5633e3e 100644 --- a/example-jetty-embedded/pom.xml +++ b/example-jetty-embedded/pom.xml @@ -38,6 +38,11 @@ org.eclipse.jetty jetty-jmx ${project.version} + + + org.eclipse.jetty.spdy + spdy-jetty-http + ${project.version} org.eclipse.jetty.toolchain diff --git a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java index 7c4083551cb..68247199641 100644 --- a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java +++ b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java @@ -27,8 +27,7 @@ import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Handler; -import org.eclipse.jetty.server.HttpConfiguration; -import org.eclipse.jetty.server.HttpServerConnectionFactory; +import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.NCSARequestLog; import org.eclipse.jetty.server.SelectChannelConnector; import org.eclipse.jetty.server.Server; @@ -65,13 +64,11 @@ public class LikeJettyXml mbContainer.addBean(new Log()); // Setup Connectors - SelectChannelConnector connector = new SelectChannelConnector(server); + HttpConnectionFactory http = new HttpConnectionFactory(); + http.getHttpChannelConfig().setSecurePort(8443); + SelectChannelConnector connector = new SelectChannelConnector(server,http); connector.setPort(8080); connector.setIdleTimeout(30000); - HttpConfiguration httpConfiguration = new HttpConfiguration(null, false); - httpConfiguration.setConfidentialPort(8443); - connector.setDefaultConnectionFactory(new HttpServerConnectionFactory(connector, httpConfiguration)); - // TODO connector.setStatsOn(false); server.setConnectors(new Connector[] { connector }); @@ -80,7 +77,7 @@ public class LikeJettyXml sslContextFactory.setKeyStorePath(jetty_home + "/etc/keystore"); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); - sslContextFactory.setTrustStore(jetty_home + "/etc/keystore"); + sslContextFactory.setTrustStorePath(jetty_home + "/etc/keystore"); sslContextFactory.setTrustStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setExcludeCipherSuites( new String[]{ @@ -94,7 +91,6 @@ public class LikeJettyXml }); SelectChannelConnector sslConnector = new SelectChannelConnector(server,sslContextFactory); sslConnector.setPort(8443); - // TODO sslConnector.setStatsOn(false); server.addConnector(sslConnector); sslConnector.open(); diff --git a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java index 4dc4411c806..40255d5e742 100644 --- a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java +++ b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java @@ -18,10 +18,23 @@ package org.eclipse.jetty.embedded; +import org.eclipse.jetty.io.ArrayByteBufferPool; +import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.ForwardedRequestCustomizer; +import org.eclipse.jetty.server.HttpChannelConfig; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.SelectChannelConnector; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.spdy.NPNServerConnectionFactory; +import org.eclipse.jetty.spdy.http.PushStrategy; +import org.eclipse.jetty.spdy.http.ReferrerPushStrategy; +import org.eclipse.jetty.spdy.http.HTTPSPDYServerConnectionFactory; import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.TimerScheduler; /* ------------------------------------------------------------ */ /** @@ -32,31 +45,73 @@ public class ManyConnectors { public static void main(String[] args) throws Exception { + String jetty_home = System.getProperty("jetty.home","../jetty-server/src/main/config"); + System.setProperty("jetty.home", jetty_home); + Server server = new Server(); + // HTTP connector SelectChannelConnector connector0 = new SelectChannelConnector(server); connector0.setPort(8080); connector0.setIdleTimeout(30000); - SelectChannelConnector connector1 = new SelectChannelConnector(server); - connector1.setHost("127.0.0.1"); - connector1.setPort(8888); - - String jetty_home = System.getProperty("jetty.home","../jetty-distribution/target/distribution"); - System.setProperty("jetty.home", jetty_home); + // HTTPS connector SslContextFactory sslContextFactory = new SslContextFactory(); sslContextFactory.setKeyStorePath(jetty_home + "/etc/keystore"); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); - SelectChannelConnector sslConnector = new SelectChannelConnector(server,sslContextFactory); - sslConnector.setPort(8443); - - server.setConnectors(new Connector[] - { connector0, connector1, sslConnector }); + + SelectChannelConnector connector1 = new SelectChannelConnector(server,sslContextFactory); + connector1.setPort(8443); + + + // A verbosely fully configured connector with SSL, SPDY and HTTP + + HttpChannelConfig config = new HttpChannelConfig(); + config.setSecureScheme("https"); + config.setSecurePort(8443); + config.setOutputBufferSize(32768); + config.setRequestHeaderSize(8192); + config.setResponseHeaderSize(8192); + config.addCustomizer(new ForwardedRequestCustomizer()); + config.addCustomizer(new SecureRequestCustomizer()); + + HttpConnectionFactory http = new HttpConnectionFactory(config); + http.setInputBufferSize(16384); + + 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); + + SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory,npn.getProtocol()); + + QueuedThreadPool threadPool = new QueuedThreadPool(); + threadPool.setMaxThreads(256); + TimerScheduler scheduler = new TimerScheduler(); + ByteBufferPool bufferPool= new ArrayByteBufferPool(32,4096,32768); + + SelectChannelConnector connector2 = new SelectChannelConnector(server,threadPool,scheduler,bufferPool,2,2,ssl,npn,spdy3,spdy2,http); + connector2.setDefaultProtocol("ssl-npn"); + connector2.setPort(8444); + connector2.setIdleTimeout(30000); + connector2.setSoLingerTime(10000); + + // Set the connectors + server.setConnectors(new Connector[] { connector0, connector1, connector2 }); + server.setHandler(new HelloHandler()); server.start(); + server.dumpStdErr(); server.join(); } } diff --git a/example-jetty-embedded/src/test/java/org/eclipse/jetty/embedded/TestXml.java b/example-jetty-embedded/src/test/java/org/eclipse/jetty/embedded/TestXml.java new file mode 100644 index 00000000000..58a3319f85c --- /dev/null +++ b/example-jetty-embedded/src/test/java/org/eclipse/jetty/embedded/TestXml.java @@ -0,0 +1,36 @@ +// +// ======================================================================== +// 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 org.eclipse.jetty.xml.XmlConfiguration; + +public class TestXml +{ + public static void main(String[] args) throws Exception + { + System.setProperty("jetty.home","../jetty-distribution/target/distribution"); + XmlConfiguration.main(new String[] + { + "../jetty-jmx/src/main/config/etc/jetty-jmx.xml", + "../jetty-server/src/main/config/etc/jetty.xml", + "../jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy.xml" + } + ); + } +} diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java index 4af47822d28..b80eb86a2b4 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java @@ -18,6 +18,7 @@ package org.eclipse.jetty.client; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -25,21 +26,33 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.jetty.client.api.Authentication; +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.client.util.BufferingResponseListener; import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; -public class AuthenticationProtocolHandler extends Response.Listener.Adapter implements ProtocolHandler +public class AuthenticationProtocolHandler implements ProtocolHandler { - private static final Pattern WWW_AUTHENTICATE_PATTERN = Pattern.compile("([^\\s]+)\\s+realm=\"([^\"]+)\"(\\s*,\\s*)?(.*)", Pattern.CASE_INSENSITIVE); + 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; public AuthenticationProtocolHandler(HttpClient client) + { + this(client, 4096); + } + + public AuthenticationProtocolHandler(HttpClient client, int maxContentLength) { this.client = client; + this.maxContentLength = maxContentLength; } @Override @@ -51,102 +64,133 @@ public class AuthenticationProtocolHandler extends Response.Listener.Adapter imp @Override public Response.Listener getResponseListener() { - return this; + return new AuthenticationListener(); } - @Override - public void onComplete(Result result) + private class AuthenticationListener extends BufferingResponseListener { - if (!result.isFailed()) + private AuthenticationListener() { - List wwwAuthenticates = parseWWWAuthenticate(result.getResponse()); + super(maxContentLength); + } + + @Override + public void onComplete(Result result) + { + Request request = result.getRequest(); + ContentResponse response = new HttpContentResponse(result.getResponse(), getContent()); + if (result.isFailed()) + { + Throwable failure = result.getFailure(); + LOG.debug("Authentication challenge failed {}", failure); + forwardFailure(request, response, failure); + return; + } + + List wwwAuthenticates = parseWWWAuthenticate(response); if (wwwAuthenticates.isEmpty()) { - // TODO + LOG.debug("Authentication challenge without WWW-Authenticate header"); + forwardFailure(request, response, new HttpResponseException("HTTP protocol violation: 401 without WWW-Authenticate header", response)); + return; } - else + + final String uri = request.uri(); + Authentication authentication = null; + WWWAuthenticate wwwAuthenticate = null; + for (WWWAuthenticate wwwAuthn : wwwAuthenticates) { - Request request = result.getRequest(); - final String uri = request.uri(); - Authentication authentication = null; - for (WWWAuthenticate wwwAuthenticate : wwwAuthenticates) - { - authentication = client.getAuthenticationStore().findAuthentication(wwwAuthenticate.type, uri, wwwAuthenticate.realm); - if (authentication != null) - break; - } + authentication = client.getAuthenticationStore().findAuthentication(wwwAuthn.type, uri, wwwAuthn.realm); if (authentication != null) { - final Authentication authn = authentication; - authn.authenticate(request); - request.send(new Adapter() - { - @Override - public void onComplete(Result result) - { - if (!result.isFailed()) - { - Authentication.Result authnResult = new Authentication.Result(uri, authn); - client.getAuthenticationStore().addAuthenticationResult(authnResult); - } - } - }); - } - else - { - noAuthentication(request, result.getResponse()); + wwwAuthenticate = wwwAuthn; + break; } } - } - } - - private List parseWWWAuthenticate(Response response) - { - List result = new ArrayList<>(); - List values = Collections.list(response.headers().getValues(HttpHeader.WWW_AUTHENTICATE.asString())); - for (String value : values) - { - Matcher matcher = WWW_AUTHENTICATE_PATTERN.matcher(value); - if (matcher.matches()) + if (authentication == null) { - String type = matcher.group(1); - String realm = matcher.group(2); - String params = matcher.group(4); - WWWAuthenticate wwwAuthenticate = new WWWAuthenticate(type, realm, params); - result.add(wwwAuthenticate); + LOG.debug("No authentication available for {}", request); + forwardSuccess(request, response); + return; } - } - return result; - } - private void noAuthentication(Request request, Response response) - { - HttpConversation conversation = client.getConversation(request); - Response.Listener listener = conversation.exchanges().peekFirst().listener(); - notifier.notifyBegin(listener, response); - notifier.notifyHeaders(listener, response); - notifier.notifySuccess(listener, response); - // TODO: this call here is horrid, but needed... but here it is too late for the exchange - // TODO: to figure out that the conversation is finished, so we need to manually do it here, no matter what. - // TODO: However, we also need to make sure that requests are not resent with the same ID - // TODO: because here the connection has already been returned to the pool, so the "new" request may see - // TODO: the same conversation but it's not really the case. - // TODO: perhaps the factory for requests should be the conversation ? - conversation.complete(); - notifier.notifyComplete(listener, new Result(request, response)); + 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); + return; + } + + authnResult.apply(request); + request.send(new Response.Listener.Empty() + { + @Override + public void onSuccess(Response response) + { + client.getAuthenticationStore().addAuthenticationResult(authnResult); + } + }); + } + + 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 parseWWWAuthenticate(Response response) + { + // TODO: these should be ordered by strength + List result = new ArrayList<>(); + List values = Collections.list(response.headers().getValues(HttpHeader.WWW_AUTHENTICATE.asString())); + for (String value : values) + { + Matcher matcher = WWW_AUTHENTICATE_PATTERN.matcher(value); + if (matcher.matches()) + { + String type = matcher.group(1); + String realm = matcher.group(2); + WWWAuthenticate wwwAuthenticate = new WWWAuthenticate(value, type, realm); + result.add(wwwAuthenticate); + } + } + return result; + } } private class WWWAuthenticate { + private final String value; private final String type; private final String realm; - private final String params; - public WWWAuthenticate(String type, String realm, String params) + public WWWAuthenticate(String value, String type, String realm) { + this.value = value; this.type = type; this.realm = realm; - this.params = params; } } } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpAuthenticationStore.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpAuthenticationStore.java index da11bfa5702..a8033a30195 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpAuthenticationStore.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpAuthenticationStore.java @@ -29,7 +29,7 @@ import org.eclipse.jetty.client.api.AuthenticationStore; public class HttpAuthenticationStore implements AuthenticationStore { private final List authentications = new CopyOnWriteArrayList<>(); - private final Map results = new ConcurrentHashMap<>(); + private final Map results = new ConcurrentHashMap<>(); @Override public void addAuthentication(Authentication authentication) @@ -57,20 +57,26 @@ public class HttpAuthenticationStore implements AuthenticationStore @Override public void addAuthenticationResult(Authentication.Result result) { - results.put(result.getURI(), result.getAuthentication()); + results.put(result.getURI(), result); } @Override - public void removeAuthenticationResults() + public void removeAuthenticationResult(Authentication.Result result) + { + results.remove(result.getURI()); + } + + @Override + public void clearAuthenticationResults() { results.clear(); } @Override - public Authentication findAuthenticationResult(String uri) + public Authentication.Result findAuthenticationResult(String uri) { // TODO: I should match the longest URI - for (Map.Entry entry : results.entrySet()) + for (Map.Entry entry : results.entrySet()) { if (uri.startsWith(entry.getKey())) return entry.getValue(); diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java index aada2edfccd..dba21c5dc61 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java @@ -69,6 +69,7 @@ import org.eclipse.jetty.util.thread.TimerScheduler; * and HTTP parameters (such as whether to follow redirects).

*

{@link HttpClient} transparently pools connections to servers, but allows direct control of connections * for cases where this is needed.

+ *

{@link HttpClient} also acts as a central configuration point for cookies, via {@link #getCookieStore()}.

*

Typical usage:

*
  * // One liner:
@@ -264,6 +265,11 @@ public class HttpClient extends AggregateLifeCycle
     }
 
     public Destination getDestination(String scheme, String host, int port)
+    {
+        return provideDestination(scheme, host, port);
+    }
+
+    private HttpDestination provideDestination(String scheme, String host, int port)
     {
         String address = address(scheme, host, port);
         HttpDestination destination = destinations.get(address);
@@ -321,7 +327,7 @@ public class HttpClient extends AggregateLifeCycle
         if (port < 0)
             port = "https".equals(scheme) ? 443 : 80;
 
-        getDestination(scheme, host, port).send(request, listener);
+        provideDestination(scheme, host, port).send(request, listener);
     }
 
     public Executor getExecutor()
@@ -438,7 +444,7 @@ public class HttpClient extends AggregateLifeCycle
     }
 
     // TODO: find a better method name
-    public Response.Listener lookup(Request request, Response response)
+    protected Response.Listener lookup(Request request, Response response)
     {
         for (ProtocolHandler handler : handlers)
         {
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java
index 4f1e3286b7e..c7a03c02dbe 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java
@@ -146,7 +146,7 @@ public class HttpConnection extends AbstractConnection implements Connection
         }
 
         // Cookies
-        List cookies = client.getCookieStore().getCookies(getDestination(), request.path());
+        List cookies = client.getCookieStore().findCookies(getDestination(), request.path());
         StringBuilder cookieString = null;
         for (int i = 0; i < cookies.size(); ++i)
         {
@@ -161,9 +161,9 @@ public class HttpConnection extends AbstractConnection implements Connection
             request.header(HttpHeader.COOKIE.asString(), cookieString.toString());
 
         // Authorization
-        Authentication authentication = client.getAuthenticationStore().findAuthenticationResult(request.uri());
-        if (authentication != null)
-            authentication.authenticate(request);
+        Authentication.Result authnResult = client.getAuthenticationStore().findAuthenticationResult(request.uri());
+        if (authnResult != null)
+            authnResult.apply(request);
 
         // TODO: decoder headers
 
@@ -250,6 +250,11 @@ public class HttpConnection extends AbstractConnection implements Connection
         }
     }
 
+    public void abort(HttpResponse response)
+    {
+        receiver.fail(new HttpResponseException("Response aborted", response));
+    }
+
     @Override
     public void close()
     {
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpCookieStore.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpCookieStore.java
index a198514c9c8..043447b3578 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpCookieStore.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpCookieStore.java
@@ -35,7 +35,7 @@ public class HttpCookieStore implements CookieStore
     private final ConcurrentMap> allCookies = new ConcurrentHashMap<>();
 
     @Override
-    public List getCookies(Destination destination, String path)
+    public List findCookies(Destination destination, String path)
     {
         List result = new ArrayList<>();
 
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
index 894c48da364..7d965ec580c 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
@@ -91,7 +91,6 @@ public class HttpDestination implements Destination, AutoCloseable
         return port;
     }
 
-    @Override
     public void send(Request request, Response.Listener listener)
     {
         if (!scheme.equals(request.scheme()))
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java
index 26024e83c96..4e6f669bc10 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java
@@ -42,7 +42,7 @@ public class HttpExchange
         this.connection = connection;
         this.request = request;
         this.listener = listener;
-        this.response = new HttpResponse(listener);
+        this.response = new HttpResponse(this, listener);
     }
 
     public HttpConversation conversation()
@@ -117,6 +117,12 @@ public class HttpExchange
         return false;
     }
 
+    public void abort()
+    {
+        LOG.debug("Aborting {}", response);
+        connection.abort(response);
+    }
+
     @Override
     public String toString()
     {
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
index 3ecda154f2b..f3695916768 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
@@ -27,38 +27,37 @@ import java.nio.file.Path;
 import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicLong;
 
-import org.eclipse.jetty.client.api.ContentDecoder;
 import org.eclipse.jetty.client.api.ContentProvider;
 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.client.util.BufferingResponseListener;
+import org.eclipse.jetty.client.util.BlockingResponseListener;
 import org.eclipse.jetty.client.util.PathContentProvider;
 import org.eclipse.jetty.http.HttpFields;
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpMethod;
 import org.eclipse.jetty.http.HttpVersion;
 import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.FutureCallback;
 
 public class HttpRequest implements Request
 {
     private static final AtomicLong ids = new AtomicLong();
 
+    private final HttpFields headers = new HttpFields();
+    private final Fields params = new Fields();
     private final HttpClient client;
     private final long id;
-    private String scheme;
     private final String host;
     private final int port;
+    private String scheme;
     private String path;
     private HttpMethod method;
     private HttpVersion version;
     private long idleTimeout;
     private Listener listener;
     private ContentProvider content;
-    private final HttpFields headers = new HttpFields();
-    private final Fields params = new Fields();
+    private boolean followRedirects;
+    private volatile boolean aborted;
 
     public HttpRequest(HttpClient client, URI uri)
     {
@@ -82,6 +81,7 @@ public class HttpRequest implements Request
                 param(parts[0], parts.length < 2 ? "" : urlDecode(parts[1]));
             }
         }
+        followRedirects(client.isFollowRedirects());
     }
 
     private String urlDecode(String value)
@@ -199,9 +199,9 @@ public class HttpRequest implements Request
     }
 
     @Override
-    public Request agent(String userAgent)
+    public Request agent(String agent)
     {
-        headers.put(HttpHeader.USER_AGENT, userAgent);
+        headers.put(HttpHeader.USER_AGENT, agent);
         return this;
     }
 
@@ -261,15 +261,22 @@ public class HttpRequest implements Request
         return content(new PathContentProvider(file));
     }
 
+//    @Override
+//    public Request decoder(ContentDecoder decoder)
+//    {
+//        return this;
+//    }
+
     @Override
-    public Request decoder(ContentDecoder decoder)
+    public boolean followRedirects()
     {
-        return this;
+        return followRedirects;
     }
 
     @Override
     public Request followRedirects(boolean follow)
     {
+        this.followRedirects = follow;
         return this;
     }
 
@@ -289,22 +296,9 @@ public class HttpRequest implements Request
     @Override
     public Future send()
     {
-        final FutureCallback callback = new FutureCallback<>();
-        BufferingResponseListener listener = new BufferingResponseListener()
-        {
-            @Override
-            public void onComplete(Result result)
-            {
-                super.onComplete(result);
-                HttpContentResponse contentResponse = new HttpContentResponse(result.getResponse(), content());
-                if (!result.isFailed())
-                    callback.completed(contentResponse);
-                else
-                    callback.failed(contentResponse, result.getFailure());
-            }
-        };
+        BlockingResponseListener listener = new BlockingResponseListener();
         send(listener);
-        return callback;
+        return listener;
     }
 
     @Override
@@ -313,6 +307,18 @@ public class HttpRequest implements Request
         client.send(this, listener);
     }
 
+    @Override
+    public void abort()
+    {
+        aborted = true;
+    }
+
+    @Override
+    public boolean aborted()
+    {
+        return aborted;
+    }
+
     @Override
     public String toString()
     {
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ChannelHttpServer.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequestException.java
similarity index 65%
rename from jetty-server/src/test/java/org/eclipse/jetty/server/ChannelHttpServer.java
rename to jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequestException.java
index 51842cbbb86..0541eee14ca 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ChannelHttpServer.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequestException.java
@@ -16,19 +16,22 @@
 //  ========================================================================
 //
 
-package org.eclipse.jetty.server;
+package org.eclipse.jetty.client;
 
-public class ChannelHttpServer
+import org.eclipse.jetty.client.api.Request;
+
+public class HttpRequestException extends Throwable
 {
-    public static void main(String[] s) throws Exception
+    private final Request request;
+
+    public HttpRequestException(String message, Request request)
     {
-        Server server = new Server();
-        SelectChannelConnector connector = new SelectChannelConnector(server);
-        connector.setPort(8080);
-        server.addConnector(connector);
-        server.setHandler(new DumpHandler());
-        server.start();
-        server.dumpStdErr();
-        server.join();
+        super(message);
+        this.request = request;
+    }
+
+    public Request getRequest()
+    {
+        return request;
     }
 }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpResponse.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpResponse.java
index 5f9c2468574..42be83cc9c3 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpResponse.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpResponse.java
@@ -25,13 +25,15 @@ import org.eclipse.jetty.http.HttpVersion;
 public class HttpResponse implements Response
 {
     private final HttpFields headers = new HttpFields();
+    private final HttpExchange exchange;
     private final Listener listener;
     private HttpVersion version;
     private int status;
     private String reason;
 
-    public HttpResponse(Response.Listener listener)
+    public HttpResponse(HttpExchange exchange, Listener listener)
     {
+        this.exchange = exchange;
         this.listener = listener;
     }
 
@@ -84,7 +86,7 @@ public class HttpResponse implements Response
     @Override
     public void abort()
     {
-//        request.abort();
+        exchange.abort();
     }
 
     @Override
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java
index a9308d31353..e75854fc1bf 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java
@@ -60,12 +60,20 @@ public class HttpSender
 
     public void send(HttpExchange exchange)
     {
-        LOG.debug("Sending {}", exchange.request());
-        requestNotifier.notifyBegin(exchange.request());
-        ContentProvider content = exchange.request().content();
-        this.contentLength = content == null ? -1 : content.length();
-        this.contentChunks = content == null ? Collections.emptyIterator() : content.iterator();
-        send();
+        Request request = exchange.request();
+        if (request.aborted())
+        {
+            fail(new HttpRequestException("Request aborted", request));
+        }
+        else
+        {
+            LOG.debug("Sending {}", request);
+            requestNotifier.notifyBegin(request);
+            ContentProvider content = request.content();
+            this.contentLength = content == null ? -1 : content.length();
+            this.contentChunks = content == null ? Collections.emptyIterator() : content.iterator();
+            send();
+        }
     }
 
     private void send()
@@ -122,38 +130,45 @@ public class HttpSender
                     }
                     case FLUSH:
                     {
-                        StatefulExecutorCallback callback = new StatefulExecutorCallback(client.getExecutor())
+                        if (request.aborted())
                         {
-                            @Override
-                            protected void pendingCompleted()
+                            fail(new HttpRequestException("Request aborted", request));
+                        }
+                        else
+                        {
+                            StatefulExecutorCallback callback = new StatefulExecutorCallback(client.getExecutor())
+                            {
+                                @Override
+                                protected void pendingCompleted()
+                                {
+                                    if (!committed)
+                                        committed(request);
+                                    send();
+                                }
+
+                                @Override
+                                protected void failed(Throwable x)
+                                {
+                                    fail(x);
+                                }
+                            };
+                            if (header == null)
+                                header = BufferUtil.EMPTY_BUFFER;
+                            if (chunk == null)
+                                chunk = BufferUtil.EMPTY_BUFFER;
+                            endPoint.write(null, callback, header, chunk, content);
+                            if (callback.pending())
+                                return;
+
+                            if (callback.completed())
                             {
                                 if (!committed)
                                     committed(request);
-                                send();
+
+                                releaseBuffers();
+                                content = contentChunks.hasNext() ? contentChunks.next() : BufferUtil.EMPTY_BUFFER;
+                                lastContent = !contentChunks.hasNext();
                             }
-
-                            @Override
-                            protected void failed(Throwable x)
-                            {
-                                fail(x);
-                            }
-                        };
-                        if (header == null)
-                            header = BufferUtil.EMPTY_BUFFER;
-                        if (chunk == null)
-                            chunk = BufferUtil.EMPTY_BUFFER;
-                        endPoint.write(null, callback, header, chunk, content);
-                        if (callback.pending())
-                            return;
-
-                        if (callback.completed())
-                        {
-                            if (!committed)
-                                committed(request);
-
-                            releaseBuffers();
-                            content = contentChunks.hasNext() ? contentChunks.next() : BufferUtil.EMPTY_BUFFER;
-                            lastContent = !contentChunks.hasNext();
                         }
                         break;
                     }
@@ -234,7 +249,7 @@ public class HttpSender
         // Notify after
         HttpExchange exchange = connection.getExchange();
         Request request = exchange.request();
-        LOG.debug("Failed {}", request);
+        LOG.debug("Failed {} {}", request, failure);
 
         boolean exchangeCompleted = exchange.requestComplete(false);
         if (!exchangeCompleted && !committed)
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/RedirectProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/RedirectProtocolHandler.java
index 180e66098a7..588b1fffb6c 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/RedirectProtocolHandler.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/RedirectProtocolHandler.java
@@ -24,7 +24,7 @@ import org.eclipse.jetty.client.api.Result;
 import org.eclipse.jetty.http.HttpFields;
 import org.eclipse.jetty.http.HttpMethod;
 
-public class RedirectProtocolHandler extends Response.Listener.Adapter implements ProtocolHandler
+public class RedirectProtocolHandler extends Response.Listener.Empty implements ProtocolHandler
 {
     private static final String ATTRIBUTE = RedirectProtocolHandler.class.getName() + ".redirect";
 
@@ -45,7 +45,7 @@ public class RedirectProtocolHandler extends Response.Listener.Adapter implement
             case 302:
             case 303:
             case 307:
-                return true;
+                return request.followRedirects();
         }
         return false;
     }
@@ -128,7 +128,7 @@ public class RedirectProtocolHandler extends Response.Listener.Adapter implement
             // Copy content
             redirect.content(request.content());
 
-            redirect.send(new Adapter());
+            redirect.send(new Response.Listener.Empty());
         }
         else
         {
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Authentication.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Authentication.java
index 5aa17ee98d4..05ad760a525 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Authentication.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Authentication.java
@@ -18,31 +18,67 @@
 
 package org.eclipse.jetty.client.api;
 
+import org.eclipse.jetty.util.Attributes;
+
+/**
+ * {@link Authentication} represents a mechanism to authenticate requests for protected resources.
+ * 

+ * {@link Authentication}s are added to an {@link AuthenticationStore}, which is then + * {@link #matches(String, String, String) queried} to find the right + * {@link Authentication} mechanism to use based on its type, URI and realm, as returned by + * {@code WWW-Authenticate} response headers. + *

+ * If an {@link Authentication} mechanism is found, it is then + * {@link #authenticate(Request, ContentResponse, String, Attributes) executed} for the given request, + * returning an {@link Authentication.Result}, which is then stored in the {@link AuthenticationStore} + * so that subsequent requests can be preemptively authenticated. + */ public interface Authentication { + /** + * Matches {@link Authentication}s based on the given parameters + * @param type the {@link Authentication} type such as "Basic" or "Digest" + * @param uri the request URI + * @param realm the authentication realm as provided in the {@code WWW-Authenticate} response header + * @return true if this authentication matches, false otherwise + */ boolean matches(String type, String uri, String realm); - void authenticate(Request request); + /** + * Executes the authentication mechanism for the given request, returning a {@link Result} that can be + * used to actually authenticate the request via {@link Result#apply(Request)}. + *

+ * If a request for {@code "/secure"} returns a {@link Result}, then the result may be used for other + * requests such as {@code "/secure/foo"} or {@code "/secure/bar"}, unless those resources are protected + * by other realms. + * + * @param request the request to execute the authentication mechanism for + * @param response the 401 response obtained in the previous attempt to request the protected resource + * @param wwwAuthenticate the {@code WWW-Authenticate} header chosen for this authentication + * (among the many that the response may contain) + * @param context the conversation context in case the authentication needs multiple exchanges + * to be completed and information needs to be stored across exchanges + * @return the authentication result, or null if the authentication could not be performed + */ + Result authenticate(Request request, ContentResponse response, String wwwAuthenticate, Attributes context); - public static class Result + /** + * {@link Result} holds the information needed to authenticate a {@link Request} via {@link #apply(Request)}. + */ + public static interface Result { - private final String uri; - private final Authentication authentication; + /** + * @return the URI of the request that has been used to generate this {@link Result} + */ + String getURI(); - public Result(String uri, Authentication authentication) - { - this.uri = uri; - this.authentication = authentication; - } - - public String getURI() - { - return uri; - } - - public Authentication getAuthentication() - { - return authentication; - } + /** + * Applies the authentication result to the given request. + * Typically, a {@code Authorization} header is added to the request, with the right information to + * successfully authenticate at the server. + * + * @param request the request to authenticate + */ + void apply(Request request); } } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/AuthenticationStore.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/AuthenticationStore.java index afdcea697f2..2835ed73fcc 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/AuthenticationStore.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/AuthenticationStore.java @@ -18,17 +18,54 @@ package org.eclipse.jetty.client.api; +/** + * A store for {@link Authentication}s and {@link Authentication.Result}s. + */ public interface AuthenticationStore { + /** + * @param authentication the {@link Authentication} to add + */ public void addAuthentication(Authentication authentication); + /** + * @param authentication the {@link Authentication} to remove + */ public void removeAuthentication(Authentication authentication); + /** + * Returns the authentication that matches the given type (for example, "Basic" or "Digest"), + * the given request URI and the given realm. + * If no such authentication can be found, returns null. + * + * @param type the {@link Authentication} type such as "Basic" or "Digest" + * @param uri the request URI + * @param realm the authentication realm + * @return the authentication that matches the given parameters, or null + */ public Authentication findAuthentication(String type, String uri, String realm); + /** + * @param result the {@link Authentication.Result} to add + */ public void addAuthenticationResult(Authentication.Result result); - public void removeAuthenticationResults(); + /** + * @param result the {@link Authentication.Result} to remove + */ + public void removeAuthenticationResult(Authentication.Result result); - public Authentication findAuthenticationResult(String uri); + /** + * Removes all authentication results stored + */ + public void clearAuthenticationResults(); + + /** + * Returns an {@link Authentication.Result} that matches the given URI, or null if no + * {@link Authentication.Result}s match the given URI. + * + * @param uri the request URI + * @return the {@link Authentication.Result} that matches the given URI, or null + */ + public Authentication.Result findAuthenticationResult(String uri); } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Connection.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Connection.java index 1d7be57c75d..23d963ca5c8 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Connection.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Connection.java @@ -18,8 +18,25 @@ package org.eclipse.jetty.client.api; +/** + * {@link Connection} represent a connection to a {@link Destination} and allow applications to send + * requests via {@link #send(Request, Response.Listener)}. + *

+ * {@link Connection}s are normally pooled by {@link Destination}s, but unpooled {@link Connection}s + * may be created by applications that want to do their own connection management via + * {@link Destination#newConnection()}. + */ public interface Connection extends AutoCloseable { + /** + * Sends a request with an associated response listener. + *

+ * {@link Request#send(Response.Listener)} will eventually call this method to send the request. + * It is exposed to allow applications to send requests via unpooled connections. + * + * @param request the request to send + * @param listener the response listener + */ void send(Request request, Response.Listener listener); @Override diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentDecoder.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentDecoder.java index 656f490e0aa..b8dc313e7a0 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentDecoder.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentDecoder.java @@ -18,6 +18,7 @@ package org.eclipse.jetty.client.api; -public interface ContentDecoder +// TODO +interface ContentDecoder { } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentProvider.java index 0be40d0c1fc..68d626e0056 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentProvider.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentProvider.java @@ -20,7 +20,21 @@ package org.eclipse.jetty.client.api; import java.nio.ByteBuffer; +import org.eclipse.jetty.client.util.ByteBufferContentProvider; +import org.eclipse.jetty.client.util.PathContentProvider; + +/** + * {@link ContentProvider} provides a repeatable source of request content. + *

+ * Implementations should return a new "view" over the same content every time {@link #iterator()} is invoked. + *

+ * Applications should rely on utility classes such as {@link ByteBufferContentProvider} + * or {@link PathContentProvider}. + */ public interface ContentProvider extends Iterable { + /** + * @return the content length, if known, or -1 if the content length is unknown + */ long length(); } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentResponse.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentResponse.java index 0be9edfe8e4..f960db2dfe6 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentResponse.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentResponse.java @@ -18,7 +18,13 @@ package org.eclipse.jetty.client.api; +/** + * A specialized {@link Response} that can hold a limited content in memory. + */ public interface ContentResponse extends Response { + /** + * @return the response content + */ byte[] content(); } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/CookieStore.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/CookieStore.java index 6aa77072c78..7ac798c58b6 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/CookieStore.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/CookieStore.java @@ -20,13 +20,39 @@ package org.eclipse.jetty.client.api; import java.util.List; +import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.http.HttpCookie; +/** + * A store for HTTP cookies that offers methods to match cookies for a given destination and path. + * + * @see HttpClient#getCookieStore() + */ public interface CookieStore { - List getCookies(Destination destination, String path); + /** + * Returns the non-expired cookies that match the given destination and path, + * recursively matching parent paths (for the same domain) and parent domains + * (for the root path). + * + * @param destination the destination representing the domain + * @param path the request path + * @return the list of matching cookies + */ + List findCookies(Destination destination, String path); + /** + * Adds the given cookie to this store for the given destination. + * If the cookie's domain and the destination host do not match, the cookie is not added. + * + * @param destination the destination the cookie should belong to + * @param cookie the cookie to add + * @return whether the cookie has been added or not + */ boolean addCookie(Destination destination, HttpCookie cookie); + /** + * Removes all the cookies from this store. + */ void clear(); } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Destination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Destination.java index 2ec5a765e2d..3ed6524699c 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Destination.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Destination.java @@ -20,15 +20,36 @@ package org.eclipse.jetty.client.api; import java.util.concurrent.Future; +import org.eclipse.jetty.client.HttpClient; + +/** + * {@link Destination} represents the triple made of the {@link #scheme()}, the {@link #host()} + * and the {@link #port()}. + *

+ * {@link Destination} holds a pool of {@link Connection}s, but allows to create unpooled + * connections if the application wants full control over connection management via {@link #newConnection()}. + *

+ * {@link Destination}s may be obtained via {@link HttpClient#getDestination(String, String, int)} + */ public interface Destination { + /** + * @return the scheme of this destination, such as "http" or "https" + */ String scheme(); + /** + * @return the host of this destination, such as "127.0.0.1" or "google.com" + */ String host(); + /** + * @return the port of this destination such as 80 or 443 + */ int port(); + /** + * @return a future to a new, unpooled, {@link Connection} + */ Future newConnection(); - - void send(Request request, Response.Listener listener); } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java index 6b0f5a300dc..9fbb1373883 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java @@ -22,88 +22,274 @@ import java.io.IOException; import java.nio.file.Path; import java.util.concurrent.Future; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.util.StreamingResponseListener; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.util.Fields; +/** + *

{@link Request} represents a HTTP request, and offers a fluent interface to customize + * various attributes such as the path, the headers, the content, etc.

+ *

You can create {@link Request} objects via {@link HttpClient#newRequest(String)} and + * you can send them using either {@link #send()} for a blocking semantic, or + * {@link #send(Response.Listener)} for an asynchronous semantic.

+ * + * @see Response + */ public interface Request { + /** + * @return the conversation id + */ long id(); + /** + * @return the scheme of this request, such as "http" or "https" + */ String scheme(); + /** + * @param scheme the scheme of this request, such as "http" or "https" + * @return this request object + */ Request scheme(String scheme); + /** + * @return the host of this request, such as "127.0.0.1" or "google.com" + */ String host(); + /** + * @return the port of this request such as 80 or 443 + */ int port(); + /** + * @return the method of this request, such as GET or POST + */ HttpMethod method(); + /** + * @param method the method of this request, such as GET or POST + * @return this request object + */ Request method(HttpMethod method); + /** + * @return the path of this request, such as "/" + */ String path(); + /** + * @param path the path of this request, such as "/" + * @return this request object + */ Request path(String path); + /** + * @return the full URI of this request such as "http://host:port/path" + */ String uri(); + /** + * @return the HTTP version of this request, such as "HTTP/1.1" + */ HttpVersion version(); + /** + * @param version the HTTP version of this request, such as "HTTP/1.1" + * @return this request object + */ Request version(HttpVersion version); + /** + * @return the query parameters of this request + */ Fields params(); + /** + * @param name the name of the query parameter + * @param value the value of the query parameter + * @return this request object + */ Request param(String name, String value); + /** + * @return the headers of this request + */ HttpFields headers(); + /** + * @param name the name of the header + * @param value the value of the header + * @return this request object + */ Request header(String name, String value); + /** + * @return the content provider of this request + */ ContentProvider content(); - Request content(ContentProvider buffer); + /** + * @param content the content provider of this request + * @return this request object + */ + Request content(ContentProvider content); + /** + * Shortcut method to specify a file as a content for this request, with the default content type of + * "application/octect-stream". + * + * @param file the file to upload + * @return this request object + * @throws IOException if the file does not exist or cannot be read + */ Request file(Path file) throws IOException; + /** + * Shortcut method to specify a file as a content for this request, with the given content type. + * + * @param file the file to upload + * @param contentType the content type of the file + * @return this request object + * @throws IOException if the file does not exist or cannot be read + */ Request file(Path file, String contentType) throws IOException; - Request decoder(ContentDecoder decoder); +// Request decoder(ContentDecoder decoder); + /** + * @return the user agent for this request + */ String agent(); - Request agent(String userAgent); + /** + * @param agent the user agent for this request + * @return this request object + */ + Request agent(String agent); + /** + * @return the idle timeout for this request + */ long idleTimeout(); + /** + * @param timeout the idle timeout for this request + * @return this request object + */ Request idleTimeout(long timeout); + /** + * @return whether this request follows redirects + */ + boolean followRedirects(); + + /** + * @param follow whether this request follows redirects + * @return this request object + */ Request followRedirects(boolean follow); + /** + * @return the listener for request events + */ Listener listener(); + /** + * @param listener the listener for request events + * @return this request object + */ Request listener(Listener listener); + /** + * Sends this request and returns a {@link Future} that can be used to wait for the + * request and the response to be completed (either with a success or a failure). + *

+ * This method should be used when a simple blocking semantic is needed, and when it is known + * that the response content can be buffered without exceeding memory constraints. + * For example, this method is not appropriate to download big files from a server; consider using + * {@link #send(Response.Listener)} instead, passing your own {@link Response.Listener} or a utility + * listener such as {@link StreamingResponseListener}. + *

+ * The future will return when {@link Response.Listener#onComplete(Result)} is invoked. + * + * @return a {@link Future} to wait on for request and response completion + * @see Response.Listener#onComplete(Result) + */ Future send(); + /** + * Sends this request and asynchronously notifies the given listener for response events. + *

+ * This method should be used when the application needs to be notified of the various response events + * as they happen, or when the application needs to efficiently manage the response content. + * + * @param listener the listener that receives response events + */ void send(Response.Listener listener); + /** + * Attempts to abort the send of this request. + * + * @see #aborted() + */ + void abort(); + + /** + * @return whether {@link #abort()} was called + */ + boolean aborted(); + + /** + * Listener for request events + */ public interface Listener { + /** + * Callback method invoked when the request is queued, waiting to be sent + * + * @param request the request being queued + */ public void onQueued(Request request); + /** + * Callback method invoked when the request begins being processed in order to be sent. + * This is the last opportunity to modify the request. + * + * @param request the request that begins being processed + */ public void onBegin(Request request); + /** + * Callback method invoked when the request headers (and perhaps small content) have been sent. + * The request is now committed, and in transit to the server, and further modifications to the + * request may have no effect. + * @param request the request that has been committed + */ public void onHeaders(Request request); - public void onFlush(Request request, int bytes); - + /** + * Callback method invoked when the request has been successfully sent. + * + * @param request the request sent + */ public void onSuccess(Request request); + /** + * Callback method invoked when the request has failed to be sent + * @param request the request that failed + * @param failure the failure + */ public void onFailure(Request request, Throwable failure); - public static class Adapter implements Listener + /** + * An empty implementation of {@link Listener} + */ + public static class Empty implements Listener { @Override public void onQueued(Request request) @@ -120,11 +306,6 @@ public interface Request { } - @Override - public void onFlush(Request request, int bytes) - { - } - @Override public void onSuccess(Request request) { diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Response.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Response.java index f4caa38f08f..79e09752b3d 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Response.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Response.java @@ -20,38 +20,118 @@ package org.eclipse.jetty.client.api; import java.nio.ByteBuffer; +import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpVersion; +/** + *

{@link Response} represents a HTTP response and offers methods to retrieve status code, HTTP version + * and headers.

+ *

{@link Response} objects are passed as parameters to {@link Response.Listener} callbacks, or as + * future result of {@link Request#send()}.

+ *

{@link Response} objects do not contain getters for the response content, because it may be too large + * to fit into memory. + * The response content should be retrieved via {@link Response.Listener#onContent(Response, ByteBuffer) content + * events}, or via utility classes such as {@link BufferingResponseListener}.

+ */ public interface Response { + /** + * @return the response listener passed to {@link Request#send(Listener)} + */ Listener listener(); + /** + * @return the HTTP version of this response, such as "HTTP/1.1" + */ HttpVersion version(); + /** + * @return the HTTP status code of this response, such as 200 or 404 + */ int status(); + /** + * @return the HTTP reason associated to the {@link #status()} + */ String reason(); + /** + * @return the headers of this response + */ HttpFields headers(); + /** + * Attempts to abort the send of this request. + */ void abort(); + /** + * Listener for response events + */ public interface Listener { + /** + * Callback method invoked when the response line containing HTTP version, + * HTTP status code and reason has been received and parsed. + *

+ * This method is the best approximation to detect when the first bytes of the response arrived to the client. + * + * @param response the response containing the response line data + */ public void onBegin(Response response); + /** + * Callback method invoked when the response headers have been received and parsed. + * + * @param response the response containing the response line data and the headers + */ public void onHeaders(Response response); + /** + * Callback method invoked when the response content has been received. + * This method may be invoked multiple times, and the {@code content} buffer must be consumed + * before returning from this method. + * + * @param response the response containing the response line data and the headers + * @param content the content bytes received + */ public void onContent(Response response, ByteBuffer content); + /** + * Callback method invoked when the whole response has been successfully received. + * + * @param response the response containing the response line data and the headers + */ public void onSuccess(Response response); + /** + * Callback method invoked when the response has failed in the process of being received + * + * @param response the response containing data up to the point the failure happened + * @param failure the failure happened + */ public void onFailure(Response response, Throwable failure); + /** + * Callback method invoked when the request and the response have been processed, + * either successfully or not. + *

+ * The {@code result} parameter contains the request, the response, and eventual failures. + *

+ * Requests may complete after response, for example in case of big uploads that are + * discarded or read asynchronously by the server. + * This method is always invoked after {@link #onSuccess(Response)} or + * {@link #onFailure(Response, Throwable)}, and only when request indicates that it is completed. + * + * @param result the result of the request / response exchange + */ public void onComplete(Result result); - public static class Adapter implements Listener + /** + * An empty implementation of {@link Listener} + */ + public static class Empty implements Listener { @Override public void onBegin(Response response) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Result.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Result.java index ff59d9e2cf0..8ad009354a3 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Result.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Result.java @@ -18,6 +18,10 @@ package org.eclipse.jetty.client.api; +/** + * The result of a request / response exchange, containing the {@link Request}, the {@link Response} + * and eventual failures of either. + */ public class Result { private final Request request; @@ -48,21 +52,49 @@ public class Result this.responseFailure = responseFailure; } + /** + * @return the request object + */ public Request getRequest() { return request; } + /** + * @return the request failure, if any + */ + public Throwable getRequestFailure() + { + return requestFailure; + } + + /** + * @return the response object + */ public Response getResponse() { return response; } + /** + * @return the response failure, if any + */ + public Throwable getResponseFailure() + { + return responseFailure; + } + + /** + * @return whether either the response or the request failed + */ public boolean isFailed() { return getFailure() != null; } + /** + * @return the response failure, if any, otherwise the request failure, if any + */ public Throwable getFailure() { return responseFailure != null ? responseFailure : requestFailure; diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/package-info.java b/jetty-client/src/main/java/org/eclipse/jetty/client/package-info.java new file mode 100644 index 00000000000..3e4c1890950 --- /dev/null +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/package-info.java @@ -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. +// ======================================================================== +// + +/** + * This package provides APIs, utility classes and implementation class of an asynchronous HTTP client + *

+ * The core class is {@link HttpClient}, which acts as a central configuration object (for example + * for {@link HttpClient#setIdleTimeout(long) idle timeouts}, {@link HttpClient#setMaxConnectionsPerAddress(int) + * max connections per domain}, etc.) and as a factory for {@link Request} objects. + *

+ * The HTTP protocol is based on the request/response paradigm, a unit that in this implementation is called + * exchange and is represented by {@link HttpExchange}. + * An initial request may trigger a sequence of exchanges with one or more servers, called a conversation + * and represented by {@link HttpConversation}. A typical example of a conversation is a redirect, where + * upon a request for a resource URI, the server replies with a redirect (for example with the 303 status code) + * to another URI. This conversation is made of a first exchange made of the original request and its 303 response, + * and of a second exchange made of the request for the new URI and its 200 response. + *

+ * {@link HttpClient} holds a number of {@link HttpDestination destinations}, which in turn hold a number of + * pooled {@link HttpConnection connections}. + *

+ * When a request is sent, its exchange is associated to a connection, either taken from an idle queue or created + * anew, and when both the request and response are completed, the exchange is disassociated from the connection. + * Conversation may span multiple connections on different destinations, and therefore are maintained at the + * {@link HttpClient} level. + *

+ * Applications may decide to send the request and wait for the response in a blocking way, using + * {@link Request#send()}. + * Alternatively, application may ask to be notified of response events asynchronously, using + * {@link Request#send(Response.Listener)}. + */ +package org.eclipse.jetty.client; + +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.api.Response; diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/BasicAuthentication.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BasicAuthentication.java index 8d77d71f390..640b98f1c04 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/BasicAuthentication.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BasicAuthentication.java @@ -22,8 +22,10 @@ import java.io.UnsupportedEncodingException; import java.nio.charset.UnsupportedCharsetException; import org.eclipse.jetty.client.api.Authentication; +import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.B64Code; import org.eclipse.jetty.util.StringUtil; @@ -55,17 +57,48 @@ public class BasicAuthentication implements Authentication } @Override - public void authenticate(Request request) + public Result authenticate(Request request, ContentResponse response, String wwwAuthenticate, Attributes context) { String encoding = StringUtil.__ISO_8859_1; try { String value = "Basic " + B64Code.encode(user + ":" + password, encoding); - request.header(HttpHeader.AUTHORIZATION.asString(), value); + return new BasicResult(request.uri(), value); } catch (UnsupportedEncodingException x) { throw new UnsupportedCharsetException(encoding); } } + + private static class BasicResult implements Result + { + private final String uri; + private final String value; + + public BasicResult(String uri, String value) + { + this.uri = uri; + this.value = value; + } + + @Override + public String getURI() + { + return uri; + } + + @Override + public void apply(Request request) + { + if (request.uri().startsWith(uri)) + request.header(HttpHeader.AUTHORIZATION.asString(), value); + } + + @Override + public String toString() + { + return String.format("Basic authentication result for %s", uri); + } + } } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/BlockingResponseListener.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BlockingResponseListener.java new file mode 100644 index 00000000000..39d619782af --- /dev/null +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BlockingResponseListener.java @@ -0,0 +1,117 @@ +// +// ======================================================================== +// 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.util; + +import java.nio.ByteBuffer; +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.client.HttpContentResponse; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Response; +import org.eclipse.jetty.client.api.Result; + +public class BlockingResponseListener extends BufferingResponseListener implements Future +{ + private final CountDownLatch latch = new CountDownLatch(1); + private ContentResponse response; + private Throwable failure; + private volatile boolean cancelled; + + @Override + public void onBegin(Response response) + { + super.onBegin(response); + if (cancelled) + response.abort(); + } + + @Override + public void onHeaders(Response response) + { + super.onHeaders(response); + if (cancelled) + response.abort(); + } + + @Override + public void onContent(Response response, ByteBuffer content) + { + super.onContent(response, content); + if (cancelled) + response.abort(); + } + + @Override + public void onComplete(Result result) + { + super.onComplete(result); + response = new HttpContentResponse(result.getResponse(), getContent()); + failure = result.getFailure(); + latch.countDown(); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) + { + cancelled = true; + return latch.getCount() == 0; + } + + @Override + public boolean isCancelled() + { + return cancelled; + } + + @Override + public boolean isDone() + { + return latch.getCount() == 0 || isCancelled(); + } + + @Override + public ContentResponse get() throws InterruptedException, ExecutionException + { + latch.await(); + return result(); + } + + @Override + public ContentResponse get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException + { + boolean expired = !latch.await(timeout, unit); + if (expired) + throw new TimeoutException(); + return result(); + } + + private ContentResponse result() throws ExecutionException + { + if (isCancelled()) + throw (CancellationException)new CancellationException().initCause(failure); + if (failure != null) + throw new ExecutionException(failure); + return response; + } +} diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/BufferingResponseListener.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BufferingResponseListener.java index 0db2571ff23..04e426a2cb1 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/BufferingResponseListener.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BufferingResponseListener.java @@ -18,38 +18,32 @@ package org.eclipse.jetty.client.util; +import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; +import java.nio.charset.UnsupportedCharsetException; import org.eclipse.jetty.client.api.Response; -public class BufferingResponseListener extends Response.Listener.Adapter +public class BufferingResponseListener extends Response.Listener.Empty { - private final CountDownLatch latch = new CountDownLatch(1); - private final int maxCapacity; - private Response response; - private Throwable failure; - private byte[] buffer = new byte[0]; + private final int maxLength; + private volatile byte[] buffer = new byte[0]; public BufferingResponseListener() { - this(16 * 1024 * 1024); + this(2 * 1024 * 1024); } - public BufferingResponseListener(int maxCapacity) + public BufferingResponseListener(int maxLength) { - this.maxCapacity = maxCapacity; + this.maxLength = maxLength; } @Override public void onContent(Response response, ByteBuffer content) { long newLength = buffer.length + content.remaining(); - if (newLength > maxCapacity) + if (newLength > maxLength) throw new IllegalStateException("Buffering capacity exceeded"); byte[] newBuffer = new byte[(int)newLength]; @@ -58,38 +52,20 @@ public class BufferingResponseListener extends Response.Listener.Adapter buffer = newBuffer; } - @Override - public void onSuccess(Response response) - { - this.response = response; - latch.countDown(); - } - - @Override - public void onFailure(Response response, Throwable failure) - { - this.response = response; - this.failure = failure; - latch.countDown(); - } - - public Response await(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException - { - boolean expired = !latch.await(timeout, unit); - if (failure != null) - throw new ExecutionException(failure); - if (expired) - throw new TimeoutException(); - return response; - } - - public byte[] content() + public byte[] getContent() { return buffer; } - public String contentAsString(String encoding) + public String getContent(String encoding) { - return new String(content(), Charset.forName(encoding)); + try + { + return new String(getContent(), encoding); + } + catch (UnsupportedEncodingException x) + { + throw new UnsupportedCharsetException(encoding); + } } } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java new file mode 100644 index 00000000000..b21d46213d8 --- /dev/null +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/DigestAuthentication.java @@ -0,0 +1,271 @@ +// +// ======================================================================== +// 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.util; + +import java.nio.charset.Charset; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jetty.client.api.Authentication; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.util.Attributes; +import org.eclipse.jetty.util.TypeUtil; + +public class DigestAuthentication implements Authentication +{ + private static final Pattern PARAM_PATTERN = Pattern.compile("([^=]+)=(.*)"); + + private final String uri; + private final String realm; + private final String user; + private final String password; + + public DigestAuthentication(String uri, String realm, String user, String password) + { + this.uri = uri; + this.realm = realm; + this.user = user; + this.password = password; + } + + @Override + public boolean matches(String type, String uri, String realm) + { + if (!"digest".equalsIgnoreCase(type)) + return false; + + if (!uri.startsWith(this.uri)) + return false; + + return this.realm.equals(realm); + } + + @Override + public Result authenticate(Request request, ContentResponse response, String wwwAuthenticate, Attributes context) + { + // Avoid case sensitivity problems on the 'D' character + String type = "igest"; + wwwAuthenticate = wwwAuthenticate.substring(wwwAuthenticate.indexOf(type) + type.length()); + + Map params = parseParams(wwwAuthenticate); + String nonce = params.get("nonce"); + if (nonce == null || nonce.length() == 0) + return null; + String opaque = params.get("opaque"); + String algorithm = params.get("algorithm"); + if (algorithm == null) + algorithm = "MD5"; + MessageDigest digester = getMessageDigest(algorithm); + if (digester == null) + return null; + String serverQOP = params.get("qop"); + String clientQOP = null; + if (serverQOP != null) + { + List serverQOPValues = Arrays.asList(serverQOP.split(",")); + if (serverQOPValues.contains("auth")) + clientQOP = "auth"; + else if (serverQOPValues.contains("auth-int")) + clientQOP = "auth-int"; + } + + return new DigestResult(request.uri(), response.content(), realm, user, password, algorithm, nonce, clientQOP, opaque); + } + + private Map parseParams(String wwwAuthenticate) + { + Map result = new HashMap<>(); + List parts = splitParams(wwwAuthenticate); + for (String part : parts) + { + Matcher matcher = PARAM_PATTERN.matcher(part); + if (matcher.matches()) + { + String name = matcher.group(1).trim().toLowerCase(); + String value = matcher.group(2).trim(); + if (value.startsWith("\"") && value.endsWith("\"")) + value = value.substring(1, value.length() - 1); + result.put(name, value); + } + } + return result; + } + + private List splitParams(String paramString) + { + List result = new ArrayList<>(); + int start = 0; + for (int i = 0; i < paramString.length(); ++i) + { + int quotes = 0; + char ch = paramString.charAt(i); + switch (ch) + { + case '\\': + ++i; + break; + case '"': + ++quotes; + break; + case ',': + if (quotes % 2 == 0) + { + result.add(paramString.substring(start, i).trim()); + start = i + 1; + } + break; + default: + break; + } + } + result.add(paramString.substring(start, paramString.length()).trim()); + return result; + } + + private MessageDigest getMessageDigest(String algorithm) + { + try + { + return MessageDigest.getInstance(algorithm); + } + catch (NoSuchAlgorithmException x) + { + return null; + } + } + + private class DigestResult implements Result + { + private final AtomicInteger nonceCount = new AtomicInteger(); + private final String uri; + private final byte[] content; + private final String realm; + private final String user; + private final String password; + private final String algorithm; + private final String nonce; + private final String qop; + private final String opaque; + + public DigestResult(String uri, byte[] content, String realm, String user, String password, String algorithm, String nonce, String qop, String opaque) + { + this.uri = uri; + this.content = content; + this.realm = realm; + this.user = user; + this.password = password; + this.algorithm = algorithm; + this.nonce = nonce; + this.qop = qop; + this.opaque = opaque; + } + + @Override + public String getURI() + { + return uri; + } + + @Override + public void apply(Request request) + { + if (!request.uri().startsWith(uri)) + return; + + MessageDigest digester = getMessageDigest(algorithm); + if (digester == null) + return; + + Charset charset = Charset.forName("ISO-8859-1"); + String A1 = user + ":" + realm + ":" + password; + String hashA1 = toHexString(digester.digest(A1.getBytes(charset))); + + String A2 = request.method().asString() + ":" + request.uri(); + if ("auth-int".equals(qop)) + A2 += ":" + toHexString(digester.digest(content)); + String hashA2 = toHexString(digester.digest(A2.getBytes(charset))); + + String nonceCount; + String clientNonce; + String A3; + if (qop != null) + { + nonceCount = nextNonceCount(); + clientNonce = newClientNonce(); + A3 = hashA1 + ":" + nonce + ":" + nonceCount + ":" + clientNonce + ":" + qop + ":" + hashA2; + } + else + { + nonceCount = null; + clientNonce = null; + A3 = hashA1 + ":" + nonce + ":" + hashA2; + } + String hashA3 = toHexString(digester.digest(A3.getBytes(charset))); + + StringBuilder value = new StringBuilder("Digest"); + value.append(" username=\"").append(user).append("\""); + value.append(", realm=\"").append(realm).append("\""); + value.append(", nonce=\"").append(nonce).append("\""); + if (opaque != null) + value.append(", opaque=\"").append(opaque).append("\""); + value.append(", algorithm=\"").append(algorithm).append("\""); + value.append(", uri=\"").append(request.uri()).append("\""); + if (qop != null) + { + value.append(", qop=\"").append(qop).append("\""); + value.append(", nc=\"").append(nonceCount).append("\""); + value.append(", cnonce=\"").append(clientNonce).append("\""); + } + value.append(", response=\"").append(hashA3).append("\""); + + request.header(HttpHeader.AUTHORIZATION.asString(), value.toString()); + } + + private String nextNonceCount() + { + String padding = "00000000"; + String next = Integer.toHexString(nonceCount.incrementAndGet()).toLowerCase(); + return padding.substring(0, padding.length() - next.length()) + next; + } + + private String newClientNonce() + { + Random random = new Random(); + byte[] bytes = new byte[8]; + random.nextBytes(bytes); + return toHexString(bytes); + } + + private String toHexString(byte[] bytes) + { + return TypeUtil.toHexString(bytes).toLowerCase(); + } + } +} diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/StreamingResponseListener.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/StreamingResponseListener.java index 1337c1d0e48..e0402b01284 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/StreamingResponseListener.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/StreamingResponseListener.java @@ -24,7 +24,7 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jetty.client.api.Response; -public class StreamingResponseListener extends Response.Listener.Adapter +public class StreamingResponseListener extends Response.Listener.Empty { public Response get(long timeout, TimeUnit seconds) { diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpClientServerTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpClientServerTest.java index 4aeeabad4d6..48ce8e3efa7 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpClientServerTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpClientServerTest.java @@ -49,7 +49,8 @@ public class AbstractHttpClientServerTest public void start(Handler handler) throws Exception { - server = new Server(); + if (server == null) + server = new Server(); connector = new SelectChannelConnector(server); server.addConnector(connector); server.setHandler(handler); @@ -62,11 +63,12 @@ public class AbstractHttpClientServerTest } @After - public void destroy() throws Exception + public void dispose() throws Exception { if (client != null) client.stop(); if (server != null) server.stop(); + server = null; } } diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/EmptyHandler.java b/jetty-client/src/test/java/org/eclipse/jetty/client/EmptyServerHandler.java similarity index 95% rename from jetty-client/src/test/java/org/eclipse/jetty/client/EmptyHandler.java rename to jetty-client/src/test/java/org/eclipse/jetty/client/EmptyServerHandler.java index bfedac1c5a7..d5af3dd9534 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/EmptyHandler.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/EmptyServerHandler.java @@ -26,7 +26,7 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; -public class EmptyHandler extends AbstractHandler +public class EmptyServerHandler extends AbstractHandler { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java index 77ed14f2e35..7a06dca80c5 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java @@ -18,36 +18,90 @@ package org.eclipse.jetty.client; -import java.io.IOException; +import java.io.File; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.client.api.Authentication; import org.eclipse.jetty.client.api.AuthenticationStore; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.util.BasicAuthentication; +import org.eclipse.jetty.client.util.DigestAuthentication; import org.eclipse.jetty.http.HttpHeader; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.eclipse.jetty.util.B64Code; -import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.security.Authenticator; +import org.eclipse.jetty.security.ConstraintMapping; +import org.eclipse.jetty.security.ConstraintSecurityHandler; +import org.eclipse.jetty.security.HashLoginService; +import org.eclipse.jetty.security.LoginService; +import org.eclipse.jetty.security.authentication.BasicAuthenticator; +import org.eclipse.jetty.security.authentication.DigestAuthenticator; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.security.Constraint; import org.junit.Assert; import org.junit.Test; public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest { - @Test - public void test_BasicAuthentication_WithChallenge() throws Exception - { - start(new BasicAuthenticationHandler()); + private String realm = "TestRealm"; + public void startBasic(Handler handler) throws Exception + { + start(new BasicAuthenticator(), handler); + } + + public void startDigest(Handler handler) throws Exception + { + start(new DigestAuthenticator(), handler); + } + + private void start(Authenticator authenticator, Handler handler) throws Exception + { + server = new Server(); + File realmFile = MavenTestingUtils.getTestResourceFile("realm.properties"); + LoginService loginService = new HashLoginService(realm, realmFile.getAbsolutePath()); + server.addBean(loginService); + + ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler(); + + Constraint constraint = new Constraint(); + constraint.setAuthenticate(true); + constraint.setRoles(new String[]{"*"}); + ConstraintMapping mapping = new ConstraintMapping(); + mapping.setPathSpec("/*"); + mapping.setConstraint(constraint); + + securityHandler.addConstraintMapping(mapping); + securityHandler.setAuthenticator(authenticator); + securityHandler.setLoginService(loginService); + securityHandler.setStrict(false); + + securityHandler.setHandler(handler); + start(securityHandler); + } + + @Test + public void test_BasicAuthentication() throws Exception + { + startBasic(new EmptyServerHandler()); + test_Authentication(new BasicAuthentication("http://localhost:" + connector.getLocalPort(), realm, "basic", "basic")); + } + + @Test + public void test_DigestAuthentication() throws Exception + { + startDigest(new EmptyServerHandler()); + test_Authentication(new DigestAuthentication("http://localhost:" + connector.getLocalPort(), realm, "digest", "digest")); + } + + private void test_Authentication(Authentication authentication) throws Exception + { AuthenticationStore authenticationStore = client.getAuthenticationStore(); - String realm = "test"; final AtomicInteger requests = new AtomicInteger(); - Request.Listener.Adapter requestListener = new Request.Listener.Adapter() + Request.Listener.Empty requestListener = new Request.Listener.Empty() { @Override public void onSuccess(Request request) @@ -58,10 +112,7 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest client.getRequestListeners().add(requestListener); // Request without Authentication causes a 401 - Request request = client.newRequest("localhost", connector.getLocalPort()) - .path("/test") - .param("type", "Basic") - .param("realm", realm); + Request request = client.newRequest("localhost", connector.getLocalPort()).path("/test"); ContentResponse response = request.send().get(5, TimeUnit.SECONDS); Assert.assertNotNull(response); Assert.assertEquals(401, response.status()); @@ -69,11 +120,9 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest client.getRequestListeners().remove(requestListener); requests.set(0); - String user = "jetty"; - String password = "rocks"; - authenticationStore.addAuthentication(new BasicAuthentication("http://localhost:" + connector.getLocalPort(), realm, user, password)); + authenticationStore.addAuthentication(authentication); - requestListener = new Request.Listener.Adapter() + requestListener = new Request.Listener.Empty() { @Override public void onSuccess(Request request) @@ -84,7 +133,6 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest client.getRequestListeners().add(requestListener); // Request with authentication causes a 401 (no previous successful authentication) + 200 - request.param("user", user).param("password", password); response = request.send().get(5, TimeUnit.SECONDS); Assert.assertNotNull(response); Assert.assertEquals(200, response.status()); @@ -92,7 +140,7 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest client.getRequestListeners().remove(requestListener); requests.set(0); - requestListener = new Request.Listener.Adapter() + requestListener = new Request.Listener.Empty() { @Override public void onSuccess(Request request) @@ -105,65 +153,11 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest // Further requests do not trigger 401 because there is a previous successful authentication // Remove existing header to be sure it's added by the implementation request.header(HttpHeader.AUTHORIZATION.asString(), null); - response = request.send().get(555, TimeUnit.SECONDS); + response = request.send().get(5, TimeUnit.SECONDS); Assert.assertNotNull(response); Assert.assertEquals(200, response.status()); Assert.assertEquals(1, requests.get()); client.getRequestListeners().remove(requestListener); requests.set(0); } - - private class BasicAuthenticationHandler extends AbstractHandler - { - @Override - public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException - { - try - { - String type = request.getParameter("type"); - String authorization = request.getHeader(HttpHeader.AUTHORIZATION.asString()); - if (authorization == null) - { - String realm = request.getParameter("realm"); - response.setStatus(401); - switch (type) - { - case "Basic": - { - response.setHeader("WWW-Authenticate", "Basic realm=\"" + realm + "\""); - break; - } - default: - { - throw new IllegalStateException(); - } - } - } - else - { - switch (type) - { - case "Basic": - { - String user = request.getParameter("user"); - String password = request.getParameter("password"); - String expected = "Basic " + B64Code.encode(user + ":" + password); - if (!expected.equals(authorization)) - throw new IOException(expected + " != " + authorization); - IO.copy(request.getInputStream(), response.getOutputStream()); - break; - } - default: - { - throw new IllegalStateException(); - } - } - } - } - finally - { - baseRequest.setHandled(true); - } - } - } } diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientStreamTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientStreamTest.java index 8ea31a6c67a..6bc17b34895 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientStreamTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientStreamTest.java @@ -49,12 +49,12 @@ public class HttpClientStreamTest extends AbstractHttpClientServerTest output.write(kb); } - start(new EmptyHandler()); + start(new EmptyServerHandler()); final AtomicLong requestTime = new AtomicLong(); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .file(upload) - .listener(new Request.Listener.Adapter() + .listener(new Request.Listener.Empty() { @Override public void onSuccess(Request request) diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java index 8eb976960cc..e9380ef0eb1 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java @@ -57,7 +57,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest @Test public void testStoppingClosesConnections() throws Exception { - start(new EmptyHandler()); + start(new EmptyServerHandler()); String scheme = "http"; String host = "localhost"; @@ -84,14 +84,14 @@ public class HttpClientTest extends AbstractHttpClientServerTest Assert.assertEquals(0, client.getDestinations().size()); Assert.assertEquals(0, destination.getIdleConnections().size()); Assert.assertEquals(0, destination.getActiveConnections().size()); - Assert.assertEquals(0, client.getCookieStore().getCookies(destination, path).size()); + Assert.assertEquals(0, client.getCookieStore().findCookies(destination, path).size()); Assert.assertFalse(connection.getEndPoint().isOpen()); } @Test public void test_DestinationCount() throws Exception { - start(new EmptyHandler()); + start(new EmptyServerHandler()); String scheme = "http"; String host = "localhost"; @@ -111,7 +111,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest @Test public void test_GET_ResponseWithoutContent() throws Exception { - start(new EmptyHandler()); + start(new EmptyServerHandler()); Response response = client.GET("http://localhost:" + connector.getLocalPort()).get(5, TimeUnit.SECONDS); @@ -212,14 +212,14 @@ public class HttpClientTest extends AbstractHttpClientServerTest @Test public void test_QueuedRequest_IsSent_WhenPreviousRequestSucceeded() throws Exception { - start(new EmptyHandler()); + start(new EmptyServerHandler()); client.setMaxConnectionsPerAddress(1); final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch successLatch = new CountDownLatch(2); client.newRequest("http://localhost:" + connector.getLocalPort()) - .listener(new org.eclipse.jetty.client.api.Request.Listener.Adapter() + .listener(new org.eclipse.jetty.client.api.Request.Listener.Empty() { @Override public void onBegin(org.eclipse.jetty.client.api.Request request) @@ -234,7 +234,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest } } }) - .send(new Response.Listener.Adapter() + .send(new Response.Listener.Empty() { @Override public void onSuccess(Response response) @@ -245,7 +245,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest }); client.newRequest("http://localhost:" + connector.getLocalPort()) - .listener(new org.eclipse.jetty.client.api.Request.Listener.Adapter() + .listener(new org.eclipse.jetty.client.api.Request.Listener.Empty() { @Override public void onQueued(org.eclipse.jetty.client.api.Request request) @@ -253,7 +253,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest latch.countDown(); } }) - .send(new Response.Listener.Adapter() + .send(new Response.Listener.Empty() { @Override public void onSuccess(Response response) @@ -270,7 +270,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest @Test public void test_QueuedRequest_IsSent_WhenPreviousRequestClosedConnection() throws Exception { - start(new EmptyHandler()); + start(new EmptyServerHandler()); client.setMaxConnectionsPerAddress(1); final long idleTimeout = 1000; @@ -278,7 +278,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest final CountDownLatch latch = new CountDownLatch(3); client.newRequest("http://localhost:" + connector.getLocalPort()) - .listener(new org.eclipse.jetty.client.api.Request.Listener.Adapter() + .listener(new org.eclipse.jetty.client.api.Request.Listener.Empty() { @Override public void onBegin(org.eclipse.jetty.client.api.Request request) @@ -299,7 +299,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest latch.countDown(); } }) - .send(new Response.Listener.Adapter() + .send(new Response.Listener.Empty() { @Override public void onFailure(Response response, Throwable failure) @@ -309,7 +309,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest }); client.newRequest("http://localhost:" + connector.getLocalPort()) - .send(new Response.Listener.Adapter() + .send(new Response.Listener.Empty() { @Override public void onSuccess(Response response) @@ -326,7 +326,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest @Test public void test_ExchangeIsComplete_OnlyWhenBothRequestAndResponseAreComplete() throws Exception { - start(new EmptyHandler()); + start(new EmptyServerHandler()); // Prepare a big file to upload Path targetTestsDir = MavenTestingUtils.getTargetTestingDir().toPath(); @@ -345,7 +345,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest final AtomicLong responseTime = new AtomicLong(); client.newRequest("localhost", connector.getLocalPort()) .file(file) - .listener(new org.eclipse.jetty.client.api.Request.Listener.Adapter() + .listener(new org.eclipse.jetty.client.api.Request.Listener.Empty() { @Override public void onSuccess(org.eclipse.jetty.client.api.Request request) @@ -354,7 +354,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest latch.countDown(); } }) - .send(new Response.Listener.Adapter() + .send(new Response.Listener.Empty() { @Override public void onSuccess(Response response) @@ -414,7 +414,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest return Arrays.asList(ByteBuffer.allocate(chunkSize), null).iterator(); } }) - .send(new Response.Listener.Adapter() + .send(new Response.Listener.Empty() { @Override public void onComplete(Result result) @@ -429,13 +429,13 @@ public class HttpClientTest extends AbstractHttpClientServerTest @Test public void test_ExchangeIsComplete_WhenRequestFails_WithNoResponse() throws Exception { - start(new EmptyHandler()); + start(new EmptyServerHandler()); final CountDownLatch latch = new CountDownLatch(1); final String host = "localhost"; final int port = connector.getLocalPort(); client.newRequest(host, port) - .listener(new org.eclipse.jetty.client.api.Request.Listener.Adapter() + .listener(new org.eclipse.jetty.client.api.Request.Listener.Empty() { @Override public void onBegin(org.eclipse.jetty.client.api.Request request) @@ -444,7 +444,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest destination.getActiveConnections().peek().close(); } }) - .send(new Response.Listener.Adapter() + .send(new Response.Listener.Empty() { @Override public void onComplete(Result result) diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java index 19f97bdbb03..5746d4a6d16 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java @@ -42,7 +42,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest @Test public void test_SuccessfulRequest_ReturnsConnection() throws Exception { - start(new EmptyHandler()); + start(new EmptyServerHandler()); String scheme = "http"; String host = "localhost"; @@ -58,7 +58,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest final CountDownLatch headersLatch = new CountDownLatch(1); final CountDownLatch successLatch = new CountDownLatch(3); client.newRequest(host, port) - .listener(new Request.Listener.Adapter() + .listener(new Request.Listener.Empty() { @Override public void onSuccess(Request request) @@ -66,7 +66,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest successLatch.countDown(); } }) - .send(new Response.Listener.Adapter() + .send(new Response.Listener.Empty() { @Override public void onHeaders(Response response) @@ -100,7 +100,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest @Test public void test_FailedRequest_RemovesConnection() throws Exception { - start(new EmptyHandler()); + start(new EmptyServerHandler()); String scheme = "http"; String host = "localhost"; @@ -115,7 +115,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest final CountDownLatch headersLatch = new CountDownLatch(1); final CountDownLatch failureLatch = new CountDownLatch(2); - client.newRequest(host, port).listener(new Request.Listener.Adapter() + client.newRequest(host, port).listener(new Request.Listener.Empty() { @Override public void onBegin(Request request) @@ -129,7 +129,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest { failureLatch.countDown(); } - }).send(new Response.Listener.Adapter() + }).send(new Response.Listener.Empty() { @Override public void onComplete(Result result) @@ -151,7 +151,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest @Test public void test_BadRequest_RemovesConnection() throws Exception { - start(new EmptyHandler()); + start(new EmptyServerHandler()); String scheme = "http"; String host = "localhost"; @@ -166,7 +166,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest final CountDownLatch successLatch = new CountDownLatch(3); client.newRequest(host, port) - .listener(new Request.Listener.Adapter() + .listener(new Request.Listener.Empty() { @Override public void onBegin(Request request) @@ -181,7 +181,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest successLatch.countDown(); } }) - .send(new Response.Listener.Adapter() + .send(new Response.Listener.Empty() { @Override public void onSuccess(Response response) @@ -209,7 +209,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest @Test public void test_ConnectionFailure_RemovesConnection() throws Exception { - start(new EmptyHandler()); + start(new EmptyServerHandler()); String scheme = "http"; String host = "localhost"; @@ -226,7 +226,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest final CountDownLatch failureLatch = new CountDownLatch(2); client.newRequest(host, port) - .listener(new Request.Listener.Adapter() + .listener(new Request.Listener.Empty() { @Override public void onFailure(Request request, Throwable failure) @@ -234,7 +234,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest failureLatch.countDown(); } }) - .send(new Response.Listener.Adapter() + .send(new Response.Listener.Empty() { @Override public void onComplete(Result result) @@ -276,7 +276,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest final CountDownLatch latch = new CountDownLatch(1); client.newRequest(host, port) - .send(new Response.Listener.Adapter() + .send(new Response.Listener.Empty() { @Override public void onComplete(Result result) @@ -321,7 +321,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest final CountDownLatch latch = new CountDownLatch(1); client.newRequest(host, port) .content(new ByteBufferContentProvider(ByteBuffer.allocate(16 * 1024 * 1024))) - .send(new Response.Listener.Adapter() + .send(new Response.Listener.Empty() { @Override public void onComplete(Result result) diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpCookieStoreTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpCookieStoreTest.java index 1a8e54f9225..b616dd18449 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpCookieStoreTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpCookieStoreTest.java @@ -37,7 +37,7 @@ public class HttpCookieStoreTest Destination destination = new HttpDestination(client, "http", "localhost", 80); Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1"))); - List result = cookies.getCookies(destination, "/"); + List result = cookies.findCookies(destination, "/"); Assert.assertNotNull(result); Assert.assertEquals(1, result.size()); HttpCookie cookie = result.get(0); @@ -52,7 +52,7 @@ public class HttpCookieStoreTest Destination destination = new HttpDestination(client, "http", "localhost", 80); Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", "child.localhost", "/"))); - List result = cookies.getCookies(destination, "/"); + List result = cookies.findCookies(destination, "/"); Assert.assertNotNull(result); Assert.assertEquals(1, result.size()); HttpCookie cookie = result.get(0); @@ -75,7 +75,7 @@ public class HttpCookieStoreTest Destination destination = new HttpDestination(client, "http", "localhost", 80); Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", null, "/path"))); - List result = cookies.getCookies(destination, "/"); + List result = cookies.findCookies(destination, "/"); Assert.assertNotNull(result); Assert.assertEquals(0, result.size()); } @@ -87,7 +87,7 @@ public class HttpCookieStoreTest Destination destination = new HttpDestination(client, "http", "localhost", 80); Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", null, "/"))); - List result = cookies.getCookies(destination, "/path"); + List result = cookies.findCookies(destination, "/path"); Assert.assertNotNull(result); Assert.assertEquals(1, result.size()); HttpCookie cookie = result.get(0); @@ -108,7 +108,7 @@ public class HttpCookieStoreTest Destination grandChildDestination = new HttpDestination(client, "http", "grand.child.localhost.org", 80); Assert.assertTrue(cookies.addCookie(grandChildDestination, new HttpCookie("b", "2", null, "/"))); - List result = cookies.getCookies(grandChildDestination, "/path"); + List result = cookies.findCookies(grandChildDestination, "/path"); Assert.assertNotNull(result); Assert.assertEquals(2, result.size()); } @@ -120,7 +120,7 @@ public class HttpCookieStoreTest Destination destination = new HttpDestination(client, "http", "localhost.org", 80); Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", null, "/", 0, false, false))); - List result = cookies.getCookies(destination, "/"); + List result = cookies.findCookies(destination, "/"); Assert.assertNotNull(result); Assert.assertEquals(0, result.size()); } @@ -132,7 +132,7 @@ public class HttpCookieStoreTest Destination destination = new HttpDestination(client, "http", "localhost.org", 80); Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", null, "/", -1, false, true))); - List result = cookies.getCookies(destination, "/"); + List result = cookies.findCookies(destination, "/"); Assert.assertNotNull(result); Assert.assertEquals(0, result.size()); } @@ -145,6 +145,6 @@ public class HttpCookieStoreTest Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", null, "/", -1, false, true))); cookies.clear(); - Assert.assertEquals(0, cookies.getCookies(destination, "/").size()); + Assert.assertEquals(0, cookies.findCookies(destination, "/").size()); } } diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpCookieTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpCookieTest.java index c79054096cf..b1cbf7c6e1f 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpCookieTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpCookieTest.java @@ -59,7 +59,7 @@ public class HttpCookieTest extends AbstractHttpClientServerTest Assert.assertEquals(200, response.status()); Destination destination = client.getDestination(scheme, host, port); - List cookies = client.getCookieStore().getCookies(destination, path); + List cookies = client.getCookieStore().findCookies(destination, path); Assert.assertNotNull(cookies); Assert.assertEquals(1, cookies.size()); HttpCookie cookie = cookies.get(0); diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpDestinationTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpDestinationTest.java index 2d57e34949c..9cebbf403bc 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpDestinationTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpDestinationTest.java @@ -32,7 +32,7 @@ public class HttpDestinationTest extends AbstractHttpClientServerTest @Before public void init() throws Exception { - start(new EmptyHandler()); + start(new EmptyServerHandler()); } @Test diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpReceiverTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpReceiverTest.java index 07f300de06d..d8a8ef4b817 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpReceiverTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpReceiverTest.java @@ -26,7 +26,7 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jetty.client.api.Response; -import org.eclipse.jetty.client.util.BufferingResponseListener; +import org.eclipse.jetty.client.util.BlockingResponseListener; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpVersion; @@ -66,6 +66,7 @@ public class HttpReceiverTest HttpExchange exchange = new HttpExchange(conversation, connection, null, listener); conversation.exchanges().offer(exchange); connection.setExchange(exchange); + exchange.requestComplete(true); return exchange; } @@ -78,7 +79,7 @@ public class HttpReceiverTest "\r\n"); final AtomicReference responseRef = new AtomicReference<>(); final CountDownLatch latch = new CountDownLatch(1); - HttpExchange exchange = newExchange(new Response.Listener.Adapter() + HttpExchange exchange = newExchange(new Response.Listener.Empty() { @Override public void onSuccess(Response response) @@ -110,11 +111,11 @@ public class HttpReceiverTest "Content-length: " + content.length() + "\r\n" + "\r\n" + content); - BufferingResponseListener listener = new BufferingResponseListener(); + BlockingResponseListener listener = new BlockingResponseListener(); HttpExchange exchange = newExchange(listener); exchange.receive(); - Response response = listener.await(5, TimeUnit.SECONDS); + Response response = listener.get(5, TimeUnit.SECONDS); Assert.assertNotNull(response); Assert.assertEquals(200, response.status()); Assert.assertEquals("OK", response.reason()); @@ -123,7 +124,7 @@ public class HttpReceiverTest Assert.assertNotNull(headers); Assert.assertEquals(1, headers.size()); Assert.assertEquals(String.valueOf(content.length()), headers.get(HttpHeader.CONTENT_LENGTH)); - String received = listener.contentAsString("UTF-8"); + String received = listener.getContent("UTF-8"); Assert.assertEquals(content, received); } @@ -137,7 +138,7 @@ public class HttpReceiverTest "Content-length: " + (content1.length() + content2.length()) + "\r\n" + "\r\n" + content1); - BufferingResponseListener listener = new BufferingResponseListener(); + BlockingResponseListener listener = new BlockingResponseListener(); HttpExchange exchange = newExchange(listener); exchange.receive(); endPoint.setInputEOF(); @@ -145,7 +146,7 @@ public class HttpReceiverTest try { - listener.await(5, TimeUnit.SECONDS); + listener.get(5, TimeUnit.SECONDS); Assert.fail(); } catch (ExecutionException e) @@ -161,7 +162,7 @@ public class HttpReceiverTest "HTTP/1.1 200 OK\r\n" + "Content-length: 1\r\n" + "\r\n"); - BufferingResponseListener listener = new BufferingResponseListener(); + BlockingResponseListener listener = new BlockingResponseListener(); HttpExchange exchange = newExchange(listener); exchange.receive(); // Simulate an idle timeout @@ -169,7 +170,7 @@ public class HttpReceiverTest try { - listener.await(5, TimeUnit.SECONDS); + listener.get(5, TimeUnit.SECONDS); Assert.fail(); } catch (ExecutionException e) @@ -185,13 +186,13 @@ public class HttpReceiverTest "HTTP/1.1 200 OK\r\n" + "Content-length: A\r\n" + "\r\n"); - BufferingResponseListener listener = new BufferingResponseListener(); + BlockingResponseListener listener = new BlockingResponseListener(); HttpExchange exchange = newExchange(listener); exchange.receive(); try { - listener.await(5, TimeUnit.SECONDS); + listener.get(5, TimeUnit.SECONDS); Assert.fail(); } catch (ExecutionException e) diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpRequestAbortTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpRequestAbortTest.java new file mode 100644 index 00000000000..a6c15f85246 --- /dev/null +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpRequestAbortTest.java @@ -0,0 +1,188 @@ +// +// ======================================================================== +// 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.IOException; +import java.nio.ByteBuffer; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.util.ByteBufferContentProvider; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.util.IO; +import org.junit.Assert; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class HttpRequestAbortTest extends AbstractHttpClientServerTest +{ + @Test + public void testAbortOnQueued() throws Exception + { + start(new EmptyServerHandler()); + + final AtomicBoolean begin = new AtomicBoolean(); + try + { + client.newRequest("localhost", connector.getLocalPort()) + .listener(new Request.Listener.Empty() + { + @Override + public void onQueued(Request request) + { + request.abort(); + } + + @Override + public void onBegin(Request request) + { + begin.set(true); + } + }) + .send().get(5, TimeUnit.SECONDS); + fail(); + } + catch (ExecutionException x) + { + HttpRequestException xx = (HttpRequestException)x.getCause(); + Request request = xx.getRequest(); + Assert.assertNotNull(request); + Assert.assertFalse(begin.get()); + } + } + + @Test + public void testAbortOnBegin() throws Exception + { + start(new EmptyServerHandler()); + + final AtomicBoolean headers = new AtomicBoolean(); + try + { + client.newRequest("localhost", connector.getLocalPort()) + .listener(new Request.Listener.Empty() + { + @Override + public void onBegin(Request request) + { + request.abort(); + } + + @Override + public void onHeaders(Request request) + { + headers.set(true); + } + }) + .send().get(5, TimeUnit.SECONDS); + fail(); + } + catch (ExecutionException x) + { + HttpRequestException xx = (HttpRequestException)x.getCause(); + Request request = xx.getRequest(); + Assert.assertNotNull(request); + Assert.assertFalse(headers.get()); + } + } + + @Test + public void testAbortOnHeaders() throws Exception + { + start(new EmptyServerHandler()); + + ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) + .listener(new Request.Listener.Empty() + { + @Override + public void onHeaders(Request request) + { + // Too late to abort + request.abort(); + } + }) + .send().get(5, TimeUnit.SECONDS); + assertEquals(200, response.status()); + } + + @Test + public void testAbortOnHeadersWithContent() throws Exception + { + final AtomicReference failure = new AtomicReference<>(); + 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); + IO.copy(request.getInputStream(), response.getOutputStream()); + } + catch (IOException x) + { + failure.set(x); + throw x; + } + } + }); + + // Test can behave in 2 ways: + // A) if the request is failed before the request arrived, then we get an ExecutionException + // B) if the request is failed after the request arrived, then we get a 500 + try + { + ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) + .listener(new Request.Listener.Empty() + { + @Override + public void onHeaders(Request request) + { + request.abort(); + } + }) + .content(new ByteBufferContentProvider(ByteBuffer.wrap(new byte[]{0}), ByteBuffer.wrap(new byte[]{1})) + { + @Override + public long length() + { + return -1; + } + }) + .send().get(5, TimeUnit.SECONDS); + Assert.assertNotNull(failure.get()); + assertEquals(500, response.status()); + } + catch (ExecutionException x) + { + HttpRequestException xx = (HttpRequestException)x.getCause(); + Request request = xx.getRequest(); + Assert.assertNotNull(request); + } + } +} diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseAbortTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseAbortTest.java new file mode 100644 index 00000000000..30a5dbb5cca --- /dev/null +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseAbortTest.java @@ -0,0 +1,169 @@ +// +// ======================================================================== +// 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.IOException; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import javax.servlet.ServletException; +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.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.junit.Assert; +import org.junit.Test; + +public class HttpResponseAbortTest extends AbstractHttpClientServerTest +{ + @Test + public void testAbortOnBegin() throws Exception + { + start(new EmptyServerHandler()); + + final CountDownLatch latch = new CountDownLatch(1); + client.newRequest("localhost", connector.getLocalPort()) + .send(new Response.Listener.Empty() + { + @Override + public void onBegin(Response response) + { + response.abort(); + } + + @Override + public void onComplete(Result result) + { + Assert.assertTrue(result.isFailed()); + latch.countDown(); + } + }); + Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); + } + + @Test + public void testAbortOnHeaders() throws Exception + { + start(new EmptyServerHandler()); + + final CountDownLatch latch = new CountDownLatch(1); + client.newRequest("localhost", connector.getLocalPort()) + .send(new Response.Listener.Empty() + { + @Override + public void onHeaders(Response response) + { + response.abort(); + } + + @Override + public void onComplete(Result result) + { + Assert.assertTrue(result.isFailed()); + latch.countDown(); + } + }); + Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); + } + + @Test + public void testAbortOnContent() throws Exception + { + start(new AbstractHandler() + { + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + try + { + baseRequest.setHandled(true); + OutputStream output = response.getOutputStream(); + output.write(1); + output.flush(); + output.write(2); + output.flush(); + } + catch (IOException ignored) + { + // The client may have already closed, and we'll get an exception here, but it's expected + } + } + }); + + final CountDownLatch latch = new CountDownLatch(1); + client.newRequest("localhost", connector.getLocalPort()) + .send(new Response.Listener.Empty() + { + @Override + public void onContent(Response response, ByteBuffer content) + { + response.abort(); + } + + @Override + public void onComplete(Result result) + { + Assert.assertTrue(result.isFailed()); + latch.countDown(); + } + }); + Assert.assertTrue(latch.await(555, TimeUnit.SECONDS)); + } + + @Test(expected = CancellationException.class) + public void testCancelFuture() throws Exception + { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference> ref = new AtomicReference<>(); + start(new AbstractHandler() + { + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + try + { + latch.await(5, TimeUnit.SECONDS); + baseRequest.setHandled(true); + ref.get().cancel(true); + OutputStream output = response.getOutputStream(); + output.write(new byte[]{0, 1, 2, 3, 4, 5, 6, 7}); + } + catch (InterruptedException x) + { + throw new InterruptedIOException(); + } + } + }); + + Future future = client.newRequest("localhost", connector.getLocalPort()).send(); + ref.set(future); + latch.countDown(); + + future.get(5, TimeUnit.SECONDS); + } +} diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpSenderTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpSenderTest.java index 7a25877e952..9a5c0ec891c 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpSenderTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpSenderTest.java @@ -60,7 +60,7 @@ public class HttpSenderTest Request request = client.newRequest(URI.create("http://localhost/")); final CountDownLatch headersLatch = new CountDownLatch(1); final CountDownLatch successLatch = new CountDownLatch(1); - request.listener(new Request.Listener.Adapter() + request.listener(new Request.Listener.Empty() { @Override public void onHeaders(Request request) @@ -121,7 +121,7 @@ public class HttpSenderTest HttpConnection connection = new HttpConnection(client, endPoint, destination); Request request = client.newRequest(URI.create("http://localhost/")); final CountDownLatch failureLatch = new CountDownLatch(2); - request.listener(new Request.Listener.Adapter() + request.listener(new Request.Listener.Empty() { @Override public void onFailure(Request request, Throwable x) @@ -129,7 +129,7 @@ public class HttpSenderTest failureLatch.countDown(); } }); - connection.send(request, new Response.Listener.Adapter() + connection.send(request, new Response.Listener.Empty() { @Override public void onComplete(Result result) @@ -150,7 +150,7 @@ public class HttpSenderTest HttpConnection connection = new HttpConnection(client, endPoint, destination); Request request = client.newRequest(URI.create("http://localhost/")); final CountDownLatch failureLatch = new CountDownLatch(2); - request.listener(new Request.Listener.Adapter() + request.listener(new Request.Listener.Empty() { @Override public void onFailure(Request request, Throwable x) @@ -158,7 +158,7 @@ public class HttpSenderTest failureLatch.countDown(); } }); - connection.send(request, new Response.Listener.Adapter() + connection.send(request, new Response.Listener.Empty() { @Override public void onComplete(Result result) @@ -188,7 +188,7 @@ public class HttpSenderTest request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content.getBytes("UTF-8")))); final CountDownLatch headersLatch = new CountDownLatch(1); final CountDownLatch successLatch = new CountDownLatch(1); - request.listener(new Request.Listener.Adapter() + request.listener(new Request.Listener.Empty() { @Override public void onHeaders(Request request) @@ -223,7 +223,7 @@ public class HttpSenderTest request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content1.getBytes("UTF-8")), ByteBuffer.wrap(content2.getBytes("UTF-8")))); final CountDownLatch headersLatch = new CountDownLatch(1); final CountDownLatch successLatch = new CountDownLatch(1); - request.listener(new Request.Listener.Adapter() + request.listener(new Request.Listener.Empty() { @Override public void onHeaders(Request request) @@ -265,7 +265,7 @@ public class HttpSenderTest }); final CountDownLatch headersLatch = new CountDownLatch(1); final CountDownLatch successLatch = new CountDownLatch(1); - request.listener(new Request.Listener.Adapter() + request.listener(new Request.Listener.Empty() { @Override public void onHeaders(Request request) diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/api/Usage.java b/jetty-client/src/test/java/org/eclipse/jetty/client/api/Usage.java index d3babdf8ea8..55dbec3e87c 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/api/Usage.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/api/Usage.java @@ -27,7 +27,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.client.util.BufferingResponseListener; +import org.eclipse.jetty.client.util.BlockingResponseListener; import org.eclipse.jetty.client.util.PathContentProvider; import org.eclipse.jetty.client.util.StreamingResponseListener; import org.eclipse.jetty.http.HttpCookie; @@ -68,7 +68,7 @@ public class Usage .param("a", "b") .header("X-Header", "Y-value") .agent("Jetty HTTP Client") - .decoder(null) +// .decoder(null) .content(null) .idleTimeout(5000L); Future responseFuture = request.send(); @@ -82,7 +82,7 @@ public class Usage HttpClient client = new HttpClient(); final AtomicReference responseRef = new AtomicReference<>(); final CountDownLatch latch = new CountDownLatch(1); - client.newRequest("localhost", 8080).send(new Response.Listener.Adapter() + client.newRequest("localhost", 8080).send(new Response.Listener.Empty() { @Override public void onSuccess(Response response) @@ -102,7 +102,7 @@ public class Usage { HttpClient client = new HttpClient(); Response response = client.newRequest("localhost", 8080) - .listener(new Request.Listener.Adapter() + .listener(new Request.Listener.Empty() { @Override public void onSuccess(Request request) @@ -119,9 +119,9 @@ public class Usage try (Connection connection = client.getDestination("http", "localhost", 8080).newConnection().get(5, TimeUnit.SECONDS)) { Request request = client.newRequest("localhost", 8080); - BufferingResponseListener listener = new BufferingResponseListener(); + BlockingResponseListener listener = new BlockingResponseListener(); connection.send(request, listener); - Response response = listener.await(5, TimeUnit.SECONDS); + Response response = listener.get(5, TimeUnit.SECONDS); Assert.assertNotNull(response); Assert.assertEquals(200, response.status()); } diff --git a/jetty-client/src/test/resources/realm.properties b/jetty-client/src/test/resources/realm.properties new file mode 100644 index 00000000000..54ace472cb6 --- /dev/null +++ b/jetty-client/src/test/resources/realm.properties @@ -0,0 +1,3 @@ +# Format is :, +basic:basic +digest:digest diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml index b77c9dd653d..a348cfe27d0 100644 --- a/jetty-distribution/pom.xml +++ b/jetty-distribution/pom.xml @@ -349,6 +349,12 @@ ${project.version} + + org.eclipse.jetty + jetty-plus + ${project.version} + + org.eclipse.jetty jetty-annotations diff --git a/jetty-distribution/src/main/resources/start.ini b/jetty-distribution/src/main/resources/start.ini index b86d24d2c77..e9dfa9abd28 100644 --- a/jetty-distribution/src/main/resources/start.ini +++ b/jetty-distribution/src/main/resources/start.ini @@ -51,7 +51,7 @@ # for a full listing do # java -jar start.jar --list-options #----------------------------------------------------------- -OPTIONS=Server,jsp,resources,websocket,ext,plus +OPTIONS=Server,jsp,resources,websocket,ext #----------------------------------------------------------- @@ -64,7 +64,6 @@ etc/jetty.xml # etc/jetty-ssl.xml # etc/jetty-requestlog.xml etc/jetty-deploy.xml -#etc/jetty-overlay.xml etc/jetty-webapps.xml etc/jetty-contexts.xml etc/jetty-testrealm.xml diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java index 9c9862f8d52..3200d0edb36 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java @@ -47,6 +47,8 @@ import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; // TODO: Make this class inherit from oej.util.Fields +// TODO move this class to jetty-http? + /** * HTTP Fields. A collection of HTTP header and or Trailer fields. @@ -787,11 +789,11 @@ public class HttpFields implements Iterable // Format value and params StringBuilder buf = new StringBuilder(128); String name_value_params; - boolean quoted = QuotedStringTokenizer.quoteIfNeeded(buf, name, delim); + QuotedStringTokenizer.quoteIfNeeded(buf, name, delim); buf.append('='); String start=buf.toString(); if (value != null && value.length() > 0) - quoted|=QuotedStringTokenizer.quoteIfNeeded(buf, value, delim); + QuotedStringTokenizer.quoteIfNeeded(buf, value, delim); if (path != null && path.length() > 0) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java index 2db27b62bc5..cc1d00be436 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java @@ -59,9 +59,9 @@ public class HttpParser CLOSED }; - private final HttpHandler _handler; - private final RequestHandler _requestHandler; - private final ResponseHandler _responseHandler; + private final HttpHandler _handler; + private final RequestHandler _requestHandler; + private final ResponseHandler _responseHandler; private final int _maxHeaderBytes; private HttpHeader _header; private String _headerString; @@ -91,19 +91,19 @@ public class HttpParser private final Utf8StringBuilder _utf8=new Utf8StringBuilder(); /* ------------------------------------------------------------------------------- */ - public HttpParser(RequestHandler handler) + public HttpParser(RequestHandler handler) { this(handler,-1); } /* ------------------------------------------------------------------------------- */ - public HttpParser(ResponseHandler handler) + public HttpParser(ResponseHandler handler) { this(handler,-1); } /* ------------------------------------------------------------------------------- */ - public HttpParser(RequestHandler handler,int maxHeaderBytes) + public HttpParser(RequestHandler handler,int maxHeaderBytes) { _handler=handler; _requestHandler=handler; @@ -112,7 +112,7 @@ public class HttpParser } /* ------------------------------------------------------------------------------- */ - public HttpParser(ResponseHandler handler,int maxHeaderBytes) + public HttpParser(ResponseHandler handler,int maxHeaderBytes) { _handler=handler; _requestHandler=null; @@ -931,12 +931,14 @@ public class HttpParser case CLOSED: if (BufferUtil.hasContent(buffer)) { - _headerBytes+=buffer.remaining(); + int len=buffer.remaining(); + _headerBytes+=len; if (_headerBytes>_maxHeaderBytes) { + Thread.sleep(100); String chars = BufferUtil.toDetailString(buffer); BufferUtil.clear(buffer); - throw new IllegalStateException(this+" data when CLOSED: "+chars); + throw new IllegalStateException(String.format("%s %d/%d data when CLOSED:%s",this,len,_headerBytes,chars)); } BufferUtil.clear(buffer); } @@ -1109,6 +1111,11 @@ public class HttpParser } break; } + case CLOSED: + { + BufferUtil.clear(buffer); + return false; + } } } @@ -1157,7 +1164,7 @@ public class HttpParser case CLOSED: break; - + default: setState(State.END); if (!_headResponse) @@ -1182,6 +1189,7 @@ public class HttpParser } setState(State.CLOSED); _endOfContent=EndOfContent.UNKNOWN_CONTENT; + _contentLength=-1; _contentPosition=0; _responseStatus=0; _headerBytes=0; @@ -1194,6 +1202,7 @@ public class HttpParser // reset state setState(State.START); _endOfContent=EndOfContent.UNKNOWN_CONTENT; + _contentLength=-1; _contentPosition=0; _responseStatus=0; _contentChunk=null; @@ -1204,8 +1213,6 @@ public class HttpParser /* ------------------------------------------------------------------------------- */ private void setState(State state) { - if (_state==State.CLOSED && state==State.END) - new Throwable().printStackTrace(); _state=state; } diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java index aeebc0c6140..89f5bd4c292 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java @@ -115,9 +115,9 @@ public class HttpFieldsTest header.put("name:2", "value:\r\n2"); ByteBuffer buffer = BufferUtil.allocate(1024); - buffer.clear(); + BufferUtil.flipToFill(buffer); header.putTo(buffer); - buffer.flip(); + BufferUtil.flipToFlush(buffer,0); String out = BufferUtil.toString(buffer); assertThat(out,containsString("name0: value??0")); assertThat(out,containsString("name??1: value1")); @@ -134,9 +134,9 @@ public class HttpFieldsTest header.put("CONTENT-ENCODING", "gZIP"); ByteBuffer buffer = BufferUtil.allocate(1024); - buffer.clear(); + BufferUtil.flipToFill(buffer); header.putTo(buffer); - buffer.flip(); + BufferUtil.flipToFlush(buffer,0); String out = BufferUtil.toString(buffer); Assert.assertThat(out,Matchers.containsString(HttpHeader.CONNECTION+": "+HttpHeaderValue.KEEP_ALIVE)); diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java index abe1e1f7c3f..cb62efdbcb3 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java @@ -45,6 +45,8 @@ public abstract class AbstractConnection implements Connection private enum State {IDLE,INTERESTED,FILLING,FILLING_INTERESTED}; private final AtomicReference _state = new AtomicReference<>(State.IDLE); + private int _inputBufferSize=8192; + public AbstractConnection(EndPoint endp, Executor executor) { this(endp, executor, true); @@ -117,6 +119,16 @@ public abstract class AbstractConnection implements Connection } }; } + + public int getInputBufferSize() + { + return _inputBufferSize; + } + + public void setInputBufferSize(int inputBufferSize) + { + _inputBufferSize = inputBufferSize; + } public Executor getExecutor() { @@ -146,7 +158,6 @@ public abstract class AbstractConnection implements Connection break; case FILLING: - if (_state.compareAndSet(State.FILLING,State.FILLING_INTERESTED)) break loop; break; diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java index 8abdc2287bc..44177a76975 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java @@ -159,12 +159,6 @@ public abstract class AbstractEndPoint implements EndPoint _writeFlusher.write(context, callback, buffers); } - @Override - public boolean isBufferingOutput() - { - return false; - } - protected abstract void onIncompleteFlush(); protected abstract boolean needsFill() throws IOException; diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java index e29895c93f0..6c94ac8c658 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java @@ -321,14 +321,15 @@ public class ByteArrayEndPoint extends AbstractEndPoint * @see org.eclipse.io.EndPoint#flush(org.eclipse.io.Buffer, org.eclipse.io.Buffer, org.eclipse.io.Buffer) */ @Override - public int flush(ByteBuffer... buffers) throws IOException + public boolean flush(ByteBuffer... buffers) throws IOException { if (_closed) throw new IOException("CLOSED"); if (_oshut) throw new IOException("OSHUT"); - int flushed=0; + boolean flushed=true; + boolean idle=true; for (ByteBuffer b : buffers) { @@ -345,13 +346,17 @@ public class ByteArrayEndPoint extends AbstractEndPoint } } - flushed+=BufferUtil.flipPutFlip(b,_out); + if (BufferUtil.flipPutFlip(b,_out)>0) + idle=false; if (BufferUtil.hasContent(b)) + { + flushed=false; break; + } } } - if (flushed>0) + if (!idle) notIdle(); return flushed; } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java index dd90d35ef03..39ac51dcc0d 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java @@ -38,7 +38,7 @@ import org.eclipse.jetty.util.thread.Scheduler; * Channel End Point. *

Holds the channel and socket for an NIO endpoint. */ -public class ChannelEndPoint extends AbstractEndPoint +public class ChannelEndPoint extends AbstractEndPoint implements SocketBased { private static final Logger LOG = Log.getLogger(ChannelEndPoint.class); @@ -154,7 +154,7 @@ public class ChannelEndPoint extends AbstractEndPoint } @Override - public int flush(ByteBuffer... buffers) throws IOException + public boolean flush(ByteBuffer... buffers) throws IOException { int flushed=0; try @@ -172,7 +172,7 @@ public class ChannelEndPoint extends AbstractEndPoint int l=_channel.write(b); if (l>0) flushed+=l; - else + if (b.hasRemaining()) break; } } @@ -183,16 +183,23 @@ public class ChannelEndPoint extends AbstractEndPoint { throw new EofException(e); } + + boolean all_flushed=true; if (flushed>0) { notIdle(); // clear empty buffers to prevent position creeping up the buffer for (ByteBuffer b : buffers) + { if (BufferUtil.isEmpty(b)) BufferUtil.clear(b); + else + all_flushed=false; + } } - return flushed; + + return all_flushed; } public ByteChannel getChannel() @@ -206,6 +213,7 @@ public class ChannelEndPoint extends AbstractEndPoint return _channel; } + @Override public Socket getSocket() { return _socket; diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/EndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/EndPoint.java index 1809451e490..fb7213f6031 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/EndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/EndPoint.java @@ -170,11 +170,12 @@ public interface EndPoint extends Closeable * Flush data from the passed header/buffer to this endpoint. As many bytes as can be consumed * are taken from the header/buffer position up until the buffer limit. The header/buffers position * is updated to indicate how many bytes have been consumed. + * @return True IFF all the buffers have been consumed and the endpoint has flushed the data to its + * destination (ie is not buffering any data). * - * @return the number of bytes written * @throws EofException If the endpoint is closed or output is shutdown. */ - int flush(ByteBuffer... buffer) throws IOException; + boolean flush(ByteBuffer... buffer) throws IOException; /* ------------------------------------------------------------ */ /** @@ -242,9 +243,4 @@ public interface EndPoint extends Closeable void onClose(); - /** - * @return True if the endpoint is buffering output. - */ - boolean isBufferingOutput(); - } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSelectChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSelectChannelEndPoint.java index 3451a077c58..00672e4d25b 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSelectChannelEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSelectChannelEndPoint.java @@ -49,23 +49,22 @@ public class NetworkTrafficSelectChannelEndPoint extends SelectChannelEndPoint } @Override - public int flush(ByteBuffer... buffers) throws IOException + public boolean flush(ByteBuffer... buffers) throws IOException { - int written=0; + boolean flushed=true; for (ByteBuffer b : buffers) { if (b.hasRemaining()) { int position = b.position(); - int l = super.flush(b); + flushed|=super.flush(b); + int l=b.position()-position; notifyOutgoing(b, position, l); - if (l==0) + if (!flushed) break; - else - written+=l; } } - return written; + return flushed; } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/SelectChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/SelectChannelEndPoint.java index fa0c8507641..75822c86dcd 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/SelectChannelEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/SelectChannelEndPoint.java @@ -109,7 +109,10 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements SelectorMa Connection old = getConnection(); super.setConnection(connection); if (old != null && old != connection) + { + LOG.debug("Upgrading connection {} -> {} on endPoint {}", old, connection, this); _selector.getSelectorManager().connectionUpgraded(this, old); + } } @Override diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/SocketBased.java b/jetty-io/src/main/java/org/eclipse/jetty/io/SocketBased.java new file mode 100644 index 00000000000..0ca139a851d --- /dev/null +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/SocketBased.java @@ -0,0 +1,29 @@ +// +// ======================================================================== +// 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.net.Socket; + +/** + * Interface for Socket based I/O + */ +public interface SocketBased +{ + public Socket getSocket(); +} diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java b/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java index 1def518c55b..a5a8db6b48a 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java @@ -297,12 +297,12 @@ abstract public class WriteFlusher try { - _endPoint.flush(buffers); + boolean flushed=_endPoint.flush(buffers); // Are we complete? for (ByteBuffer b : buffers) { - if (BufferUtil.hasContent(b)) + if (!flushed||BufferUtil.hasContent(b)) { PendingState pending=new PendingState<>(buffers, context, callback); if (updateState(__WRITING,pending)) @@ -312,17 +312,6 @@ abstract public class WriteFlusher return; } } - - // Handle buffering endpoint - if (_endPoint.isBufferingOutput()) - { - PendingState pending=new PendingState<>(buffers, context, callback); - if (updateState(__WRITING,pending)) - onIncompleteFlushed(); - else - fail(new PendingState<>(buffers, context, callback)); - return; - } // If updateState didn't succeed, we don't care as our buffers have been written if (!updateState(__WRITING,__IDLE)) @@ -368,12 +357,12 @@ abstract public class WriteFlusher { ByteBuffer[] buffers = pending.getBuffers(); - _endPoint.flush(buffers); + boolean flushed=_endPoint.flush(buffers); // Are we complete? for (ByteBuffer b : buffers) { - if (BufferUtil.hasContent(b)) + if (!flushed || BufferUtil.hasContent(b)) { if (updateState(__COMPLETING,pending)) onIncompleteFlushed(); @@ -383,16 +372,6 @@ abstract public class WriteFlusher } } - // Handle buffering endpoint - if (_endPoint.isBufferingOutput()) - { - if (updateState(__COMPLETING,pending)) - onIncompleteFlushed(); - else - fail(pending); - return; - } - // If updateState didn't succeed, we don't care as our buffers have been written if (!updateState(__COMPLETING,__IDLE)) ignoreFail(); diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index 9efa74ddccc..2482a014a53 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -19,7 +19,9 @@ package org.eclipse.jetty.io.ssl; import java.io.IOException; +import java.net.SocketException; import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; import java.util.Arrays; import java.util.concurrent.Executor; import javax.net.ssl.SSLEngine; @@ -31,12 +33,14 @@ import javax.net.ssl.SSLException; import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.AbstractEndPoint; import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.ChannelEndPoint; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.FillInterest; import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.io.SelectChannelEndPoint; +import org.eclipse.jetty.io.SocketBased; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -111,6 +115,18 @@ public class SslConnection extends AbstractConnection this._bufferPool = byteBufferPool; this._sslEngine = sslEngine; this._decryptedEndPoint = new DecryptedEndPoint(); + + if (endPoint instanceof SocketBased) + { + try + { + ((SocketBased) endPoint).getSocket().setSoLinger(true,30000); + } + catch (SocketException e) + { + throw new RuntimeIOException(e); + } + } } public SSLEngine getSSLEngine() @@ -397,9 +413,18 @@ public class SslConnection extends AbstractConnection } @Override - public boolean isBufferingOutput() + public void setConnection(Connection connection) { - return BufferUtil.hasContent(_encryptedOutput); + if (connection instanceof AbstractConnection) + { + AbstractConnection a = (AbstractConnection)connection; + if (a.getInputBufferSize()<_sslEngine.getSession().getApplicationBufferSize()); + a.setInputBufferSize(_sslEngine.getSession().getApplicationBufferSize()); + } + + connection.onOpen(); + + super.setConnection(connection); } public SslConnection getSslConnection() @@ -613,7 +638,7 @@ public class SslConnection extends AbstractConnection } @Override - public synchronized int flush(ByteBuffer... appOuts) throws IOException + public synchronized boolean flush(ByteBuffer... appOuts) throws IOException { // TODO remove this when we are certain it is OK if (Thread.currentThread().getName().contains("selector")) @@ -631,7 +656,7 @@ public class SslConnection extends AbstractConnection try { if (_cannotAcceptMoreAppDataToFlush) - return 0; + return false; // We will need a network buffer if (_encryptedOutput == null) @@ -647,13 +672,16 @@ public class SslConnection extends AbstractConnection LOG.debug("{} wrap {}", SslConnection.this, wrapResult); BufferUtil.flipToFlush(_encryptedOutput, pos); if (wrapResult.bytesConsumed()>0) - { consumed+=wrapResult.bytesConsumed(); - // clear empty buffers to prevent position creeping up the buffer - for (ByteBuffer b : appOuts) - if (BufferUtil.isEmpty(b)) - BufferUtil.clear(b); + boolean all_consumed=true; + // clear empty buffers to prevent position creeping up the buffer + for (ByteBuffer b : appOuts) + { + if (BufferUtil.isEmpty(b)) + BufferUtil.clear(b); + else + all_consumed=false; } // and deal with the results returned from the sslEngineWrap @@ -669,15 +697,11 @@ public class SslConnection extends AbstractConnection // the write has progressed normally and let a subsequent call to flush (or WriteFlusher#onIncompleteFlushed) // to finish writing the close handshake. The caller will find out about the close on a subsequent flush or fill. if (BufferUtil.hasContent(_encryptedOutput)) - return consumed; + return false; } - - // If we were flushing because of a fill needing to wrap, return normally and it will handle the closed state. - if (appOuts[0]==__FILL_CALLED_FLUSH) - return consumed; - + // otherwise we have written, and the caller will close the underlying connection - return consumed; + return all_consumed; case BUFFER_UNDERFLOW: throw new IllegalStateException(); @@ -695,7 +719,7 @@ public class SslConnection extends AbstractConnection { case NOT_HANDSHAKING: // Return with the number of bytes consumed (which may be 0) - return consumed; + return all_consumed&&BufferUtil.isEmpty(_encryptedOutput); case NEED_TASK: // run the task and continue @@ -715,7 +739,7 @@ public class SslConnection extends AbstractConnection _flushRequiresFillToProgress = true; fill(__FLUSH_CALLED_FILL); } - return consumed; + return all_consumed&&BufferUtil.isEmpty(_encryptedOutput); case FINISHED: throw new IllegalStateException(); diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/ByteArrayEndPointTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/ByteArrayEndPointTest.java index b772b1df92a..8f75429df6d 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/ByteArrayEndPointTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/ByteArrayEndPointTest.java @@ -115,23 +115,20 @@ public class ByteArrayEndPointTest ByteArrayEndPoint endp = new ByteArrayEndPoint((byte[])null,15); endp.setGrowOutput(true); - assertEquals(11,endp.flush(BufferUtil.toBuffer("some output"))); + assertEquals(true,endp.flush(BufferUtil.toBuffer("some output"))); assertEquals("some output",endp.getOutputString()); - assertEquals(10,endp.flush(BufferUtil.toBuffer(" some more"))); + assertEquals(true,endp.flush(BufferUtil.toBuffer(" some more"))); assertEquals("some output some more",endp.getOutputString()); - assertEquals(0,endp.flush()); + assertEquals(true,endp.flush()); assertEquals("some output some more",endp.getOutputString()); - assertEquals(0,endp.flush(BufferUtil.EMPTY_BUFFER)); + assertEquals(true,endp.flush(BufferUtil.EMPTY_BUFFER)); assertEquals("some output some more",endp.getOutputString()); - assertEquals(9,endp.flush(BufferUtil.EMPTY_BUFFER,BufferUtil.toBuffer(" and"),BufferUtil.toBuffer(" more"))); + assertEquals(true,endp.flush(BufferUtil.EMPTY_BUFFER,BufferUtil.toBuffer(" and"),BufferUtil.toBuffer(" more"))); assertEquals("some output some more and more",endp.getOutputString()); - - - } @Test @@ -142,13 +139,13 @@ public class ByteArrayEndPointTest endp.setOutput(BufferUtil.allocate(10)); ByteBuffer data = BufferUtil.toBuffer("Some more data."); - assertEquals(10,endp.flush(data)); + assertEquals(false,endp.flush(data)); assertEquals("Some more ",endp.getOutputString()); assertEquals("data.",BufferUtil.toString(data)); assertEquals("Some more ",endp.takeOutputString()); - assertEquals(5,endp.flush(data)); + assertEquals(true,endp.flush(data)); assertEquals("data.",BufferUtil.toString(endp.takeOutput())); } diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/SelectChannelEndPointTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/SelectChannelEndPointTest.java index 669f2ae3cbc..977622da394 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/SelectChannelEndPointTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/SelectChannelEndPointTest.java @@ -179,7 +179,6 @@ public class SelectChannelEndPointTest _endp.write(null, blockingWrite, out.asReadOnlyBuffer()); blockingWrite.get(); } - LOG.info("Finished writing {}", _writeCount); progress = true; } diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/WriteFlusherTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/WriteFlusherTest.java index 4ad43309895..e153660128f 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/WriteFlusherTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/WriteFlusherTest.java @@ -348,8 +348,10 @@ public class WriteFlusherTest } @Test - public void testConcurrentAccessToWriteAndOnFail() throws IOException, InterruptedException, ExecutionException + public void testConcurrentAccessToWriteAndOnFail() throws Exception { + // TODO review this test - It was changed for the boolean flush return, but not really well inspected + final CountDownLatch failedCalledLatch = new CountDownLatch(1); final CountDownLatch writeCalledLatch = new CountDownLatch(1); final CountDownLatch writeCompleteLatch = new CountDownLatch(1); @@ -375,14 +377,28 @@ public class WriteFlusherTest executor.submit(new Writer(writeFlusher, callback)); assertThat("Write has been called.", writeCalledLatch.await(5, TimeUnit.SECONDS), is(true)); executor.submit(new FailedCaller(writeFlusher, failedCalledLatch)).get(); + + // callback failed is NOT called because in WRITING state failed() doesn't know about the callback. However // either the write succeeds or we get an IOException which will call callback.failed() - assertThat("callback failed", callback.isFailed(), is(false)); assertThat("write complete", writeCompleteLatch.await(5, TimeUnit.SECONDS), is(true)); + + // in this testcase we more or less emulate that the write has successfully finished and we return from // EndPoint.flush() back to WriteFlusher.write(). Then someone calls failed. So the callback should have been // completed. - assertThat("callback completed", callback.isCompleted(), is(true)); + try + { + callback.get(5,TimeUnit.SECONDS); + assertThat("callback completed", callback.isCompleted(), is(true)); + assertThat("callback failed", callback.isFailed(), is(false)); + } + catch(ExecutionException e) + { + // ignored because failure is expected + assertThat("callback failed", callback.isFailed(), is(true)); + } + assertThat("callback completed", callback.isDone(), is(true)); } private class ExposingStateCallback extends FutureCallback @@ -437,7 +453,7 @@ public class WriteFlusherTest flushCalledLatch.countDown(); // make sure we stay here, so write is called twice at the same time Thread.sleep(5000); - return null; + return Boolean.TRUE; } }); @@ -528,7 +544,7 @@ public class WriteFlusherTest } @Override - public int flush(ByteBuffer... buffers) throws IOException + public boolean flush(ByteBuffer... buffers) throws IOException { writeCalledLatch.countDown(); ByteBuffer byteBuffer = buffers[0]; @@ -549,13 +565,16 @@ public class WriteFlusherTest else if (byteBuffer.remaining() == 3) { byteBuffer.position(1); // pretend writing one byte - return 1; } else { byteBuffer.position(byteBuffer.limit()); } - return byteBuffer.limit() - oldPos; + + for (ByteBuffer b: buffers) + if (BufferUtil.hasContent(b)) + return false; + return true; } } diff --git a/jetty-io/src/test/resources/jetty-logging.properties b/jetty-io/src/test/resources/jetty-logging.properties index 9456416380c..d4922ad1951 100644 --- a/jetty-io/src/test/resources/jetty-logging.properties +++ b/jetty-io/src/test/resources/jetty-logging.properties @@ -1,6 +1,2 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -org.eclipse.jetty.LEVEL=WARN -# Enable the next two to debug SSL write blocked issue -org.eclipse.jetty.io.LEVEL=INFO -org.eclipse.jetty.io.SelectorManager.LEVEL=DEBUG -org.eclipse.jetty.io.SelectChannelEndPointTest.LEVEL=DEBUG \ No newline at end of file +org.eclipse.jetty.LEVEL=INFO diff --git a/jetty-plus/src/main/config/start.d/100-jetty-plus.ini b/jetty-plus/src/main/config/start.d/100-jetty-plus.ini new file mode 100644 index 00000000000..7e23d701fd0 --- /dev/null +++ b/jetty-plus/src/main/config/start.d/100-jetty-plus.ini @@ -0,0 +1,2 @@ +OPTIONS=plus +etc/jetty-plus.xml \ No newline at end of file diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java index d323fce2a72..a9e688bbfbb 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java @@ -28,9 +28,10 @@ import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.PathMap; import org.eclipse.jetty.server.HttpChannel; -import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpChannelConfig; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.UserIdentity; @@ -87,12 +88,14 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr /** * @return Returns the constraintMappings. */ + @Override public List getConstraintMappings() { return _constraintMappings; } /* ------------------------------------------------------------ */ + @Override public Set getRoles() { return _roles; @@ -134,6 +137,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr * The constraintMappings to set. * @param roles The known roles (or null to determine them from the mappings) */ + @Override public void setConstraintMappings(List constraintMappings, Set roles) { if (isStarted()) @@ -181,6 +185,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr /** * @see org.eclipse.jetty.security.ConstraintAware#addConstraintMapping(org.eclipse.jetty.security.ConstraintMapping) */ + @Override public void addConstraintMapping(ConstraintMapping mapping) { _constraintMappings.add(mapping); @@ -198,6 +203,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr /** * @see org.eclipse.jetty.security.ConstraintAware#addRole(java.lang.String) */ + @Override public void addRole(String role) { boolean modified = _roles.add(role); @@ -325,6 +331,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr } } + @Override protected RoleInfo prepareConstraintInfo(String pathInContext, Request request) { Map mappings = _constraintMap.match(pathInContext); @@ -354,34 +361,16 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr if (dataConstraint == null || dataConstraint == UserDataConstraint.None) return true; - HttpConfiguration httpConfiguration = HttpChannel.getCurrentHttpChannel().getHttpConfiguration(); + HttpChannelConfig httpConfig = HttpChannel.getCurrentHttpChannel().getHttpChannelConfig(); - if (dataConstraint == UserDataConstraint.Integral) + if (dataConstraint == UserDataConstraint.Confidential || dataConstraint == UserDataConstraint.Integral) { - if (httpConfiguration.isIntegral(request)) - return true; - if (httpConfiguration.getIntegralPort() > 0) - { - String url = httpConfiguration.getIntegralScheme() + "://" + request.getServerName() + ":" + httpConfiguration.getIntegralPort() + request.getRequestURI(); - if (request.getQueryString() != null) - url += "?" + request.getQueryString(); - response.setContentLength(0); - response.sendRedirect(url); - } - else - response.sendError(Response.SC_FORBIDDEN,"!Integral"); - - request.setHandled(true); - return false; - } - else if (dataConstraint == UserDataConstraint.Confidential) - { - if (httpConfiguration.isConfidential(request)) + if (request.isSecure()) return true; - if (httpConfiguration.getConfidentialPort() > 0) + if (httpConfig.getSecurePort() > 0) { - String url = httpConfiguration.getConfidentialScheme() + "://" + request.getServerName() + ":" + httpConfiguration.getConfidentialPort() + String url = httpConfig.getSecureScheme() + "://" + request.getServerName() + ":" + httpConfig.getSecurePort() + request.getRequestURI(); if (request.getQueryString() != null) url += "?" + request.getQueryString(); @@ -390,7 +379,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr response.sendRedirect(url); } else - response.sendError(Response.SC_FORBIDDEN,"!Confidential"); + response.sendError(HttpStatus.FORBIDDEN_403,"!Secure"); request.setHandled(true); return false; @@ -402,6 +391,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr } + @Override protected boolean isAuthMandatory(Request baseRequest, Response base_response, Object constraintInfo) { return constraintInfo != null && ((RoleInfo)constraintInfo).isChecked(); diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/DataConstraintsTest.java b/jetty-security/src/test/java/org/eclipse/jetty/security/DataConstraintsTest.java index 321031f0611..65c8eacc95a 100644 --- a/jetty-security/src/test/java/org/eclipse/jetty/security/DataConstraintsTest.java +++ b/jetty-security/src/test/java/org/eclipse/jetty/security/DataConstraintsTest.java @@ -28,8 +28,8 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.security.authentication.BasicAuthenticator; import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.HttpConfiguration; -import org.eclipse.jetty.server.HttpServerConnectionFactory; +import org.eclipse.jetty.server.HttpChannelConfig; +import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.LocalConnector; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; @@ -60,38 +60,25 @@ public class DataConstraintsTest public void startServer() { _server = new Server(); - _connector = new LocalConnector(_server); + + HttpConnectionFactory http = new HttpConnectionFactory(); + http.getHttpChannelConfig().setSecurePort(9999); + http.getHttpChannelConfig().setSecureScheme("BWTP"); + _connector = new LocalConnector(_server,http); _connector.setIdleTimeout(300000); - HttpConfiguration httpConfiguration = new HttpConfiguration(null, false); - httpConfiguration.setIntegralPort(9998); - httpConfiguration.setIntegralScheme("FTP"); - httpConfiguration.setConfidentialPort(9999); - httpConfiguration.setConfidentialScheme("SPDY"); - _connector.setDefaultConnectionFactory(new HttpServerConnectionFactory(_connector, httpConfiguration)); - _connectorS = new LocalConnector(_server); - _connectorS.setDefaultConnectionFactory(new HttpServerConnectionFactory(_connectorS, new HttpConfiguration(null,false) + HttpConnectionFactory https = new HttpConnectionFactory(); + https.getHttpChannelConfig().addCustomizer(new HttpChannelConfig.Customizer() { @Override - public void customize(Request request) throws IOException + public void customize(Connector connector, HttpChannelConfig channelConfig, Request request) { request.setScheme(HttpScheme.HTTPS.asString()); - super.customize(request); + request.setSecure(true); } - - - @Override - public boolean isIntegral(Request request) - { - return true; - } - - @Override - public boolean isConfidential(Request request) - { - return true; - } - })); + }); + + _connectorS = new LocalConnector(_server,https); _server.setConnectors(new Connector[]{_connector,_connectorS}); ContextHandler _context = new ContextHandler(); @@ -106,6 +93,7 @@ public class DataConstraintsTest _security.setHandler(new AbstractHandler() { + @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); @@ -149,8 +137,8 @@ public class DataConstraintsTest response = _connector.getResponses("GET /ctx/integral/info HTTP/1.0\r\n\r\n"); assertThat(response, containsString("HTTP/1.1 302 Found")); - assertThat(response, containsString("Location: FTP://")); - assertThat(response, containsString(":9998")); + assertThat(response, containsString("Location: BWTP://")); + assertThat(response, containsString(":9999")); response = _connectorS.getResponses("GET /ctx/integral/info HTTP/1.0\r\n\r\n"); assertThat(response, containsString("HTTP/1.1 404 Not Found")); @@ -181,7 +169,7 @@ public class DataConstraintsTest response = _connector.getResponses("GET /ctx/confid/info HTTP/1.0\r\n\r\n"); assertThat(response, containsString("HTTP/1.1 302 Found")); - assertThat(response, containsString("Location: SPDY://")); + assertThat(response, containsString("Location: BWTP://")); assertThat(response, containsString(":9999")); response = _connectorS.getResponses("GET /ctx/confid/info HTTP/1.0\r\n\r\n"); @@ -444,11 +432,14 @@ public class DataConstraintsTest { this.identityService = identityService; } + + @Override public String getName() { return "name"; } + @Override public UserIdentity login(String username, Object credentials) { if("admin".equals(username) && "password".equals(credentials)) @@ -456,20 +447,24 @@ public class DataConstraintsTest return null; } + @Override public boolean validate(UserIdentity user) { return false; } + @Override public IdentityService getIdentityService() { return identityService; } + @Override public void setIdentityService(IdentityService service) { } + @Override public void logout(UserIdentity user) { } diff --git a/jetty-server/pom.xml b/jetty-server/pom.xml index e6ff775df97..f9b4596b7ed 100644 --- a/jetty-server/pom.xml +++ b/jetty-server/pom.xml @@ -100,6 +100,12 @@ jetty-http ${project.version} + + org.eclipse.jetty + jetty-xml + ${project.version} + test + org.eclipse.jetty jetty-jmx diff --git a/jetty-server/src/main/config/etc/jetty-ssl.xml b/jetty-server/src/main/config/etc/jetty-ssl.xml index 0a2d63061dd..e90f04a8f7f 100644 --- a/jetty-server/src/main/config/etc/jetty-ssl.xml +++ b/jetty-server/src/main/config/etc/jetty-ssl.xml @@ -1,5 +1,5 @@ - + @@ -11,25 +11,52 @@ - - - - /etc/keystore + + + + + /etc/keystore OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4 OBF:1u2u1wml1z7s1z7a1wnl1u2g - /etc/keystore + /etc/keystore OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4 + + + + + + + + + - + + + + - - - 8443 - 30000 - 2 - 100 - + + + + + + + http/1.1 + + + + + + + + + + + + 8443 + 30000 + diff --git a/jetty-server/src/main/config/etc/jetty.xml b/jetty-server/src/main/config/etc/jetty.xml index 6d5a7e79409..dec2d4da994 100644 --- a/jetty-server/src/main/config/etc/jetty.xml +++ b/jetty-server/src/main/config/etc/jetty.xml @@ -29,6 +29,21 @@ + + + + + + https + 8443 + 32768 + 8192 + 8192 + + + + + @@ -36,9 +51,16 @@ + + + + + + + - 300000 + 30000 @@ -68,7 +90,7 @@ true true 1000 - false + true false diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnectionFactory.java new file mode 100644 index 00000000000..d11f33fe818 --- /dev/null +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnectionFactory.java @@ -0,0 +1,78 @@ +// +// ======================================================================== +// 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 org.eclipse.jetty.util.ArrayUtil; +import org.eclipse.jetty.util.component.AggregateLifeCycle; +import org.eclipse.jetty.util.ssl.SslContextFactory; + +public abstract class AbstractConnectionFactory extends AggregateLifeCycle implements ConnectionFactory +{ + final String _protocol; + int _inputbufferSize=8192; + + protected AbstractConnectionFactory(String protocol) + { + _protocol=protocol; + } + + @Override + public String getProtocol() + { + return _protocol; + } + + public int getInputBufferSize() + { + return _inputbufferSize; + } + + public void setInputBufferSize(int size) + { + _inputbufferSize=size; + } + + @Override + public String toString() + { + return String.format("%s@%x{%s}",this.getClass().getSimpleName(),hashCode(),getProtocol()); + } + + public static ConnectionFactory[] getFactories(SslContextFactory sslContextFactory, ConnectionFactory... factories) + { + factories=ArrayUtil.removeNulls(factories); + + if (sslContextFactory==null) + return factories; + + for (ConnectionFactory factory : factories) + { + if (factory instanceof HttpChannelConfig.ConnectionFactory) + { + HttpChannelConfig config = ((HttpChannelConfig.ConnectionFactory)factory).getHttpChannelConfig(); + if (config.getCustomizer(SecureRequestCustomizer.class)==null) + config.addCustomizer(new SecureRequestCustomizer()); + } + } + return ArrayUtil.prependToArray(new SslConnectionFactory(sslContextFactory,factories[0].getProtocol()),factories,ConnectionFactory.class); + + } + + +} diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java index 4afd4bdc0ee..f5b83019ddb 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java @@ -20,7 +20,10 @@ package org.eclipse.jetty.server; import java.io.IOException; import java.net.Socket; +import java.util.ArrayList; +import java.util.Collection; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; @@ -37,51 +40,63 @@ import org.eclipse.jetty.util.component.AggregateLifeCycle; 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.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.Scheduler; import org.eclipse.jetty.util.thread.TimerScheduler; /** *

Partial implementation of {@link Connector}

+ * + *

+ * The connector keeps a collection of {@link ConnectionFactory} instances, each of which are known by their + * protocol name. The protocol name may be a real protocol (eg http/1.1 or spdy/3) or it may be a private name + * that represents a special connection factory. For example, the name "SSL-http/1.1" is used for + * an {@link SslConnectionFactory} that has been instantiated with the {@link HttpConnectionFactory} as it's + * next protocol. + *

+ * If NPN is used to select the real protocol used by an SSL connection, then the name "SSL-NPN" is used, + * which represents a {@link SslConnectionFactory} with a NPNConnectionFactory as the next protocol. Once + * the NPN connection is established, it will get the next protocol from the NPN extension and then call + * {@link #getConnectionFactory(String)} to get the next connection factory. + * */ @ManagedObject("Abstract implementation of the Connector Interface") public abstract class AbstractConnector extends AggregateLifeCycle implements Connector, Dumpable { protected final Logger LOG = Log.getLogger(getClass()); // Order is important on server side, so we use a LinkedHashMap - private final Map factories = new LinkedHashMap<>(); + private final Map _factories = new LinkedHashMap<>(); private final Statistics _stats = new ConnectorStatistics(); private final Server _server; - private final SslContextFactory _sslContextFactory; private final Executor _executor; private final Scheduler _scheduler; private final ByteBufferPool _byteBufferPool; private final Thread[] _acceptors; private volatile CountDownLatch _stopping; - private volatile long _idleTimeout = 200000; - private volatile ConnectionFactory defaultConnectionFactory; + private long _idleTimeout = 200000; + private String _defaultProtocol; + private ConnectionFactory _defaultConnectionFactory; /** * @param server The server this connector will be added to. Must not be null. + * @param factory TODO + * @param sslContextFactory the SSL context factory to make this connector SSL enabled, or null * @param executor An executor for this connector or null to use the servers executor * @param scheduler A scheduler for this connector or null to use the servers scheduler * @param pool A buffer pool for this connector or null to use a default {@link ByteBufferPool} - * @param sslContextFactory the SSL context factory to make this connector SSL enabled, or null * @param acceptors the number of acceptor threads to use, or 0 for a default value. */ public AbstractConnector( Server server, Executor executor, Scheduler scheduler, - ByteBufferPool pool, - SslContextFactory sslContextFactory, - int acceptors) + ByteBufferPool pool, + int acceptors, + ConnectionFactory... factories) { _server=server; _executor=executor!=null?executor:_server.getThreadPool(); _scheduler=scheduler!=null?scheduler:new TimerScheduler(); _byteBufferPool = pool!=null?pool:new ArrayByteBufferPool(); - _sslContextFactory = sslContextFactory; addBean(_server,false); addBean(_executor); @@ -89,9 +104,11 @@ public abstract class AbstractConnector extends AggregateLifeCycle implements Co unmanage(_executor); // inherited from server addBean(_scheduler); addBean(_byteBufferPool); - addBean(_sslContextFactory); addBean(_stats,true); + for (ConnectionFactory factory:factories) + addConnectionFactory(factory); + if (acceptors<=0) acceptors=Math.max(1,(Runtime.getRuntime().availableProcessors()) / 4); if (acceptors > 2 * Runtime.getRuntime().availableProcessors()) @@ -123,12 +140,6 @@ public abstract class AbstractConnector extends AggregateLifeCycle implements Co return _byteBufferPool; } - @Override - public SslContextFactory getSslContextFactory() - { - return _sslContextFactory; - } - @Override public long getIdleTimeout() { @@ -162,10 +173,13 @@ public abstract class AbstractConnector extends AggregateLifeCycle implements Co return _acceptors.length; } - @Override protected void doStart() throws Exception { + _defaultConnectionFactory = getConnectionFactory(_defaultProtocol); + if(_defaultConnectionFactory==null) + throw new IllegalStateException("No protocol factory for default protocol: "+_defaultProtocol); + super.doStart(); _stopping=new CountDownLatch(_acceptors.length); @@ -232,56 +246,97 @@ public abstract class AbstractConnector extends AggregateLifeCycle implements Co return isRunning(); } + @Override public ConnectionFactory getConnectionFactory(String protocol) { - synchronized (factories) + synchronized (_factories) { - return factories.get(protocol); + return _factories.get(protocol.toLowerCase()); } } - public ConnectionFactory putConnectionFactory(String protocol, ConnectionFactory factory) + @Override + public T getConnectionFactory(Class factoryType) { - synchronized (factories) + synchronized (_factories) { - return factories.put(protocol, factory); + for (ConnectionFactory f : _factories.values()) + if (factoryType.isAssignableFrom(f.getClass())) + return (T)f; + return null; + } + } + + public void addConnectionFactory(ConnectionFactory factory) + { + synchronized (_factories) + { + ConnectionFactory old=_factories.remove(factory.getProtocol()); + if (old!=null) + removeBean(old); + _factories.put(factory.getProtocol().toLowerCase(), factory); + addBean(factory); + if (_defaultProtocol==null) + _defaultProtocol=factory.getProtocol(); } } public ConnectionFactory removeConnectionFactory(String protocol) { - synchronized (factories) + synchronized (_factories) { - return factories.remove(protocol); + ConnectionFactory factory= _factories.remove(protocol.toLowerCase()); + removeBean(factory); + return factory; } } - public Map getConnectionFactories() + @Override + public Collection getConnectionFactories() { - synchronized (factories) + synchronized (_factories) { - return new LinkedHashMap<>(factories); + return _factories.values(); + } + } + + @Override + public List getProtocols() + { + synchronized (_factories) + { + return new ArrayList<>(_factories.keySet()); } } public void clearConnectionFactories() { - synchronized (factories) + synchronized (_factories) { - factories.clear(); + _factories.clear(); } } + public String getDefaultProtocol() + { + return _defaultProtocol; + } + + public void setDefaultProtocol(String defaultProtocol) + { + _defaultProtocol = defaultProtocol.toLowerCase(); + if (isRunning()) + _defaultConnectionFactory=getConnectionFactory(_defaultProtocol); + } + + @Override public ConnectionFactory getDefaultConnectionFactory() { - return defaultConnectionFactory; + if (isStarted()) + return _defaultConnectionFactory; + return getConnectionFactory(_defaultProtocol); } - - public void setDefaultConnectionFactory(ConnectionFactory defaultConnectionFactory) - { - this.defaultConnectionFactory = defaultConnectionFactory; - } - + private class Acceptor implements Runnable { private final int _acceptor; @@ -358,4 +413,13 @@ public abstract class AbstractConnector extends AggregateLifeCycle implements Co { return _scheduler; } + + @Override + public String toString() + { + return String.format("%s@%x{%s}", + getClass().getSimpleName(), + hashCode(), + getDefaultProtocol()); + } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNetworkConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNetworkConnector.java index 21fafc7a50c..59070233d33 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNetworkConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNetworkConnector.java @@ -23,7 +23,6 @@ import java.util.concurrent.Executor; import java.util.concurrent.Future; import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.Scheduler; /** @@ -34,9 +33,9 @@ public abstract class AbstractNetworkConnector extends AbstractConnector impleme private volatile String _host; private volatile int _port = 0; - public AbstractNetworkConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, SslContextFactory sslContextFactory, int acceptors) + public AbstractNetworkConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, ConnectionFactory... factories) { - super(server, executor, scheduler, pool, sslContextFactory, acceptors); + super(server,executor,scheduler,pool,acceptors,factories); } public void setHost(String host) @@ -110,8 +109,8 @@ public abstract class AbstractNetworkConnector extends AbstractConnector impleme @Override public String toString() { - return String.format("%s@%s:%d", - getClass().getSimpleName(), + return String.format("%s{%s:%d}", + super.toString(), getHost() == null ? "0.0.0.0" : getHost(), getLocalPort() <= 0 ? getPort() : getLocalPort()); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ByteBufferHttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ByteBufferHttpInput.java index 680deb511f6..8d81c83c7b5 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ByteBufferHttpInput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ByteBufferHttpInput.java @@ -42,6 +42,5 @@ public class ByteBufferHttpInput extends HttpInput @Override protected void onContentConsumed(ByteBuffer item) { - item.clear(); } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionFactory.java index daad1fa5c9c..b129baf62f9 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionFactory.java @@ -18,7 +18,6 @@ package org.eclipse.jetty.server; -import java.nio.channels.SocketChannel; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; @@ -28,12 +27,14 @@ import org.eclipse.jetty.io.EndPoint; */ public interface ConnectionFactory { + public String getProtocol(); + /** *

Creates a new {@link Connection} with the given parameters

- * @param channel the {@link SocketChannel} associated with the connection + * @param connector The {@link Connector} creating this connection * @param endPoint the {@link EndPoint} associated with the connection - * @param attachment the attachment associated with the connection * @return a new {@link Connection} */ - public Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment); + public Connection newConnection(Connector connector, EndPoint endPoint); + } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java index 908abd0db07..c37f3fd5463 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java @@ -18,6 +18,8 @@ package org.eclipse.jetty.server; +import java.util.Collection; +import java.util.List; import java.util.concurrent.Executor; import org.eclipse.jetty.io.ByteBufferPool; @@ -25,7 +27,6 @@ import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.Graceful; import org.eclipse.jetty.util.component.LifeCycle; -import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.Scheduler; /** @@ -57,10 +58,22 @@ public interface Connector extends LifeCycle, Graceful public ByteBufferPool getByteBufferPool(); /** - * @return the {@link SslContextFactory} associated with this {@link Connector} + * @return the {@link ConnectionFactory} associated with the protocol name */ - public SslContextFactory getSslContextFactory(); + public ConnectionFactory getConnectionFactory(String nextProtocol); + + public T getConnectionFactory(Class factoryType); + + /** + * @return the default {@link ConnectionFactory} associated with the default protocol name + */ + public ConnectionFactory getDefaultConnectionFactory(); + + public Collection getConnectionFactories(); + + public List getProtocols(); + /** * @return the dle timeout for connections in milliseconds */ @@ -199,4 +212,6 @@ public interface Connector extends LifeCycle, Graceful */ public void connectionClosed(long duration, int messagesIn, int messagesOut); } + + } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java index 90954666c6f..9919323211b 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; + import javax.servlet.DispatcherType; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java new file mode 100644 index 00000000000..8f9f5825d76 --- /dev/null +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java @@ -0,0 +1,280 @@ +// +// ======================================================================== +// 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.net.InetSocketAddress; + +import javax.servlet.ServletRequest; + +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpScheme; +import org.eclipse.jetty.server.HttpChannelConfig.Customizer; + +public class ForwardedRequestCustomizer implements Customizer +{ + private String _hostHeader; + private String _forwardedHostHeader = HttpHeader.X_FORWARDED_HOST.toString(); + private String _forwardedServerHeader = HttpHeader.X_FORWARDED_SERVER.toString(); + private String _forwardedForHeader = HttpHeader.X_FORWARDED_FOR.toString(); + private String _forwardedProtoHeader = HttpHeader.X_FORWARDED_PROTO.toString(); + private String _forwardedCipherSuiteHeader; + private String _forwardedSslSessionIdHeader; + + + /* ------------------------------------------------------------ */ + public String getHostHeader() + { + return _hostHeader; + } + + /* ------------------------------------------------------------ */ + /** + * Set a forced valued for the host header to control what is returned by {@link ServletRequest#getServerName()} and {@link ServletRequest#getServerPort()}. + * This value is only used if {@link #isForwarded()} is true. + * + * @param hostHeader + * The value of the host header to force. + */ + public void setHostHeader(String hostHeader) + { + _hostHeader = hostHeader; + } + + /* ------------------------------------------------------------ */ + /* + * + * @see #setForwarded(boolean) + */ + public String getForwardedHostHeader() + { + return _forwardedHostHeader; + } + + /* ------------------------------------------------------------ */ + /** + * @param forwardedHostHeader + * The header name for forwarded hosts (default x-forwarded-host) + * @see #setForwarded(boolean) + */ + public void setForwardedHostHeader(String forwardedHostHeader) + { + _forwardedHostHeader = forwardedHostHeader; + } + + /* ------------------------------------------------------------ */ + /** + * @return the header name for forwarded server. + * @see #setForwarded(boolean) + */ + public String getForwardedServerHeader() + { + return _forwardedServerHeader; + } + + /* ------------------------------------------------------------ */ + /** + * @param forwardedServerHeader + * The header name for forwarded server (default x-forwarded-server) + * @see #setForwarded(boolean) + */ + public void setForwardedServerHeader(String forwardedServerHeader) + { + _forwardedServerHeader = forwardedServerHeader; + } + + /* ------------------------------------------------------------ */ + /** + * @return the forwarded for header + * @see #setForwarded(boolean) + */ + public String getForwardedForHeader() + { + return _forwardedForHeader; + } + + /* ------------------------------------------------------------ */ + /** + * @param forwardedRemoteAddressHeader + * The header name for forwarded for (default x-forwarded-for) + * @see #setForwarded(boolean) + */ + public void setForwardedForHeader(String forwardedRemoteAddressHeader) + { + _forwardedForHeader = forwardedRemoteAddressHeader; + } + + /* ------------------------------------------------------------ */ + /** + * Get the forwardedProtoHeader. + * + * @return the forwardedProtoHeader (default X-Forwarded-For) + * @see #setForwarded(boolean) + */ + public String getForwardedProtoHeader() + { + return _forwardedProtoHeader; + } + + /* ------------------------------------------------------------ */ + /** + * Set the forwardedProtoHeader. + * + * @param forwardedProtoHeader + * the forwardedProtoHeader to set (default X-Forwarded-For) + * @see #setForwarded(boolean) + */ + public void setForwardedProtoHeader(String forwardedProtoHeader) + { + _forwardedProtoHeader = forwardedProtoHeader; + } + + /* ------------------------------------------------------------ */ + /** + * @return The header name holding a forwarded cipher suite (default null) + */ + public String getForwardedCipherSuiteHeader() + { + return _forwardedCipherSuiteHeader; + } + + /* ------------------------------------------------------------ */ + /** + * @param forwardedCipherSuite + * The header name holding a forwarded cipher suite (default null) + */ + public void setForwardedCipherSuiteHeader(String forwardedCipherSuite) + { + _forwardedCipherSuiteHeader = forwardedCipherSuite; + } + + /* ------------------------------------------------------------ */ + /** + * @return The header name holding a forwarded SSL Session ID (default null) + */ + public String getForwardedSslSessionIdHeader() + { + return _forwardedSslSessionIdHeader; + } + + /* ------------------------------------------------------------ */ + /** + * @param forwardedSslSessionId + * The header name holding a forwarded SSL Session ID (default null) + */ + public void setForwardedSslSessionIdHeader(String forwardedSslSessionId) + { + _forwardedSslSessionIdHeader = forwardedSslSessionId; + } + + /* ------------------------------------------------------------ */ + @Override + public void customize(Connector connector, HttpChannelConfig config, Request request) + { + HttpFields httpFields = request.getHttpFields(); + + // Do SSL first + if (getForwardedCipherSuiteHeader()!=null) + { + String cipher_suite=httpFields.getStringField(getForwardedCipherSuiteHeader()); + if (cipher_suite!=null) + request.setAttribute("javax.servlet.request.cipher_suite",cipher_suite); + } + if (getForwardedSslSessionIdHeader()!=null) + { + String ssl_session_id=httpFields.getStringField(getForwardedSslSessionIdHeader()); + if(ssl_session_id!=null) + { + request.setAttribute("javax.servlet.request.ssl_session_id", ssl_session_id); + request.setScheme(HttpScheme.HTTPS.asString()); + } + } + + // Retrieving headers from the request + String forwardedHost = getLeftMostFieldValue(httpFields,getForwardedHostHeader()); + String forwardedServer = getLeftMostFieldValue(httpFields,getForwardedServerHeader()); + String forwardedFor = getLeftMostFieldValue(httpFields,getForwardedForHeader()); + String forwardedProto = getLeftMostFieldValue(httpFields,getForwardedProtoHeader()); + + if (_hostHeader != null) + { + // Update host header + httpFields.put(HttpHeader.HOST.toString(),_hostHeader); + request.setServerName(null); + request.setServerPort(-1); + request.getServerName(); + } + else if (forwardedHost != null) + { + // Update host header + httpFields.put(HttpHeader.HOST.toString(),forwardedHost); + request.setServerName(null); + request.setServerPort(-1); + request.getServerName(); + } + else if (forwardedServer != null) + { + // Use provided server name + request.setServerName(forwardedServer); + } + + if (forwardedFor != null) + { + request.setRemoteAddr(new InetSocketAddress(forwardedFor,request.getRemotePort())); + } + + if (forwardedProto != null) + { + request.setScheme(forwardedProto); + if (forwardedProto.equals(config.getSecureScheme())) + request.setSecure(true); + } + } + + /* ------------------------------------------------------------ */ + protected String getLeftMostFieldValue(HttpFields fields, String header) + { + if (header == null) + return null; + + String headerValue = fields.getStringField(header); + + if (headerValue == null) + return null; + + int commaIndex = headerValue.indexOf(','); + + if (commaIndex == -1) + { + // Single value + return headerValue; + } + + // The left-most value is the farthest downstream client + return headerValue.substring(0,commaIndex); + } + + + /* ------------------------------------------------------------ */ + @Override + public String toString() + { + return String.format("%s@%x",this.getClass().getSimpleName(),hashCode()); + } +} diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Handler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Handler.java index b2715a9c634..1a6205f59ef 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Handler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Handler.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.server; import java.io.IOException; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java index 29fac7f48a2..24536935237 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java @@ -23,6 +23,7 @@ import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; + import javax.servlet.DispatcherType; import javax.servlet.RequestDispatcher; @@ -63,14 +64,14 @@ import org.eclipse.jetty.util.thread.Scheduler; public class HttpChannel implements HttpParser.RequestHandler, Runnable { private static final Logger LOG = Log.getLogger(HttpChannel.class); - private static final ThreadLocal __currentChannel = new ThreadLocal<>(); + private static final ThreadLocal> __currentChannel = new ThreadLocal<>(); - public static HttpChannel getCurrentHttpChannel() + public static HttpChannel getCurrentHttpChannel() { return __currentChannel.get(); } - protected static void setCurrentHttpChannel(HttpChannel channel) + protected static void setCurrentHttpChannel(HttpChannel channel) { __currentChannel.set(channel); } @@ -78,7 +79,7 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable private final AtomicBoolean _committed = new AtomicBoolean(); private final AtomicInteger _requests = new AtomicInteger(); private final Connector _connector; - private final HttpConfiguration _configuration; + private final HttpChannelConfig _configuration; private final EndPoint _endPoint; private final HttpTransport _transport; private final HttpURI _uri; @@ -90,7 +91,7 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable private boolean _expect100Continue = false; private boolean _expect102Processing = false; - public HttpChannel(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput input) + public HttpChannel(Connector connector, HttpChannelConfig configuration, EndPoint endPoint, HttpTransport transport, HttpInput input) { _connector = connector; _configuration = configuration; @@ -131,7 +132,7 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable return _connector.getByteBufferPool(); } - public HttpConfiguration getHttpConfiguration() + public HttpChannelConfig getHttpChannelConfig() { return _configuration; } @@ -237,8 +238,11 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable if (_state.isInitial()) { + _request.setTimeStamp(System.currentTimeMillis()); _request.setDispatcherType(DispatcherType.REQUEST); - getHttpConfiguration().customize(_request); + + for (HttpChannelConfig.Customizer customizer : _configuration.getCustomizers()) + customizer.customize(getConnector(),_configuration,_request); getServer().handle(this); } else @@ -321,7 +325,7 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable if (_state.isSuspended()) { HttpFields fields = new HttpFields(); - ResponseInfo info = new ResponseInfo(_request.getHttpVersion(), fields, 0, Response.SC_INTERNAL_SERVER_ERROR, null, _request.isHead()); + ResponseInfo info = new ResponseInfo(_request.getHttpVersion(), fields, 0, HttpStatus.INTERNAL_SERVER_ERROR_500, null, _request.isHead()); boolean committed = commitResponse(info, null, true); if (!committed) LOG.warn("Could not send response error 500: "+x); @@ -515,7 +519,7 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable if (LOG.isDebugEnabled()) LOG.debug("{} content {}", this, item); @SuppressWarnings("unchecked") - HttpInput input = _request.getHttpInput(); + HttpInput input = (HttpInput)_request.getHttpInput(); input.content(item); return true; } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelConfig.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelConfig.java new file mode 100644 index 00000000000..2ef9016b82c --- /dev/null +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelConfig.java @@ -0,0 +1,124 @@ +// +// ======================================================================== +// 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.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.eclipse.jetty.http.HttpScheme; + +public class HttpChannelConfig +{ + private List _customizers=new CopyOnWriteArrayList<>(); + private int _outputBufferSize=32*1024; + private int _requestHeaderSize=8*1024; + private int _responseHeaderSize=8*1024; + private int _securePort; + private String _secureScheme = HttpScheme.HTTPS.asString(); + + public interface Customizer + { + public void customize(Connector connector, HttpChannelConfig channelConfig, Request request); + } + + public interface ConnectionFactory + { + HttpChannelConfig getHttpChannelConfig(); + } + + public void addCustomizer(Customizer customizer) + { + _customizers.add(customizer); + } + + public List getCustomizers() + { + return _customizers; + } + + public T getCustomizer(Class type) + { + for (Customizer c : _customizers) + if (type.isAssignableFrom(c.getClass())) + return (T)c; + return null; + } + + public int getOutputBufferSize() + { + return _outputBufferSize; + } + + public int getRequestHeaderSize() + { + return _requestHeaderSize; + } + + public int getResponseHeaderSize() + { + return _responseHeaderSize; + } + + public int getSecurePort() + { + return _securePort; + } + + public String getSecureScheme() + { + return _secureScheme; + } + + public void setCustomizers(List customizers) + { + _customizers.clear(); + _customizers.addAll(customizers); + } + + public void setOutputBufferSize(int responseBufferSize) + { + _outputBufferSize = responseBufferSize; + } + + public void setRequestHeaderSize(int requestHeaderSize) + { + _requestHeaderSize = requestHeaderSize; + } + + public void setResponseHeaderSize(int responseHeaderSize) + { + _responseHeaderSize = responseHeaderSize; + } + + public void setSecurePort(int confidentialPort) + { + _securePort = confidentialPort; + } + + public void setSecureScheme(String confidentialScheme) + { + _secureScheme = confidentialScheme; + } + + @Override + public String toString() + { + return String.format("%s@%x{%d,%d/%d,%s://:%d,%s}",this.getClass().getSimpleName(),hashCode(),_outputBufferSize,_requestHeaderSize,_responseHeaderSize,_secureScheme,_securePort,_customizers); + } +} diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java index 83e7f0ee079..04c3b7008b1 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.server; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; + import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java deleted file mode 100644 index 266b22f3e8b..00000000000 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java +++ /dev/null @@ -1,556 +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.server; - -import java.io.IOException; -import java.net.InetSocketAddress; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLSession; -import javax.servlet.ServletRequest; - -import org.eclipse.jetty.http.HttpFields; -import org.eclipse.jetty.http.HttpHeader; -import org.eclipse.jetty.http.HttpScheme; -import org.eclipse.jetty.io.ssl.SslConnection; -import org.eclipse.jetty.server.ssl.SslCertificates; -import org.eclipse.jetty.util.component.AggregateLifeCycle; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.util.ssl.SslContextFactory; - - -public class HttpConfiguration extends AggregateLifeCycle -{ - static final Logger LOG = Log.getLogger(HttpConfiguration.class); - - private final SslContextFactory _sslContextFactory; - private final boolean _ssl; - - private String _integralScheme = HttpScheme.HTTPS.asString(); - private int _integralPort = 0; - private String _confidentialScheme = HttpScheme.HTTPS.asString(); - private int _confidentialPort = 0; - private boolean _forwarded; - private String _hostHeader; - private String _forwardedHostHeader = HttpHeader.X_FORWARDED_HOST.toString(); - private String _forwardedServerHeader = HttpHeader.X_FORWARDED_SERVER.toString(); - private String _forwardedForHeader = HttpHeader.X_FORWARDED_FOR.toString(); - private String _forwardedProtoHeader = HttpHeader.X_FORWARDED_PROTO.toString(); - private String _forwardedCipherSuiteHeader; - private String _forwardedSslSessionIdHeader; - private int _requestHeaderSize=8*1024; - private int _requestBufferSize=16*1024; - private int _responseHeaderSize=8*1024; - private int _responseBufferSize=32*1024; - - public HttpConfiguration(SslContextFactory sslContextFactory,boolean ssl) - { - _sslContextFactory=sslContextFactory!=null?sslContextFactory:ssl?new SslContextFactory(SslContextFactory.DEFAULT_KEYSTORE_PATH):null; - _ssl=ssl; - if (_sslContextFactory!=null) - addBean(_sslContextFactory,sslContextFactory==null); - } - - public SslContextFactory getSslContextFactory() - { - return _sslContextFactory; - } - - public boolean isSecure() - { - return _ssl; - } - - public int getRequestHeaderSize() - { - return _requestHeaderSize; - } - - public void setRequestHeaderSize(int requestHeaderSize) - { - _requestHeaderSize = requestHeaderSize; - } - - public int getRequestBufferSize() - { - return _requestBufferSize; - } - - public void setRequestBufferSize(int requestBufferSize) - { - _requestBufferSize = requestBufferSize; - } - - public int getResponseHeaderSize() - { - return _responseHeaderSize; - } - - public void setResponseHeaderSize(int responseHeaderSize) - { - _responseHeaderSize = responseHeaderSize; - } - - public int getResponseBufferSize() - { - return _responseBufferSize; - } - - public void setResponseBufferSize(int responseBufferSize) - { - _responseBufferSize = responseBufferSize; - } - - /* ------------------------------------------------------------ */ - /** - * Allow the Listener a chance to customise the request. before the server - * does its stuff.
- * This allows the required attributes to be set for SSL requests.
- * The requirements of the Servlet specs are: - *
    - *
  • an attribute named "javax.servlet.request.ssl_session_id" of type - * String (since Servlet Spec 3.0).
  • - *
  • an attribute named "javax.servlet.request.cipher_suite" of type - * String.
  • - *
  • an attribute named "javax.servlet.request.key_size" of type Integer.
  • - *
  • an attribute named "javax.servlet.request.X509Certificate" of type - * java.security.cert.X509Certificate[]. This is an array of objects of type - * X509Certificate, the order of this array is defined as being in ascending - * order of trust. The first certificate in the chain is the one set by the - * client, the next is the one used to authenticate the first, and so on. - *
  • - *
- */ - public void customize(Request request) throws IOException - { - if (isSecure()) - { - request.setScheme(HttpScheme.HTTPS.asString()); - SslConnection.DecryptedEndPoint ssl_endp = (SslConnection.DecryptedEndPoint)request.getHttpChannel().getEndPoint(); - SslConnection sslConnection = ssl_endp.getSslConnection(); - SSLEngine sslEngine=sslConnection.getSSLEngine(); - SslCertificates.customize(sslEngine,request); - } - - request.setTimeStamp(System.currentTimeMillis()); - if (isForwarded()) - checkForwardedHeaders(request); - } - - /* ------------------------------------------------------------ */ - protected void checkForwardedHeaders(Request request) throws IOException - { - HttpFields httpFields = request.getHttpFields(); - - // Do SSL first - if (getForwardedCipherSuiteHeader()!=null) - { - String cipher_suite=httpFields.getStringField(getForwardedCipherSuiteHeader()); - if (cipher_suite!=null) - request.setAttribute("javax.servlet.request.cipher_suite",cipher_suite); - } - if (getForwardedSslSessionIdHeader()!=null) - { - String ssl_session_id=httpFields.getStringField(getForwardedSslSessionIdHeader()); - if(ssl_session_id!=null) - { - request.setAttribute("javax.servlet.request.ssl_session_id", ssl_session_id); - request.setScheme(HttpScheme.HTTPS.asString()); - } - } - - // Retrieving headers from the request - String forwardedHost = getLeftMostFieldValue(httpFields,getForwardedHostHeader()); - String forwardedServer = getLeftMostFieldValue(httpFields,getForwardedServerHeader()); - String forwardedFor = getLeftMostFieldValue(httpFields,getForwardedForHeader()); - String forwardedProto = getLeftMostFieldValue(httpFields,getForwardedProtoHeader()); - - if (_hostHeader != null) - { - // Update host header - httpFields.put(HttpHeader.HOST.toString(),_hostHeader); - request.setServerName(null); - request.setServerPort(-1); - request.getServerName(); - } - else if (forwardedHost != null) - { - // Update host header - httpFields.put(HttpHeader.HOST.toString(),forwardedHost); - request.setServerName(null); - request.setServerPort(-1); - request.getServerName(); - } - else if (forwardedServer != null) - { - // Use provided server name - request.setServerName(forwardedServer); - } - - if (forwardedFor != null) - { - request.setRemoteAddr(new InetSocketAddress(forwardedFor,request.getRemotePort())); - } - - if (forwardedProto != null) - { - request.setScheme(forwardedProto); - } - } - - /* ------------------------------------------------------------ */ - protected String getLeftMostFieldValue(HttpFields fields, String header) - { - if (header == null) - return null; - - String headerValue = fields.getStringField(header); - - if (headerValue == null) - return null; - - int commaIndex = headerValue.indexOf(','); - - if (commaIndex == -1) - { - // Single value - return headerValue; - } - - // The left-most value is the farthest downstream client - return headerValue.substring(0,commaIndex); - } - - /* ------------------------------------------------------------ */ - /* - * @see org.eclipse.jetty.server.Connector#getConfidentialPort() - */ - public int getConfidentialPort() - { - return _confidentialPort; - } - - /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ - /* - * @see org.eclipse.jetty.server.Connector#getConfidentialScheme() - */ - public String getConfidentialScheme() - { - return _confidentialScheme; - } - - /* ------------------------------------------------------------ */ - /** - * The request is integral IFF it is secure AND the server port - * matches any configured {@link #getIntegralPort()}. - * This allows separation of listeners providing INTEGRAL versus - * CONFIDENTIAL constraints, such as one SSL listener configured to require - * client certs providing CONFIDENTIAL, whereas another SSL listener not - * requiring client certs providing mere INTEGRAL constraints. - *

- * The request is secure if it is SSL or it {@link #isForwarded()} is true - * and the scheme matches {@link #getIntegralScheme()()} - */ - public boolean isIntegral(Request request) - { - boolean https = isSecure() || _forwarded && _integralScheme.equalsIgnoreCase(request.getScheme()); - int iPort=getIntegralPort(); - boolean port = iPort<=0||iPort==request.getServerPort(); - return https && port; - } - - /* ------------------------------------------------------------ */ - /* - * @see org.eclipse.jetty.server.Connector#getConfidentialPort() - */ - public int getIntegralPort() - { - return _integralPort; - } - - /* ------------------------------------------------------------ */ - /* - * @see org.eclipse.jetty.server.Connector#getIntegralScheme() - */ - public String getIntegralScheme() - { - return _integralScheme; - } - - /* ------------------------------------------------------------ */ - /** - * The request is confidential IFF it is secure AND the server port - * matches any configured {@link #getConfidentialPort()}. - * This allows separation of listeners providing INTEGRAL versus - * CONFIDENTIAL constraints, such as one SSL listener configured to require - * client certs providing CONFIDENTIAL, whereas another SSL listener not - * requiring client certs providing mere INTEGRAL constraints. - *

- * The request is secure if it is SSL or it {@link #isForwarded()} is true - * and the scheme matches {@link #getConfidentialScheme()} - */ - public boolean isConfidential(Request request) - { - boolean https = isSecure() || _forwarded && _confidentialScheme.equalsIgnoreCase(request.getScheme()); - int confidentialPort=getConfidentialPort(); - boolean port = confidentialPort<=0||confidentialPort==request.getServerPort(); - return https && port; - } - - /* ------------------------------------------------------------ */ - /** - * @param confidentialPort - * The confidentialPort to set. - */ - public void setConfidentialPort(int confidentialPort) - { - _confidentialPort = confidentialPort; - } - - /* ------------------------------------------------------------ */ - /** - * @param confidentialScheme - * The confidentialScheme to set. - */ - public void setConfidentialScheme(String confidentialScheme) - { - _confidentialScheme = confidentialScheme; - } - - /* ------------------------------------------------------------ */ - /** - * @param integralPort - * The integralPort to set. - */ - public void setIntegralPort(int integralPort) - { - _integralPort = integralPort; - } - - /* ------------------------------------------------------------ */ - /** - * @param integralScheme - * The integralScheme to set. - */ - public void setIntegralScheme(String integralScheme) - { - _integralScheme = integralScheme; - } - - /* ------------------------------------------------------------ */ - /** - * Is reverse proxy handling on? - * - * @return true if this connector is checking the x-forwarded-for/host/server headers - */ - public boolean isForwarded() - { - return _forwarded; - } - - /* ------------------------------------------------------------ */ - /** - * Set reverse proxy handling. If set to true, then the X-Forwarded headers (or the headers set in their place) are looked for to set the request protocol, - * host, server and client ip. - * - * @param check true if this connector is checking the x-forwarded-for/host/server headers - * @see #setForwardedForHeader(String) - * @see #setForwardedHostHeader(String) - * @see #setForwardedProtoHeader(String) - * @see #setForwardedServerHeader(String) - */ - public void setForwarded(boolean check) - { - if (check) - LOG.debug("{} is forwarded",this); - _forwarded = check; - } - - /* ------------------------------------------------------------ */ - public String getHostHeader() - { - return _hostHeader; - } - - /* ------------------------------------------------------------ */ - /** - * Set a forced valued for the host header to control what is returned by {@link ServletRequest#getServerName()} and {@link ServletRequest#getServerPort()}. - * This value is only used if {@link #isForwarded()} is true. - * - * @param hostHeader - * The value of the host header to force. - */ - public void setHostHeader(String hostHeader) - { - _hostHeader = hostHeader; - } - - /* ------------------------------------------------------------ */ - /* - * - * @see #setForwarded(boolean) - */ - public String getForwardedHostHeader() - { - return _forwardedHostHeader; - } - - /* ------------------------------------------------------------ */ - /** - * @param forwardedHostHeader - * The header name for forwarded hosts (default x-forwarded-host) - * @see #setForwarded(boolean) - */ - public void setForwardedHostHeader(String forwardedHostHeader) - { - _forwardedHostHeader = forwardedHostHeader; - } - - /* ------------------------------------------------------------ */ - /** - * @return the header name for forwarded server. - * @see #setForwarded(boolean) - */ - public String getForwardedServerHeader() - { - return _forwardedServerHeader; - } - - /* ------------------------------------------------------------ */ - /** - * @param forwardedServerHeader - * The header name for forwarded server (default x-forwarded-server) - * @see #setForwarded(boolean) - */ - public void setForwardedServerHeader(String forwardedServerHeader) - { - _forwardedServerHeader = forwardedServerHeader; - } - - /* ------------------------------------------------------------ */ - /** - * @return the forwarded for header - * @see #setForwarded(boolean) - */ - public String getForwardedForHeader() - { - return _forwardedForHeader; - } - - /* ------------------------------------------------------------ */ - /** - * @param forwardedRemoteAddressHeader - * The header name for forwarded for (default x-forwarded-for) - * @see #setForwarded(boolean) - */ - public void setForwardedForHeader(String forwardedRemoteAddressHeader) - { - _forwardedForHeader = forwardedRemoteAddressHeader; - } - - /* ------------------------------------------------------------ */ - /** - * Get the forwardedProtoHeader. - * - * @return the forwardedProtoHeader (default X-Forwarded-For) - * @see #setForwarded(boolean) - */ - public String getForwardedProtoHeader() - { - return _forwardedProtoHeader; - } - - /* ------------------------------------------------------------ */ - /** - * Set the forwardedProtoHeader. - * - * @param forwardedProtoHeader - * the forwardedProtoHeader to set (default X-Forwarded-For) - * @see #setForwarded(boolean) - */ - public void setForwardedProtoHeader(String forwardedProtoHeader) - { - _forwardedProtoHeader = forwardedProtoHeader; - } - - /* ------------------------------------------------------------ */ - /** - * @return The header name holding a forwarded cipher suite (default null) - */ - public String getForwardedCipherSuiteHeader() - { - return _forwardedCipherSuiteHeader; - } - - /* ------------------------------------------------------------ */ - /** - * @param forwardedCipherSuite - * The header name holding a forwarded cipher suite (default null) - */ - public void setForwardedCipherSuiteHeader(String forwardedCipherSuite) - { - _forwardedCipherSuiteHeader = forwardedCipherSuite; - } - - /* ------------------------------------------------------------ */ - /** - * @return The header name holding a forwarded SSL Session ID (default null) - */ - public String getForwardedSslSessionIdHeader() - { - return _forwardedSslSessionIdHeader; - } - - /* ------------------------------------------------------------ */ - /** - * @param forwardedSslSessionId - * The header name holding a forwarded SSL Session ID (default null) - */ - public void setForwardedSslSessionIdHeader(String forwardedSslSessionId) - { - _forwardedSslSessionIdHeader = forwardedSslSessionId; - } - - - /* ------------------------------------------------------------ */ - @Override - protected void doStart() throws Exception - { - if (_sslContextFactory!=null) - { - _sslContextFactory.checkKeyStore(); - - super.doStart(); - - SSLEngine sslEngine = _sslContextFactory.newSSLEngine(); - - sslEngine.setUseClientMode(false); - - SSLSession sslSession = sslEngine.getSession(); - - if (getRequestHeaderSize() __currentConnection = new ThreadLocal<>(); - private final HttpConfiguration _configuration; + private final HttpChannelConfig _config; private final Connector _connector; - private final ByteBufferPool _bufferPool; // TODO: remove field, use a _connector.getByteBufferPool() + private final ByteBufferPool _bufferPool; private final HttpGenerator _generator; private final HttpChannelOverHttp _channel; private final HttpParser _parser; - private ByteBuffer _requestBuffer = null; - private ByteBuffer _chunk = null; + private volatile ByteBuffer _requestBuffer = null; + private volatile ByteBuffer _chunk = null; public static HttpConnection getCurrentConnection() { @@ -67,12 +67,17 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http { __currentConnection.set(connection); } + + public HttpChannelConfig getHttpChannelConfig() + { + return _config; + } - public HttpConnection(HttpConfiguration config, Connector connector, EndPoint endPoint) + public HttpConnection(HttpChannelConfig config, Connector connector, EndPoint endPoint) { super(endPoint, connector.getExecutor()); - _configuration = config; + _config = config; _connector = connector; _bufferPool = _connector.getByteBufferPool(); _generator = new HttpGenerator(); // TODO: consider moving the generator to the transport, where it belongs @@ -98,7 +103,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http return _connector; } - public HttpChannel getHttpChannel() + public HttpChannel getHttpChannel() { return _channel; } @@ -145,8 +150,9 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http { if (_requestBuffer != null && !_requestBuffer.hasRemaining()) { - _bufferPool.release(_requestBuffer); - _requestBuffer = null; + ByteBuffer buffer=_requestBuffer; + _requestBuffer=null; + _bufferPool.release(buffer); } } @@ -175,7 +181,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http if (!event && BufferUtil.isEmpty(_requestBuffer)) { if (_requestBuffer == null) - _requestBuffer = _bufferPool.acquire(_configuration.getRequestHeaderSize(), false); + _requestBuffer = _bufferPool.acquire(getInputBufferSize(), false); int filled = getEndPoint().fill(_requestBuffer); if (filled==0) // Do a retry on fill 0 (optimisation for SSL connections) @@ -315,7 +321,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http { case NEED_HEADER: { - header = _bufferPool.acquire(_configuration.getResponseHeaderSize(), false); + header = _bufferPool.acquire(_config.getResponseHeaderSize(), false); continue; } case NEED_CHUNK: @@ -533,7 +539,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http // We will need a buffer to read into if (_requestBuffer==null) - _requestBuffer=_bufferPool.acquire(_configuration.getRequestBufferSize(),false); + _requestBuffer=_bufferPool.acquire(getInputBufferSize(),false); // read some data int filled=getEndPoint().fill(_requestBuffer); @@ -590,9 +596,9 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http private class HttpChannelOverHttp extends HttpChannel { - public HttpChannelOverHttp(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput input) + public HttpChannelOverHttp(Connector connector, HttpChannelConfig config, EndPoint endPoint, HttpTransport transport, HttpInput input) { - super(connector,configuration,endPoint,transport,input); + super(connector,config,endPoint,transport,input); } @Override diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpServerConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java similarity index 51% rename from jetty-server/src/main/java/org/eclipse/jetty/server/HttpServerConnectionFactory.java rename to jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java index 09784704d9a..05986d89aa2 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpServerConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java @@ -19,39 +19,41 @@ package org.eclipse.jetty.server; -import java.nio.channels.SocketChannel; +import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.util.annotation.Name; -public class HttpServerConnectionFactory implements ConnectionFactory +public class HttpConnectionFactory extends AbstractConnectionFactory implements HttpChannelConfig.ConnectionFactory { - private final Connector connector; - private final HttpConfiguration configuration; + private final HttpChannelConfig _config; - public HttpServerConnectionFactory(Connector connector) + public HttpConnectionFactory() { - this(connector, new HttpConfiguration(connector.getSslContextFactory(), connector.getSslContextFactory() != null)); + this(new HttpChannelConfig()); + setInputBufferSize(16384); } - public HttpServerConnectionFactory(Connector connector, HttpConfiguration configuration) + + public HttpConnectionFactory(@Name("config") HttpChannelConfig config) { - this.connector = connector; - this.configuration = configuration; + super(HttpVersion.HTTP_1_1.toString()); + _config=config; + addBean(_config); } - - public Connector getConnector() + + @Override + public HttpChannelConfig getHttpChannelConfig() { - return connector; - } - - public HttpConfiguration getHttpConfiguration() - { - return configuration; + return _config; } @Override - public Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment) + public Connection newConnection(Connector connector, EndPoint endPoint) { - return new HttpConnection(getHttpConfiguration(), getConnector(), endPoint); + HttpConnection connection = new HttpConnection(_config, connector, endPoint); + connection.setInputBufferSize(getInputBufferSize()); // TODO constructor injection + return connection; } + } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java index 3fad5007748..98a86b8a017 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.server; import java.io.IOException; import java.io.InterruptedIOException; + import javax.servlet.ServletInputStream; import org.eclipse.jetty.io.EofException; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java index 1a0b845f58e..631a09ddbd5 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java @@ -22,6 +22,7 @@ import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; + import javax.servlet.RequestDispatcher; import javax.servlet.ServletOutputStream; import javax.servlet.ServletRequest; @@ -54,7 +55,7 @@ public class HttpOutput extends ServletOutputStream public HttpOutput(HttpChannel channel) { _channel = channel; - _bufferSize = _channel.getHttpConfiguration().getResponseBufferSize(); + _bufferSize = _channel.getHttpChannelConfig().getOutputBufferSize(); } public boolean isWritten() @@ -209,7 +210,7 @@ public class HttpOutput extends ServletOutputStream HttpContent httpContent = (HttpContent)content; Response response = _channel.getResponse(); String contentType = httpContent.getContentType(); - if (contentType != null) + if (contentType != null && !response.getHttpFields().containsKey(HttpHeader.CONTENT_TYPE.asString())) response.getHttpFields().put(HttpHeader.CONTENT_TYPE, contentType); if (httpContent.getContentLength() > 0) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpServerConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpServerConnector.java index 19db4db28e8..c7f67174379 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpServerConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpServerConnector.java @@ -27,19 +27,44 @@ import org.eclipse.jetty.util.thread.Scheduler; public class HttpServerConnector extends SelectChannelConnector { - public HttpServerConnector(Server server) + public HttpServerConnector( + @Name("server") Server server) { - this(server, null); + this(server,null,null,null,null,null,0,0); } - public HttpServerConnector(Server server, SslContextFactory sslContextFactory) + public HttpServerConnector( + @Name("server") Server server, + @Name("sslContextFactory") SslContextFactory sslContextFactory) { - this(server, null, null, null, sslContextFactory, 0, 0); + this(server,null,sslContextFactory, null, null, null, 0, 0); } - public HttpServerConnector(@Name("server") Server server, @Name("executor") Executor executor, @Name("scheduler") Scheduler scheduler, @Name("bufferPool") ByteBufferPool pool, @Name("sslContextFactory") SslContextFactory sslContextFactory, @Name("acceptors") int acceptors, @Name("selectors") int selectors) + public HttpServerConnector( + @Name("server") Server server, + @Name("connectionFactory") HttpConnectionFactory connectionFactory) { - super(server, executor, scheduler, pool, sslContextFactory, acceptors, selectors); - setDefaultConnectionFactory(new HttpServerConnectionFactory(this)); + this(server,connectionFactory,null, null, null, null, 0, 0); + } + + public HttpServerConnector( + @Name("server") Server server, + @Name("connectionFactory") HttpConnectionFactory connectionFactory, + @Name("sslContextFactory") SslContextFactory sslContextFactory) + { + this(server,connectionFactory,sslContextFactory, null, null, null, 0, 0); + } + + public HttpServerConnector( + @Name("server") Server server, + @Name("connectionFactory") HttpConnectionFactory connectionFactory, + @Name("sslContextFactory") SslContextFactory sslContextFactory, + @Name("executor") Executor executor, + @Name("scheduler") Scheduler scheduler, + @Name("bufferPool") ByteBufferPool pool, + @Name("acceptors") int acceptors, + @Name("selectors") int selectors) + { + super(server,executor,scheduler,pool,acceptors,selectors,AbstractConnectionFactory.getFactories(sslContextFactory,connectionFactory==null?new HttpConnectionFactory():connectionFactory)); } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/LocalConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/LocalConnector.java index 9764dd32a84..0cfec6445b4 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/LocalConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/LocalConnector.java @@ -25,13 +25,10 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; -import javax.net.ssl.SSLEngine; import org.eclipse.jetty.io.ByteArrayEndPoint; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; -import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.ssl.SslContextFactory; @@ -41,22 +38,31 @@ public class LocalConnector extends AbstractConnector { private final BlockingQueue _connects = new LinkedBlockingQueue<>(); + + public LocalConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, ConnectionFactory... factories) + { + super(server,executor,scheduler,pool,acceptors,factories); + setIdleTimeout(30000); + } + public LocalConnector(Server server) { - this(server,null); + this(server, null, null, null, 0, new HttpConnectionFactory()); } public LocalConnector(Server server, SslContextFactory sslContextFactory) { - this(server, null, null, null, sslContextFactory, 0); + this(server, null, null, null, 0,AbstractConnectionFactory.getFactories(sslContextFactory,new HttpConnectionFactory())); } - public LocalConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, - SslContextFactory sslContextFactory, int acceptors) + public LocalConnector(Server server, ConnectionFactory connectionFactory) { - super(server,executor,scheduler,pool, sslContextFactory, acceptors); - setIdleTimeout(30000); - setDefaultConnectionFactory(new HttpServerConnectionFactory(this)); + this(server, null, null, null, 0, connectionFactory); + } + + public LocalConnector(Server server, ConnectionFactory connectionFactory, SslContextFactory sslContextFactory) + { + this(server, null, null, null, 0,AbstractConnectionFactory.getFactories(sslContextFactory,connectionFactory)); } @Override @@ -159,29 +165,10 @@ public class LocalConnector extends AbstractConnector LocalEndPoint endPoint = _connects.take(); endPoint.onOpen(); - SslContextFactory sslContextFactory = getSslContextFactory(); - if (sslContextFactory != null) - { - SSLEngine engine = sslContextFactory.newSSLEngine(endPoint.getRemoteAddress()); - engine.setUseClientMode(false); - - SslConnection sslConnection = new SslConnection(getByteBufferPool(), getExecutor(), endPoint, engine); - endPoint.setConnection(sslConnection); - connectionOpened(sslConnection); - sslConnection.onOpen(); - - EndPoint appEndPoint = sslConnection.getDecryptedEndPoint(); - Connection connection = getDefaultConnectionFactory().newConnection(null, appEndPoint, null); - appEndPoint.setConnection(connection); - connection.onOpen(); - } - else - { - Connection connection = getDefaultConnectionFactory().newConnection(null, endPoint, null); - endPoint.setConnection(connection); - connectionOpened(connection); - connection.onOpen(); - } + Connection connection = getDefaultConnectionFactory().newConnection(this, endPoint); + endPoint.setConnection(connection); + connectionOpened(connection); + connection.onOpen(); } public class LocalEndPoint extends ByteArrayEndPoint diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/NCSARequestLog.java b/jetty-server/src/main/java/org/eclipse/jetty/server/NCSARequestLog.java index dadf023485c..02594402957 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/NCSARequestLog.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/NCSARequestLog.java @@ -24,6 +24,7 @@ import java.io.OutputStreamWriter; import java.io.Writer; import java.util.Locale; import java.util.TimeZone; + import javax.servlet.http.Cookie; import org.eclipse.jetty.http.HttpHeader; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index d38187f278f..4975a2c6008 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -37,6 +37,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; + import javax.servlet.AsyncContext; import javax.servlet.AsyncListener; import javax.servlet.DispatcherType; @@ -118,28 +119,30 @@ public class Request implements HttpServletRequest private static final Collection __defaultLocale = Collections.singleton(Locale.getDefault()); private static final int __NONE = 0, _STREAM = 1, __READER = 2; - private final HttpChannel _channel; + private final HttpChannel _channel; private final HttpFields _fields=new HttpFields(); private final List _requestAttributeListeners=new ArrayList<>(); - private final HttpInput _input; + private final HttpInput _input; + private boolean _secure; private boolean _asyncSupported = true; + private boolean _newContext; + private boolean _cookiesExtracted = false; + private boolean _handled = false; + private boolean _paramsExtracted; + private boolean _requestedSessionIdFromCookie = false; private volatile Attributes _attributes; private Authentication _authentication; private MultiMap _baseParameters; private String _characterEncoding; private ContextHandler.Context _context; - private boolean _newContext; private String _contextPath; private CookieCutter _cookies; - private boolean _cookiesExtracted = false; private DispatcherType _dispatcherType; - private boolean _handled = false; private int _inputState = __NONE; private HttpMethod _httpMethod; private String _httpMethodString; private MultiMap _parameters; - private boolean _paramsExtracted; private String _pathInfo; private int _port; private HttpVersion _httpVersion = HttpVersion.HTTP_1_1; @@ -149,7 +152,6 @@ public class Request implements HttpServletRequest private String _readerEncoding; private InetSocketAddress _remote; private String _requestedSessionId; - private boolean _requestedSessionIdFromCookie = false; private String _requestURI; private Map _savedNewSessions; private String _scheme = URIUtil.HTTP; @@ -164,7 +166,7 @@ public class Request implements HttpServletRequest private MultiPartInputStream _multiPartInputStream; //if the request is a multi-part mime /* ------------------------------------------------------------ */ - public Request(HttpChannel channel, HttpInput input) + public Request(HttpChannel channel, HttpInput input) { _channel = channel; _input = input; @@ -177,7 +179,7 @@ public class Request implements HttpServletRequest } /* ------------------------------------------------------------ */ - public HttpInput getHttpInput() + public HttpInput getHttpInput() { return _input; } @@ -394,7 +396,7 @@ public class Request implements HttpServletRequest /** * @return Returns the connection. */ - public HttpChannel getHttpChannel() + public HttpChannel getHttpChannel() { return _channel; } @@ -613,7 +615,6 @@ public class Request implements HttpServletRequest return Collections.enumeration(__defaultLocale); List langs = new ArrayList<>(); - int size = acceptLanguage.size(); // convert to locals for (String language : acceptLanguage) @@ -1374,7 +1375,13 @@ public class Request implements HttpServletRequest @Override public boolean isSecure() { - return _channel.getHttpConfiguration().isConfidential(this); + return _secure; + } + + /* ------------------------------------------------------------ */ + public void setSecure(boolean secure) + { + _secure=secure; } /* ------------------------------------------------------------ */ diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java index 831719b1c53..d145e2173ad 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java @@ -25,6 +25,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Locale; import java.util.concurrent.atomic.AtomicInteger; + import javax.servlet.RequestDispatcher; import javax.servlet.ServletOutputStream; import javax.servlet.http.Cookie; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java new file mode 100644 index 00000000000..63b0a2ce95b --- /dev/null +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java @@ -0,0 +1,169 @@ +// +// ======================================================================== +// 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.security.cert.X509Certificate; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLSession; + +import org.eclipse.jetty.http.HttpScheme; +import org.eclipse.jetty.io.ssl.SslConnection; +import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint; +import org.eclipse.jetty.server.ssl.ServletSSL; +import org.eclipse.jetty.server.ssl.SslCertificates; +import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +public class SecureRequestCustomizer implements HttpChannelConfig.Customizer +{ + private static final Logger LOG = Log.getLogger(SecureRequestCustomizer.class); + + /** + * The name of the SSLSession attribute that will contain any cached information. + */ + public static final String CACHED_INFO_ATTR = CachedInfo.class.getName(); + + + @Override + public void customize(Connector connector, HttpChannelConfig channelConfig, Request request) + { + if (request.getHttpChannel().getEndPoint() instanceof DecryptedEndPoint) + { + request.setScheme(HttpScheme.HTTPS.asString()); + request.setSecure(true); + SslConnection.DecryptedEndPoint ssl_endp = (DecryptedEndPoint)request.getHttpChannel().getEndPoint(); + SslConnection sslConnection = ssl_endp.getSslConnection(); + SSLEngine sslEngine=sslConnection.getSSLEngine(); + customize(sslEngine,request); + } + + } + + /* ------------------------------------------------------------ */ + /* + * Allow the Listener a chance to customise the request. before the server + * does its stuff.
+ * This allows the required attributes to be set for SSL requests.
+ * The requirements of the Servlet specs are: + *

    + *
  • an attribute named "javax.servlet.request.ssl_session_id" of type + * String (since Servlet Spec 3.0).
  • + *
  • an attribute named "javax.servlet.request.cipher_suite" of type + * String.
  • + *
  • an attribute named "javax.servlet.request.key_size" of type Integer.
  • + *
  • an attribute named "javax.servlet.request.X509Certificate" of type + * java.security.cert.X509Certificate[]. This is an array of objects of type + * X509Certificate, the order of this array is defined as being in ascending + * order of trust. The first certificate in the chain is the one set by the + * client, the next is the one used to authenticate the first, and so on. + *
  • + *
+ * + * @param request + * HttpRequest to be customised. + */ + public void customize(SSLEngine sslEngine, Request request) + { + request.setScheme(HttpScheme.HTTPS.asString()); + SSLSession sslSession = sslEngine.getSession(); + + try + { + String cipherSuite=sslSession.getCipherSuite(); + Integer keySize; + X509Certificate[] certs; + String idStr; + + CachedInfo cachedInfo=(CachedInfo)sslSession.getValue(CACHED_INFO_ATTR); + if (cachedInfo!=null) + { + keySize=cachedInfo.getKeySize(); + certs=cachedInfo.getCerts(); + idStr=cachedInfo.getIdStr(); + } + else + { + keySize=new Integer(ServletSSL.deduceKeyLength(cipherSuite)); + certs=SslCertificates.getCertChain(sslSession); + byte[] bytes = sslSession.getId(); + idStr = TypeUtil.toHexString(bytes); + cachedInfo=new CachedInfo(keySize,certs,idStr); + sslSession.putValue(CACHED_INFO_ATTR,cachedInfo); + } + + if (certs!=null) + request.setAttribute("javax.servlet.request.X509Certificate",certs); + + request.setAttribute("javax.servlet.request.cipher_suite",cipherSuite); + request.setAttribute("javax.servlet.request.key_size",keySize); + request.setAttribute("javax.servlet.request.ssl_session_id", idStr); + } + catch (Exception e) + { + LOG.warn(Log.EXCEPTION,e); + } + } + + @Override + public String toString() + { + return String.format("%s@%x",this.getClass().getSimpleName(),hashCode()); + } + + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + /** + * Simple bundle of information that is cached in the SSLSession. Stores the + * effective keySize and the client certificate chain. + */ + private static class CachedInfo + { + private final X509Certificate[] _certs; + private final Integer _keySize; + private final String _idStr; + + CachedInfo(Integer keySize, X509Certificate[] certs,String idStr) + { + this._keySize=keySize; + this._certs=certs; + this._idStr=idStr; + } + + X509Certificate[] getCerts() + { + return _certs; + } + + Integer getKeySize() + { + return _keySize; + } + + String getIdStr() + { + return _idStr; + } + } + + + +} diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SelectChannelConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SelectChannelConnector.java index 6aec9e8ec85..92a167207a1 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/SelectChannelConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SelectChannelConnector.java @@ -29,7 +29,6 @@ import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.concurrent.Executor; import java.util.concurrent.Future; -import javax.net.ssl.SSLEngine; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; @@ -37,7 +36,6 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.SelectChannelEndPoint; import org.eclipse.jetty.io.SelectorManager; import org.eclipse.jetty.io.SelectorManager.ManagedSelector; -import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.ssl.SslContextFactory; @@ -57,14 +55,33 @@ public class SelectChannelConnector extends AbstractNetworkConnector private volatile boolean _reuseAddress = true; private volatile int _lingerTime = -1; - public SelectChannelConnector(@Name("server") Server server) + + public SelectChannelConnector( + @Name("server") Server server) { - this(server, null); + this(server,null,null,null,0,0,new HttpConnectionFactory()); } - - public SelectChannelConnector(@Name("server")Server server, @Name("sslContextFactory") SslContextFactory sslContextFactory) + + public SelectChannelConnector( + @Name("server") Server server, + @Name("factories") ConnectionFactory... factories) { - this(server, null, null, null, sslContextFactory, 0, 0); + this(server,null,null,null,0,0,factories); + } + + public SelectChannelConnector( + @Name("server") Server server, + @Name("sslContextFactory") SslContextFactory sslContextFactory) + { + this(server,null,null,null,0,0,AbstractConnectionFactory.getFactories(sslContextFactory,new HttpConnectionFactory())); + } + + public SelectChannelConnector( + @Name("server") Server server, + @Name("sslContextFactory") SslContextFactory sslContextFactory, + @Name("factories") ConnectionFactory... factories) + { + this(server,null,null,null,0,0,AbstractConnectionFactory.getFactories(sslContextFactory,factories)); } /** @@ -73,26 +90,20 @@ public class SelectChannelConnector extends AbstractNetworkConnector * @param scheduler A scheduler for this connector or null to use the servers scheduler * @param pool A buffer pool for this connector or null to use a default {@link ByteBufferPool} * @param acceptors the number of acceptor threads to use, or 0 for a default value. + * @param factories Zero or more {@link ConnectionFactory} instances. */ public SelectChannelConnector( - @Name("server") Server server, - @Name("executor") Executor executor, - @Name("scheduler") Scheduler scheduler, - @Name("bufferPool") ByteBufferPool pool, - @Name("sslContextFactory") SslContextFactory sslContextFactory, - @Name("acceptors") int acceptors, - @Name("selectors") int selectors) + @Name("server") Server server, + @Name("executor") Executor executor, + @Name("scheduler") Scheduler scheduler, + @Name("bufferPool") ByteBufferPool pool, + @Name("acceptors") int acceptors, + @Name("selectors") int selectors, + @Name("factories") ConnectionFactory... factories) { - super(server, executor, scheduler, pool, sslContextFactory, acceptors); + super(server,executor,scheduler,pool,acceptors,factories); _manager = new ConnectorSelectorManager(selectors > 0 ? selectors : Math.max(1, (Runtime.getRuntime().availableProcessors()) / 4)); addBean(_manager, true); - - // TODO: why we need to set the linger time when in SSL mode ? - if (sslContextFactory != null) - setSoLingerTime(30000); - - // TODO: we hardcode HTTP, but this is a generic connector that should not hardcode anything - setDefaultConnectionFactory(new HttpServerConnectionFactory(this)); } @Override @@ -257,25 +268,7 @@ public class SelectChannelConnector extends AbstractNetworkConnector protected Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment) { - SslContextFactory sslContextFactory = getSslContextFactory(); - if (sslContextFactory != null) - { - SSLEngine engine = sslContextFactory.newSSLEngine(endPoint.getRemoteAddress()); - engine.setUseClientMode(false); - - SslConnection sslConnection = new SslConnection(getByteBufferPool(), getExecutor(), endPoint, engine); - - EndPoint appEndPoint = sslConnection.getDecryptedEndPoint(); - Connection connection = getDefaultConnectionFactory().newConnection(channel, appEndPoint, attachment); - appEndPoint.setConnection(connection); - connection.onOpen(); - - return sslConnection; - } - else - { - return getDefaultConnectionFactory().newConnection(channel, endPoint, attachment); - } + return getDefaultConnectionFactory().newConnection(this, endPoint); } /** diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java index 8d71ba884e6..d176d73baa4 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -94,7 +95,7 @@ public class Server extends HandlerWrapper implements Attributes public Server(@Name("port")int port) { this((ThreadPool)null); - SelectChannelConnector connector=new SelectChannelConnector(this); + SelectChannelConnector connector=new HttpServerConnector(this); connector.setPort(port); setConnectors(new Connector[]{connector}); } @@ -106,7 +107,7 @@ public class Server extends HandlerWrapper implements Attributes public Server(@Name("address")InetSocketAddress addr) { this((ThreadPool)null); - SelectChannelConnector connector=new SelectChannelConnector(this); + SelectChannelConnector connector=new HttpServerConnector(this); connector.setHost(addr.getHostName()); connector.setPort(addr.getPort()); setConnectors(new Connector[]{connector}); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ServletRequestHttpWrapper.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ServletRequestHttpWrapper.java index f3ca9dcd06b..242a27b441f 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ServletRequestHttpWrapper.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ServletRequestHttpWrapper.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.security.Principal; import java.util.Collection; import java.util.Enumeration; + import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletRequestWrapper; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ServletResponseHttpWrapper.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ServletResponseHttpWrapper.java index 502ef1f7c7e..91278460b70 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ServletResponseHttpWrapper.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ServletResponseHttpWrapper.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.server; import java.io.IOException; import java.util.Collection; + import javax.servlet.ServletResponse; import javax.servlet.ServletResponseWrapper; import javax.servlet.http.Cookie; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java index 83825648679..76a2eaacdd4 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.server; import java.util.EventListener; import java.util.Set; + import javax.servlet.SessionCookieConfig; import javax.servlet.SessionTrackingMode; import javax.servlet.http.Cookie; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java new file mode 100644 index 00000000000..4a818cf4665 --- /dev/null +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java @@ -0,0 +1,98 @@ +// +// ======================================================================== +// 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 javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLSession; + +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.ssl.SslConnection; +import org.eclipse.jetty.util.annotation.Name; +import org.eclipse.jetty.util.ssl.SslContextFactory; + +public class SslConnectionFactory extends AbstractConnectionFactory +{ + private final SslContextFactory _sslContextFactory; + private final String _nextProtocol; + + public SslConnectionFactory() + { + this(null,HttpVersion.HTTP_1_1.asString()); + } + + public SslConnectionFactory(@Name("next") String nextProtocol) + { + this(null,nextProtocol); + } + + public SslConnectionFactory(@Name("sslContextFactory") SslContextFactory factory, @Name("next") String nextProtocol) + { + super("SSL-"+nextProtocol); + _sslContextFactory=factory==null?new SslContextFactory():factory; + _nextProtocol=nextProtocol; + addBean(_sslContextFactory); + } + + public SslContextFactory getSslContextFactory() + { + return _sslContextFactory; + } + + + + @Override + protected void doStart() throws Exception + { + super.doStart(); + + SSLEngine engine = _sslContextFactory.newSSLEngine(); + engine.setUseClientMode(false); + SSLSession session=engine.getSession(); + + if (session.getPacketBufferSize()>getInputBufferSize()) + setInputBufferSize(session.getPacketBufferSize()); + } + + @Override + public Connection newConnection(Connector connector, EndPoint endPoint) + { + SSLEngine engine = _sslContextFactory.newSSLEngine(endPoint.getRemoteAddress()); + engine.setUseClientMode(false); + + SslConnection sslConnection = new SslConnection(connector.getByteBufferPool(), connector.getExecutor(), endPoint, engine); + sslConnection.setInputBufferSize(getInputBufferSize()); + EndPoint decrypted_endp = sslConnection.getDecryptedEndPoint(); + + ConnectionFactory next = connector.getConnectionFactory(_nextProtocol); + Connection connection = next.newConnection(connector, decrypted_endp); + + decrypted_endp.setConnection(connection); + return sslConnection; + } + + @Override + public String toString() + { + return String.format("%s@%x{%s}",this.getClass().getSimpleName(),hashCode(),getProtocol()); + } +} diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/UserIdentity.java b/jetty-server/src/main/java/org/eclipse/jetty/server/UserIdentity.java index 5c39e7f5084..90f072619dd 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/UserIdentity.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/UserIdentity.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.server; import java.security.Principal; import java.util.Map; + import javax.security.auth.Subject; /* ------------------------------------------------------------ */ diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java index 22f31e99228..d97874d1ac0 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java @@ -37,6 +37,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.Future; + import javax.servlet.DispatcherType; import javax.servlet.Filter; import javax.servlet.FilterRegistration; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java index b68575878e6..f5ced00d3ff 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.server.handler; import java.io.IOException; import java.util.HashMap; import java.util.Map; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java index 1664aa0b94d..a9c86c13f6b 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.util.Locale; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java index f1298dba67b..35e1aa10781 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.server.handler; import java.io.IOException; import java.io.OutputStream; import java.net.URL; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -162,6 +163,10 @@ public class DefaultHandler extends AbstractHandler } } + writer.write("
"); + writer.write(" "); + writer.write("Powered by Jetty:// Java Web Server
\n"); + writer.write("\n\n\n"); writer.flush(); response.setContentLength(writer.size()); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java index 71e66990161..e8d7311fe75 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java index b9e15f19c3d..2a2d8e3ba36 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.server.handler; import java.io.IOException; import java.util.List; import java.util.concurrent.CountDownLatch; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerList.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerList.java index 3dca6423057..e295516198f 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerList.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerList.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.server.handler; import java.io.IOException; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java index a18b937e7d8..dd5dd51fe8e 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.server.handler; import java.io.IOException; import java.util.List; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HotSwapHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HotSwapHandler.java index ff5ce0c735f..a930c35a27d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HotSwapHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HotSwapHandler.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.server.handler; import java.io.IOException; import java.util.List; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java index cd5000bd809..71024db947b 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java @@ -23,6 +23,7 @@ import java.net.InetSocketAddress; import java.util.Collections; import java.util.List; import java.util.Map; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/MovedContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/MovedContextHandler.java index f57ef9fbae1..c21f256c562 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/MovedContextHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/MovedContextHandler.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.server.handler; import java.io.IOException; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java index e6ca16b7948..6131e3b3cd0 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.server.handler; import java.io.IOException; + import javax.servlet.DispatcherType; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java index f7ae5eaae6d..28a81149bcd 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.server.handler; import java.io.IOException; import java.io.OutputStream; import java.net.MalformedURLException; + import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ScopedHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ScopedHandler.java index a3ceb46a400..60a049dc545 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ScopedHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ScopedHandler.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.server.handler; import java.io.IOException; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java index 972ac8ba64c..3ddfad8b2f1 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.server.handler; import java.io.IOException; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java index 8d802eb73ab..b982b914e2e 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.server.handler; import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/nio/NetworkTrafficSelectChannelConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/nio/NetworkTrafficSelectChannelConnector.java index f4c94cea698..1f8079e7714 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/nio/NetworkTrafficSelectChannelConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/nio/NetworkTrafficSelectChannelConnector.java @@ -24,14 +24,20 @@ import java.nio.channels.SocketChannel; import java.util.ConcurrentModificationException; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executor; +import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.NetworkTrafficListener; import org.eclipse.jetty.io.NetworkTrafficSelectChannelEndPoint; import org.eclipse.jetty.io.SelectChannelEndPoint; import org.eclipse.jetty.io.SelectorManager; +import org.eclipse.jetty.server.ConnectionFactory; +import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.SelectChannelConnector; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.util.thread.Scheduler; /** *

A specialized version of {@link SelectChannelConnector} that supports {@link NetworkTrafficListener}s.

@@ -42,10 +48,30 @@ public class NetworkTrafficSelectChannelConnector extends SelectChannelConnector { private final List listeners = new CopyOnWriteArrayList(); - public NetworkTrafficSelectChannelConnector(Server server) { - super(server); + this(server,null,null,null,0,0,new HttpConnectionFactory()); + } + + public NetworkTrafficSelectChannelConnector(Server server, ConnectionFactory connectionFactory, SslContextFactory sslContextFactory) + { + super(server,sslContextFactory,connectionFactory); + } + + public NetworkTrafficSelectChannelConnector(Server server, ConnectionFactory connectionFactory) + { + super(server,connectionFactory); + } + + public NetworkTrafficSelectChannelConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, int selectors, + ConnectionFactory... factories) + { + super(server,executor,scheduler,pool,acceptors,selectors,factories); + } + + public NetworkTrafficSelectChannelConnector(Server server, SslContextFactory sslContextFactory) + { + super(server,sslContextFactory); } /** diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java index 3dd69f4530a..79dba9b601e 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java @@ -27,6 +27,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; + import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSessionActivationListener; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java index 0d9aa45a4ec..e26ae62ed5c 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.server.session; import java.security.SecureRandom; import java.util.Random; + import javax.servlet.http.HttpServletRequest; import org.eclipse.jetty.server.SessionIdManager; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java index 191836f502b..cca7aaf9109 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java @@ -18,6 +18,8 @@ package org.eclipse.jetty.server.session; +import static java.lang.Math.round; + import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; @@ -28,6 +30,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; + import javax.servlet.SessionCookieConfig; import javax.servlet.SessionTrackingMode; import javax.servlet.http.HttpServletRequest; @@ -51,8 +54,6 @@ import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.statistic.CounterStatistic; import org.eclipse.jetty.util.statistic.SampleStatistic; -import static java.lang.Math.round; - /* ------------------------------------------------------------ */ /** * An Abstract implementation of SessionManager. The partial implementation of diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java index d98e353569b..b4e7850c570 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java @@ -28,6 +28,7 @@ import java.util.Iterator; import java.util.Map; import java.util.Random; import java.util.Set; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java index 7d88eae893e..878365e0517 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java @@ -31,6 +31,7 @@ import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; + import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java index 7db59d1aa92..550d0520404 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.util.Enumeration; + import javax.servlet.http.HttpServletRequest; import org.eclipse.jetty.util.IO; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java index b759b815e30..2e82d7e5c28 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java @@ -35,6 +35,7 @@ import java.util.List; import java.util.Random; import java.util.Timer; import java.util.TimerTask; + import javax.naming.InitialContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java index fe32a93688f..e5d1712a593 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java @@ -35,6 +35,7 @@ import java.util.ListIterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java index cc18ab03302..bb3f020ec3d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.server.session; import java.io.IOException; import java.util.EnumSet; import java.util.EventListener; + import javax.servlet.DispatcherType; import javax.servlet.ServletException; import javax.servlet.SessionTrackingMode; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslCertificates.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslCertificates.java index a1829ef5cdb..3bfa33b5edf 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslCertificates.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslCertificates.java @@ -19,15 +19,11 @@ package org.eclipse.jetty.server.ssl; import java.io.ByteArrayInputStream; -import java.io.IOException; import java.security.cert.X509Certificate; -import javax.net.ssl.SSLEngine; + import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; -import org.eclipse.jetty.http.HttpScheme; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -35,11 +31,6 @@ public class SslCertificates { private static final Logger LOG = Log.getLogger(SslCertificates.class); - /** - * The name of the SSLSession attribute that will contain any cached information. - */ - static final String CACHED_INFO_ATTR = CachedInfo.class.getName(); - public static X509Certificate[] getCertChain(SSLSession sslSession) { try @@ -73,105 +64,4 @@ public class SslCertificates } - /* ------------------------------------------------------------ */ - /** - * Allow the Listener a chance to customise the request. before the server - * does its stuff.
- * This allows the required attributes to be set for SSL requests.
- * The requirements of the Servlet specs are: - *
    - *
  • an attribute named "javax.servlet.request.ssl_session_id" of type - * String (since Servlet Spec 3.0).
  • - *
  • an attribute named "javax.servlet.request.cipher_suite" of type - * String.
  • - *
  • an attribute named "javax.servlet.request.key_size" of type Integer.
  • - *
  • an attribute named "javax.servlet.request.X509Certificate" of type - * java.security.cert.X509Certificate[]. This is an array of objects of type - * X509Certificate, the order of this array is defined as being in ascending - * order of trust. The first certificate in the chain is the one set by the - * client, the next is the one used to authenticate the first, and so on. - *
  • - *
- * - * @param request - * HttpRequest to be customised. - */ - public static void customize(SSLEngine sslEngine, Request request) throws IOException - { - request.setScheme(HttpScheme.HTTPS.asString()); - SSLSession sslSession = sslEngine.getSession(); - - try - { - String cipherSuite=sslSession.getCipherSuite(); - Integer keySize; - X509Certificate[] certs; - String idStr; - - CachedInfo cachedInfo=(CachedInfo)sslSession.getValue(CACHED_INFO_ATTR); - if (cachedInfo!=null) - { - keySize=cachedInfo.getKeySize(); - certs=cachedInfo.getCerts(); - idStr=cachedInfo.getIdStr(); - } - else - { - keySize=new Integer(ServletSSL.deduceKeyLength(cipherSuite)); - certs=SslCertificates.getCertChain(sslSession); - byte[] bytes = sslSession.getId(); - idStr = TypeUtil.toHexString(bytes); - cachedInfo=new CachedInfo(keySize,certs,idStr); - sslSession.putValue(CACHED_INFO_ATTR,cachedInfo); - } - - if (certs!=null) - request.setAttribute("javax.servlet.request.X509Certificate",certs); - - request.setAttribute("javax.servlet.request.cipher_suite",cipherSuite); - request.setAttribute("javax.servlet.request.key_size",keySize); - request.setAttribute("javax.servlet.request.ssl_session_id", idStr); - } - catch (Exception e) - { - LOG.warn(Log.EXCEPTION,e); - } - } - - /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ - /** - * Simple bundle of information that is cached in the SSLSession. Stores the - * effective keySize and the client certificate chain. - */ - private static class CachedInfo - { - private final X509Certificate[] _certs; - private final Integer _keySize; - private final String _idStr; - - CachedInfo(Integer keySize, X509Certificate[] certs,String idStr) - { - this._keySize=keySize; - this._certs=certs; - this._idStr=idStr; - } - - X509Certificate[] getCerts() - { - return _certs; - } - - Integer getKeySize() - { - return _keySize; - } - - String getIdStr() - { - return _idStr; - } - } - } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslConnector.java index 19f40ba0f9b..073080a0d37 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslConnector.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.server.ssl; import java.io.File; import java.security.SecureRandom; import java.security.Security; + import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java index e1fff1058dc..8301bb0b534 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java @@ -18,6 +18,8 @@ package org.eclipse.jetty.server.ssl; +import org.eclipse.jetty.server.AbstractConnectionFactory; +import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.SelectChannelConnector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.ssl.SslContextFactory; @@ -33,6 +35,6 @@ public class SslSelectChannelConnector extends SelectChannelConnector { public SslSelectChannelConnector(Server server) { - super(server,new SslContextFactory()); + super(server,null,null,null,0,0,AbstractConnectionFactory.getFactories(new SslContextFactory(),new HttpConnectionFactory())); } } diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractHttpTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractHttpTest.java index 0d8277a8324..1267bd9672f 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractHttpTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractHttpTest.java @@ -18,6 +18,9 @@ package org.eclipse.jetty.server; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -25,6 +28,7 @@ import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; import java.net.URISyntaxException; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -37,9 +41,6 @@ import org.eclipse.jetty.util.log.StdErrLog; import org.junit.After; import org.junit.Before; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - public abstract class AbstractHttpTest { protected static Server server; @@ -56,7 +57,7 @@ public abstract class AbstractHttpTest public void setUp() throws Exception { server = new Server(); - connector = new SelectChannelConnector(server); + connector = new HttpServerConnector(server); connector.setIdleTimeout(10000); server.addConnector(connector); httpParser = new SimpleHttpParser(); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java index c84fd06fe6e..6e5097b3e12 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java @@ -18,6 +18,9 @@ package org.eclipse.jetty.server; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -25,6 +28,7 @@ import java.net.Socket; import java.util.Arrays; import java.util.concurrent.Exchanger; import java.util.concurrent.TimeUnit; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -39,9 +43,6 @@ import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - public class AsyncRequestReadTest { private static Server server; @@ -52,7 +53,7 @@ public class AsyncRequestReadTest public static void startServer() throws Exception { server = new Server(); - connector = new SelectChannelConnector(server); + connector = new HttpServerConnector(server); connector.setIdleTimeout(10000); server.addConnector(connector); server.setHandler(new EmptyHandler()); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncStressTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncStressTest.java index 409fcbbef6b..77829778d10 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncStressTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncStressTest.java @@ -18,6 +18,8 @@ package org.eclipse.jetty.server; +import static org.junit.Assert.assertEquals; + import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; @@ -25,6 +27,7 @@ import java.net.Socket; import java.util.Random; import java.util.Timer; import java.util.TimerTask; + import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; @@ -44,8 +47,6 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; -import static org.junit.Assert.assertEquals; - public class AsyncStressTest { private static final Logger LOG = Log.getLogger(AsyncStressTest.class); @@ -72,7 +73,7 @@ public class AsyncStressTest { _server.manage(_threads); _threads.setMaxThreads(50); - _connector = new SelectChannelConnector(_server); + _connector = new HttpServerConnector(_server); _connector.setIdleTimeout(120000); _server.setConnectors(new Connector[]{ _connector }); _server.setHandler(_handler); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/CheckReverseProxyHeadersTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/CheckReverseProxyHeadersTest.java index 2c0f72dff19..987b360fe0c 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/CheckReverseProxyHeadersTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/CheckReverseProxyHeadersTest.java @@ -18,7 +18,12 @@ package org.eclipse.jetty.server; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import java.io.IOException; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -26,10 +31,6 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.handler.AbstractHandler; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - /** * */ @@ -97,11 +98,11 @@ public class CheckReverseProxyHeadersTest private void testRequest(String headers, RequestValidator requestValidator) throws Exception { Server server = new Server(); - LocalConnector connector = new LocalConnector(server); // Activate reverse proxy headers checking - HttpConfiguration httpConfiguration = new HttpConfiguration(null, false); - httpConfiguration.setForwarded(true); - connector.setDefaultConnectionFactory(new HttpServerConnectionFactory(connector, httpConfiguration)); + HttpConnectionFactory http = new HttpConnectionFactory(); + http.getHttpChannelConfig().addCustomizer(new ForwardedRequestCustomizer()); + + LocalConnector connector = new LocalConnector(server,http); server.setConnectors(new Connector[] {connector}); ValidationHandler validationHandler = new ValidationHandler(requestValidator); @@ -159,6 +160,7 @@ public class CheckReverseProxyHeadersTest return _error; } + @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorCloseTestBase.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorCloseTestBase.java index 73ddc0ca1ff..f7409ea175c 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorCloseTestBase.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorCloseTestBase.java @@ -18,6 +18,8 @@ package org.eclipse.jetty.server; +import static org.junit.Assert.assertTrue; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -28,8 +30,6 @@ import java.util.concurrent.TimeUnit; import org.junit.Test; -import static org.junit.Assert.assertTrue; - /** * HttpServer Tester. */ diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java index 0a1e019e171..0d8a0bc0081 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java @@ -18,6 +18,10 @@ package org.eclipse.jetty.server; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.matchers.JUnitMatchers.containsString; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -25,6 +29,7 @@ import java.net.Socket; import java.net.SocketException; import java.util.concurrent.Exchanger; import java.util.concurrent.TimeUnit; + import javax.net.ssl.SSLException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -37,10 +42,6 @@ import org.eclipse.jetty.util.IO; import org.junit.Assert; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.matchers.JUnitMatchers.containsString; - public abstract class ConnectorTimeoutTest extends HttpServerTestFixture { protected static final int MAX_IDLE_TIME=500; @@ -243,14 +244,12 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture // further writes will get broken pipe or similar try { - for (int i=0;i<1000;i++) + long end=System.currentTimeMillis()+MAX_IDLE_TIME+3000; + while (System.currentTimeMillis()(null,null,null,null,null) + HttpChannel channel = new HttpChannel(null,new HttpChannelConfig(),null,null,null) { - @Override - public HttpConfiguration getHttpConfiguration() - { - return configuration; - } - @Override public ByteBufferPool getByteBufferPool() { diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/InclusiveByteRangeTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/InclusiveByteRangeTest.java index 08b94975b2a..43e46f0b82c 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/InclusiveByteRangeTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/InclusiveByteRangeTest.java @@ -18,15 +18,15 @@ package org.eclipse.jetty.server; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + import java.util.List; import java.util.Vector; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - public class InclusiveByteRangeTest { @SuppressWarnings("unchecked") diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java index 3b178c10ac4..7b1c0e9ce2e 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java @@ -18,9 +18,12 @@ package org.eclipse.jetty.server; +import static org.junit.Assert.assertEquals; + import java.io.IOException; import java.io.InputStream; import java.util.concurrent.atomic.AtomicInteger; + import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; @@ -37,8 +40,6 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.assertEquals; - public class LocalAsyncContextTest { protected Server _server = new Server(); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/LocalConnectorTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/LocalConnectorTest.java index 817eb6509de..5191aaa7738 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/LocalConnectorTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/LocalConnectorTest.java @@ -18,13 +18,13 @@ package org.eclipse.jetty.server; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertThat; + import org.junit.After; import org.junit.Before; import org.junit.Test; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertThat; - public class LocalConnectorTest { private Server _server; diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/NetworkTrafficListenerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/NetworkTrafficListenerTest.java index bb9d20a1846..80885c74940 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/NetworkTrafficListenerTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/NetworkTrafficListenerTest.java @@ -18,6 +18,9 @@ package org.eclipse.jetty.server; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -27,6 +30,7 @@ import java.nio.ByteBuffer; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; + import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; @@ -41,9 +45,6 @@ import org.junit.After; import org.junit.Ignore; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - @Ignore public class NetworkTrafficListenerTest { diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/PartialRFC2616Test.java b/jetty-server/src/test/java/org/eclipse/jetty/server/PartialRFC2616Test.java index de65ba3c81d..11887164f33 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/PartialRFC2616Test.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/PartialRFC2616Test.java @@ -24,6 +24,11 @@ */ package org.eclipse.jetty.server; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.util.Date; import java.util.Enumeration; import java.util.List; @@ -37,11 +42,6 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - /** * */ diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java index f2ffab3e23b..926a6ecfed7 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java @@ -18,6 +18,14 @@ package org.eclipse.jetty.server; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + import java.io.BufferedReader; import java.io.File; import java.io.FileReader; @@ -29,6 +37,7 @@ import java.util.Arrays; import java.util.Enumeration; import java.util.Map; import java.util.concurrent.TimeUnit; + import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; @@ -48,14 +57,6 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - public class RequestTest { private static final Logger LOG = Log.getLogger(RequestTest.class); @@ -67,15 +68,13 @@ public class RequestTest public void init() throws Exception { _server = new Server(); - _connector = new LocalConnector(_server); - HttpConfiguration httpConfiguration = new HttpConfiguration(null, false); - httpConfiguration.setRequestHeaderSize(512); - httpConfiguration.setRequestBufferSize(1024); - httpConfiguration.setResponseHeaderSize(512); - httpConfiguration.setResponseBufferSize(2048); - httpConfiguration.setForwarded(true); - HttpServerConnectionFactory defaultConnectionFactory = new HttpServerConnectionFactory(_connector, httpConfiguration); - _connector.setDefaultConnectionFactory(defaultConnectionFactory); + HttpConnectionFactory http = new HttpConnectionFactory(); + http.setInputBufferSize(1024); + http.getHttpChannelConfig().setRequestHeaderSize(512); + http.getHttpChannelConfig().setResponseHeaderSize(512); + http.getHttpChannelConfig().setOutputBufferSize(2048); + http.getHttpChannelConfig().addCustomizer(new ForwardedRequestCustomizer()); + _connector = new LocalConnector(_server,http); _server.addConnector(_connector); _handler = new RequestHandler(); _server.setHandler(_handler); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ResourceCacheTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ResourceCacheTest.java index 5f2d81ea21a..3566366b799 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ResourceCacheTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ResourceCacheTest.java @@ -18,6 +18,9 @@ package org.eclipse.jetty.server; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; @@ -30,9 +33,6 @@ import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceCollection; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - public class ResourceCacheTest { @Test diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java index 71123685f53..bfa46cc2a57 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java @@ -18,6 +18,11 @@ package org.eclipse.jetty.server; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.io.IOException; import java.io.InputStreamReader; import java.io.LineNumberReader; @@ -26,6 +31,7 @@ import java.net.Socket; import java.nio.ByteBuffer; import java.util.Iterator; import java.util.Locale; + import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; @@ -49,11 +55,6 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - public class ResponseTest { private Server _server; @@ -65,14 +66,15 @@ public class ResponseTest { _server = new Server(); _scheduler = new TimerScheduler(); - LocalConnector connector = new LocalConnector(_server, null, _scheduler, null, null, 1); + HttpChannelConfig config = new HttpChannelConfig(); + LocalConnector connector = new LocalConnector(_server,null,_scheduler,null,1,new HttpConnectionFactory(config)); _server.addConnector(connector); _server.setHandler(new DumpHandler()); _server.start(); AbstractEndPoint endp = new ByteArrayEndPoint(_scheduler, 5000); ByteBufferHttpInput input = new ByteBufferHttpInput(); - _channel = new HttpChannel(connector, new HttpConfiguration(null, false), endp, new HttpTransport() + _channel = new HttpChannel(connector, new HttpChannelConfig(), endp, new HttpTransport() { @Override public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent) throws IOException diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/SelectChannelStatisticsTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/SelectChannelStatisticsTest.java index b57e593833e..faa824b12fb 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/SelectChannelStatisticsTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/SelectChannelStatisticsTest.java @@ -18,6 +18,9 @@ package org.eclipse.jetty.server; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -25,6 +28,7 @@ import java.io.PrintWriter; import java.net.Socket; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -41,9 +45,6 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - public class SelectChannelStatisticsTest { private static final Logger LOG = Log.getLogger(SelectChannelStatisticsTest.class); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/SelectChannelTimeoutTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/SelectChannelTimeoutTest.java index a7cdfc94310..d91cf2cb539 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/SelectChannelTimeoutTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/SelectChannelTimeoutTest.java @@ -18,6 +18,8 @@ package org.eclipse.jetty.server; +import static org.junit.Assert.assertTrue; + import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; @@ -28,8 +30,6 @@ import org.eclipse.jetty.util.IO; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.assertTrue; - public class SelectChannelTimeoutTest extends ConnectorTimeoutTest { @Before diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/SlowClientWithPipelinedRequestTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/SlowClientWithPipelinedRequestTest.java index bf55b5a7a78..8e10bc21c45 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/SlowClientWithPipelinedRequestTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/SlowClientWithPipelinedRequestTest.java @@ -18,14 +18,16 @@ package org.eclipse.jetty.server; +import static org.hamcrest.Matchers.lessThan; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.nio.ByteBuffer; -import java.nio.channels.SocketChannel; import java.util.Arrays; import java.util.concurrent.atomic.AtomicInteger; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -38,8 +40,6 @@ import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; -import static org.hamcrest.Matchers.lessThan; - public class SlowClientWithPipelinedRequestTest { private final AtomicInteger handles = new AtomicInteger(); @@ -49,13 +49,12 @@ public class SlowClientWithPipelinedRequestTest public void startServer(Handler handler) throws Exception { server = new Server(); - connector = new SelectChannelConnector(server); - connector.setDefaultConnectionFactory(new HttpServerConnectionFactory(connector) + connector = new SelectChannelConnector(server,new HttpConnectionFactory() { @Override - public Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment) + public Connection newConnection(Connector connector, EndPoint endPoint) { - return new HttpConnection(getHttpConfiguration(), getConnector(), endPoint) + return new HttpConnection(new HttpChannelConfig(),connector,endPoint) { @Override public void onFillable() diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/StressTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/StressTest.java index 56a90931b41..1e1e93d1e23 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/StressTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/StressTest.java @@ -18,12 +18,17 @@ package org.eclipse.jetty.server; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + import java.io.IOException; import java.net.Socket; import java.util.Queue; import java.util.Random; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicInteger; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -41,10 +46,6 @@ import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeTrue; - @Ignore public class StressTest { @@ -93,7 +94,7 @@ public class StressTest _server = new Server(_threads); _server.manage(_threads); - _connector = new SelectChannelConnector(_server,null,null,null,null,1,1); + _connector = new SelectChannelConnector(_server,null,null,null,1, 1,new HttpConnectionFactory()); _connector.setAcceptQueueSize(5000); _connector.setIdleTimeout(30000); _server.addConnector(_connector); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/SuspendHandler.java b/jetty-server/src/test/java/org/eclipse/jetty/server/SuspendHandler.java index aafaef02ff3..c15ab9ca64f 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/SuspendHandler.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/SuspendHandler.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.server; import java.io.IOException; import java.io.InputStream; + import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerCollectionTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerCollectionTest.java index 895375d340d..589b7a08821 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerCollectionTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerCollectionTest.java @@ -18,7 +18,12 @@ package org.eclipse.jetty.server.handler; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import java.io.IOException; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -29,10 +34,6 @@ import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - public class ContextHandlerCollectionTest { @Test diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerTest.java index 49562d89d89..0831e44d9b7 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerTest.java @@ -18,12 +18,18 @@ package org.eclipse.jetty.server.handler; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -37,11 +43,6 @@ import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - /** * @version $Revision$ */ diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/IPAccessHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/IPAccessHandlerTest.java index 19d2d2a4fb6..c9e412d0e29 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/IPAccessHandlerTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/IPAccessHandlerTest.java @@ -18,6 +18,9 @@ package org.eclipse.jetty.server.handler; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.BufferedReader; import java.io.EOFException; import java.io.IOException; @@ -30,6 +33,7 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -47,9 +51,6 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - @RunWith(Parameterized.class) public class IPAccessHandlerTest { diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerTest.java index f5a84b756a5..ac6f360e885 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerTest.java @@ -22,6 +22,7 @@ import java.net.URI; import junit.framework.Assert; import junit.framework.TestCase; + import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.SelectChannelConnector; import org.eclipse.jetty.server.Server; diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ScopedHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ScopedHandlerTest.java index 4c8f98ab355..3199ab82495 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ScopedHandlerTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ScopedHandlerTest.java @@ -18,7 +18,10 @@ package org.eclipse.jetty.server.handler; +import static org.junit.Assert.assertEquals; + import java.io.IOException; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -27,8 +30,6 @@ import org.eclipse.jetty.server.Request; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.assertEquals; - public class ScopedHandlerTest { private StringBuilder _history=new StringBuilder(); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ShutdownHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ShutdownHandlerTest.java index fa519bbfbfd..b5c32d77df7 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ShutdownHandlerTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ShutdownHandlerTest.java @@ -18,8 +18,13 @@ package org.eclipse.jetty.server.handler; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -31,10 +36,6 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.when; - public class ShutdownHandlerTest { @Mock private HttpServletRequest request; diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/StatisticsHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/StatisticsHandlerTest.java index c18d438ac94..c8adcab2901 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/StatisticsHandlerTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/StatisticsHandlerTest.java @@ -18,11 +18,17 @@ package org.eclipse.jetty.server.handler; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -37,11 +43,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - public class StatisticsHandlerTest { private Server _server; diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java index 71f4030b5d2..17193fcd5e8 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java @@ -18,15 +18,15 @@ package org.eclipse.jetty.server.session; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import javax.servlet.SessionCookieConfig; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.eclipse.jetty.http.HttpCookie; import org.junit.Test; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; /** * SessionCookieTest * diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLCloseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLCloseTest.java index a9fb86689e9..b01aca39f42 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLCloseTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLCloseTest.java @@ -26,6 +26,7 @@ import java.io.OutputStream; import java.net.Socket; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; + import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; @@ -34,6 +35,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import junit.framework.TestCase; + import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Request; diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java index 328b7989d58..a155e200a02 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java @@ -23,6 +23,11 @@ package org.eclipse.jetty.server.ssl; +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -31,8 +36,10 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.net.HttpURLConnection; import java.net.Socket; +import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.URL; + import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; @@ -42,8 +49,7 @@ import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.server.HttpConfiguration; -import org.eclipse.jetty.server.HttpServerConnectionFactory; +import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.SelectChannelConnector; import org.eclipse.jetty.server.Server; @@ -56,11 +62,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import static org.hamcrest.Matchers.greaterThan; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; - /** * */ @@ -74,9 +75,11 @@ public class SSLEngineTest /** The request. */ private static final String REQUEST0_HEADER="POST /r0 HTTP/1.1\n"+"Host: localhost\n"+"Content-Type: text/xml\n"+"Content-Length: "; private static final String REQUEST1_HEADER="POST /r1 HTTP/1.1\n"+"Host: localhost\n"+"Content-Type: text/xml\n"+"Connection: close\n"+"Content-Length: "; - private static final String REQUEST_CONTENT="\n" - +"\n"+""; + private static final String REQUEST_CONTENT= + "\n"+ + "\n"+ + ""; private static final String REQUEST0=REQUEST0_HEADER+REQUEST_CONTENT.getBytes().length+"\n\n"+REQUEST_CONTENT; private static final String REQUEST1=REQUEST1_HEADER+REQUEST_CONTENT.getBytes().length+"\n\n"+REQUEST_CONTENT; @@ -101,12 +104,11 @@ public class SSLEngineTest sslContextFactory.setKeyManagerPassword("keypwd"); server=new Server(); - connector=new SelectChannelConnector(server, sslContextFactory); + HttpConnectionFactory http = new HttpConnectionFactory(); + http.setInputBufferSize(512); + http.getHttpChannelConfig().setRequestHeaderSize(512); + connector=new SelectChannelConnector(server, sslContextFactory, http); connector.setPort(0); - HttpConfiguration httpConfiguration = new HttpConfiguration(sslContextFactory, true); - httpConfiguration.setRequestBufferSize(512); - httpConfiguration.setRequestHeaderSize(512); - connector.setDefaultConnectionFactory(new HttpServerConnectionFactory(connector, httpConfiguration)); server.addConnector(connector); } @@ -223,7 +225,7 @@ public class SSLEngineTest // System.err.println("read:"+i); // Read the response. String responses=readResponse(client[i]); - // Check the response + // Check the responses assertEquals(String.format("responses loop=%d connection=%d",l,i),RESPONSE0+RESPONSE0+RESPONSE1,responses); } } @@ -233,7 +235,13 @@ public class SSLEngineTest { if (client[i]!=null) { - client[i].close(); + try + { + assertEquals(-1,client[i].getInputStream().read()); + } + catch(SocketException e) + { + } } } } @@ -340,12 +348,13 @@ public class SSLEngineTest private static class HelloWorldHandler extends AbstractHandler { + @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // System.err.println("HANDLE "+request.getRequestURI()); String ssl_id = (String)request.getAttribute("javax.servlet.request.ssl_session_id"); assertNotNull(ssl_id); - + if (request.getParameter("dump")!=null) { ServletOutputStream out=response.getOutputStream(); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLSelectChannelConnectorLoadTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLSelectChannelConnectorLoadTest.java index 246e0955b84..9becb9e2044 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLSelectChannelConnectorLoadTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLSelectChannelConnectorLoadTest.java @@ -32,6 +32,7 @@ import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; + import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManagerFactory; @@ -62,7 +63,7 @@ public class SSLSelectChannelConnectorLoadTest sslContextFactory.setKeyStorePath(keystorePath); sslContextFactory.setKeyStorePassword("storepwd"); sslContextFactory.setKeyManagerPassword("keypwd"); - sslContextFactory.setTrustStore(keystorePath); + sslContextFactory.setTrustStorePath(keystorePath); sslContextFactory.setTrustStorePassword("storepwd"); server = new Server(); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SelectChannelServerSslTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SelectChannelServerSslTest.java index 81ef6f254b2..f046b9cf734 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SelectChannelServerSslTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SelectChannelServerSslTest.java @@ -18,12 +18,15 @@ package org.eclipse.jetty.server.ssl; +import static org.junit.Assert.assertEquals; + import java.io.FileInputStream; import java.io.OutputStream; import java.net.Socket; import java.net.SocketException; import java.security.KeyStore; import java.util.Arrays; + import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; @@ -37,8 +40,6 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; -import static org.junit.Assert.assertEquals; - /** * HttpServer Tester. */ @@ -102,14 +103,14 @@ public class SelectChannelServerSslTest extends HttpServerTestBase sslContextFactory.setKeyStorePath(keystorePath); sslContextFactory.setKeyStorePassword("storepwd"); sslContextFactory.setKeyManagerPassword("keypwd"); - sslContextFactory.setTrustStore(keystorePath); + sslContextFactory.setTrustStorePath(keystorePath); sslContextFactory.setTrustStorePassword("storepwd"); SelectChannelConnector connector = new SelectChannelConnector(_server, sslContextFactory); startServer(connector); KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); - keystore.load(new FileInputStream(connector.getSslContextFactory().getKeyStorePath()), "storepwd".toCharArray()); + keystore.load(new FileInputStream(sslContextFactory.getKeyStorePath()), "storepwd".toCharArray()); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keystore); __sslContext = SSLContext.getInstance("TLS"); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslBytesServerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslBytesServerTest.java index 77908e9de69..e5afb0b8e71 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslBytesServerTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslBytesServerTest.java @@ -38,6 +38,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Matcher; import java.util.regex.Pattern; + import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.servlet.ServletException; diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslSelectChannelTimeoutTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslSelectChannelTimeoutTest.java index 036727da2e3..62e7eb1db20 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslSelectChannelTimeoutTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslSelectChannelTimeoutTest.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.server.ssl; import java.io.FileInputStream; import java.net.Socket; import java.security.KeyStore; + import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; @@ -47,7 +48,7 @@ public class SslSelectChannelTimeoutTest extends ConnectorTimeoutTest sslContextFactory.setKeyStorePath(keystorePath); sslContextFactory.setKeyStorePassword("storepwd"); sslContextFactory.setKeyManagerPassword("keypwd"); - sslContextFactory.setTrustStore(keystorePath); + sslContextFactory.setTrustStorePath(keystorePath); sslContextFactory.setTrustStorePassword("storepwd"); SelectChannelConnector connector = new SelectChannelConnector(_server, sslContextFactory); connector.setIdleTimeout(MAX_IDLE_TIME); //250 msec max idle diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslUploadTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslUploadTest.java index acea0dda80d..da6aacf5f63 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslUploadTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslUploadTest.java @@ -18,12 +18,16 @@ package org.eclipse.jetty.server.ssl; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.KeyStore; import java.util.Arrays; + import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManagerFactory; @@ -34,6 +38,7 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.SelectChannelConnector; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.ssl.SslContextFactory; @@ -42,9 +47,6 @@ import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - /** */ public class SslUploadTest @@ -61,7 +63,7 @@ public class SslUploadTest sslContextFactory.setKeyStorePath(keystorePath); sslContextFactory.setKeyStorePassword("storepwd"); sslContextFactory.setKeyManagerPassword("keypwd"); - sslContextFactory.setTrustStore(keystorePath); + sslContextFactory.setTrustStorePath(keystorePath); sslContextFactory.setTrustStorePassword("storepwd"); server = new Server(); @@ -85,7 +87,8 @@ public class SslUploadTest public void test() throws Exception { KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); - keystore.load(new FileInputStream(connector.getSslContextFactory().getKeyStorePath()), "storepwd".toCharArray()); + SslContextFactory ctx=connector.getConnectionFactory(SslConnectionFactory.class).getSslContextFactory(); + keystore.load(new FileInputStream(ctx.getKeyStorePath()), "storepwd".toCharArray()); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keystore); SSLContext sslContext = SSLContext.getInstance("SSL"); @@ -137,13 +140,14 @@ public class SslUploadTest assertTrue (response.indexOf("200")>0); // System.err.println(response); - long end = System.nanoTime(); + // long end = System.nanoTime(); // System.out.println("upload time: " + TimeUnit.NANOSECONDS.toMillis(end - start)); assertEquals(requestContent.length, total); } private static class EmptyHandler extends AbstractHandler { + @Override public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { request.setHandled(true); diff --git a/jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy.xml b/jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy.xml index b0d0059939c..e50ee4f4fa9 100644 --- a/jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy.xml +++ b/jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy.xml @@ -1,17 +1,31 @@ - + - - src/main/resources/keystore.jks - storepwd - src/main/resources/truststore.jks - storepwd - TLSv1 - - - + + + + /etc/keystore + OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4 + OBF:1u2u1wml1z7s1z7a1wnl1u2g + /etc/keystore + OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4 + + + + + + + + + + + + + + @@ -23,66 +37,66 @@ - --> - - - - - - - - - 8080 - - - - - - - - - - - - - - 8443 - - - + + + + + + + + + + + + npn + + + + + + + + + spdy/3 + spdy/2 + http/1.1 + + + http/1.1 + + + + + + 3 + + + + + + + + 2 + + + + + + + + + + + + + 8443 + 30000 + + + + + diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYConnectionFactory.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYServerConnectionFactory.java similarity index 73% rename from jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYConnectionFactory.java rename to jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYServerConnectionFactory.java index c4f499523c5..4aaf9f0c15d 100644 --- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYConnectionFactory.java +++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYServerConnectionFactory.java @@ -19,13 +19,10 @@ package org.eclipse.jetty.spdy.http; -import java.util.concurrent.Executor; - -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.HttpConfiguration; -import org.eclipse.jetty.spdy.ServerSPDYConnectionFactory; +import org.eclipse.jetty.server.HttpChannelConfig; +import org.eclipse.jetty.spdy.SPDYServerConnectionFactory; import org.eclipse.jetty.spdy.api.DataInfo; import org.eclipse.jetty.spdy.api.HeadersInfo; import org.eclipse.jetty.spdy.api.ReplyInfo; @@ -34,40 +31,57 @@ import org.eclipse.jetty.spdy.api.StreamFrameListener; import org.eclipse.jetty.spdy.api.SynInfo; import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener; import org.eclipse.jetty.util.Fields; +import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.util.thread.Scheduler; -public class ServerHTTPSPDYConnectionFactory extends ServerSPDYConnectionFactory +public class HTTPSPDYServerConnectionFactory extends SPDYServerConnectionFactory implements HttpChannelConfig.ConnectionFactory { private static final String CHANNEL_ATTRIBUTE = "org.eclipse.jetty.spdy.http.HTTPChannelOverSPDY"; - private static final Logger logger = Log.getLogger(ServerHTTPSPDYConnectionFactory.class); + private static final Logger logger = Log.getLogger(HTTPSPDYServerConnectionFactory.class); - private final Connector connector; private final PushStrategy pushStrategy; - private final HttpConfiguration configuration; + private final HttpChannelConfig httpChannelConfig; - public ServerHTTPSPDYConnectionFactory(short version, ByteBufferPool bufferPool, Executor threadPool, Scheduler scheduler, Connector connector, PushStrategy pushStrategy) + public HTTPSPDYServerConnectionFactory( + @Name("version") int version, + @Name("config") HttpChannelConfig config) { - super(version, bufferPool, threadPool, scheduler); - this.connector = connector; + this(version,config,new PushStrategy.None()); + } + + public HTTPSPDYServerConnectionFactory( + @Name("version") int version, + @Name("config") HttpChannelConfig config, + @Name("pushStrategy") PushStrategy pushStrategy) + { + super(version); this.pushStrategy = pushStrategy; - this.configuration = new HttpConfiguration(connector.getSslContextFactory(), connector.getSslContextFactory() != null); + httpChannelConfig = config; + addBean(httpChannelConfig); } @Override - protected ServerSessionFrameListener provideServerSessionFrameListener(EndPoint endPoint, Object attachment) + public HttpChannelConfig getHttpChannelConfig() { - return new HTTPServerFrameListener(endPoint); + return httpChannelConfig; + } + + @Override + protected ServerSessionFrameListener provideServerSessionFrameListener(Connector connector, EndPoint endPoint) + { + return new HTTPServerFrameListener(connector,endPoint); } private class HTTPServerFrameListener extends ServerSessionFrameListener.Adapter implements StreamFrameListener { + private final Connector connector; private final EndPoint endPoint; - public HTTPServerFrameListener(EndPoint endPoint) + public HTTPServerFrameListener(Connector connector,EndPoint endPoint) { this.endPoint = endPoint; + this.connector=connector; } @Override @@ -82,9 +96,9 @@ public class ServerHTTPSPDYConnectionFactory extends ServerSPDYConnectionFactory logger.debug("Received {} on {}", synInfo, stream); Fields headers = synInfo.getHeaders(); - HttpTransportOverSPDY transport = new HttpTransportOverSPDY(connector, configuration, endPoint, pushStrategy, stream, headers); + HttpTransportOverSPDY transport = new HttpTransportOverSPDY(connector, httpChannelConfig, endPoint, pushStrategy, stream, headers); HttpInputOverSPDY input = new HttpInputOverSPDY(); - HttpChannelOverSPDY channel = new HttpChannelOverSPDY(connector, configuration, endPoint, transport, input, stream); + HttpChannelOverSPDY channel = new HttpChannelOverSPDY(connector, httpChannelConfig, endPoint, transport, input, stream); stream.setAttribute(CHANNEL_ATTRIBUTE, channel); channel.requestStart(headers, synInfo.isClose()); diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYServerConnector.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYServerConnector.java index 876fb4c467f..a7c4630675e 100644 --- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYServerConnector.java +++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYServerConnector.java @@ -22,13 +22,19 @@ package org.eclipse.jetty.spdy.http; import java.util.Collections; import java.util.Map; -import org.eclipse.jetty.server.HttpServerConnectionFactory; +import org.eclipse.jetty.server.AbstractConnectionFactory; +import org.eclipse.jetty.server.ConnectionFactory; +import org.eclipse.jetty.server.HttpChannelConfig; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SelectChannelConnector; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.spdy.NPNServerConnectionFactory; import org.eclipse.jetty.spdy.SPDYServerConnector; import org.eclipse.jetty.spdy.api.SPDY; +import org.eclipse.jetty.spdy.http.PushStrategy.None; import org.eclipse.jetty.util.ssl.SslContextFactory; -public class HTTPSPDYServerConnector extends SPDYServerConnector +public class HTTPSPDYServerConnector extends SelectChannelConnector { public HTTPSPDYServerConnector(Server server) { @@ -47,21 +53,30 @@ public class HTTPSPDYServerConnector extends SPDYServerConnector public HTTPSPDYServerConnector(Server server, SslContextFactory sslContextFactory, Map pushStrategies) { - // We pass a null ServerSessionFrameListener because for - // HTTP over SPDY we need one that references the endPoint - super(server, sslContextFactory, null); - clearConnectionFactories(); - // The "spdy/3" protocol handles HTTP over SPDY - putConnectionFactory("spdy/3", new ServerHTTPSPDYConnectionFactory(SPDY.V3, getByteBufferPool(), getExecutor(), getScheduler(), this, getPushStrategy(SPDY.V3, pushStrategies))); - // The "spdy/2" protocol handles HTTP over SPDY - putConnectionFactory("spdy/2", new ServerHTTPSPDYConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), this, getPushStrategy(SPDY.V2, pushStrategies))); - // The "http/1.1" protocol handles browsers that support NPN but not SPDY - putConnectionFactory("http/1.1", new HttpServerConnectionFactory(this)); - // The default connection factory handles plain HTTP on non-SSL or non-NPN connections - setDefaultConnectionFactory(getConnectionFactory("http/1.1")); + this(server,new HttpChannelConfig(),sslContextFactory,pushStrategies); } - private PushStrategy getPushStrategy(short version, Map pushStrategies) + public HTTPSPDYServerConnector(Server server, short version, HttpChannelConfig httpChannelConfig, PushStrategy push) + { + super(server,new HTTPSPDYServerConnectionFactory(version,httpChannelConfig,push)); + + } + + public HTTPSPDYServerConnector(Server server, HttpChannelConfig config, SslContextFactory sslContextFactory, Map pushStrategies) + { + super(server,AbstractConnectionFactory.getFactories(sslContextFactory, + sslContextFactory==null + ?new ConnectionFactory[] {new HttpConnectionFactory(config)} + :new ConnectionFactory[] {new NPNServerConnectionFactory("spdy/3","spdy/2","http/1.1"), + new HttpConnectionFactory(config), + new HTTPSPDYServerConnectionFactory(SPDY.V3,new HttpChannelConfig(),getPushStrategy(SPDY.V3, pushStrategies)), + new HTTPSPDYServerConnectionFactory(SPDY.V2,new HttpChannelConfig(),getPushStrategy(SPDY.V2, pushStrategies))})); + if (getConnectionFactory(NPNServerConnectionFactory.class)!=null) + getConnectionFactory(NPNServerConnectionFactory.class).setDefaultProtocol("http/1.1"); + } + + + private static PushStrategy getPushStrategy(short version, Map pushStrategies) { PushStrategy pushStrategy = pushStrategies.get(version); if (pushStrategy == null) diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HttpChannelOverSPDY.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HttpChannelOverSPDY.java index d69bbcd7738..ca6672c27bd 100644 --- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HttpChannelOverSPDY.java +++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HttpChannelOverSPDY.java @@ -28,7 +28,7 @@ import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; -import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpChannelConfig; import org.eclipse.jetty.server.HttpTransport; import org.eclipse.jetty.spdy.api.ByteBufferDataInfo; import org.eclipse.jetty.spdy.api.DataInfo; @@ -46,7 +46,7 @@ public class HttpChannelOverSPDY extends HttpChannel private boolean dispatched; // Guarded by synchronization on tasks private boolean headersComplete; - public HttpChannelOverSPDY(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInputOverSPDY input, Stream stream) + public HttpChannelOverSPDY(Connector connector, HttpChannelConfig configuration, EndPoint endPoint, HttpTransport transport, HttpInputOverSPDY input, Stream stream) { super(connector, configuration, endPoint, transport, input); this.stream = stream; diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HttpTransportOverSPDY.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HttpTransportOverSPDY.java index a74aa92d822..f8f7e4142d8 100644 --- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HttpTransportOverSPDY.java +++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HttpTransportOverSPDY.java @@ -29,7 +29,7 @@ import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpChannelConfig; import org.eclipse.jetty.server.HttpTransport; import org.eclipse.jetty.spdy.api.ByteBufferDataInfo; import org.eclipse.jetty.spdy.api.ReplyInfo; @@ -47,13 +47,13 @@ public class HttpTransportOverSPDY implements HttpTransport private static final Logger LOG = Log.getLogger(HttpTransportOverSPDY.class); private final Connector connector; - private final HttpConfiguration configuration; + private final HttpChannelConfig configuration; private final EndPoint endPoint; private final PushStrategy pushStrategy; private final Stream stream; private final Fields requestHeaders; - public HttpTransportOverSPDY(Connector connector, HttpConfiguration configuration, EndPoint endPoint, PushStrategy pushStrategy, Stream stream, Fields requestHeaders) + public HttpTransportOverSPDY(Connector connector, HttpChannelConfig configuration, EndPoint endPoint, PushStrategy pushStrategy, Stream stream, Fields requestHeaders) { this.connector = connector; this.configuration = configuration; diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/HTTPSPDYProxyConnector.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/HTTPSPDYProxyConnector.java index 7f7569ec552..d135c8f177f 100644 --- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/HTTPSPDYProxyConnector.java +++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/HTTPSPDYProxyConnector.java @@ -19,9 +19,9 @@ package org.eclipse.jetty.spdy.proxy; +import org.eclipse.jetty.server.HttpChannelConfig; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.spdy.SPDYServerConnector; -import org.eclipse.jetty.spdy.ServerSPDYConnectionFactory; import org.eclipse.jetty.spdy.api.SPDY; import org.eclipse.jetty.util.ssl.SslContextFactory; @@ -35,11 +35,6 @@ public class HTTPSPDYProxyConnector extends SPDYServerConnector public HTTPSPDYProxyConnector(Server server, SslContextFactory sslContextFactory, ProxyEngineSelector proxyEngineSelector) { super(server, sslContextFactory, proxyEngineSelector); - clearConnectionFactories(); - - putConnectionFactory("spdy/3", new ServerSPDYConnectionFactory(SPDY.V3, getByteBufferPool(), getExecutor(), getScheduler(), proxyEngineSelector)); - putConnectionFactory("spdy/2", new ServerSPDYConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), proxyEngineSelector)); - putConnectionFactory("http/1.1", new ProxyHTTPConnectionFactory(this, SPDY.V2, proxyEngineSelector)); - setDefaultConnectionFactory(getConnectionFactory("http/1.1")); + addConnectionFactory(new ProxyHTTPConnectionFactory(new HttpChannelConfig(), SPDY.V2, proxyEngineSelector)); } } diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPConnectionFactory.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPConnectionFactory.java index 85eaa34ee42..6e3380b2647 100644 --- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPConnectionFactory.java +++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPConnectionFactory.java @@ -19,29 +19,40 @@ package org.eclipse.jetty.spdy.proxy; -import java.nio.channels.SocketChannel; +import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.server.AbstractConnectionFactory; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.HttpChannelConfig; -public class ProxyHTTPConnectionFactory implements ConnectionFactory +public class ProxyHTTPConnectionFactory extends AbstractConnectionFactory implements HttpChannelConfig.ConnectionFactory { - private final Connector connector; private final short version; private final ProxyEngineSelector proxyEngineSelector; + private final HttpChannelConfig httpChannelConfig; - public ProxyHTTPConnectionFactory(Connector connector, short version, ProxyEngineSelector proxyEngineSelector) + public ProxyHTTPConnectionFactory(HttpChannelConfig httpChannelConfig,short version, ProxyEngineSelector proxyEngineSelector) { - this.connector = connector; + // replaces http/1.1 + super(HttpVersion.HTTP_1_1.asString()); this.version = version; this.proxyEngineSelector = proxyEngineSelector; + this.httpChannelConfig=httpChannelConfig; } @Override - public Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment) + public Connection newConnection(Connector connector, EndPoint endPoint) { - return new ProxyHTTPSPDYConnection(connector, endPoint, version, proxyEngineSelector); + return new ProxyHTTPSPDYConnection(connector, httpChannelConfig, endPoint, version, proxyEngineSelector); } + + @Override + public HttpChannelConfig getHttpChannelConfig() + { + return httpChannelConfig; + } + } diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPSPDYConnection.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPSPDYConnection.java index 91c4b18263f..babb325426d 100644 --- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPSPDYConnection.java +++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPSPDYConnection.java @@ -32,8 +32,9 @@ import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpChannelConfig; import org.eclipse.jetty.server.HttpConnection; +import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.spdy.ISession; import org.eclipse.jetty.spdy.IStream; import org.eclipse.jetty.spdy.StandardSession; @@ -61,9 +62,9 @@ public class ProxyHTTPSPDYConnection extends HttpConnection implements HttpParse private HTTPStream stream; private ByteBuffer content; - public ProxyHTTPSPDYConnection(Connector connector, EndPoint endPoint, short version, ProxyEngineSelector proxyEngineSelector) + public ProxyHTTPSPDYConnection(Connector connector, HttpChannelConfig config, EndPoint endPoint, short version, ProxyEngineSelector proxyEngineSelector) { - super(new HttpConfiguration(connector.getSslContextFactory(), connector.getSslContextFactory() != null), connector, endPoint); + super(config,connector,endPoint); this.version = version; this.proxyEngineSelector = proxyEngineSelector; this.session = new HTTPSession(version, connector); @@ -80,7 +81,7 @@ public class ProxyHTTPSPDYConnection extends HttpConnection implements HttpParse public boolean startRequest(HttpMethod method, String methodString, String uri, HttpVersion httpVersion) { Connector connector = getConnector(); - String scheme = connector.getSslContextFactory() != null ? "https" : "http"; + String scheme = connector.getConnectionFactory(SslConnectionFactory.class) != null ? "https" : "http"; headers.put(HTTPSPDYHeader.SCHEME.name(version), scheme); headers.put(HTTPSPDYHeader.METHOD.name(version), methodString); headers.put(HTTPSPDYHeader.URI.name(version), uri); diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/AbstractHTTPSPDYTest.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/AbstractHTTPSPDYTest.java index d9e44e43a9a..1a6d22144ea 100644 --- a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/AbstractHTTPSPDYTest.java +++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/AbstractHTTPSPDYTest.java @@ -26,6 +26,7 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.HttpChannelConfig; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.spdy.SPDYClient; import org.eclipse.jetty.spdy.SPDYServerConnector; @@ -65,7 +66,7 @@ public abstract class AbstractHTTPSPDYTest protected final short version; protected Server server; protected SPDYClient.Factory clientFactory; - protected SPDYServerConnector connector; + protected HTTPSPDYServerConnector connector; protected AbstractHTTPSPDYTest(short version) { @@ -89,12 +90,10 @@ public abstract class AbstractHTTPSPDYTest return new InetSocketAddress("localhost", connector.getLocalPort()); } - protected SPDYServerConnector newHTTPSPDYServerConnector(short version) + protected HTTPSPDYServerConnector newHTTPSPDYServerConnector(short version) { // For these tests, we need the connector to speak HTTP over SPDY even in non-SSL - SPDYServerConnector connector = new HTTPSPDYServerConnector(server); - ConnectionFactory defaultFactory = new ServerHTTPSPDYConnectionFactory(version, connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, new PushStrategy.None()); - connector.setDefaultConnectionFactory(defaultFactory); + HTTPSPDYServerConnector connector = new HTTPSPDYServerConnector(server,version,new HttpChannelConfig(), new PushStrategy.None()); return connector; } diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ProtocolNegotiationTest.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ProtocolNegotiationTest.java index fc189643382..3680ee229b0 100644 --- a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ProtocolNegotiationTest.java +++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ProtocolNegotiationTest.java @@ -29,7 +29,7 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import org.eclipse.jetty.npn.NextProtoNego; -import org.eclipse.jetty.server.HttpServerConnectionFactory; +import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.spdy.SPDYServerConnector; import org.eclipse.jetty.util.ssl.SslContextFactory; @@ -75,7 +75,7 @@ public class ProtocolNegotiationTest SslContextFactory sslContextFactory = new SslContextFactory(); sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks"); sslContextFactory.setKeyStorePassword("storepwd"); - sslContextFactory.setTrustStore("src/test/resources/truststore.jks"); + sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks"); sslContextFactory.setTrustStorePassword("storepwd"); sslContextFactory.setProtocol("TLSv1"); sslContextFactory.setIncludeProtocols("TLSv1"); @@ -86,7 +86,7 @@ public class ProtocolNegotiationTest public void testServerAdvertisingHTTPSpeaksHTTP() throws Exception { InetSocketAddress address = startServer(null); - connector.putConnectionFactory("http/1.1", new HttpServerConnectionFactory(connector)); + connector.addConnectionFactory(new HttpConnectionFactory()); SslContextFactory sslContextFactory = newSslContextFactory(); sslContextFactory.start(); @@ -142,7 +142,7 @@ public class ProtocolNegotiationTest public void testServerAdvertisingSPDYAndHTTPSpeaksHTTPWhenNegotiated() throws Exception { InetSocketAddress address = startServer(null); - connector.putConnectionFactory("http/1.1", new HttpServerConnectionFactory(connector)); + connector.addConnectionFactory(new HttpConnectionFactory()); SslContextFactory sslContextFactory = newSslContextFactory(); sslContextFactory.start(); @@ -201,8 +201,7 @@ public class ProtocolNegotiationTest public void testServerAdvertisingSPDYAndHTTPSpeaksDefaultProtocolWhenNPNMissing() throws Exception { InetSocketAddress address = startServer(null); - connector.setDefaultConnectionFactory(new HttpServerConnectionFactory(connector)); - connector.putConnectionFactory("http/1.1", connector.getDefaultConnectionFactory()); + connector.addConnectionFactory(new HttpConnectionFactory()); SslContextFactory sslContextFactory = newSslContextFactory(); sslContextFactory.start(); diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyTest.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyTest.java index 14d5bba2c94..036afdecd76 100644 --- a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyTest.java +++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyTest.java @@ -28,8 +28,10 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.ConnectionFactory; +import org.eclipse.jetty.server.HttpChannelConfig; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.spdy.NPNServerConnectionFactory; import org.eclipse.jetty.spdy.SPDYServerConnector; import org.eclipse.jetty.spdy.api.DataInfo; import org.eclipse.jetty.spdy.api.ReplyInfo; @@ -54,11 +56,10 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest } @Override - protected SPDYServerConnector newHTTPSPDYServerConnector(short version) + protected HTTPSPDYServerConnector newHTTPSPDYServerConnector(short version) { - SPDYServerConnector connector = super.newHTTPSPDYServerConnector(version); - ConnectionFactory defaultFactory = new ServerHTTPSPDYConnectionFactory(version, connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, new ReferrerPushStrategy()); - connector.setDefaultConnectionFactory(defaultFactory); + HTTPSPDYServerConnector connector = + new HTTPSPDYServerConnector(server,version,new HttpChannelConfig(),new ReferrerPushStrategy()); return connector; } @@ -70,8 +71,14 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest ReferrerPushStrategy pushStrategy = new ReferrerPushStrategy(); int referrerPushPeriod = 1000; pushStrategy.setReferrerPushPeriod(referrerPushPeriod); - ConnectionFactory defaultFactory = new ServerHTTPSPDYConnectionFactory(version, connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, pushStrategy); - connector.setDefaultConnectionFactory(defaultFactory); + ConnectionFactory defaultFactory = new HTTPSPDYServerConnectionFactory(version,new HttpChannelConfig(), pushStrategy); + connector.addConnectionFactory(defaultFactory); + if (connector.getConnectionFactory(NPNServerConnectionFactory.class)!=null) + connector.getConnectionFactory(NPNServerConnectionFactory.class).setDefaultProtocol(defaultFactory.getProtocol()); + else + connector.setDefaultProtocol(defaultFactory.getProtocol()); + + connector.setDefaultProtocol(defaultFactory.getProtocol()); // TODO I don't think this is right Fields mainRequestHeaders = createHeadersWithoutReferrer(mainResource); Session session1 = sendMainRequestAndCSSRequest(address, mainRequestHeaders); @@ -92,9 +99,12 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest ReferrerPushStrategy pushStrategy = new ReferrerPushStrategy(); int referrerPushPeriod = 1000; pushStrategy.setReferrerPushPeriod(referrerPushPeriod); - ConnectionFactory defaultFactory = new ServerHTTPSPDYConnectionFactory(version, - connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, pushStrategy); - connector.setDefaultConnectionFactory(defaultFactory); + ConnectionFactory defaultFactory = new HTTPSPDYServerConnectionFactory(version,new HttpChannelConfig(), pushStrategy); + connector.addConnectionFactory(defaultFactory); + if (connector.getConnectionFactory(NPNServerConnectionFactory.class)!=null) + connector.getConnectionFactory(NPNServerConnectionFactory.class).setDefaultProtocol(defaultFactory.getProtocol()); + else + connector.setDefaultProtocol(defaultFactory.getProtocol()); Fields mainRequestHeaders = createHeadersWithoutReferrer(mainResource); Session session1 = sendMainRequestAndCSSRequest(address, mainRequestHeaders); @@ -114,9 +124,9 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest ReferrerPushStrategy pushStrategy = new ReferrerPushStrategy(); pushStrategy.setMaxAssociatedResources(1); - ConnectionFactory defaultFactory = new ServerHTTPSPDYConnectionFactory(version, - connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, pushStrategy); - connector.setDefaultConnectionFactory(defaultFactory); + ConnectionFactory defaultFactory = new HTTPSPDYServerConnectionFactory(version,new HttpChannelConfig(), pushStrategy); + connector.addConnectionFactory(defaultFactory); + connector.setDefaultProtocol(defaultFactory.getProtocol()); // TODO I don't think this is right Fields mainRequestHeaders = createHeadersWithoutReferrer(mainResource); Session session1 = sendMainRequestAndCSSRequest(address, mainRequestHeaders); diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYTest.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYTest.java index edebfd9cac5..21eba341990 100644 --- a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYTest.java +++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYTest.java @@ -843,6 +843,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { + // TODO suppress stack from test log throw new NullPointerException("thrown_explicitly_by_the_test"); } }), null); diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPSPDYTest.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPSPDYTest.java index 2753496fd4b..8ca2cdaaeba 100644 --- a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPSPDYTest.java +++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/proxy/ProxyHTTPSPDYTest.java @@ -33,7 +33,7 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.spdy.SPDYClient; import org.eclipse.jetty.spdy.SPDYServerConnector; -import org.eclipse.jetty.spdy.ServerSPDYConnectionFactory; +import org.eclipse.jetty.spdy.SPDYServerConnectionFactory; import org.eclipse.jetty.spdy.api.BytesDataInfo; import org.eclipse.jetty.spdy.api.DataInfo; import org.eclipse.jetty.spdy.api.GoAwayInfo; @@ -100,8 +100,7 @@ public class ProxyHTTPSPDYTest { server = new Server(); SPDYServerConnector serverConnector = new SPDYServerConnector(server, listener); - serverConnector.setDefaultConnectionFactory(new ServerSPDYConnectionFactory(version, - serverConnector.getByteBufferPool(), serverConnector.getExecutor(), serverConnector.getScheduler(), listener)); + serverConnector.addConnectionFactory(new SPDYServerConnectionFactory(version,listener)); serverConnector.setPort(0); server.addConnector(serverConnector); server.start(); @@ -464,7 +463,7 @@ public class ProxyHTTPSPDYTest return null; } })); - proxyConnector.setDefaultConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version)); + proxyConnector.addConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version)); Session client = factory.newSPDYClient(version).connect(proxyAddress, null).get(5, TimeUnit.SECONDS); @@ -509,7 +508,7 @@ public class ProxyHTTPSPDYTest return null; } })); - proxyConnector.setDefaultConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version)); + proxyConnector.addConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version)); Session client = factory.newSPDYClient(version).connect(proxyAddress, null).get(5, TimeUnit.SECONDS); @@ -630,7 +629,7 @@ public class ProxyHTTPSPDYTest return null; } })); - proxyConnector.setDefaultConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version)); + proxyConnector.addConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version)); final CountDownLatch pushSynLatch = new CountDownLatch(1); final CountDownLatch pushDataLatch = new CountDownLatch(1); @@ -689,7 +688,7 @@ public class ProxyHTTPSPDYTest // We just verify that it works InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter())); - proxyConnector.setDefaultConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version)); + proxyConnector.addConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version)); final CountDownLatch pingLatch = new CountDownLatch(1); Session client = factory.newSPDYClient(version).connect(proxyAddress, new SessionFrameListener.Adapter() @@ -761,7 +760,7 @@ public class ProxyHTTPSPDYTest return null; } })); - proxyConnector.setDefaultConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version)); + proxyConnector.addConnectionFactory(proxyConnector.getConnectionFactory("spdy/" + version)); final CountDownLatch resetLatch = new CountDownLatch(1); Session client = factory.newSPDYClient(version).connect(proxyAddress, new SessionFrameListener.Adapter() diff --git a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/EmptyEndPoint.java b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/EmptyEndPoint.java index 7ad23da83e6..bada1ee976f 100644 --- a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/EmptyEndPoint.java +++ b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/EmptyEndPoint.java @@ -59,12 +59,6 @@ public class EmptyEndPoint implements EndPoint { oshut = true; } - - @Override - public boolean isBufferingOutput() - { - return false; - } @Override public boolean isOutputShutdown() @@ -91,9 +85,9 @@ public class EmptyEndPoint implements EndPoint } @Override - public int flush(ByteBuffer... buffer) throws IOException + public boolean flush(ByteBuffer... buffer) throws IOException { - return 0; + return false; } @Override diff --git a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/NPNServerConnectionFactory.java b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/NPNServerConnectionFactory.java new file mode 100644 index 00000000000..559f5ac7a34 --- /dev/null +++ b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/NPNServerConnectionFactory.java @@ -0,0 +1,108 @@ +// +// ======================================================================== +// 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.spdy; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint; +import org.eclipse.jetty.npn.NextProtoNego; +import org.eclipse.jetty.server.AbstractConnectionFactory; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.util.annotation.Name; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +public class NPNServerConnectionFactory extends AbstractConnectionFactory +{ + private static final Logger LOG = Log.getLogger(NPNServerConnectionFactory.class); + private final List _protocols; + private String _defaultProtocol; + + /* ------------------------------------------------------------ */ + /** + * @param protocols List of supported protocols in priority order + */ + public NPNServerConnectionFactory(@Name("protocols")String... protocols) + { + super("npn"); + _protocols=Arrays.asList(protocols); + + try + { + if (NextProtoNego.class.getClassLoader()!=null) + { + LOG.warn("NextProtoNego not from bootloader classloader: "+NextProtoNego.class.getClassLoader()); + throw new IllegalStateException("NextProtoNego not on bootloader"); + } + } + catch(Throwable th) + { + LOG.warn("NextProtoNego not available: "+th); + throw new IllegalStateException("NextProtoNego not available",th); + } + } + + public String getDefaultProtocol() + { + return _defaultProtocol; + } + + public void setDefaultProtocol(String defaultProtocol) + { + _defaultProtocol = defaultProtocol; + } + + public List getProtocols() + { + return _protocols; + } + + @Override + public Connection newConnection(Connector connector, EndPoint endPoint) + { + List protocols=_protocols; + if (protocols==null || protocols.size()==0) + { + protocols=connector.getProtocols(); + for (Iterator i=protocols.iterator();i.hasNext();) + { + String protocol=i.next(); + if (protocol.startsWith("SSL-")||protocol.equals("NPN")) + i.remove(); + } + } + + String dft=_defaultProtocol; + if (dft==null) + dft=_protocols.get(0); + + return new NextProtoNegoServerConnection((DecryptedEndPoint)endPoint, connector,protocols,_defaultProtocol); + } + + @Override + public String toString() + { + return String.format("%s@%x{%s,%s,%s}",this.getClass().getSimpleName(),hashCode(),getProtocol(),getDefaultProtocol(),getProtocols()); + } +} diff --git a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/NextProtoNegoServerConnection.java b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/NextProtoNegoServerConnection.java index 2526fb266be..83fbefebae0 100644 --- a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/NextProtoNegoServerConnection.java +++ b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/NextProtoNegoServerConnection.java @@ -19,14 +19,17 @@ package org.eclipse.jetty.spdy; import java.io.IOException; -import java.nio.channels.SocketChannel; import java.util.List; +import javax.net.ssl.SSLEngine; + import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint; import org.eclipse.jetty.npn.NextProtoNego; import org.eclipse.jetty.server.ConnectionFactory; +import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -34,15 +37,22 @@ import org.eclipse.jetty.util.log.Logger; public class NextProtoNegoServerConnection extends AbstractConnection implements NextProtoNego.ServerProvider { private final Logger logger = Log.getLogger(getClass()); - private final SocketChannel channel; - private final SPDYServerConnector connector; + private final Connector connector; + private final SSLEngine engine; + private final List protocols; + private final String defaultProtocol; private boolean completed; // No need to be volatile: it is modified and read by the same thread - - public NextProtoNegoServerConnection(SocketChannel channel, EndPoint endPoint, SPDYServerConnector connector) + + + public NextProtoNegoServerConnection(DecryptedEndPoint endPoint, Connector connector, Listprotocols, String defaultProtocol) { super(endPoint, connector.getExecutor()); - this.channel = channel; - this.connector = connector; + this.connector = connector; + this.protocols = protocols; + this.defaultProtocol=defaultProtocol; + engine = endPoint.getSslConnection().getSSLEngine(); + + NextProtoNego.put(engine,this); } @Override @@ -51,6 +61,13 @@ public class NextProtoNegoServerConnection extends AbstractConnection implements super.onOpen(); fillInterested(); } + + @Override + public void onClose() + { + NextProtoNego.remove(engine); + super.onClose(); + }; @Override public void onFillable() @@ -82,17 +99,13 @@ public class NextProtoNegoServerConnection extends AbstractConnection implements @Override public void unsupported() { - ConnectionFactory connectionFactory = connector.getDefaultConnectionFactory(); - EndPoint endPoint = getEndPoint(); - Connection connection = connectionFactory.newConnection(channel, endPoint, connector); - connector.replaceConnection(endPoint, connection); - completed = true; + protocolSelected(defaultProtocol); } @Override public List protocols() { - return connector.provideProtocols(); + return protocols; } @Override @@ -100,8 +113,9 @@ public class NextProtoNegoServerConnection extends AbstractConnection implements { ConnectionFactory connectionFactory = connector.getConnectionFactory(protocol); EndPoint endPoint = getEndPoint(); - Connection connection = connectionFactory.newConnection(channel, endPoint, connector); - connector.replaceConnection(endPoint, connection); + Connection connection = connectionFactory.newConnection(connector, endPoint); + endPoint.setConnection(connection); + NextProtoNego.remove(engine); completed = true; } } diff --git a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYClient.java b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYClient.java index 0a064d8efee..7a3424ea213 100644 --- a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYClient.java +++ b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYClient.java @@ -30,6 +30,7 @@ import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executor; import java.util.concurrent.Future; + import javax.net.ssl.SSLEngine; import org.eclipse.jetty.io.ByteBufferPool; @@ -40,11 +41,8 @@ import org.eclipse.jetty.io.SelectChannelEndPoint; import org.eclipse.jetty.io.SelectorManager; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.npn.NextProtoNego; -import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.spdy.api.Session; import org.eclipse.jetty.spdy.api.SessionFrameListener; -import org.eclipse.jetty.spdy.generator.Generator; -import org.eclipse.jetty.spdy.parser.Parser; import org.eclipse.jetty.util.component.AggregateLifeCycle; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; @@ -53,9 +51,9 @@ import org.eclipse.jetty.util.thread.TimerScheduler; public class SPDYClient { - private final ConnectionFactory connectionFactory = new ClientSPDYConnectionFactory(); - private final short version; - private final Factory factory; + private final SPDYClientConnectionFactory connectionFactory = new SPDYClientConnectionFactory(); + final short version; + final Factory factory; private volatile SocketAddress bindAddress; private volatile long idleTimeout = -1; private volatile int initialWindowSize; @@ -135,7 +133,7 @@ public class SPDYClient return null; } - public ConnectionFactory getConnectionFactory() + public SPDYClientConnectionFactory getConnectionFactory() { return connectionFactory; } @@ -164,9 +162,9 @@ public class SPDYClient public static class Factory extends AggregateLifeCycle { private final Queue sessions = new ConcurrentLinkedQueue<>(); - private final ByteBufferPool bufferPool = new MappedByteBufferPool(); - private final Scheduler scheduler = new TimerScheduler(); - private final Executor executor; + final ByteBufferPool bufferPool = new MappedByteBufferPool(); + final Scheduler scheduler = new TimerScheduler(); + final Executor executor; private final SslContextFactory sslContextFactory; private final SelectorManager selector; private final long idleTimeout; @@ -223,13 +221,13 @@ public class SPDYClient super.doStop(); } - private boolean sessionOpened(Session session) + boolean sessionOpened(Session session) { // Add sessions only if the factory is not stopping return isRunning() && sessions.offer(session); } - private boolean sessionClosed(Session session) + boolean sessionClosed(Session session) { // Remove sessions only if the factory is not stopping // to avoid concurrent removes during iterations @@ -300,7 +298,7 @@ public class SPDYClient } else { - ConnectionFactory connectionFactory = new ClientSPDYConnectionFactory(); + SPDYClientConnectionFactory connectionFactory = new SPDYClientConnectionFactory(); return connectionFactory.newConnection(channel, endPoint, attachment); } } @@ -313,11 +311,11 @@ public class SPDYClient } } - private static class SessionPromise extends Promise + static class SessionPromise extends Promise { private final SocketChannel channel; - private final SPDYClient client; - private final SessionFrameListener listener; + final SPDYClient client; + final SessionFrameListener listener; private SessionPromise(SocketChannel channel, SPDYClient client, SessionFrameListener listener) { @@ -341,51 +339,4 @@ public class SPDYClient } } } - - private static class ClientSPDYConnectionFactory implements ConnectionFactory - { - @Override - public Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment) - { - SessionPromise sessionPromise = (SessionPromise)attachment; - SPDYClient client = sessionPromise.client; - Factory factory = client.factory; - - CompressionFactory compressionFactory = new StandardCompressionFactory(); - Parser parser = new Parser(compressionFactory.newDecompressor()); - Generator generator = new Generator(factory.bufferPool, compressionFactory.newCompressor()); - - SPDYConnection connection = new ClientSPDYConnection(endPoint, factory.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); - session.setWindowSize(client.getInitialWindowSize()); - parser.addListener(session); - sessionPromise.completed(session); - connection.setSession(session); - - factory.sessionOpened(session); - - return connection; - } - - private class ClientSPDYConnection extends SPDYConnection - { - private final Factory factory; - - public ClientSPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Factory factory) - { - super(endPoint, bufferPool, parser, factory.executor); - this.factory = factory; - } - - @Override - public void onClose() - { - super.onClose(); - factory.sessionClosed(getSession()); - } - } - } } diff --git a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYClientConnectionFactory.java b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYClientConnectionFactory.java new file mode 100644 index 00000000000..75c4114cb91 --- /dev/null +++ b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYClientConnectionFactory.java @@ -0,0 +1,75 @@ +// +// ======================================================================== +// 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.spdy; + +import java.nio.channels.SocketChannel; + +import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.spdy.SPDYClient.Factory; +import org.eclipse.jetty.spdy.SPDYClient.SessionPromise; +import org.eclipse.jetty.spdy.generator.Generator; +import org.eclipse.jetty.spdy.parser.Parser; + +public class SPDYClientConnectionFactory +{ + public Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment) + { + SessionPromise sessionPromise = (SessionPromise)attachment; + SPDYClient client = sessionPromise.client; + Factory factory = client.factory; + + CompressionFactory compressionFactory = new StandardCompressionFactory(); + Parser parser = new Parser(compressionFactory.newDecompressor()); + Generator generator = new Generator(factory.bufferPool, compressionFactory.newCompressor()); + + SPDYConnection connection = new ClientSPDYConnection(endPoint, factory.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); + session.setWindowSize(client.getInitialWindowSize()); + parser.addListener(session); + sessionPromise.completed(session); + connection.setSession(session); + + factory.sessionOpened(session); + + return connection; + } + + private class ClientSPDYConnection extends SPDYConnection + { + private final Factory factory; + + public ClientSPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Factory factory) + { + super(endPoint, bufferPool, parser, factory.executor); + this.factory = factory; + } + + @Override + public void onClose() + { + super.onClose(); + factory.sessionClosed(getSession()); + } + } +} diff --git a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYConnection.java b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYConnection.java index 304a3580dd6..5a5b8ce0517 100644 --- a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYConnection.java +++ b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYConnection.java @@ -36,15 +36,23 @@ public class SPDYConnection extends AbstractConnection implements Controller sessions = new ConcurrentLinkedQueue<>(); + + public SPDYServerConnectionFactory(int version) + { + this(version, null); + } + + public SPDYServerConnectionFactory(int version, ServerSessionFrameListener listener) + { + super("spdy/"+version); + this.version = (short)version; + this.listener = listener; + setInitialWindowSize(65536); + } + + public short getVersion() + { + return version; + } + + public ServerSessionFrameListener getServerSessionFrameListener() + { + return listener; + } + + @Override + public Connection newConnection(Connector connector, EndPoint endPoint) + { + CompressionFactory compressionFactory = new StandardCompressionFactory(); + Parser parser = new Parser(compressionFactory.newDecompressor()); + Generator generator = new Generator(connector.getByteBufferPool(), compressionFactory.newCompressor()); + + ServerSessionFrameListener listener = provideServerSessionFrameListener(connector,endPoint); + SPDYConnection connection = new ServerSPDYConnection(connector,endPoint, parser, listener, getInputBufferSize()); + + FlowControlStrategy flowControlStrategy = newFlowControlStrategy(version); + + StandardSession session = new StandardSession(getVersion(), connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connection, connection, 2, listener, generator, flowControlStrategy); + session.setAttribute("org.eclipse.jetty.spdy.remoteAddress", endPoint.getRemoteAddress()); // TODO: make this available through API + session.setWindowSize(getInitialWindowSize()); + parser.addListener(session); + connection.setSession(session); + + sessionOpened(session); + + return connection; + } + + protected FlowControlStrategy newFlowControlStrategy(short version) + { + return FlowControlStrategyFactory.newFlowControlStrategy(version); + } + + protected ServerSessionFrameListener provideServerSessionFrameListener(Connector connector, EndPoint endPoint) + { + return listener; + } + + public int getInitialWindowSize() + { + return initialWindowSize; + } + + public void setInitialWindowSize(int initialWindowSize) + { + this.initialWindowSize = initialWindowSize; + } + + protected boolean sessionOpened(Session session) + { + // Add sessions only if the connector is not stopping + return sessions.offer(session); + } + + protected boolean sessionClosed(Session session) + { + // Remove sessions only if the connector is not stopping + // to avoid concurrent removes during iterations + return sessions.remove(session); + } + + void closeSessions() + { + for (Session session : sessions) + session.goAway(); + sessions.clear(); + } + + @Override + protected void doStop() throws Exception + { + closeSessions(); + super.doStop(); + } + + protected Collection getSessions() + { + return Collections.unmodifiableCollection(sessions); + } + + private class ServerSPDYConnection extends SPDYConnection implements Runnable + { + private final ServerSessionFrameListener listener; + private final AtomicBoolean connected = new AtomicBoolean(); + + private ServerSPDYConnection(Connector connector,EndPoint endPoint, Parser parser, ServerSessionFrameListener listener, int bufferSize) + { + super(endPoint, connector.getByteBufferPool(), parser, connector.getExecutor(),bufferSize); + this.listener = listener; + } + + @Override + public void onOpen() + { + super.onOpen(); + if (connected.compareAndSet(false, true)) + getExecutor().execute(this); + } + + @Override + public void onClose() + { + super.onClose(); + sessionClosed(getSession()); + } + + @Override + public void run() + { + // NPE guard to support tests + if (listener != null) + listener.onConnect(getSession()); + } + } + +} diff --git a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYServerConnector.java b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYServerConnector.java index f0eb71df08d..5cd4a638cdc 100644 --- a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYServerConnector.java +++ b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYServerConnector.java @@ -18,34 +18,16 @@ package org.eclipse.jetty.spdy; -import java.io.IOException; -import java.nio.channels.SocketChannel; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; -import javax.net.ssl.SSLEngine; - -import org.eclipse.jetty.io.Connection; -import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.ssl.SslConnection; -import org.eclipse.jetty.npn.NextProtoNego; +import org.eclipse.jetty.server.ConnectionFactory; +import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.SelectChannelConnector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.spdy.api.SPDY; -import org.eclipse.jetty.spdy.api.Session; import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener; -import org.eclipse.jetty.util.component.AggregateLifeCycle; import org.eclipse.jetty.util.ssl.SslContextFactory; public class SPDYServerConnector extends SelectChannelConnector { - private final Queue sessions = new ConcurrentLinkedQueue<>(); - private final ServerSessionFrameListener listener; - private volatile int initialWindowSize; - public SPDYServerConnector(Server server, ServerSessionFrameListener listener) { this(server, null, listener); @@ -53,131 +35,18 @@ public class SPDYServerConnector extends SelectChannelConnector public SPDYServerConnector(Server server, SslContextFactory sslContextFactory, ServerSessionFrameListener listener) { - super(server, sslContextFactory); - this.listener = listener; - setInitialWindowSize(65536); - putConnectionFactory("spdy/3", new ServerSPDYConnectionFactory(SPDY.V3, getByteBufferPool(), getExecutor(), getScheduler(), listener)); - putConnectionFactory("spdy/2", new ServerSPDYConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), listener)); - setDefaultConnectionFactory(getConnectionFactory("spdy/2")); + super(server, + sslContextFactory, + sslContextFactory==null + ?new ConnectionFactory[]{new SPDYServerConnectionFactory(SPDY.V2, listener)} + :new ConnectionFactory[]{ + new NPNServerConnectionFactory("spdy/3","spdy/2","http/1.1"), + new HttpConnectionFactory(), + new SPDYServerConnectionFactory(SPDY.V2, listener), + new SPDYServerConnectionFactory(SPDY.V3, listener)}); + if (getConnectionFactory(NPNServerConnectionFactory.class)!=null) + getConnectionFactory(NPNServerConnectionFactory.class).setDefaultProtocol("http/1.1"); + } - public ServerSessionFrameListener getServerSessionFrameListener() - { - return listener; - } - - @Override - protected void doStart() throws Exception - { - super.doStart(); - LOG.info("SPDY support is experimental. Please report feedback at jetty-dev@eclipse.org"); - } - - @Override - protected void doStop() throws Exception - { - closeSessions(); - super.doStop(); - } - - protected List provideProtocols() - { - return new ArrayList<>(getConnectionFactories().keySet()); - } - - @Override - protected Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment) - { - SslContextFactory sslContextFactory = getSslContextFactory(); - if (sslContextFactory != null) - { - final SSLEngine engine = newSSLEngine(sslContextFactory, channel); - SslConnection sslConnection = new SslConnection(getByteBufferPool(), getExecutor(), endPoint, engine) - { - @Override - public void onClose() - { - NextProtoNego.remove(engine); - super.onClose(); - } - }; - - final EndPoint sslEndPoint = sslConnection.getDecryptedEndPoint(); - NextProtoNegoServerConnection connection = new NextProtoNegoServerConnection(channel, sslEndPoint, this); - sslEndPoint.setConnection(connection); - getSelectorManager().connectionOpened(connection); - - NextProtoNego.put(engine, connection); - - return sslConnection; - } - else - { - return super.newConnection(channel, endPoint, this); - } - } - - protected FlowControlStrategy newFlowControlStrategy(short version) - { - return FlowControlStrategyFactory.newFlowControlStrategy(version); - } - - protected SSLEngine newSSLEngine(SslContextFactory sslContextFactory, SocketChannel channel) - { - String peerHost = channel.socket().getInetAddress().getHostAddress(); - int peerPort = channel.socket().getPort(); - SSLEngine engine = sslContextFactory.newSSLEngine(peerHost, peerPort); - engine.setUseClientMode(false); - return engine; - } - - protected boolean sessionOpened(Session session) - { - // Add sessions only if the connector is not stopping - return isRunning() && sessions.offer(session); - } - - protected boolean sessionClosed(Session session) - { - // Remove sessions only if the connector is not stopping - // to avoid concurrent removes during iterations - return isRunning() && sessions.remove(session); - } - - private void closeSessions() - { - for (Session session : sessions) - session.goAway(); - sessions.clear(); - } - - protected Collection getSessions() - { - return Collections.unmodifiableCollection(sessions); - } - - public int getInitialWindowSize() - { - return initialWindowSize; - } - - public void setInitialWindowSize(int initialWindowSize) - { - this.initialWindowSize = initialWindowSize; - } - - public void replaceConnection(EndPoint endPoint, Connection connection) - { - Connection oldConnection = endPoint.getConnection(); - LOG.debug("Upgrading connection {} -> {} on endPoint {}", oldConnection, connection, endPoint); - endPoint.setConnection(connection); - getSelectorManager().connectionUpgraded(endPoint, oldConnection); - } - - @Override - public void dump(Appendable out, String indent) throws IOException - { - super.dump(out, indent); - AggregateLifeCycle.dump(out, indent, new ArrayList<>(sessions)); - } } diff --git a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/ServerSPDYConnectionFactory.java b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/ServerSPDYConnectionFactory.java deleted file mode 100644 index 57116a538dc..00000000000 --- a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/ServerSPDYConnectionFactory.java +++ /dev/null @@ -1,142 +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.spdy; - -import java.nio.channels.SocketChannel; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.Connection; -import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.server.ConnectionFactory; -import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener; -import org.eclipse.jetty.spdy.generator.Generator; -import org.eclipse.jetty.spdy.parser.Parser; -import org.eclipse.jetty.util.thread.Scheduler; - -public class ServerSPDYConnectionFactory implements ConnectionFactory -{ - private final ByteBufferPool bufferPool; - private final Executor executor; - private final Scheduler scheduler; - private final short version; - private final ServerSessionFrameListener listener; - - public ServerSPDYConnectionFactory(short version, ByteBufferPool bufferPool, Executor executor, Scheduler scheduler) - { - this(version, bufferPool, executor, scheduler, null); - } - - public ServerSPDYConnectionFactory(short version, ByteBufferPool bufferPool, Executor executor, Scheduler scheduler, ServerSessionFrameListener listener) - { - this.version = version; - this.bufferPool = bufferPool; - this.executor = executor; - this.scheduler = scheduler; - this.listener = listener; - } - - public short getVersion() - { - return version; - } - - @Override - public Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment) - { - CompressionFactory compressionFactory = new StandardCompressionFactory(); - Parser parser = new Parser(compressionFactory.newDecompressor()); - Generator generator = new Generator(bufferPool, compressionFactory.newCompressor()); - - SPDYServerConnector connector = (SPDYServerConnector)attachment; - - ServerSessionFrameListener listener = provideServerSessionFrameListener(endPoint, attachment); - SPDYConnection connection = new ServerSPDYConnection(endPoint, bufferPool, parser, listener, connector); - - FlowControlStrategy flowControlStrategy = connector.newFlowControlStrategy(version); - - StandardSession session = new StandardSession(getVersion(), getBufferPool(), getExecutor(), getScheduler(), connection, connection, 2, listener, generator, flowControlStrategy); - session.setAttribute("org.eclipse.jetty.spdy.remoteAddress", endPoint.getRemoteAddress()); // TODO: make this available through API - session.setWindowSize(connector.getInitialWindowSize()); - parser.addListener(session); - connection.setSession(session); - - connector.sessionOpened(session); - - return connection; - } - - protected ServerSessionFrameListener provideServerSessionFrameListener(EndPoint endPoint, Object attachment) - { - return listener; - } - - protected ByteBufferPool getBufferPool() - { - return bufferPool; - } - - protected Executor getExecutor() - { - return executor; - } - - public Scheduler getScheduler() - { - return scheduler; - } - - private static class ServerSPDYConnection extends SPDYConnection implements Runnable - { - private final ServerSessionFrameListener listener; - private final SPDYServerConnector connector; - private final AtomicBoolean connected = new AtomicBoolean(); - - private ServerSPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, ServerSessionFrameListener listener, SPDYServerConnector connector) - { - super(endPoint, bufferPool, parser, connector.getExecutor()); - this.listener = listener; - this.connector = connector; - } - - @Override - public void onOpen() - { - super.onOpen(); - if (connected.compareAndSet(false, true)) - getExecutor().execute(this); - } - - @Override - public void onClose() - { - super.onClose(); - connector.sessionClosed(getSession()); - } - - @Override - public void run() - { - // NPE guard to support tests - if (listener != null) - listener.onConnect(getSession()); - } - } -} diff --git a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/AbstractTest.java b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/AbstractTest.java index c5ba9af3860..2bb726b96b8 100644 --- a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/AbstractTest.java +++ b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/AbstractTest.java @@ -23,6 +23,7 @@ import java.net.InetSocketAddress; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; +import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.spdy.api.SPDY; import org.eclipse.jetty.spdy.api.Session; @@ -66,10 +67,18 @@ public abstract class AbstractTest if (connector == null) connector = newSPDYServerConnector(server, listener); if (listener == null) - listener = connector.getServerSessionFrameListener(); - connector.setDefaultConnectionFactory(new ServerSPDYConnectionFactory(version, connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), listener)); + listener = connector.getConnectionFactory(SPDYServerConnectionFactory.class).getServerSessionFrameListener(); + + ConnectionFactory spdy = new SPDYServerConnectionFactory(version, listener); + connector.addConnectionFactory(spdy); connector.setPort(0); server.addConnector(connector); + + if (connector.getConnectionFactory(NPNServerConnectionFactory.class)!=null) + connector.getConnectionFactory(NPNServerConnectionFactory.class).setDefaultProtocol(spdy.getProtocol()); + else + connector.setDefaultProtocol(spdy.getProtocol()); + server.start(); return new InetSocketAddress("localhost", connector.getLocalPort()); } @@ -114,7 +123,7 @@ public abstract class AbstractTest SslContextFactory sslContextFactory = new SslContextFactory(); sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks"); sslContextFactory.setKeyStorePassword("storepwd"); - sslContextFactory.setTrustStore("src/test/resources/truststore.jks"); + sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks"); sslContextFactory.setTrustStorePassword("storepwd"); sslContextFactory.setProtocol("TLSv1"); sslContextFactory.setIncludeProtocols("TLSv1"); diff --git a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ClosedStreamTest.java b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ClosedStreamTest.java index 65a1edce603..49ddc2c661d 100644 --- a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ClosedStreamTest.java +++ b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ClosedStreamTest.java @@ -18,6 +18,10 @@ package org.eclipse.jetty.spdy; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; + import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; @@ -52,10 +56,6 @@ import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.junit.Assert.assertThat; - public class ClosedStreamTest extends AbstractTest { //TODO: Right now it sends a rst as the stream is unknown to the session once it's closed. diff --git a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/FlowControlTest.java b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/FlowControlTest.java index 39b1c6842a3..1327154a58a 100644 --- a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/FlowControlTest.java +++ b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/FlowControlTest.java @@ -19,6 +19,9 @@ package org.eclipse.jetty.spdy; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + import java.nio.ByteBuffer; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; @@ -45,9 +48,6 @@ import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener; import org.junit.Assert; import org.junit.Test; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - public class FlowControlTest extends AbstractTest { @Test @@ -189,7 +189,7 @@ public class FlowControlTest extends AbstractTest } } }); - + DataInfo dataInfo = exchanger.exchange(null, 5, TimeUnit.SECONDS); checkThatWeAreFlowControlStalled(exchanger); diff --git a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ProtocolViolationsTest.java b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ProtocolViolationsTest.java index 302a2ffbffb..f3fdb626c4d 100644 --- a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ProtocolViolationsTest.java +++ b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ProtocolViolationsTest.java @@ -18,6 +18,9 @@ package org.eclipse.jetty.spdy; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; @@ -46,9 +49,6 @@ import org.eclipse.jetty.util.Fields; import org.junit.Assert; import org.junit.Test; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - public class ProtocolViolationsTest extends AbstractTest { @Test diff --git a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/PushStreamTest.java b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/PushStreamTest.java index 01967c3c925..84fbc1de144 100644 --- a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/PushStreamTest.java +++ b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/PushStreamTest.java @@ -19,6 +19,12 @@ package org.eclipse.jetty.spdy; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.sameInstance; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; @@ -63,12 +69,6 @@ import org.eclipse.jetty.util.Fields; import org.junit.Assert; import org.junit.Test; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.sameInstance; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; - public class PushStreamTest extends AbstractTest { @Test diff --git a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ResetStreamTest.java b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ResetStreamTest.java index 83cbda285bb..51793191b6c 100644 --- a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ResetStreamTest.java +++ b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ResetStreamTest.java @@ -18,6 +18,12 @@ package org.eclipse.jetty.spdy; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @@ -35,12 +41,6 @@ import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener; import org.eclipse.jetty.util.Callback; import org.junit.Test; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - public class ResetStreamTest extends AbstractTest { @Test diff --git a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SPDYClientFactoryTest.java b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SPDYClientFactoryTest.java index e324601c561..8bba13d6e67 100644 --- a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SPDYClientFactoryTest.java +++ b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SPDYClientFactoryTest.java @@ -23,6 +23,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import junit.framework.Assert; + import org.eclipse.jetty.spdy.api.GoAwayInfo; import org.eclipse.jetty.spdy.api.Session; import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener; diff --git a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SPDYServerConnectorTest.java b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SPDYServerConnectorTest.java index 02f3d65b0b7..8b6906bebd6 100644 --- a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SPDYServerConnectorTest.java +++ b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SPDYServerConnectorTest.java @@ -23,6 +23,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import junit.framework.Assert; + import org.eclipse.jetty.spdy.api.GoAwayInfo; import org.eclipse.jetty.spdy.api.Session; import org.eclipse.jetty.spdy.api.SessionFrameListener; @@ -50,7 +51,7 @@ public class SPDYServerConnectorTest extends AbstractTest connector.stop(); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); - Assert.assertTrue(connector.getSessions().isEmpty()); + Assert.assertTrue(connector.getConnectionFactory(SPDYServerConnectionFactory.class).getSessions().isEmpty()); } @Test @@ -64,6 +65,6 @@ public class SPDYServerConnectorTest extends AbstractTest // since it is done asynchronously by the selector thread TimeUnit.SECONDS.sleep(1); - Assert.assertTrue(connector.getSessions().isEmpty()); + Assert.assertTrue(connector.getConnectionFactory(SPDYServerConnectionFactory.class).getSessions().isEmpty()); } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayUtil.java index 74151ea2bf2..740599d7e8d 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayUtil.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; + /* ------------------------------------------------------------ */ /** */ @@ -73,12 +74,37 @@ public class ArrayUtil } else { - // TODO: Replace with Arrays.copyOf(T[] original, int newLength) from Java 1.6+ + T[] na = Arrays.copyOf(array,array.length+1); + na[array.length]=item; + return na; + } + } + + /* ------------------------------------------------------------ */ + /** Add element to the start of an array + * @param array The array to add to (or null) + * @param item The item to add + * @param type The type of the array (in case of null array) + * @return new array with contents of array plus item + */ + public static T[] prependToArray(T item, T[] array, Class type) + { + if (array==null) + { + if (type==null && item!=null) + type= item.getClass(); + @SuppressWarnings("unchecked") + T[] na = (T[])Array.newInstance(type, 1); + na[0]=item; + return na; + } + else + { Class c = array.getClass().getComponentType(); @SuppressWarnings("unchecked") T[] na = (T[])Array.newInstance(c, Array.getLength(array)+1); - System.arraycopy(array, 0, na, 0, array.length); - na[array.length]=item; + System.arraycopy(array, 0, na, 1, array.length); + na[0]=item; return na; } } @@ -94,6 +120,23 @@ public class ArrayUtil return new ArrayList(); return new ArrayList(Arrays.asList(array)); } + + /* ------------------------------------------------------------ */ + public static T[] removeNulls(T[] array) + { + for (T t : array) + { + if (t==null) + { + List list = new ArrayList<>(); + for (T t2:array) + if (t2!=null) + list.add(t2); + return list.toArray(Arrays.copyOf(array,list.size())); + } + } + return array; + } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ExecutorCallback.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ExecutorCallback.java index c90b7515161..0db02579b96 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ExecutorCallback.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ExecutorCallback.java @@ -90,6 +90,11 @@ public abstract class ExecutorCallback implements Callback { @Override public void run() { onCompleted(context);} + @Override + public String toString() + { + return String.format("ExectorCB$Completed@%x{%s}",hashCode(),context); + } }); } @@ -110,7 +115,7 @@ public abstract class ExecutorCallback implements Callback @Override public String toString() { - return String.format("%s@%x{%s,%s}",ExecutorCallback.class.getSimpleName(),hashCode(),context,x); + return String.format("ExectorCB$Failed@%x{%s,%s}",hashCode(),context,x); } }; diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/AggregateLifeCycle.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/AggregateLifeCycle.java index 5c1bf9d711e..611bf05b9c2 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/AggregateLifeCycle.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/AggregateLifeCycle.java @@ -48,16 +48,19 @@ public class AggregateLifeCycle extends AbstractLifeCycle implements Destroyable private final List _beans = new CopyOnWriteArrayList<>(); private boolean _started = false; + enum Managed { MANAGED, UNMANAGED, AUTO }; + private class Bean { private final Object _bean; - private volatile boolean _managed = true; + private volatile Managed _managed = Managed.AUTO; private Bean(Object b) { _bean = b; } + @Override public String toString() { return String.format("{%s,%b}", _bean, _managed); @@ -70,26 +73,34 @@ public class AggregateLifeCycle extends AbstractLifeCycle implements Destroyable @Override protected void doStart() throws Exception { + // indicate that we are started, so that addBean will start other beans added. + _started = true; + + // start our managed and auto beans for (Bean b : _beans) { if (b._bean instanceof LifeCycle) { LifeCycle l = (LifeCycle)b._bean; - if (!l.isRunning()) + switch(b._managed) { - if (b._managed) - l.start(); + case MANAGED: + if (!l.isRunning()) + l.start(); + break; + case AUTO: + if (l.isRunning()) + b._managed=Managed.UNMANAGED; + else + { + b._managed=Managed.MANAGED; + l.start(); + } + break; } } - if (b._managed && b._bean instanceof LifeCycle) - { - LifeCycle l = (LifeCycle)b._bean; - if (!l.isRunning()) - l.start(); - } } - // indicate that we are started, so that addBean will start other beans added. - _started = true; + super.doStart(); } @@ -105,7 +116,7 @@ public class AggregateLifeCycle extends AbstractLifeCycle implements Destroyable Collections.reverse(reverse); for (Bean b : reverse) { - if (b._managed && b._bean instanceof LifeCycle) + if (b._managed==Managed.MANAGED && b._bean instanceof LifeCycle) { LifeCycle l = (LifeCycle)b._bean; if (l.isRunning()) @@ -117,13 +128,14 @@ public class AggregateLifeCycle extends AbstractLifeCycle implements Destroyable /** * Destroys the managed Destroyable beans in the reverse order they were added. */ + @Override public void destroy() { List reverse = new ArrayList<>(_beans); Collections.reverse(reverse); for (Bean b : reverse) { - if (b._bean instanceof Destroyable && b._managed) + if (b._bean instanceof Destroyable && b._managed==Managed.MANAGED) { Destroyable d = (Destroyable)b._bean; d.destroy(); @@ -154,7 +166,7 @@ public class AggregateLifeCycle extends AbstractLifeCycle implements Destroyable { for (Bean b : _beans) if (b._bean == bean) - return b._managed; + return b._managed==Managed.MANAGED; return false; } @@ -171,8 +183,12 @@ public class AggregateLifeCycle extends AbstractLifeCycle implements Destroyable */ public boolean addBean(Object o) { - // beans are joined unless they are started lifecycles - return addBean(o, !((o instanceof LifeCycle) && ((LifeCycle)o).isStarted())); + if (o instanceof LifeCycle) + { + LifeCycle l = (LifeCycle)o; + return addBean(o,l.isRunning()?Managed.UNMANAGED:Managed.AUTO); + } + return addBean(o,Managed.MANAGED); } /** @@ -183,6 +199,11 @@ public class AggregateLifeCycle extends AbstractLifeCycle implements Destroyable * @return true if the bean was added, false if it was already present */ public boolean addBean(Object o, boolean managed) + { + return addBean(o,managed?Managed.MANAGED:Managed.UNMANAGED); + } + + public boolean addBean(Object o, Managed managed) { if (contains(o)) return false; @@ -191,26 +212,41 @@ public class AggregateLifeCycle extends AbstractLifeCycle implements Destroyable b._managed = managed; _beans.add(b); - if (o instanceof LifeCycle) + if (_started) { - LifeCycle l = (LifeCycle)o; - - // Start the bean if we are started - if (managed && _started) + try { - try + if (o instanceof LifeCycle) { - l.start(); - } - catch (Exception e) - { - throw new RuntimeException(e); + LifeCycle l = (LifeCycle)o; + + switch(b._managed) + { + case MANAGED: + if (!l.isRunning()) + l.start(); + break; + case AUTO: + if (l.isRunning()) + b._managed=Managed.UNMANAGED; + else + { + b._managed=Managed.MANAGED; + l.start(); + } + break; + } } } + catch (Exception e) + { + throw new RuntimeException(e); + } } return true; } - + + /** * Manages a bean already contained by this aggregate, so that it is started/stopped/destroyed with this * aggregate. @@ -223,7 +259,7 @@ public class AggregateLifeCycle extends AbstractLifeCycle implements Destroyable { if (b._bean == bean) { - b._managed = true; + b._managed = Managed.MANAGED; return; } } @@ -242,7 +278,7 @@ public class AggregateLifeCycle extends AbstractLifeCycle implements Destroyable { if (b._bean == bean) { - b._managed = false; + b._managed = Managed.UNMANAGED; return; } } @@ -389,6 +425,7 @@ public class AggregateLifeCycle extends AbstractLifeCycle implements Destroyable } } + @Override public void dump(Appendable out, String indent) throws IOException { dumpThis(out); @@ -400,18 +437,29 @@ public class AggregateLifeCycle extends AbstractLifeCycle implements Destroyable { i++; - if (b._managed) + switch(b._managed) { - out.append(indent).append(" +- "); - if (b._bean instanceof Dumpable) - ((Dumpable)b._bean).dump(out, indent + (i == size ? " " : " | ")); - else + case MANAGED: + out.append(indent).append(" +- "); + if (b._bean instanceof Dumpable) + ((Dumpable)b._bean).dump(out, indent + (i == size ? " " : " | ")); + else + dumpObject(out, b._bean); + break; + + case UNMANAGED: + out.append(indent).append(" +~ "); dumpObject(out, b._bean); - } - else - { - out.append(indent).append(" +~ "); - dumpObject(out, b._bean); + break; + + case AUTO: + out.append(indent).append(" += "); + if (b._bean instanceof Dumpable) + ((Dumpable)b._bean).dump(out, indent + (i == size ? " " : " | ")); + else + dumpObject(out, b._bean); + break; + } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java index 999e3a12542..1d2b521feb5 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java @@ -516,7 +516,7 @@ public class SslContextFactory extends AbstractLifeCycle * @param trustStorePath * The file name or URL of the trust store location */ - public void setTrustStore(String trustStorePath) + public void setTrustStorePath(String trustStorePath) { checkNotStarted(); diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/BufferUtilTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/BufferUtilTest.java index 78e47e97893..e912c56393b 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/BufferUtilTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/BufferUtilTest.java @@ -70,9 +70,9 @@ public class BufferUtilTest for (int i=0;i 0) { LOG.debug("using named mapping"); @@ -767,14 +767,14 @@ public class XmlConfiguration LOG.debug("using normal mapping"); n = TypeUtil.construct(oClass, arguments.toArray()); } - - configure(n,node,argIndex); - return n; } catch (NoSuchMethodException e) { throw new IllegalStateException("No suitable constructor: " + node + " on " + obj); } + + configure(n,node,argIndex); + return n; } /* @@ -786,7 +786,7 @@ public class XmlConfiguration { String id = node.getAttribute("id"); obj = _idMap.get(id); - if (obj == null) + if (obj == null && node.size()>0) throw new IllegalStateException("No object for id=" + id); configure(obj,node,0); return obj; diff --git a/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java b/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java index a2f3bab1ce8..6e915371cf6 100644 --- a/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java +++ b/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java @@ -28,11 +28,12 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.server.Handler; -import org.eclipse.jetty.server.HttpServerConnectionFactory; +import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.NCSARequestLog; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.SelectChannelConnector; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.DefaultHandler; @@ -79,23 +80,24 @@ public class TestServer SelectChannelConnector connector0 = new SelectChannelConnector(server); connector0.setPort(8080); connector0.setIdleTimeout(30000); - ((HttpServerConnectionFactory)connector0.getDefaultConnectionFactory()).getHttpConfiguration().setConfidentialPort(8443); + connector0.getConnectionFactory(HttpConnectionFactory.class).getHttpChannelConfig().setSecurePort(8443); server.addConnector(connector0); // Setup Connectors SelectChannelConnector connector1 = new SelectChannelConnector(server); connector1.setPort(8081); - connector0.setIdleTimeout(30000); - ((HttpServerConnectionFactory)connector0.getDefaultConnectionFactory()).getHttpConfiguration().setConfidentialPort(8443); + connector1.setIdleTimeout(30000); + connector1.getConnectionFactory(HttpConnectionFactory.class).getHttpChannelConfig().setSecurePort(8443); server.addConnector(connector1); + SelectChannelConnector ssl_connector = new SelectChannelConnector(server,new SslContextFactory()); ssl_connector.setPort(8443); - SslContextFactory cf = ssl_connector.getSslContextFactory(); + SslContextFactory cf = ssl_connector.getConnectionFactory(SslConnectionFactory.class).getSslContextFactory(); cf.setKeyStorePath(jetty_root + "/jetty-server/src/main/config/etc/keystore"); cf.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); cf.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); - cf.setTrustStore(jetty_root + "/jetty-server/src/main/config/etc/keystore"); + cf.setTrustStorePath(jetty_root + "/jetty-server/src/main/config/etc/keystore"); cf.setTrustStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); server.addConnector(ssl_connector);