diff --git a/VERSION.txt b/VERSION.txt index c7bd8be018e..7b8a8569816 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1,4 +1,48 @@ -jetty-9.1.4-SNAPSHOT +jetty-9.2.0-SNAPSHOT + +jetty-9.1.4.v20140401 - 01 April 2014 + + 414206 Rewrite rules re-encode requestURI + + 414885 Don't expose JDT classes by default + + 417022 Access current HttpConnection from Request not ThreadLocal + + 423619 set Request timestamp on startRequest + + 423982 removed duplicate UrlResource toString + + 424107 Jetty should not finish chunked encoding on exception. + + 425991 added qml mime type + + 426897 improved ContainerLifeCycle javadoc + + 427185 Add org.objectweb.asm. as serverClass + + 427204 jetty-start / startup incorrectly requires directory in jetty.base + + 427368 start.sh fails quietly on command line error + + 428594 File upload with onMessage and InputStream fails + + 428595 JSR-356 / ClientContainer does not support SSL + + 428597 javax-websocket-client-impl and javax-websocket-server-impl jars + Manifests do not export packages for OSGI + + 428817 jetty-start / Allow for property to configure deploy manager + `webapps` directory + + 429180 Make requestlog filename parameterized + + 429357 JDBCSessionManager.Session.removeAttribute don't set dirty flag if + attribute already removed + + 429409 osgi] jetty.websocket.servlet must import jetty.websocket.server + + 429487 Runner code cleanups + + 429616 Use UTF-8 encoding for XML + + 429779 masked zero length websocket frame gives NullPointerException during + streaming read + + 430088 OnMessage*Callable decoding of streaming binary or text is not thread + safe + + 430242 added SharedBlockingCallback to support threadsafe blocking + + 430273 Cancel async timeout breaks volatile link to avoid race with slow + expire + + 430341 add apache jsp and jstl optional modules + + 430490 Added JETTY_SHELL 426738 Fixed JETTY_HOME comments + + 430649 test form encoding + + 430654 closing client connections can hang worker threads. + + 430808 OutputStreamContentProvider violates OutputStream contract. + + 430822 jetty-start / make soLingerTime configurable via property + + 430823 jetty-start / make NeedClientAuth (ssl) configurable via property + + 430824 jetty-start / use of jetty-logging.xml prevents configuration of + ThreadPool in jetty.xml + + 431103 Complete listener not called if request times out before processing + exchange. + + 431592 do not resolved forwarded-for address jetty-9.1.3.v20140225 - 25 February 2014 + 373952 Ensure MongoSessionManager un/binds session attributes on refresh diff --git a/aggregates/jetty-all/pom.xml b/aggregates/jetty-all/pom.xml index 916ec8c2c21..de451684512 100644 --- a/aggregates/jetty-all/pom.xml +++ b/aggregates/jetty-all/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/apache-jsp/pom.xml b/apache-jsp/pom.xml new file mode 100644 index 00000000000..27bafe50066 --- /dev/null +++ b/apache-jsp/pom.xml @@ -0,0 +1,95 @@ + + + org.eclipse.jetty + jetty-project + 9.2.0-SNAPSHOT + + 4.0.0 + apache-jsp + Jetty :: Apache JSP + http://www.eclipse.org/jetty + jar + + ${project.groupId}.${project.artifactId} + + + + + org.apache.felix + maven-bundle-plugin + true + + + generate-manifest + + manifest + + + + org.eclipse.jetty.apache.jsp.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}" + osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)" + osgi.serviceloader; osgi.serviceloader=javax.servlet.ServletContainerInitializer + <_nouses>true + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + config + + + + + + + + + + + org.eclipse.jetty + jetty-util + ${project.version} + + + + + org.eclipse.jetty.toolchain + jetty-schemas + + + + + javax.servlet + javax.servlet-api + + + + + javax.servlet.jsp + javax.servlet.jsp-api + + + + + org.mortbay.jasper + apache-jsp + + + + + org.eclipse.jetty.orbit + org.eclipse.jdt.core + + + diff --git a/apache-jsp/src/main/config/modules/jsp-impl/apache-jsp.mod b/apache-jsp/src/main/config/modules/jsp-impl/apache-jsp.mod new file mode 100644 index 00000000000..aed547c851f --- /dev/null +++ b/apache-jsp/src/main/config/modules/jsp-impl/apache-jsp.mod @@ -0,0 +1,10 @@ +# +# Apache JSP Module +# + +[name] +jsp-impl + +[lib] +lib/apache-jsp/*.jar + diff --git a/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp/JettyJasperInitializer.java b/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp/JettyJasperInitializer.java new file mode 100644 index 00000000000..4cd79932113 --- /dev/null +++ b/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp/JettyJasperInitializer.java @@ -0,0 +1,114 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.apache.jsp; + +import java.io.IOException; +import java.net.URL; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; + +import org.apache.jasper.servlet.JasperInitializer; +import org.apache.jasper.servlet.TldPreScanned; +import org.apache.jasper.servlet.TldScanner; +import org.xml.sax.SAXException; + +/** + * JettyJasperInitializer + * + */ +public class JettyJasperInitializer extends JasperInitializer +{ + + /** + * NullTldScanner + * + * Does nothing. Used when we can tell that all jsps have been precompiled, in which case + * the tlds are not needed. + */ + private final class NullTldScanner extends TldScanner + { + /** + * @param context + * @param namespaceAware + * @param validation + * @param blockExternal + */ + private NullTldScanner(ServletContext context, boolean namespaceAware, boolean validation, boolean blockExternal) + { + super(context, namespaceAware, validation, blockExternal); + } + + /** + * @see org.apache.jasper.servlet.TldScanner#scan() + */ + @Override + public void scan() throws IOException, SAXException + { + return; //do nothing + } + + /** + * @see org.apache.jasper.servlet.TldScanner#getListeners() + */ + @Override + public List getListeners() + { + return Collections.emptyList(); + } + + /** + * @see org.apache.jasper.servlet.TldScanner#scanJars() + */ + @Override + public void scanJars() + { + return; //do nothing + } + } + + /** + * Make a TldScanner, and prefeed it the tlds that have already been discovered in jar files + * by the MetaInfConfiguration. + * + * @see org.apache.jasper.servlet.JasperInitializer#prepareScanner(javax.servlet.ServletContext, boolean, boolean, boolean) + */ + @Override + public TldScanner newTldScanner(ServletContext context, boolean namespaceAware, boolean validate, boolean blockExternal) + { + String tmp = context.getInitParameter("org.eclipse.jetty.jsp.precompiled"); + if (tmp!=null && !tmp.equals("") && Boolean.valueOf(tmp)) + { + return new NullTldScanner(context, namespaceAware, validate, blockExternal); + } + + Collection tldUrls = (Collection)context.getAttribute("org.eclipse.jetty.tlds"); + if (tldUrls != null && !tldUrls.isEmpty()) + { + return new TldPreScanned(context,namespaceAware,validate,blockExternal,tldUrls); + } + return super.newTldScanner(context, namespaceAware, validate, blockExternal); + } + + +} diff --git a/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp/JuliLog.java b/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp/JuliLog.java new file mode 100644 index 00000000000..e58781d0798 --- /dev/null +++ b/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp/JuliLog.java @@ -0,0 +1,188 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.apache.jsp; + +public class JuliLog implements org.apache.juli.logging.Log +{ + public static org.apache.juli.logging.Log getInstance(String name) + { + return new JuliLog(name); + } + + private final org.eclipse.jetty.util.log.Logger _logger; + private final org.eclipse.jetty.util.log.StdErrLog _stdErrLog; + + public JuliLog() + { + _logger=org.eclipse.jetty.util.log.Log.getRootLogger(); + _stdErrLog=(_logger instanceof org.eclipse.jetty.util.log.StdErrLog) ? (org.eclipse.jetty.util.log.StdErrLog)_logger:null; + } + + public JuliLog(String name) + { + _logger=org.eclipse.jetty.util.log.Log.getLogger(name); + _stdErrLog=(_logger instanceof org.eclipse.jetty.util.log.StdErrLog) ? (org.eclipse.jetty.util.log.StdErrLog)_logger:null; + } + + @Override + public boolean isDebugEnabled() + { + return _logger.isDebugEnabled(); + } + + @Override + public boolean isErrorEnabled() + { + return _stdErrLog==null?true:_stdErrLog.getLevel()<=org.eclipse.jetty.util.log.StdErrLog.LEVEL_WARN; + } + + @Override + public boolean isFatalEnabled() + { + return _stdErrLog==null?true:_stdErrLog.getLevel()<=org.eclipse.jetty.util.log.StdErrLog.LEVEL_WARN; + } + + @Override + public boolean isInfoEnabled() + { + return _stdErrLog==null?true:_stdErrLog.getLevel()<=org.eclipse.jetty.util.log.StdErrLog.LEVEL_INFO; + } + + @Override + public boolean isTraceEnabled() + { + return _stdErrLog==null?true:_stdErrLog.getLevel()<=org.eclipse.jetty.util.log.StdErrLog.LEVEL_DEBUG; + } + + @Override + public boolean isWarnEnabled() + { + return _stdErrLog==null?true:_stdErrLog.getLevel()<=org.eclipse.jetty.util.log.StdErrLog.LEVEL_WARN; + } + + @Override + public void trace(Object message) + { + if (message instanceof String) + _logger.debug((String)message); + else + _logger.debug("{}",message); + } + + @Override + public void trace(Object message, Throwable t) + { + if (message instanceof String) + _logger.debug((String)message,t); + else + _logger.debug("{}",message,t); + } + + @Override + public void debug(Object message) + { + if (message instanceof String) + _logger.debug((String)message); + else + _logger.debug("{}",message); + } + + @Override + public void debug(Object message, Throwable t) + { + if (message instanceof String) + _logger.debug((String)message,t); + else + _logger.debug("{}",message,t); + } + + @Override + public void info(Object message) + { + if (message instanceof String) + _logger.info((String)message); + else + _logger.info("{}",message); + } + + @Override + public void info(Object message, Throwable t) + { + if (message instanceof String) + _logger.info((String)message,t); + else + _logger.info("{}",message,t); + } + + @Override + public void warn(Object message) + { + if (message instanceof String) + _logger.warn((String)message); + else + _logger.warn("{}",message); + } + + @Override + public void warn(Object message, Throwable t) + { + if (message instanceof String) + _logger.warn((String)message,t); + else + _logger.warn("{}",message,t); + } + + @Override + public void error(Object message) + { + if (message instanceof String) + _logger.warn((String)message); + else + _logger.warn("{}",message); + } + + @Override + public void error(Object message, Throwable t) + { + if (message instanceof String) + _logger.warn((String)message,t); + else + _logger.warn("{}",message,t); + } + + @Override + public void fatal(Object message) + { + if (message instanceof String) + _logger.warn((String)message); + else + _logger.warn("{}",message); + } + + @Override + public void fatal(Object message, Throwable t) + { + if (message instanceof String) + _logger.warn((String)message,t); + else + _logger.warn("{}",message,t); + } +} + + diff --git a/apache-jsp/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer b/apache-jsp/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer new file mode 100644 index 00000000000..634670591f6 --- /dev/null +++ b/apache-jsp/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer @@ -0,0 +1 @@ +org.eclipse.jetty.apache.jsp.JettyJasperInitializer diff --git a/apache-jsp/src/main/resources/META-INF/services/org.apache.juli.logging.Log b/apache-jsp/src/main/resources/META-INF/services/org.apache.juli.logging.Log new file mode 100644 index 00000000000..efa397b7c60 --- /dev/null +++ b/apache-jsp/src/main/resources/META-INF/services/org.apache.juli.logging.Log @@ -0,0 +1 @@ +org.eclipse.jetty.apache.jsp.JuliLog diff --git a/apache-jstl/pom.xml b/apache-jstl/pom.xml new file mode 100644 index 00000000000..38c81584d04 --- /dev/null +++ b/apache-jstl/pom.xml @@ -0,0 +1,49 @@ + + + org.eclipse.jetty + jetty-project + 9.2.0-SNAPSHOT + + 4.0.0 + apache-jstl + Apache :: JSTL module + http://tomcat.apache.org/taglibs/standard/ + jar + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + config + + + + + + + + + + + + org.apache.taglibs + taglibs-standard-spec + + + + + org.apache.taglibs + taglibs-standard-impl + + + + diff --git a/apache-jstl/src/main/config/modules/jsp-impl/apache-jstl.mod b/apache-jstl/src/main/config/modules/jsp-impl/apache-jstl.mod new file mode 100644 index 00000000000..804b19131b4 --- /dev/null +++ b/apache-jstl/src/main/config/modules/jsp-impl/apache-jstl.mod @@ -0,0 +1,8 @@ +# +# Apache JSTL +# +[name] +jstl-impl + +[lib] +lib/apache-jstl/*.jar diff --git a/apache-jstl/src/main/resources/readme.txt b/apache-jstl/src/main/resources/readme.txt new file mode 100644 index 00000000000..a516023c352 --- /dev/null +++ b/apache-jstl/src/main/resources/readme.txt @@ -0,0 +1,4 @@ +This empty jar file is purely to work around a problem with the Maven Dependency plugin. +Several modules in jetty use the Dependency plugin to copy or unpack the dependencies of other modules. +However, the Dependency plugin is not capable of unpacking or copying a dependency of type 'pom', which +this module is, as it consists purely of external dependencies needed to run jsp. diff --git a/examples/async-rest/async-rest-jar/pom.xml b/examples/async-rest/async-rest-jar/pom.xml index 71ab507a1aa..9af20787489 100644 --- a/examples/async-rest/async-rest-jar/pom.xml +++ b/examples/async-rest/async-rest-jar/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty example-async-rest - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 org.eclipse.jetty.example-async-rest diff --git a/examples/async-rest/async-rest-webapp/pom.xml b/examples/async-rest/async-rest-webapp/pom.xml index 2a626464e0f..1779812ea51 100644 --- a/examples/async-rest/async-rest-webapp/pom.xml +++ b/examples/async-rest/async-rest-webapp/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty example-async-rest - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 org.eclipse.jetty.example-async-rest diff --git a/examples/async-rest/pom.xml b/examples/async-rest/pom.xml index f45d90e997c..84bd9a4c112 100644 --- a/examples/async-rest/pom.xml +++ b/examples/async-rest/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.examples examples-parent - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/examples/embedded/pom.xml b/examples/embedded/pom.xml index f6045650879..30019e01b86 100644 --- a/examples/embedded/pom.xml +++ b/examples/embedded/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.examples examples-parent - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/examples/pom.xml b/examples/pom.xml index d2856c216fc..737b90f6145 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -21,7 +21,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT ../pom.xml org.eclipse.jetty.examples diff --git a/jetty-alpn/jetty-alpn-client/pom.xml b/jetty-alpn/jetty-alpn-client/pom.xml new file mode 100644 index 00000000000..0c3bb4dafeb --- /dev/null +++ b/jetty-alpn/jetty-alpn-client/pom.xml @@ -0,0 +1,76 @@ + + + org.eclipse.jetty + jetty-alpn-parent + 9.2.0-SNAPSHOT + + 4.0.0 + jetty-alpn-client + Jetty :: ALPN Client + Jetty ALPN client services + http://www.eclipse.org/jetty + + ${project.groupId}.alpn.client + + + + + org.apache.felix + maven-bundle-plugin + true + + + + manifest + + + + org.eclipse.jetty.alpn;resolution:=optional + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + + org.apache.maven.plugins + maven-source-plugin + + + org.codehaus.mojo + findbugs-maven-plugin + + org.eclipse.jetty.alpn.* + + + + + + + org.eclipse.jetty + jetty-io + ${project.version} + + + org.eclipse.jetty.alpn + alpn-api + ${alpn.api.version} + provided + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + diff --git a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java new file mode 100644 index 00000000000..1cc08abf90d --- /dev/null +++ b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java @@ -0,0 +1,87 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.alpn.client; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; +import javax.net.ssl.SSLEngine; + +import org.eclipse.jetty.alpn.ALPN; +import org.eclipse.jetty.io.ClientConnectionFactory; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.NegotiatingClientConnection; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +public class ALPNClientConnection extends NegotiatingClientConnection implements ALPN.ClientProvider +{ + private static final Logger LOG = Log.getLogger(ALPNClientConnection.class); + + private final String protocol; + + public ALPNClientConnection(EndPoint endPoint, Executor executor, ClientConnectionFactory connectionFactory, SSLEngine sslEngine, Map context, String protocol) + { + super(endPoint, executor, sslEngine, connectionFactory, context); + this.protocol = protocol; + ALPN.put(sslEngine, this); + } + + @Override + public boolean supports() + { + return true; + } + + @Override + public void unsupported() + { + ALPN.remove(getSSLEngine()); + completed(); + } + + @Override + public List protocols() + { + return Arrays.asList(protocol); + } + + @Override + public void selected(String protocol) + { + if (this.protocol.equals(protocol)) + { + ALPN.remove(getSSLEngine()); + completed(); + } + else + { + LOG.info("Could not negotiate protocol: server {} - client {}", protocol, this.protocol); + close(); + } + } + + @Override + public void close() + { + ALPN.remove(getSSLEngine()); + super.close(); + } +} diff --git a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java new file mode 100644 index 00000000000..881c6a51752 --- /dev/null +++ b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java @@ -0,0 +1,51 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.alpn.client; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.Executor; + +import javax.net.ssl.SSLEngine; + +import org.eclipse.jetty.io.ClientConnectionFactory; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.NegotiatingClientConnectionFactory; +import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; + +public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFactory +{ + private final Executor executor; + private final String protocol; + + public ALPNClientConnectionFactory(Executor executor, ClientConnectionFactory connectionFactory, String protocol) + { + super(connectionFactory); + this.executor = executor; + this.protocol = protocol; + } + + @Override + public Connection newConnection(EndPoint endPoint, Map context) throws IOException + { + return new ALPNClientConnection(endPoint, executor, getClientConnectionFactory(), + (SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY), context, protocol); + } +} diff --git a/jetty-alpn/jetty-alpn-server/pom.xml b/jetty-alpn/jetty-alpn-server/pom.xml new file mode 100644 index 00000000000..6634192901b --- /dev/null +++ b/jetty-alpn/jetty-alpn-server/pom.xml @@ -0,0 +1,94 @@ + + + org.eclipse.jetty + jetty-alpn-parent + 9.2.0-SNAPSHOT + + 4.0.0 + jetty-alpn-server + Jetty :: ALPN Server + Jetty ALPN server services + http://www.eclipse.org/jetty + + ${project.groupId}.alpn.server + + + + + org.apache.felix + maven-bundle-plugin + true + + + + manifest + + + + org.eclipse.jetty.alpn,* + <_nouses>true + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + config + + + + + + + + org.apache.maven.plugins + maven-source-plugin + + + org.codehaus.mojo + findbugs-maven-plugin + + org.eclipse.jetty.alpn.* + + + + + + + org.eclipse.jetty + jetty-server + ${project.version} + + + org.eclipse.jetty.alpn + alpn-api + ${alpn.api.version} + provided + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/etc/protonego-alpn.xml b/jetty-alpn/jetty-alpn-server/src/main/config/etc/protonego-alpn.xml new file mode 100644 index 00000000000..293de040bca --- /dev/null +++ b/jetty-alpn/jetty-alpn-server/src/main/config/etc/protonego-alpn.xml @@ -0,0 +1,16 @@ + + + + + + + spdy/3 + spdy/2 + http/1.1 + + + + http/1.1 + + + diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_40.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_40.mod new file mode 100644 index 00000000000..bee4693e8ac --- /dev/null +++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_40.mod @@ -0,0 +1,8 @@ +[name] +protonego-boot + +[files] +http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.0.0.v20140317/alpn-boot-7.0.0.v20140317.jar:lib/alpn/alpn-boot-7.0.0.v20140317.jar + +[exec] +-Xbootclasspath/p:lib/alpn/alpn-boot-7.0.0.v20140317.jar diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_45.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_45.mod new file mode 100644 index 00000000000..bee4693e8ac --- /dev/null +++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_45.mod @@ -0,0 +1,8 @@ +[name] +protonego-boot + +[files] +http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.0.0.v20140317/alpn-boot-7.0.0.v20140317.jar:lib/alpn/alpn-boot-7.0.0.v20140317.jar + +[exec] +-Xbootclasspath/p:lib/alpn/alpn-boot-7.0.0.v20140317.jar diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_51.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_51.mod new file mode 100644 index 00000000000..bee4693e8ac --- /dev/null +++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_51.mod @@ -0,0 +1,8 @@ +[name] +protonego-boot + +[files] +http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.0.0.v20140317/alpn-boot-7.0.0.v20140317.jar:lib/alpn/alpn-boot-7.0.0.v20140317.jar + +[exec] +-Xbootclasspath/p:lib/alpn/alpn-boot-7.0.0.v20140317.jar diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_55.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_55.mod new file mode 100644 index 00000000000..bee4693e8ac --- /dev/null +++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_55.mod @@ -0,0 +1,8 @@ +[name] +protonego-boot + +[files] +http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.0.0.v20140317/alpn-boot-7.0.0.v20140317.jar:lib/alpn/alpn-boot-7.0.0.v20140317.jar + +[exec] +-Xbootclasspath/p:lib/alpn/alpn-boot-7.0.0.v20140317.jar diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0.mod new file mode 100644 index 00000000000..4089153ac14 --- /dev/null +++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0.mod @@ -0,0 +1,8 @@ +[name] +protonego-boot + +[files] +http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.0.0.v20140317/alpn-boot-8.0.0.v20140317.jar:lib/alpn/alpn-boot-8.0.0.v20140317.jar + +[exec] +-Xbootclasspath/p:lib/alpn/alpn-boot-8.0.0.v20140317.jar diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_05.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_05.mod new file mode 100644 index 00000000000..4089153ac14 --- /dev/null +++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_05.mod @@ -0,0 +1,8 @@ +[name] +protonego-boot + +[files] +http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.0.0.v20140317/alpn-boot-8.0.0.v20140317.jar:lib/alpn/alpn-boot-8.0.0.v20140317.jar + +[exec] +-Xbootclasspath/p:lib/alpn/alpn-boot-8.0.0.v20140317.jar diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn.mod new file mode 100644 index 00000000000..0e399f05cb1 --- /dev/null +++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn.mod @@ -0,0 +1,36 @@ +# ALPN is provided via a -Xbootclasspath that modifies the secure connections +# in java to support the ALPN layer needed for SPDY (and eventually HTTP/2) +# +# This modification has a tight dependency on specific recent updates of +# Java 1.7 and Java 1.8 +# (Java versions prior to 1.7u40 are not supported) +# +# The alpn protonego module will use an appropriate alpn-boot jar for your +# specific version of Java. +# +# IMPORTANT: Versions of Java that exist after this module was created are +# not guaranteed to work with existing alpn-boot jars, and might +# need a new alpn-boot to be created / tested / deployed by the +# Jetty project in order to provide support for these future +# Java versions. +# +# All versions of alpn-boot can be found at +# http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/ + +[name] +protonego-impl + +[depend] +protonego-impl/alpn-${java.version} + +[lib] +lib/jetty-alpn-client-${jetty.version}.jar +lib/jetty-alpn-server-${jetty.version}.jar + +[xml] +etc/protonego-alpn.xml + +[files] +lib/ +lib/alpn/ + diff --git a/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnection.java b/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnection.java new file mode 100644 index 00000000000..509682eb647 --- /dev/null +++ b/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnection.java @@ -0,0 +1,77 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.alpn.server; + +import java.util.Collections; +import java.util.List; +import javax.net.ssl.SSLEngine; + +import org.eclipse.jetty.alpn.ALPN; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.NegotiatingServerConnection; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +public class ALPNServerConnection extends NegotiatingServerConnection implements ALPN.ServerProvider +{ + private static final Logger LOG = Log.getLogger(ALPNServerConnection.class); + + public ALPNServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List protocols, String defaultProtocol) + { + super(connector, endPoint, engine, protocols, defaultProtocol); + ALPN.put(engine, this); + } + + @Override + public void unsupported() + { + select(Collections.emptyList()); + } + + @Override + public String select(List clientProtocols) + { + List serverProtocols = getProtocols(); + String negotiated = null; + for (String clientProtocol : clientProtocols) + { + if (serverProtocols.contains(clientProtocol)) + { + negotiated = clientProtocol; + break; + } + } + if (negotiated == null) + { + negotiated = getDefaultProtocol(); + } + LOG.debug("{} protocol selected {}", this, negotiated); + setProtocol(negotiated); + ALPN.remove(getSSLEngine()); + return negotiated; + } + + @Override + public void close() + { + ALPN.remove(getSSLEngine()); + super.close(); + } +} diff --git a/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnectionFactory.java b/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnectionFactory.java new file mode 100644 index 00000000000..a6e386b7bda --- /dev/null +++ b/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnectionFactory.java @@ -0,0 +1,61 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.alpn.server; + +import java.util.List; +import javax.net.ssl.SSLEngine; + +import org.eclipse.jetty.alpn.ALPN; +import org.eclipse.jetty.io.AbstractConnection; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.NegotiatingServerConnectionFactory; +import org.eclipse.jetty.util.annotation.Name; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +public class ALPNServerConnectionFactory extends NegotiatingServerConnectionFactory +{ + private static final Logger LOG = Log.getLogger(ALPNServerConnectionFactory.class); + + public ALPNServerConnectionFactory(@Name("protocols") String... protocols) + { + super("alpn", protocols); + try + { + ClassLoader alpnClassLoader = ALPN.class.getClassLoader(); + if (alpnClassLoader != null) + { + LOG.warn("ALPN must be in the boot classloader, not in: " + alpnClassLoader); + throw new IllegalStateException("ALPN must be in the boot classloader"); + } + } + catch (Throwable x) + { + LOG.warn("ALPN not available", x); + throw new IllegalStateException("ALPN not available", x); + } + } + + @Override + protected AbstractConnection newServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List protocols, String defaultProtocol) + { + return new ALPNServerConnection(connector, endPoint, engine, protocols, defaultProtocol); + } +} diff --git a/jetty-alpn/pom.xml b/jetty-alpn/pom.xml new file mode 100644 index 00000000000..689b859f091 --- /dev/null +++ b/jetty-alpn/pom.xml @@ -0,0 +1,17 @@ + + + org.eclipse.jetty + jetty-project + 9.2.0-SNAPSHOT + + 4.0.0 + jetty-alpn-parent + pom + Jetty :: ALPN :: Parent + Jetty ALPN services parent + http://www.eclipse.org/jetty + + jetty-alpn-server + jetty-alpn-client + + diff --git a/jetty-annotations/pom.xml b/jetty-annotations/pom.xml index 23621354f30..f07812ffce3 100644 --- a/jetty-annotations/pom.xml +++ b/jetty-annotations/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-annotations @@ -43,8 +43,8 @@ - javax.servlet.*;version="[2.6.0,3.2)",* - osgi.serviceloader; filter:="(osgi.serviceloader=javax.servlet.ServletContainerInitializer)";cardinality:=multiple, osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)" + javax.servlet.*;version="[2.6.0,3.2)",org.objectweb.asm.*;version=4,* + osgi.serviceloader; filter:="(osgi.serviceloader=javax.servlet.ServletContainerInitializer)";resolution:=optional;cardinality:=multiple, osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)" diff --git a/jetty-ant/pom.xml b/jetty-ant/pom.xml index c5ae4795a72..3f9ade04e3c 100644 --- a/jetty-ant/pom.xml +++ b/jetty-ant/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-ant diff --git a/jetty-client/pom.xml b/jetty-client/pom.xml index 6332c066d41..ea2eb2d4c01 100644 --- a/jetty-client/pom.xml +++ b/jetty-client/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 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 79c59629ba7..15cd719d983 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 @@ -391,26 +391,29 @@ public class HttpClient extends ContainerLifeCycle .idleTimeout(oldRequest.getIdleTimeout(), TimeUnit.MILLISECONDS) .timeout(oldRequest.getTimeout(), TimeUnit.MILLISECONDS) .followRedirects(oldRequest.isFollowRedirects()); - for (HttpField header : oldRequest.getHeaders()) + for (HttpField field : oldRequest.getHeaders()) { - // We have a new URI, so skip the host header if present - if (HttpHeader.HOST == header.getHeader()) + HttpHeader header = field.getHeader(); + // We have a new URI, so skip the host header if present. + if (HttpHeader.HOST == header) continue; - // Remove expectation headers - if (HttpHeader.EXPECT == header.getHeader()) + // Remove expectation headers. + if (HttpHeader.EXPECT == header) continue; - // Remove cookies - if (HttpHeader.COOKIE == header.getHeader()) + // Remove cookies. + if (HttpHeader.COOKIE == header) continue; - // Remove authorization headers - if (HttpHeader.AUTHORIZATION == header.getHeader() || - HttpHeader.PROXY_AUTHORIZATION == header.getHeader()) + // Remove authorization headers. + if (HttpHeader.AUTHORIZATION == header || + HttpHeader.PROXY_AUTHORIZATION == header) continue; - newRequest.header(header.getName(), header.getValue()); + String value = field.getValue(); + if (!newRequest.getHeaders().contains(header, value)) + newRequest.header(field.getName(), value); } return newRequest; } 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 5a40a2c4061..cc83372c437 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 @@ -215,6 +215,10 @@ public abstract class HttpDestination implements Destination, Closeable, Dumpabl LOG.debug("Closed {}", this); } + public void release(Connection connection) + { + } + public void close(Connection connection) { } 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 8358243501e..d2e26bdafd2 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 @@ -62,7 +62,7 @@ public abstract class HttpSender implements AsyncContentProvider.Listener private final AtomicReference requestState = new AtomicReference<>(RequestState.QUEUED); private final AtomicReference senderState = new AtomicReference<>(SenderState.IDLE); private final Callback commitCallback = new CommitCallback(); - private final Callback contentCallback = new ContentCallback(); + private final IteratingCallback contentCallback = new ContentCallback(); private final Callback lastCallback = new LastContentCallback(); private final HttpChannel channel; private volatile HttpContent content; @@ -100,14 +100,7 @@ public abstract class HttpSender implements AsyncContentProvider.Listener if (updateSenderState(current, newSenderState)) { LOG.debug("Deferred content available, {} -> {}", current, newSenderState); - // TODO should just call contentCallback.iterate() here. - HttpContent content = this.content; - if (content.advance()) - sendContent(exchange, content, contentCallback); // TODO old style usage! - else if (content.isConsumed()) - sendContent(exchange, content, lastCallback); - else - throw new IllegalStateException(); + contentCallback.iterate(); return; } break; @@ -152,7 +145,7 @@ public abstract class HttpSender implements AsyncContentProvider.Listener } default: { - throw new IllegalStateException(current.toString()); + throw illegalSenderState(current); } } } @@ -178,7 +171,7 @@ public abstract class HttpSender implements AsyncContentProvider.Listener if (expects100Continue(request)) newSenderState = content.hasContent() ? SenderState.EXPECTING_WITH_CONTENT : SenderState.EXPECTING; if (!updateSenderState(SenderState.IDLE, newSenderState)) - throw new IllegalStateException(); + throw illegalSenderState(SenderState.IDLE); // Setting the listener may trigger calls to onContent() by other // threads so we must set it only after the sender state has been updated @@ -456,29 +449,15 @@ public abstract class HttpSender implements AsyncContentProvider.Listener case WAITING: { // We received the 100 Continue, now send the content if any. - HttpContent content = this.content; - // TODO should just call contentCallback.iterate() here. - if (content.advance()) - { - // There is content to send. - if (!updateSenderState(current, SenderState.SENDING)) - throw new IllegalStateException(); - LOG.debug("Proceeding while waiting"); - sendContent(exchange, content, contentCallback); // TODO old style usage! - return; - } - else - { - // No content to send yet - it's deferred. - if (!updateSenderState(current, SenderState.IDLE)) - throw new IllegalStateException(); - LOG.debug("Proceeding deferred"); - return; - } + if (!updateSenderState(current, SenderState.SENDING)) + throw illegalSenderState(current); + LOG.debug("Proceeding while waiting"); + contentCallback.iterate(); + return; } default: { - throw new IllegalStateException(current.toString()); + throw illegalSenderState(current); } } } @@ -532,6 +511,11 @@ public abstract class HttpSender implements AsyncContentProvider.Listener } } + private RuntimeException illegalSenderState(SenderState current) + { + return new IllegalStateException("Expected " + current + " found " + senderState.get() + " instead"); + } + /** * The request states {@link HttpSender} goes through when sending a request. */ @@ -660,30 +644,8 @@ public abstract class HttpSender implements AsyncContentProvider.Listener { case SENDING: { - // TODO should just call contentCallback.iterate() here. - // We have content to send ? - if (content.advance()) - { - sendContent(exchange, content, contentCallback); // TODO old style usage! - return; - } - else - { - if (content.isConsumed()) - { - sendContent(exchange, content, lastCallback); - return; - } - else - { - if (updateSenderState(current, SenderState.IDLE)) - { - LOG.debug("Waiting for deferred content for {}", request); - return; - } - break; - } - } + contentCallback.iterate(); + return; } case SENDING_WITH_CONTENT: { @@ -723,7 +685,7 @@ public abstract class HttpSender implements AsyncContentProvider.Listener } default: { - throw new IllegalStateException(); + throw illegalSenderState(current); } } } @@ -740,63 +702,47 @@ public abstract class HttpSender implements AsyncContentProvider.Listener if (exchange == null) return Action.IDLE; - Request request = exchange.getRequest(); HttpContent content = HttpSender.this.content; - - ByteBuffer contentBuffer = content.getContent(); - if (contentBuffer != null) - { - if (!someToContent(request, contentBuffer)) - return Action.IDLE; - } - while (true) { boolean advanced = content.advance(); boolean consumed = content.isConsumed(); + if (LOG.isDebugEnabled()) + LOG.debug("Content {} consumed {} for {}", advanced, consumed, exchange.getRequest()); - SenderState current = senderState.get(); + if (advanced) + { + sendContent(exchange, content, this); + return Action.SCHEDULED; + } + + if (consumed) + { + sendContent(exchange, content, lastCallback); + return Action.IDLE; + } + + SenderState current = HttpSender.this.senderState.get(); switch (current) { case SENDING: { - if (advanced) + if (updateSenderState(current, SenderState.IDLE)) { - // There is more content to send - sendContent(exchange, content, this); - return Action.SCHEDULED; - } - else if (consumed) - { - sendContent(exchange, content, lastCallback); + if (LOG.isDebugEnabled()) + LOG.debug("Content is deferred for {}", exchange.getRequest()); return Action.IDLE; } - else - { - if (updateSenderState(current, SenderState.IDLE)) - { - LOG.debug("Waiting for deferred content for {}", request); - return Action.IDLE; - } - break; - } + break; } case SENDING_WITH_CONTENT: { - if (updateSenderState(current, SenderState.SENDING)) - { - LOG.debug("Deferred content available for {}", request); - if (advanced) - { - sendContent(exchange, content, this); - return Action.SCHEDULED; - } - } - throw new IllegalStateException(); + updateSenderState(current, SenderState.SENDING); + break; } default: { - throw new IllegalStateException(); + throw illegalSenderState(current); } } } @@ -805,6 +751,8 @@ public abstract class HttpSender implements AsyncContentProvider.Listener @Override public void succeeded() { + ByteBuffer buffer = content.getContent(); + someToContent(getHttpExchange().getRequest(), buffer); content.succeeded(); super.succeeded(); } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexHttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexHttpDestination.java index 7404ce3ed1b..563c92dc904 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexHttpDestination.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexHttpDestination.java @@ -129,6 +129,15 @@ public abstract class MultiplexHttpDestination extends Htt return true; } + @Override + public void close() + { + super.close(); + C connection = this.connection; + if (connection != null) + connection.close(); + } + @Override public void close(Connection connection) { diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/PoolingHttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/PoolingHttpDestination.java index 6bba6a58205..73c13ad45da 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/PoolingHttpDestination.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/PoolingHttpDestination.java @@ -140,8 +140,11 @@ public abstract class PoolingHttpDestination extends HttpD protected abstract void send(C connection, HttpExchange exchange); - public void release(C connection) + @Override + public void release(Connection c) { + @SuppressWarnings("unchecked") + C connection = (C)c; LOG.debug("{} released", connection); HttpClient client = getHttpClient(); if (client.isRunning()) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpChannelOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpChannelOverHTTP.java index 76d99d74bda..cd5bcf274f6 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpChannelOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpChannelOverHTTP.java @@ -77,9 +77,10 @@ public class HttpChannelOverHTTP extends HttpChannel public void exchangeTerminated(Result result) { super.exchangeTerminated(result); - boolean close = result.isFailed(); HttpFields responseHeaders = result.getResponse().getHeaders(); - close |= responseHeaders.contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString()); + boolean close = result.isFailed() || + responseHeaders.contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString()) || + receiver.isShutdown(); if (close) connection.close(); else diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java index 219a56ee047..0b1b1b0fcf2 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java @@ -18,6 +18,7 @@ package org.eclipse.jetty.client.http; +import java.nio.channels.AsynchronousCloseException; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; @@ -85,13 +86,8 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec protected boolean onReadTimeout() { LOG.debug("{} idle timeout", this); - - HttpExchange exchange = channel.getHttpExchange(); - if (exchange != null) - return exchange.getRequest().abort(new TimeoutException()); - - getHttpDestination().close(this); - return true; + close(new TimeoutException()); + return false; } @Override @@ -119,14 +115,23 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec @Override public void close() + { + close(new AsynchronousCloseException()); + } + + protected void close(Throwable failure) { if (softClose()) { + // First close then abort, to be sure that the connection cannot be reused + // from an onFailure() handler or by blocking code waiting for completion. getHttpDestination().close(this); getEndPoint().shutdownOutput(); LOG.debug("{} oshut", this); getEndPoint().close(); LOG.debug("{} closed", this); + + abort(failure); } } @@ -135,6 +140,12 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec return closed.compareAndSet(false, true); } + private boolean abort(Throwable failure) + { + HttpExchange exchange = channel.getHttpExchange(); + return exchange != null && exchange.getRequest().abort(failure); + } + @Override public String toString() { diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java index 6659352b318..0447164f56c 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java @@ -38,6 +38,7 @@ import org.eclipse.jetty.util.BufferUtil; public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.ResponseHandler { private final HttpParser parser = new HttpParser(this); + private boolean shutdown; public HttpReceiverOverHTTP(HttpChannelOverHTTP channel) { @@ -124,11 +125,23 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res private void shutdown() { - // Shutting down the parser may invoke messageComplete() or earlyEOF() + // Mark this receiver as shutdown, so that we can + // close the connection when the exchange terminates. + // We cannot close the connection from here because + // the request may still be in process. + shutdown = true; + + // Shutting down the parser may invoke messageComplete() or earlyEOF(). + // In case of content delimited by EOF, without a Connection: close + // header, the connection will be closed at exchange termination + // thanks to the flag we have set above. parser.atEOF(); parser.parseNext(BufferUtil.EMPTY_BUFFER); - if (!responseFailure(new EOFException())) - getHttpConnection().close(); + } + + protected boolean isShutdown() + { + return shutdown; } @Override @@ -205,7 +218,11 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res @Override public void earlyEOF() { - failAndClose(new EOFException()); + HttpExchange exchange = getHttpExchange(); + if (exchange == null) + getHttpConnection().close(); + else + failAndClose(new EOFException()); } @Override @@ -237,7 +254,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res private void failAndClose(Throwable failure) { if (responseFailure(failure)) - getHttpChannel().getHttpConnection().close(); + getHttpConnection().close(failure); } @Override diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientExplicitConnectionTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientExplicitConnectionTest.java index 2071aa0bcc2..9762b2eb450 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientExplicitConnectionTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientExplicitConnectionTest.java @@ -82,9 +82,12 @@ public class HttpClientExplicitConnectionTest extends AbstractHttpClientServerTe Assert.assertEquals(200, response.getStatus()); + // Wait some time to have the client is an idle state. + TimeUnit.SECONDS.sleep(1); + connector.stop(); - // Give the connection some time to process the remote close + // Give the connection some time to process the remote close. TimeUnit.SECONDS.sleep(1); HttpConnectionOverHTTP httpConnection = (HttpConnectionOverHTTP)connection; diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientRedirectTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientRedirectTest.java index f7f17c324a2..0f0a9f3c854 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientRedirectTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientRedirectTest.java @@ -45,6 +45,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; public class HttpClientRedirectTest extends AbstractHttpClientServerTest @@ -244,8 +245,10 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest } @Test + @Ignore public void testRedirectFailed() throws Exception { + // TODO this test is failing with timout after an ISP upgrade?? DNS dependent? try { client.newRequest("localhost", connector.getLocalPort()) 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 a43e680a077..14de5bd7ace 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 @@ -59,6 +59,7 @@ import org.eclipse.jetty.client.http.HttpConnectionOverHTTP; import org.eclipse.jetty.client.http.HttpDestinationOverHTTP; import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.client.util.BytesContentProvider; +import org.eclipse.jetty.client.util.DeferredContentProvider; import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; @@ -73,6 +74,7 @@ import org.eclipse.jetty.util.FuturePromise; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.Assert; +import org.junit.Assume; import org.junit.Rule; import org.junit.Test; @@ -1170,4 +1172,105 @@ public class HttpClientTest extends AbstractHttpClientServerTest Assert.assertEquals(200, response.getStatus()); Assert.assertTrue(response.getHeaders().contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString())); } + + @Test + public void testLongPollIsAbortedWhenClientIsStopped() throws Exception + { + final CountDownLatch latch = new CountDownLatch(1); + start(new AbstractHandler() + { + @Override + public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + baseRequest.setHandled(true); + request.startAsync(); + latch.countDown(); + } + }); + + final CountDownLatch completeLatch = new CountDownLatch(1); + client.newRequest("localhost", connector.getLocalPort()) + .scheme(scheme) + .send(new Response.CompleteListener() + { + @Override + public void onComplete(Result result) + { + if (result.isFailed()) + completeLatch.countDown(); + } + }); + + Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); + + // Stop the client, the complete listener must be invoked. + client.stop(); + + Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS)); + } + + @Test + public void testSmallContentDelimitedByEOFWithSlowRequestHTTP10() throws Exception + { + testContentDelimitedByEOFWithSlowRequest(HttpVersion.HTTP_1_0, 1024); + } + + @Test + public void testBigContentDelimitedByEOFWithSlowRequestHTTP10() throws Exception + { + testContentDelimitedByEOFWithSlowRequest(HttpVersion.HTTP_1_0, 128 * 1024); + } + + @Test + public void testSmallContentDelimitedByEOFWithSlowRequestHTTP11() throws Exception + { + testContentDelimitedByEOFWithSlowRequest(HttpVersion.HTTP_1_1, 1024); + } + + @Test + public void testBigContentDelimitedByEOFWithSlowRequestHTTP11() throws Exception + { + testContentDelimitedByEOFWithSlowRequest(HttpVersion.HTTP_1_1, 128 * 1024); + } + + private void testContentDelimitedByEOFWithSlowRequest(final HttpVersion version, int length) throws Exception + { + // This test is crafted in a way that the response completes before the request is fully written. + // With SSL, the response coming down will close the SSLEngine so it would not be possible to + // write the last chunk of the request content, and the request will be failed, failing also the + // test, which is not what we want. + // This is a limit of Java's SSL implementation that does not allow half closes. + Assume.assumeTrue(sslContextFactory == null); + + final byte[] data = new byte[length]; + new Random().nextBytes(data); + start(new AbstractHandler() + { + @Override + public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + baseRequest.setHandled(true); + // Send Connection: close to avoid that the server chunks the content with HTTP 1.1. + if (version.compareTo(HttpVersion.HTTP_1_0) > 0) + response.setHeader("Connection", "close"); + response.getOutputStream().write(data); + } + }); + + DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.wrap(new byte[]{0})); + Request request = client.newRequest("localhost", connector.getLocalPort()) + .scheme(scheme) + .version(version) + .content(content); + FutureResponseListener listener = new FutureResponseListener(request); + request.send(listener); + // Wait some time to simulate a slow request. + Thread.sleep(1000); + content.close(); + + ContentResponse response = listener.get(5, TimeUnit.SECONDS); + + Assert.assertEquals(200, response.getStatus()); + Assert.assertArrayEquals(data, response.getContent()); + } } 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 deleted file mode 100644 index 86c0b64185e..00000000000 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpReceiverTest.java +++ /dev/null @@ -1,220 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2014 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; - -public class HttpReceiverTest -{ -// @Rule -// public final TestTracker tracker = new TestTracker(); -// -// private HttpClient client; -// private HttpDestination destination; -// private ByteArrayEndPoint endPoint; -// private HttpConnection connection; -// private HttpConversation conversation; -// -// @Before -// public void init() throws Exception -// { -// client = new HttpClient(); -// client.start(); -// destination = new HttpDestination(client, "http", "localhost", 8080); -// endPoint = new ByteArrayEndPoint(); -// connection = new HttpConnection(client, endPoint, destination); -// conversation = new HttpConversation(client, 1); -// } -// -// @After -// public void destroy() throws Exception -// { -// client.stop(); -// } -// -// protected HttpExchange newExchange() -// { -// HttpRequest request = new HttpRequest(client, URI.create("http://localhost")); -// FutureResponseListener listener = new FutureResponseListener(request); -// HttpExchange exchange = new HttpExchange(conversation, destination, request, Collections.singletonList(listener)); -// conversation.getExchanges().offer(exchange); -// connection.associate(exchange); -// exchange.requestComplete(); -// exchange.terminateRequest(); -// return exchange; -// } -// -// @Test -// public void test_Receive_NoResponseContent() throws Exception -// { -// endPoint.setInput("" + -// "HTTP/1.1 200 OK\r\n" + -// "Content-length: 0\r\n" + -// "\r\n"); -// HttpExchange exchange = newExchange(); -// FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0); -// connection.receive(); -// -// Response response = listener.get(5, TimeUnit.SECONDS); -// Assert.assertNotNull(response); -// Assert.assertEquals(200, response.getStatus()); -// Assert.assertEquals("OK", response.getReason()); -// Assert.assertSame(HttpVersion.HTTP_1_1, response.getVersion()); -// HttpFields headers = response.getHeaders(); -// Assert.assertNotNull(headers); -// Assert.assertEquals(1, headers.size()); -// Assert.assertEquals("0", headers.get(HttpHeader.CONTENT_LENGTH)); -// } -// -// @Test -// public void test_Receive_ResponseContent() throws Exception -// { -// String content = "0123456789ABCDEF"; -// endPoint.setInput("" + -// "HTTP/1.1 200 OK\r\n" + -// "Content-length: " + content.length() + "\r\n" + -// "\r\n" + -// content); -// HttpExchange exchange = newExchange(); -// FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0); -// connection.receive(); -// -// Response response = listener.get(5, TimeUnit.SECONDS); -// Assert.assertNotNull(response); -// Assert.assertEquals(200, response.getStatus()); -// Assert.assertEquals("OK", response.getReason()); -// Assert.assertSame(HttpVersion.HTTP_1_1, response.getVersion()); -// HttpFields headers = response.getHeaders(); -// Assert.assertNotNull(headers); -// Assert.assertEquals(1, headers.size()); -// Assert.assertEquals(String.valueOf(content.length()), headers.get(HttpHeader.CONTENT_LENGTH)); -// String received = listener.getContentAsString(StandardCharsets.UTF_8); -// Assert.assertEquals(content, received); -// } -// -// @Test -// public void test_Receive_ResponseContent_EarlyEOF() throws Exception -// { -// String content1 = "0123456789"; -// String content2 = "ABCDEF"; -// endPoint.setInput("" + -// "HTTP/1.1 200 OK\r\n" + -// "Content-length: " + (content1.length() + content2.length()) + "\r\n" + -// "\r\n" + -// content1); -// HttpExchange exchange = newExchange(); -// FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0); -// connection.receive(); -// endPoint.setInputEOF(); -// connection.receive(); -// -// try -// { -// listener.get(5, TimeUnit.SECONDS); -// Assert.fail(); -// } -// catch (ExecutionException e) -// { -// Assert.assertTrue(e.getCause() instanceof EOFException); -// } -// } -// -// @Test -// public void test_Receive_ResponseContent_IdleTimeout() throws Exception -// { -// endPoint.setInput("" + -// "HTTP/1.1 200 OK\r\n" + -// "Content-length: 1\r\n" + -// "\r\n"); -// HttpExchange exchange = newExchange(); -// FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0); -// connection.receive(); -// // Simulate an idle timeout -// connection.idleTimeout(); -// -// try -// { -// listener.get(5, TimeUnit.SECONDS); -// Assert.fail(); -// } -// catch (ExecutionException e) -// { -// Assert.assertTrue(e.getCause() instanceof TimeoutException); -// } -// } -// -// @Test -// public void test_Receive_BadResponse() throws Exception -// { -// endPoint.setInput("" + -// "HTTP/1.1 200 OK\r\n" + -// "Content-length: A\r\n" + -// "\r\n"); -// HttpExchange exchange = newExchange(); -// FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0); -// connection.receive(); -// -// try -// { -// listener.get(5, TimeUnit.SECONDS); -// Assert.fail(); -// } -// catch (ExecutionException e) -// { -// Assert.assertTrue(e.getCause() instanceof HttpResponseException); -// } -// } -// -// @Test -// public void test_Receive_GZIPResponseContent_Fragmented() throws Exception -// { -// byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; -// ByteArrayOutputStream baos = new ByteArrayOutputStream(); -// try (GZIPOutputStream gzipOutput = new GZIPOutputStream(baos)) -// { -// gzipOutput.write(data); -// } -// byte[] gzip = baos.toByteArray(); -// -// endPoint.setInput("" + -// "HTTP/1.1 200 OK\r\n" + -// "Content-Length: " + gzip.length + "\r\n" + -// "Content-Encoding: gzip\r\n" + -// "\r\n"); -// HttpExchange exchange = newExchange(); -// FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0); -// connection.receive(); -// endPoint.reset(); -// -// ByteBuffer buffer = ByteBuffer.wrap(gzip); -// int fragment = buffer.limit() - 1; -// buffer.limit(fragment); -// endPoint.setInput(buffer); -// connection.receive(); -// endPoint.reset(); -// -// buffer.limit(gzip.length); -// buffer.position(fragment); -// endPoint.setInput(buffer); -// connection.receive(); -// -// ContentResponse response = listener.get(5, TimeUnit.SECONDS); -// Assert.assertNotNull(response); -// Assert.assertEquals(200, response.getStatus()); -// Assert.assertArrayEquals(data, response.getContent()); -// } -} 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 deleted file mode 100644 index 3883ebfecd9..00000000000 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpSenderTest.java +++ /dev/null @@ -1,280 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2014 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; - -public class HttpSenderTest -{ -// @Rule -// public final TestTracker tracker = new TestTracker(); -// -// private HttpClient client; -// -// @Before -// public void init() throws Exception -// { -// client = new HttpClient(); -// client.start(); -// } -// -// @After -// public void destroy() throws Exception -// { -// client.stop(); -// } -// -// @Test -// public void test_Send_NoRequestContent() throws Exception -// { -// ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); -// HttpDestination destination = new HttpDestination(client, "http", "localhost", 8080); -// HttpConnection connection = new HttpConnection(client, endPoint, destination); -// 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() -// { -// @Override -// public void onHeaders(Request request) -// { -// headersLatch.countDown(); -// } -// -// @Override -// public void onSuccess(Request request) -// { -// successLatch.countDown(); -// } -// }); -// connection.send(request, (Response.CompleteListener)null); -// -// String requestString = endPoint.takeOutputString(); -// Assert.assertTrue(requestString.startsWith("GET ")); -// Assert.assertTrue(requestString.endsWith("\r\n\r\n")); -// Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); -// Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS)); -// } -// -// @Slow -// @Test -// public void test_Send_NoRequestContent_IncompleteFlush() throws Exception -// { -// ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16); -// HttpDestination destination = new HttpDestination(client, "http", "localhost", 8080); -// HttpConnection connection = new HttpConnection(client, endPoint, destination); -// Request request = client.newRequest(URI.create("http://localhost/")); -// connection.send(request, (Response.CompleteListener)null); -// -// // This take will free space in the buffer and allow for the write to complete -// StringBuilder builder = new StringBuilder(endPoint.takeOutputString()); -// -// // Wait for the write to complete -// TimeUnit.SECONDS.sleep(1); -// -// String chunk = endPoint.takeOutputString(); -// while (chunk.length() > 0) -// { -// builder.append(chunk); -// chunk = endPoint.takeOutputString(); -// } -// -// String requestString = builder.toString(); -// Assert.assertTrue(requestString.startsWith("GET ")); -// Assert.assertTrue(requestString.endsWith("\r\n\r\n")); -// } -// -// @Test -// public void test_Send_NoRequestContent_Exception() throws Exception -// { -// ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); -// // Shutdown output to trigger the exception on write -// endPoint.shutdownOutput(); -// HttpDestination destination = new HttpDestination(client, "http", "localhost", 8080); -// 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() -// { -// @Override -// public void onFailure(Request request, Throwable x) -// { -// failureLatch.countDown(); -// } -// }); -// connection.send(request, new Response.Listener.Adapter() -// { -// @Override -// public void onComplete(Result result) -// { -// Assert.assertTrue(result.isFailed()); -// failureLatch.countDown(); -// } -// }); -// -// Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS)); -// } -// -// @Test -// public void test_Send_NoRequestContent_IncompleteFlush_Exception() throws Exception -// { -// ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16); -// HttpDestination destination = new HttpDestination(client, "http", "localhost", 8080); -// 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() -// { -// @Override -// public void onFailure(Request request, Throwable x) -// { -// failureLatch.countDown(); -// } -// }); -// connection.send(request, new Response.Listener.Adapter() -// { -// @Override -// public void onComplete(Result result) -// { -// Assert.assertTrue(result.isFailed()); -// failureLatch.countDown(); -// } -// }); -// -// // Shutdown output to trigger the exception on write -// endPoint.shutdownOutput(); -// // This take will free space in the buffer and allow for the write to complete -// // although it will fail because we shut down the output -// endPoint.takeOutputString(); -// -// Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS)); -// } -// -// @Test -// public void test_Send_SmallRequestContent_InOneBuffer() throws Exception -// { -// ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); -// HttpDestination destination = new HttpDestination(client, "http", "localhost", 8080); -// HttpConnection connection = new HttpConnection(client, endPoint, destination); -// Request request = client.newRequest(URI.create("http://localhost/")); -// String content = "abcdef"; -// request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content.getBytes(StandardCharsets.UTF_8)))); -// final CountDownLatch headersLatch = new CountDownLatch(1); -// final CountDownLatch successLatch = new CountDownLatch(1); -// request.listener(new Request.Listener.Adapter() -// { -// @Override -// public void onHeaders(Request request) -// { -// headersLatch.countDown(); -// } -// -// @Override -// public void onSuccess(Request request) -// { -// successLatch.countDown(); -// } -// }); -// connection.send(request, (Response.CompleteListener)null); -// -// String requestString = endPoint.takeOutputString(); -// Assert.assertTrue(requestString.startsWith("GET ")); -// Assert.assertTrue(requestString.endsWith("\r\n\r\n" + content)); -// Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); -// Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS)); -// } -// -// @Test -// public void test_Send_SmallRequestContent_InTwoBuffers() throws Exception -// { -// ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); -// HttpDestination destination = new HttpDestination(client, "http", "localhost", 8080); -// HttpConnection connection = new HttpConnection(client, endPoint, destination); -// Request request = client.newRequest(URI.create("http://localhost/")); -// String content1 = "0123456789"; -// String content2 = "abcdef"; -// request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content1.getBytes(StandardCharsets.UTF_8)), ByteBuffer.wrap(content2.getBytes(StandardCharsets.UTF_8)))); -// final CountDownLatch headersLatch = new CountDownLatch(1); -// final CountDownLatch successLatch = new CountDownLatch(1); -// request.listener(new Request.Listener.Adapter() -// { -// @Override -// public void onHeaders(Request request) -// { -// headersLatch.countDown(); -// } -// -// @Override -// public void onSuccess(Request request) -// { -// successLatch.countDown(); -// } -// }); -// connection.send(request, (Response.CompleteListener)null); -// -// String requestString = endPoint.takeOutputString(); -// Assert.assertTrue(requestString.startsWith("GET ")); -// Assert.assertTrue(requestString.endsWith("\r\n\r\n" + content1 + content2)); -// Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); -// Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS)); -// } -// -// @Test -// public void test_Send_SmallRequestContent_Chunked_InTwoChunks() throws Exception -// { -// ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); -// HttpDestination destination = new HttpDestination(client, "http", "localhost", 8080); -// HttpConnection connection = new HttpConnection(client, endPoint, destination); -// Request request = client.newRequest(URI.create("http://localhost/")); -// String content1 = "0123456789"; -// String content2 = "ABCDEF"; -// request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content1.getBytes(StandardCharsets.UTF_8)), ByteBuffer.wrap(content2.getBytes(StandardCharsets.UTF_8))) -// { -// @Override -// public long getLength() -// { -// return -1; -// } -// }); -// final CountDownLatch headersLatch = new CountDownLatch(1); -// final CountDownLatch successLatch = new CountDownLatch(1); -// request.listener(new Request.Listener.Adapter() -// { -// @Override -// public void onHeaders(Request request) -// { -// headersLatch.countDown(); -// } -// -// @Override -// public void onSuccess(Request request) -// { -// successLatch.countDown(); -// } -// }); -// connection.send(request, (Response.CompleteListener)null); -// -// String requestString = endPoint.takeOutputString(); -// Assert.assertTrue(requestString.startsWith("GET ")); -// String content = Integer.toHexString(content1.length()).toUpperCase(Locale.ENGLISH) + "\r\n" + content1 + "\r\n"; -// content += Integer.toHexString(content2.length()).toUpperCase(Locale.ENGLISH) + "\r\n" + content2 + "\r\n"; -// content += "0\r\n\r\n"; -// Assert.assertTrue(requestString.endsWith("\r\n\r\n" + content)); -// Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); -// Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS)); -// } -} diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java new file mode 100644 index 00000000000..0be2b913973 --- /dev/null +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java @@ -0,0 +1,262 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.http; + +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.zip.GZIPOutputStream; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.HttpExchange; +import org.eclipse.jetty.client.HttpRequest; +import org.eclipse.jetty.client.HttpResponseException; +import org.eclipse.jetty.client.Origin; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Response; +import org.eclipse.jetty.client.util.FutureResponseListener; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.io.ByteArrayEndPoint; +import org.eclipse.jetty.toolchain.test.TestTracker; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +public class HttpReceiverOverHTTPTest +{ + @Rule + public final TestTracker tracker = new TestTracker(); + + private HttpClient client; + private HttpDestinationOverHTTP destination; + private ByteArrayEndPoint endPoint; + private HttpConnectionOverHTTP connection; + + @Before + public void init() throws Exception + { + client = new HttpClient(); + client.start(); + destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); + endPoint = new ByteArrayEndPoint(); + connection = new HttpConnectionOverHTTP(endPoint, destination); + } + + @After + public void destroy() throws Exception + { + client.stop(); + } + + protected HttpExchange newExchange() + { + HttpRequest request = (HttpRequest)client.newRequest("http://localhost"); + FutureResponseListener listener = new FutureResponseListener(request); + HttpExchange exchange = new HttpExchange(destination, request, Collections.singletonList(listener)); + connection.getHttpChannel().associate(exchange); + exchange.requestComplete(); + exchange.terminateRequest(null); + return exchange; + } + + @Test + public void test_Receive_NoResponseContent() throws Exception + { + endPoint.setInput("" + + "HTTP/1.1 200 OK\r\n" + + "Content-length: 0\r\n" + + "\r\n"); + HttpExchange exchange = newExchange(); + FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0); + connection.getHttpChannel().receive(); + + Response response = listener.get(5, TimeUnit.SECONDS); + Assert.assertNotNull(response); + Assert.assertEquals(200, response.getStatus()); + Assert.assertEquals("OK", response.getReason()); + Assert.assertSame(HttpVersion.HTTP_1_1, response.getVersion()); + HttpFields headers = response.getHeaders(); + Assert.assertNotNull(headers); + Assert.assertEquals(1, headers.size()); + Assert.assertEquals("0", headers.get(HttpHeader.CONTENT_LENGTH)); + } + + @Test + public void test_Receive_ResponseContent() throws Exception + { + String content = "0123456789ABCDEF"; + endPoint.setInput("" + + "HTTP/1.1 200 OK\r\n" + + "Content-length: " + content.length() + "\r\n" + + "\r\n" + + content); + HttpExchange exchange = newExchange(); + FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0); + connection.getHttpChannel().receive(); + + Response response = listener.get(5, TimeUnit.SECONDS); + Assert.assertNotNull(response); + Assert.assertEquals(200, response.getStatus()); + Assert.assertEquals("OK", response.getReason()); + Assert.assertSame(HttpVersion.HTTP_1_1, response.getVersion()); + HttpFields headers = response.getHeaders(); + Assert.assertNotNull(headers); + Assert.assertEquals(1, headers.size()); + Assert.assertEquals(String.valueOf(content.length()), headers.get(HttpHeader.CONTENT_LENGTH)); + String received = listener.getContentAsString(StandardCharsets.UTF_8); + Assert.assertEquals(content, received); + } + + @Test + public void test_Receive_ResponseContent_EarlyEOF() throws Exception + { + String content1 = "0123456789"; + String content2 = "ABCDEF"; + endPoint.setInput("" + + "HTTP/1.1 200 OK\r\n" + + "Content-length: " + (content1.length() + content2.length()) + "\r\n" + + "\r\n" + + content1); + HttpExchange exchange = newExchange(); + FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0); + connection.getHttpChannel().receive(); + endPoint.setInputEOF(); + connection.getHttpChannel().receive(); + + try + { + listener.get(5, TimeUnit.SECONDS); + Assert.fail(); + } + catch (ExecutionException e) + { + Assert.assertTrue(e.getCause() instanceof EOFException); + } + } + + @Test + public void test_Receive_ResponseContent_IdleTimeout() throws Exception + { + endPoint.setInput("" + + "HTTP/1.1 200 OK\r\n" + + "Content-length: 1\r\n" + + "\r\n"); + HttpExchange exchange = newExchange(); + FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0); + connection.getHttpChannel().receive(); + // Simulate an idle timeout + connection.onReadTimeout(); + + try + { + listener.get(5, TimeUnit.SECONDS); + Assert.fail(); + } + catch (ExecutionException e) + { + Assert.assertTrue(e.getCause() instanceof TimeoutException); + } + } + + @Test + public void test_Receive_BadResponse() throws Exception + { + endPoint.setInput("" + + "HTTP/1.1 200 OK\r\n" + + "Content-length: A\r\n" + + "\r\n"); + HttpExchange exchange = newExchange(); + FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0); + connection.getHttpChannel().receive(); + + try + { + listener.get(5, TimeUnit.SECONDS); + Assert.fail(); + } + catch (ExecutionException e) + { + Assert.assertTrue(e.getCause() instanceof HttpResponseException); + } + } + + @Test + public void test_Receive_GZIPResponseContent_Fragmented() throws Exception + { + byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (GZIPOutputStream gzipOutput = new GZIPOutputStream(baos)) + { + gzipOutput.write(data); + } + byte[] gzip = baos.toByteArray(); + + endPoint.setInput("" + + "HTTP/1.1 200 OK\r\n" + + "Content-Length: " + gzip.length + "\r\n" + + "Content-Encoding: gzip\r\n" + + "\r\n"); + + HttpRequest request = (HttpRequest)client.newRequest("http://localhost"); + final CountDownLatch latch = new CountDownLatch(1); + FutureResponseListener listener = new FutureResponseListener(request) + { + @Override + public void onContent(Response response, ByteBuffer content) + { + super.onContent(response, content); + latch.countDown(); + } + }; + HttpExchange exchange = new HttpExchange(destination, request, Collections.singletonList(listener)); + connection.getHttpChannel().associate(exchange); + exchange.requestComplete(); + exchange.terminateRequest(null); + connection.getHttpChannel().receive(); + endPoint.reset(); + + ByteBuffer buffer = ByteBuffer.wrap(gzip); + int fragment = buffer.limit() - 1; + buffer.limit(fragment); + endPoint.setInput(buffer); + connection.getHttpChannel().receive(); + endPoint.reset(); + + buffer.limit(gzip.length); + buffer.position(fragment); + endPoint.setInput(buffer); + connection.getHttpChannel().receive(); + + ContentResponse response = listener.get(5, TimeUnit.SECONDS); + Assert.assertNotNull(response); + Assert.assertEquals(200, response.getStatus()); + Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); + Assert.assertArrayEquals(data, response.getContent()); + } +} diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpSenderOverHTTPTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpSenderOverHTTPTest.java new file mode 100644 index 00000000000..afe1ec7a4c7 --- /dev/null +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpSenderOverHTTPTest.java @@ -0,0 +1,302 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.http; + +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Locale; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.Origin; +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.ByteBufferContentProvider; +import org.eclipse.jetty.io.ByteArrayEndPoint; +import org.eclipse.jetty.toolchain.test.TestTracker; +import org.eclipse.jetty.toolchain.test.annotation.Slow; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +public class HttpSenderOverHTTPTest +{ + @Rule + public final TestTracker tracker = new TestTracker(); + + private HttpClient client; + + @Before + public void init() throws Exception + { + client = new HttpClient(); + client.start(); + } + + @After + public void destroy() throws Exception + { + client.stop(); + } + + @Test + public void test_Send_NoRequestContent() throws Exception + { + ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); + HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); + HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination); + 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() + { + @Override + public void onHeaders(Request request) + { + headersLatch.countDown(); + } + + @Override + public void onSuccess(Request request) + { + successLatch.countDown(); + } + }); + connection.send(request, null); + + String requestString = endPoint.takeOutputString(); + Assert.assertTrue(requestString.startsWith("GET ")); + Assert.assertTrue(requestString.endsWith("\r\n\r\n")); + Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); + Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS)); + } + + @Slow + @Test + public void test_Send_NoRequestContent_IncompleteFlush() throws Exception + { + ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16); + HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); + HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination); + Request request = client.newRequest(URI.create("http://localhost/")); + connection.send(request, null); + + // This take will free space in the buffer and allow for the write to complete + StringBuilder builder = new StringBuilder(endPoint.takeOutputString()); + + // Wait for the write to complete + TimeUnit.SECONDS.sleep(1); + + String chunk = endPoint.takeOutputString(); + while (chunk.length() > 0) + { + builder.append(chunk); + chunk = endPoint.takeOutputString(); + } + + String requestString = builder.toString(); + Assert.assertTrue(requestString.startsWith("GET ")); + Assert.assertTrue(requestString.endsWith("\r\n\r\n")); + } + + @Test + public void test_Send_NoRequestContent_Exception() throws Exception + { + ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); + // Shutdown output to trigger the exception on write + endPoint.shutdownOutput(); + HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); + HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination); + Request request = client.newRequest(URI.create("http://localhost/")); + final CountDownLatch failureLatch = new CountDownLatch(2); + request.listener(new Request.Listener.Adapter() + { + @Override + public void onFailure(Request request, Throwable x) + { + failureLatch.countDown(); + } + }); + connection.send(request, new Response.Listener.Adapter() + { + @Override + public void onComplete(Result result) + { + Assert.assertTrue(result.isFailed()); + failureLatch.countDown(); + } + }); + + Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS)); + } + + @Test + public void test_Send_NoRequestContent_IncompleteFlush_Exception() throws Exception + { + ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16); + HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); + HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination); + Request request = client.newRequest(URI.create("http://localhost/")); + final CountDownLatch failureLatch = new CountDownLatch(2); + request.listener(new Request.Listener.Adapter() + { + @Override + public void onFailure(Request request, Throwable x) + { + failureLatch.countDown(); + } + }); + connection.send(request, new Response.Listener.Adapter() + { + @Override + public void onComplete(Result result) + { + Assert.assertTrue(result.isFailed()); + failureLatch.countDown(); + } + }); + + // Shutdown output to trigger the exception on write + endPoint.shutdownOutput(); + // This take will free space in the buffer and allow for the write to complete + // although it will fail because we shut down the output + endPoint.takeOutputString(); + + Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS)); + } + + @Test + public void test_Send_SmallRequestContent_InOneBuffer() throws Exception + { + ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); + HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); + HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination); + Request request = client.newRequest(URI.create("http://localhost/")); + String content = "abcdef"; + request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content.getBytes(StandardCharsets.UTF_8)))); + final CountDownLatch headersLatch = new CountDownLatch(1); + final CountDownLatch successLatch = new CountDownLatch(1); + request.listener(new Request.Listener.Adapter() + { + @Override + public void onHeaders(Request request) + { + headersLatch.countDown(); + } + + @Override + public void onSuccess(Request request) + { + successLatch.countDown(); + } + }); + connection.send(request, null); + + String requestString = endPoint.takeOutputString(); + Assert.assertTrue(requestString.startsWith("GET ")); + Assert.assertTrue(requestString.endsWith("\r\n\r\n" + content)); + Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); + Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS)); + } + + @Test + public void test_Send_SmallRequestContent_InTwoBuffers() throws Exception + { + ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); + HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); + HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination); + Request request = client.newRequest(URI.create("http://localhost/")); + String content1 = "0123456789"; + String content2 = "abcdef"; + request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content1.getBytes(StandardCharsets.UTF_8)), ByteBuffer.wrap(content2.getBytes(StandardCharsets.UTF_8)))); + final CountDownLatch headersLatch = new CountDownLatch(1); + final CountDownLatch successLatch = new CountDownLatch(1); + request.listener(new Request.Listener.Adapter() + { + @Override + public void onHeaders(Request request) + { + headersLatch.countDown(); + } + + @Override + public void onSuccess(Request request) + { + successLatch.countDown(); + } + }); + connection.send(request, null); + + String requestString = endPoint.takeOutputString(); + Assert.assertTrue(requestString.startsWith("GET ")); + Assert.assertTrue(requestString.endsWith("\r\n\r\n" + content1 + content2)); + Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); + Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS)); + } + + @Test + public void test_Send_SmallRequestContent_Chunked_InTwoChunks() throws Exception + { + ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); + HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); + HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination); + Request request = client.newRequest(URI.create("http://localhost/")); + String content1 = "0123456789"; + String content2 = "ABCDEF"; + request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content1.getBytes(StandardCharsets.UTF_8)), ByteBuffer.wrap(content2.getBytes(StandardCharsets.UTF_8))) + { + @Override + public long getLength() + { + return -1; + } + }); + final CountDownLatch headersLatch = new CountDownLatch(1); + final CountDownLatch successLatch = new CountDownLatch(1); + request.listener(new Request.Listener.Adapter() + { + @Override + public void onHeaders(Request request) + { + headersLatch.countDown(); + } + + @Override + public void onSuccess(Request request) + { + successLatch.countDown(); + } + }); + connection.send(request, null); + + String requestString = endPoint.takeOutputString(); + Assert.assertTrue(requestString.startsWith("GET ")); + String content = Integer.toHexString(content1.length()).toUpperCase(Locale.ENGLISH) + "\r\n" + content1 + "\r\n"; + content += Integer.toHexString(content2.length()).toUpperCase(Locale.ENGLISH) + "\r\n" + content2 + "\r\n"; + content += "0\r\n\r\n"; + Assert.assertTrue(requestString.endsWith("\r\n\r\n" + content)); + Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); + Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS)); + } +} diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java index c360f70696f..bdef5f21fd9 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java @@ -77,6 +77,7 @@ import org.junit.After; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; public class SslBytesServerTest extends SslBytesTest @@ -237,7 +238,7 @@ public class SslBytesServerTest extends SslBytesTest threadPool.shutdownNow(); } - @Test + @Test(timeout=10000) public void testHandshake() throws Exception { final SSLSocket client = newClient(); @@ -1390,7 +1391,9 @@ public class SslBytesServerTest extends SslBytesTest closeClient(client); } - @Test + // TODO work out why this test frequently fails + @Ignore + @Test(timeout=10000) public void testRequestWithContentWithRenegotiationInMiddleOfContentWhenRenegotiationIsForbidden() throws Exception { assumeJavaVersionSupportsTLSRenegotiations(); @@ -1616,7 +1619,7 @@ public class SslBytesServerTest extends SslBytesTest closeClient(client); } - @Test + @Test(timeout=10000) public void testRequestWithBigContentWithRenegotiationInMiddleOfContentWithSplitBoundary() throws Exception { assumeJavaVersionSupportsTLSRenegotiations(); diff --git a/jetty-continuation/pom.xml b/jetty-continuation/pom.xml index c5d6fe37ef0..a926837af23 100644 --- a/jetty-continuation/pom.xml +++ b/jetty-continuation/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-continuation diff --git a/jetty-deploy/pom.xml b/jetty-deploy/pom.xml index c945c292631..0325c569dee 100644 --- a/jetty-deploy/pom.xml +++ b/jetty-deploy/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-deploy diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml index 7e250a92274..5003e023f8e 100644 --- a/jetty-distribution/pom.xml +++ b/jetty-distribution/pom.xml @@ -3,7 +3,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT jetty-distribution Jetty :: Distribution Assemblies @@ -302,7 +302,7 @@ org.eclipse.jetty org.eclipse.jetty.orbit,org.eclipse.jetty.spdy,org.eclipse.jetty.websocket,org.eclipse.jetty.fcgi,org.eclipse.jetty.toolchain,org.apache.taglibs - jetty-all,jetty-jsp,jetty-start,jetty-monitor + jetty-all,jetty-jsp,apache-jsp,jetty-start,jetty-monitor jar ${assembly-directory}/lib @@ -457,6 +457,20 @@ ${assembly-directory}/lib/jsp + + copy-apache-jsp-deps + generate-resources + + copy-dependencies + + + org.eclipse.jetty,org.eclipse.jetty.toolchain,javax.servlet.jsp,org.mortbay.jasper,org.mortbay.jasper,org.eclipse.jetty.orbit + apache-jsp,javax.servlet.jsp-api,apache-el,org.eclipse.jdt.core + jar + true + ${assembly-directory}/lib/apache-jsp + + copy-jstl-api generate-resources @@ -484,6 +498,20 @@ ${assembly-directory}/lib/jsp + + copy-apache-jstl-deps + generate-resources + + copy-dependencies + + + org.glassfish.web + taglibs-standard-spec,taglibs-standard-impl + true + jar + ${assembly-directory}/lib/apache-jstl + + copy-jaspi-deps generate-resources @@ -526,7 +554,7 @@ jetty.home=${assembly-directory} jetty.base=${assembly-directory} --add-to-start=server,deploy,websocket,ext,resources - --add-to-startd=jsp,http + --add-to-startd=jsp,jstl,http @@ -541,8 +569,8 @@ jetty.home=${assembly-directory} jetty.base=${assembly-directory}/demo-base - --add-to-start=server,continuation,deploy,ext,resources,client,annotations,jndi,servlets - --add-to-startd-ini=jsp,http,https + --add-to-start=server,continuation,deploy,websocket,ext,resources,client,annotations,jndi,servlets + --add-to-startd-ini=jsp,jstl,http,https @@ -667,6 +695,11 @@ jetty-monitor ${project.version} + + org.eclipse.jetty + jetty-quickstart + ${project.version} + org.eclipse.jetty jetty-start @@ -697,6 +730,16 @@ jetty-jsp ${project.version} + + org.eclipse.jetty + apache-jsp + ${project.version} + + + org.eclipse.jetty + apache-jstl + ${project.version} + org.eclipse.jetty jetty-plus @@ -765,6 +808,11 @@ ${project.version} war + + org.eclipse.jetty + jetty-alpn-server + ${project.version} + org.eclipse.jetty.example-async-rest example-async-rest-webapp diff --git a/jetty-distribution/src/main/resources/modules/jsp.mod b/jetty-distribution/src/main/resources/modules/jsp.mod new file mode 100644 index 00000000000..4924ed91030 --- /dev/null +++ b/jetty-distribution/src/main/resources/modules/jsp.mod @@ -0,0 +1,20 @@ +# +# Jetty JSP Module +# + +[depend] +servlet +jsp-impl/${jsp-impl}-jsp + +[ini-template] +# JSP Configuration + +# Select JSP implementation, choices are +# glassfish : The reference implementation +# default in jetty <= 9.1 +# apache : The apache version +# default jetty >= 9.2 +jsp-impl=apache + +# To use an non-jdk compiler for JSP compilation uncomment next line +# -Dorg.apache.jasper.compiler.disablejsr199=true diff --git a/jetty-distribution/src/main/resources/modules/jstl.mod b/jetty-distribution/src/main/resources/modules/jstl.mod new file mode 100644 index 00000000000..cb06244f5e0 --- /dev/null +++ b/jetty-distribution/src/main/resources/modules/jstl.mod @@ -0,0 +1,14 @@ +# +# Jetty JSP Module +# + +[depend] +jsp +jsp-impl/${jsp-impl}-jstl + +[ini-template] +# JSTL Configuration +# The glassfish jsp-impl includes JSTL by default and this module +# is not required to activate it. +# The apache jsp-impl does not include JSTL by default and this module +# is required to put JSTL on the container classpath diff --git a/jetty-distribution/src/main/resources/modules/protonego.mod b/jetty-distribution/src/main/resources/modules/protonego.mod new file mode 100644 index 00000000000..d7bba9fbeca --- /dev/null +++ b/jetty-distribution/src/main/resources/modules/protonego.mod @@ -0,0 +1,15 @@ +# +# Protocol Negotiatin Selection Module +# + +[depend] +protonego-impl/${protonego} + +[ini-template] +# Protocol Negotiation Implementation Selection +# choices are: +# 'npn' : original implementation for SPDY (now deprecated) +# 'alpn' : replacement for NPN, in use by current SPDY implementations +# and the future HTTP/2 spec +# Note: java 1.8+ are ALPN only. +protonego=alpn diff --git a/jetty-fcgi/fcgi-client/pom.xml b/jetty-fcgi/fcgi-client/pom.xml index 4844b233eca..fc7542e28a2 100644 --- a/jetty-fcgi/fcgi-client/pom.xml +++ b/jetty-fcgi/fcgi-client/pom.xml @@ -1,11 +1,9 @@ - + org.eclipse.jetty.fcgi fcgi-parent - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpChannelOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpChannelOverFCGI.java index ac5687ef41a..1c33cf0518d 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpChannelOverFCGI.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpChannelOverFCGI.java @@ -28,8 +28,6 @@ import org.eclipse.jetty.fcgi.generator.Flusher; import org.eclipse.jetty.fcgi.generator.Generator; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; -import org.eclipse.jetty.http.HttpHeader; -import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.IdleTimeout; @@ -83,42 +81,43 @@ public class HttpChannelOverFCGI extends HttpChannel return receiver.abort(cause); } - protected void responseBegin(int code, String reason) + protected boolean responseBegin(int code, String reason) { HttpExchange exchange = getHttpExchange(); - if (exchange != null) - { - exchange.getResponse().version(version).status(code).reason(reason); - receiver.responseBegin(exchange); - } + if (exchange == null) + return false; + exchange.getResponse().version(version).status(code).reason(reason); + return receiver.responseBegin(exchange); } - protected void responseHeader(HttpField field) + protected boolean responseHeader(HttpField field) { HttpExchange exchange = getHttpExchange(); - if (exchange != null) - receiver.responseHeader(exchange, field); + return exchange != null && receiver.responseHeader(exchange, field); } - protected void responseHeaders() + protected boolean responseHeaders() { HttpExchange exchange = getHttpExchange(); - if (exchange != null) - receiver.responseHeaders(exchange); + return exchange != null && receiver.responseHeaders(exchange); } - protected void content(ByteBuffer buffer) + protected boolean content(ByteBuffer buffer) { HttpExchange exchange = getHttpExchange(); - if (exchange != null) - receiver.responseContent(exchange, buffer); + return exchange != null && receiver.responseContent(exchange, buffer); } - protected void responseSuccess() + protected boolean responseSuccess() { HttpExchange exchange = getHttpExchange(); - if (exchange != null) - receiver.responseSuccess(exchange); + return exchange != null && receiver.responseSuccess(exchange); + } + + protected boolean responseFailure(Throwable failure) + { + HttpExchange exchange = getHttpExchange(); + return exchange != null && receiver.responseFailure(failure); } @Override @@ -126,13 +125,11 @@ public class HttpChannelOverFCGI extends HttpChannel { super.exchangeTerminated(result); idle.onClose(); - boolean close = result.isFailed(); HttpFields responseHeaders = result.getResponse().getHeaders(); - close |= responseHeaders.contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString()); - if (close) - connection.close(); - else - connection.release(); + if (result.isFailed()) + connection.close(result.getFailure()); + else if (!connection.closeByHTTP(responseHeaders)) + connection.release(this); } protected void flush(Generator.Result... results) @@ -154,8 +151,9 @@ public class HttpChannelOverFCGI extends HttpChannel @Override protected void onIdleExpired(TimeoutException timeout) { - LOG.debug("Idle timeout for request {}", request); - abort(timeout); + if (LOG.isDebugEnabled()) + LOG.debug("Idle timeout for request {}", request); + connection.abort(timeout); } @Override diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpClientTransportOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpClientTransportOverFCGI.java index eed8c892a60..00c6778d5e3 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpClientTransportOverFCGI.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpClientTransportOverFCGI.java @@ -69,7 +69,7 @@ public class HttpClientTransportOverFCGI extends AbstractHttpClientTransport public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map context) throws IOException { HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY); - HttpConnectionOverFCGI connection = new HttpConnectionOverFCGI(endPoint, destination); + HttpConnectionOverFCGI connection = new HttpConnectionOverFCGI(endPoint, destination, isMultiplexed()); LOG.debug("Created {}", connection); @SuppressWarnings("unchecked") Promise promise = (Promise)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY); diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java index 599455f9709..f14143bfb0c 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.fcgi.client.http; import java.io.EOFException; import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousCloseException; import java.util.LinkedList; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -30,7 +31,6 @@ import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpConnection; import org.eclipse.jetty.client.HttpDestination; import org.eclipse.jetty.client.HttpExchange; -import org.eclipse.jetty.client.PoolingHttpDestination; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; @@ -38,6 +38,9 @@ import org.eclipse.jetty.fcgi.FCGI; import org.eclipse.jetty.fcgi.generator.Flusher; import org.eclipse.jetty.fcgi.parser.ClientParser; import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; @@ -54,14 +57,16 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec private final AtomicBoolean closed = new AtomicBoolean(); private final Flusher flusher; private final HttpDestination destination; + private final boolean multiplexed; private final Delegate delegate; private final ClientParser parser; - public HttpConnectionOverFCGI(EndPoint endPoint, HttpDestination destination) + public HttpConnectionOverFCGI(EndPoint endPoint, HttpDestination destination, boolean multiplexed) { super(endPoint, destination.getHttpClient().getExecutor(), destination.getHttpClient().isDispatchIO()); - this.flusher = new Flusher(endPoint); this.destination = destination; + this.multiplexed = multiplexed; + this.flusher = new Flusher(endPoint); this.delegate = new Delegate(destination); this.parser = new ClientParser(new ResponseListener()); requests.addLast(0); @@ -102,7 +107,7 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec while (true) { int read = endPoint.fill(buffer); - if (LOG.isDebugEnabled()) // Avoid boxing of variable 'read' + if (LOG.isDebugEnabled()) // Avoid boxing of variable 'read'. LOG.debug("Read {} bytes from {}", read, endPoint); if (read > 0) { @@ -123,7 +128,7 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec catch (Exception x) { LOG.debug(x); - // TODO: fail and close ? + close(x); } finally { @@ -139,47 +144,79 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec private void shutdown() { - // First close then abort, to be sure that the - // connection cannot be reused from an onFailure() - // handler or by blocking code waiting for completion. - close(); - for (HttpChannelOverFCGI channel : channels.values()) - channel.abort(new EOFException()); + // Close explicitly only if we are idle, since the request may still + // be in progress, otherwise close only if we can fail the responses. + if (channels.isEmpty()) + close(); + else + failAndClose(new EOFException()); } @Override protected boolean onReadTimeout() { - for (HttpChannelOverFCGI channel : channels.values()) - channel.abort(new TimeoutException()); - close(); + close(new TimeoutException()); return false; } - public void release() + protected void release(HttpChannelOverFCGI channel) { - if (destination instanceof PoolingHttpDestination) - { - @SuppressWarnings("unchecked") - PoolingHttpDestination fcgiDestination = - (PoolingHttpDestination)destination; - fcgiDestination.release(this); - } + channels.remove(channel.getRequest()); + destination.release(this); } @Override public void close() + { + close(new AsynchronousCloseException()); + } + + protected void close(Throwable failure) { if (closed.compareAndSet(false, true)) { + // First close then abort, to be sure that the connection cannot be reused + // from an onFailure() handler or by blocking code waiting for completion. getHttpDestination().close(this); getEndPoint().shutdownOutput(); LOG.debug("{} oshut", this); getEndPoint().close(); LOG.debug("{} closed", this); + + abort(failure); } } + protected boolean closeByHTTP(HttpFields fields) + { + if (multiplexed) + return false; + if (!fields.contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())) + return false; + close(); + return true; + } + + protected void abort(Throwable failure) + { + for (HttpChannelOverFCGI channel : channels.values()) + { + HttpExchange exchange = channel.getHttpExchange(); + if (exchange != null) + exchange.getRequest().abort(failure); + } + channels.clear(); + } + + private void failAndClose(Throwable failure) + { + boolean result = false; + for (HttpChannelOverFCGI channel : channels.values()) + result |= channel.responseFailure(failure); + if (result) + close(failure); + } + private int acquireRequest() { synchronized (requests) @@ -304,11 +341,26 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec @Override public void onEnd(int request) { - HttpChannelOverFCGI channel = channels.remove(request); + HttpChannelOverFCGI channel = channels.get(request); if (channel != null) { - channel.responseSuccess(); - releaseRequest(request); + if (channel.responseSuccess()) + releaseRequest(request); + } + else + { + noChannel(request); + } + } + + @Override + public void onFailure(int request, Throwable failure) + { + HttpChannelOverFCGI channel = channels.get(request); + if (channel != null) + { + if (channel.responseFailure(failure)) + releaseRequest(request); } else { diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ServerGenerator.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ServerGenerator.java index 39619268a7b..e113dd29b78 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ServerGenerator.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/generator/ServerGenerator.java @@ -88,21 +88,36 @@ public class ServerGenerator extends Generator return generateContent(request, buffer, true, false, callback, FCGI.FrameType.STDOUT); } - public Result generateResponseContent(int request, ByteBuffer content, boolean lastContent, Callback callback) + public Result generateResponseContent(int request, ByteBuffer content, boolean lastContent, boolean aborted, Callback callback) { - Result result = generateContent(request, content, false, lastContent, callback, FCGI.FrameType.STDOUT); - if (lastContent) + if (aborted) { - // Generate the FCGI_END_REQUEST - request &= 0xFF_FF; - ByteBuffer endRequestBuffer = byteBufferPool.acquire(8, false); - BufferUtil.clearToFill(endRequestBuffer); - endRequestBuffer.putInt(0x01_03_00_00 + request); - endRequestBuffer.putInt(0x00_08_00_00); - endRequestBuffer.putLong(0x00L); - endRequestBuffer.flip(); - result = result.append(endRequestBuffer, true); + Result result = new Result(byteBufferPool, callback); + if (lastContent) + result.append(generateEndRequest(request, true), true); + else + result.append(BufferUtil.EMPTY_BUFFER, false); + return result; } - return result; + else + { + Result result = generateContent(request, content, false, lastContent, callback, FCGI.FrameType.STDOUT); + if (lastContent) + result.append(generateEndRequest(request, false), true); + return result; + } + } + + private ByteBuffer generateEndRequest(int request, boolean aborted) + { + request &= 0xFF_FF; + ByteBuffer endRequestBuffer = byteBufferPool.acquire(8, false); + BufferUtil.clearToFill(endRequestBuffer); + endRequestBuffer.putInt(0x01_03_00_00 + request); + endRequestBuffer.putInt(0x00_08_00_00); + endRequestBuffer.putInt(aborted ? 1 : 0); + endRequestBuffer.putInt(0); + endRequestBuffer.flip(); + return endRequestBuffer; } } diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ClientParser.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ClientParser.java index 4ba70b44e22..d7a7dc6604d 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ClientParser.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ClientParser.java @@ -98,5 +98,13 @@ public class ClientParser extends Parser for (StreamContentParser streamParser : streamParsers) streamParser.end(request); } + + @Override + public void onFailure(int request, Throwable failure) + { + listener.onFailure(request, failure); + for (StreamContentParser streamParser : streamParsers) + streamParser.end(request); + } } } diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/EndRequestContentParser.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/EndRequestContentParser.java index 419536af77d..1f77eaf0ea6 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/EndRequestContentParser.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/EndRequestContentParser.java @@ -107,8 +107,12 @@ public class EndRequestContentParser extends ContentParser private void onEnd() { - // TODO: if protocol != 0, invoke an error callback - listener.onEnd(getRequest()); + if (application != 0) + listener.onFailure(getRequest(), new Exception("FastCGI application returned code " + application)); + else if (protocol != 0) + listener.onFailure(getRequest(), new Exception("FastCGI server returned code " + protocol)); + else + listener.onEnd(getRequest()); } private void reset() diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/Parser.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/Parser.java index 577219c301c..5739ef32012 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/Parser.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/Parser.java @@ -100,6 +100,8 @@ public abstract class Parser public void onEnd(int request); + public void onFailure(int request, Throwable failure); + public static class Adapter implements Listener { @Override @@ -121,6 +123,12 @@ public abstract class Parser public void onEnd(int request) { } + + @Override + public void onFailure(int request, Throwable failure) + { + + } } } diff --git a/jetty-fcgi/fcgi-client/src/test/java/org/eclipse/jetty/fcgi/parser/ClientParserTest.java b/jetty-fcgi/fcgi-client/src/test/java/org/eclipse/jetty/fcgi/parser/ClientParserTest.java index 5f80ed2827c..826ed84b43e 100644 --- a/jetty-fcgi/fcgi-client/src/test/java/org/eclipse/jetty/fcgi/parser/ClientParserTest.java +++ b/jetty-fcgi/fcgi-client/src/test/java/org/eclipse/jetty/fcgi/parser/ClientParserTest.java @@ -111,7 +111,7 @@ public class ClientParserTest ByteBufferPool byteBufferPool = new MappedByteBufferPool(); ServerGenerator generator = new ServerGenerator(byteBufferPool); Generator.Result result1 = generator.generateResponseHeaders(id, 200, "OK", fields, null); - Generator.Result result2 = generator.generateResponseContent(id, null, true, null); + Generator.Result result2 = generator.generateResponseContent(id, null, true, false, null); final AtomicInteger verifier = new AtomicInteger(); ClientParser parser = new ClientParser(new ClientParser.Listener.Adapter() @@ -162,7 +162,7 @@ public class ClientParserTest ByteBufferPool byteBufferPool = new MappedByteBufferPool(); ServerGenerator generator = new ServerGenerator(byteBufferPool); Generator.Result result1 = generator.generateResponseHeaders(id, code, "OK", fields, null); - Generator.Result result2 = generator.generateResponseContent(id, content, true, null); + Generator.Result result2 = generator.generateResponseContent(id, content, true, false, null); final AtomicInteger verifier = new AtomicInteger(); ClientParser parser = new ClientParser(new ClientParser.Listener.Adapter() @@ -214,7 +214,7 @@ public class ClientParserTest ByteBufferPool byteBufferPool = new MappedByteBufferPool(); ServerGenerator generator = new ServerGenerator(byteBufferPool); Generator.Result result1 = generator.generateResponseHeaders(id, code, "OK", fields, null); - Generator.Result result2 = generator.generateResponseContent(id, content, true, null); + Generator.Result result2 = generator.generateResponseContent(id, content, true, false, null); final AtomicInteger totalLength = new AtomicInteger(); final AtomicBoolean verifier = new AtomicBoolean(); diff --git a/jetty-fcgi/fcgi-distribution/pom.xml b/jetty-fcgi/fcgi-distribution/pom.xml index c2657968f6d..81d03d9e236 100644 --- a/jetty-fcgi/fcgi-distribution/pom.xml +++ b/jetty-fcgi/fcgi-distribution/pom.xml @@ -5,7 +5,7 @@ fcgi-parent org.eclipse.jetty.fcgi - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 diff --git a/jetty-fcgi/fcgi-http-client-transport/pom.xml b/jetty-fcgi/fcgi-http-client-transport/pom.xml index 61fe9fe9bbe..d911c12528c 100644 --- a/jetty-fcgi/fcgi-http-client-transport/pom.xml +++ b/jetty-fcgi/fcgi-http-client-transport/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.fcgi fcgi-parent - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 diff --git a/jetty-fcgi/fcgi-proxy/pom.xml b/jetty-fcgi/fcgi-proxy/pom.xml index d313dc7fcb1..149d189df73 100644 --- a/jetty-fcgi/fcgi-proxy/pom.xml +++ b/jetty-fcgi/fcgi-proxy/pom.xml @@ -5,7 +5,7 @@ fcgi-parent org.eclipse.jetty.fcgi - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 diff --git a/jetty-fcgi/fcgi-server/pom.xml b/jetty-fcgi/fcgi-server/pom.xml index b156f04d87f..4b343cbccbf 100644 --- a/jetty-fcgi/fcgi-server/pom.xml +++ b/jetty-fcgi/fcgi-server/pom.xml @@ -1,11 +1,9 @@ - + org.eclipse.jetty.fcgi fcgi-parent - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java index fd62656164e..91459ed7798 100644 --- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java +++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java @@ -24,6 +24,8 @@ import org.eclipse.jetty.fcgi.generator.Flusher; import org.eclipse.jetty.fcgi.generator.Generator; import org.eclipse.jetty.fcgi.generator.ServerGenerator; import org.eclipse.jetty.http.HttpGenerator; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.server.HttpTransport; import org.eclipse.jetty.util.BufferUtil; @@ -35,6 +37,8 @@ public class HttpTransportOverFCGI implements HttpTransport private final Flusher flusher; private final int request; private volatile boolean head; + private volatile boolean shutdown; + private volatile boolean aborted; public HttpTransportOverFCGI(ByteBufferPool byteBufferPool, Flusher flusher, int request) { @@ -47,13 +51,15 @@ public class HttpTransportOverFCGI implements HttpTransport public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback) { boolean head = this.head = info.isHead(); + boolean shutdown = this.shutdown = info.getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString()); + if (head) { if (lastContent) { Generator.Result headersResult = generator.generateResponseHeaders(request, info.getStatus(), info.getReason(), info.getHttpFields(), new Callback.Adapter()); - Generator.Result contentResult = generator.generateResponseContent(request, BufferUtil.EMPTY_BUFFER, lastContent, callback); + Generator.Result contentResult = generator.generateResponseContent(request, BufferUtil.EMPTY_BUFFER, lastContent, aborted, callback); flusher.flush(headersResult, contentResult); } else @@ -67,9 +73,12 @@ public class HttpTransportOverFCGI implements HttpTransport { Generator.Result headersResult = generator.generateResponseHeaders(request, info.getStatus(), info.getReason(), info.getHttpFields(), new Callback.Adapter()); - Generator.Result contentResult = generator.generateResponseContent(request, content, lastContent, callback); + Generator.Result contentResult = generator.generateResponseContent(request, content, lastContent, aborted, callback); flusher.flush(headersResult, contentResult); } + + if (lastContent && shutdown) + flusher.shutdown(); } @Override @@ -79,7 +88,7 @@ public class HttpTransportOverFCGI implements HttpTransport { if (lastContent) { - Generator.Result result = generator.generateResponseContent(request, BufferUtil.EMPTY_BUFFER, lastContent, callback); + Generator.Result result = generator.generateResponseContent(request, BufferUtil.EMPTY_BUFFER, lastContent, aborted, callback); flusher.flush(result); } else @@ -90,18 +99,22 @@ public class HttpTransportOverFCGI implements HttpTransport } else { - Generator.Result result = generator.generateResponseContent(request, content, lastContent, callback); + Generator.Result result = generator.generateResponseContent(request, content, lastContent, aborted, callback); flusher.flush(result); } + + if (lastContent && shutdown) + flusher.shutdown(); + } + + @Override + public void abort() + { + aborted = true; } @Override public void completed() { } - - @Override - public void abort() - { - } } diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/ServerFCGIConnection.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/ServerFCGIConnection.java index c69253e4ded..2e8187966e0 100644 --- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/ServerFCGIConnection.java +++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/ServerFCGIConnection.java @@ -175,5 +175,17 @@ public class ServerFCGIConnection extends AbstractConnection channel.dispatch(); } } + + @Override + public void onFailure(int request, Throwable failure) + { + HttpChannelOverFCGI channel = channels.remove(request); + if (LOG.isDebugEnabled()) + LOG.debug("Request {} failure on {}: {}", request, channel, failure); + if (channel != null) + { + channel.badMessage(400, failure.toString()); + } + } } } diff --git a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/HttpClientTest.java b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/HttpClientTest.java index 3db51fd48c9..2146f6305be 100644 --- a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/HttpClientTest.java +++ b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/HttpClientTest.java @@ -24,6 +24,8 @@ import java.net.URI; import java.net.URLEncoder; import java.nio.ByteBuffer; import java.util.Arrays; +import java.util.Random; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -37,7 +39,10 @@ import javax.servlet.http.HttpServletResponse; 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.BytesContentProvider; +import org.eclipse.jetty.client.util.DeferredContentProvider; +import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.toolchain.test.IO; @@ -513,4 +518,113 @@ public class HttpClientTest extends AbstractHttpClientServerTest Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(length, response.getContent().length); } + + @Test + public void testLongPollIsAbortedWhenClientIsStopped() throws Exception + { + final CountDownLatch latch = new CountDownLatch(1); + start(new AbstractHandler() + { + @Override + public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + baseRequest.setHandled(true); + request.startAsync(); + latch.countDown(); + } + }); + + final CountDownLatch completeLatch = new CountDownLatch(1); + client.newRequest("localhost", connector.getLocalPort()) + .scheme(scheme) + .send(new Response.CompleteListener() + { + @Override + public void onComplete(Result result) + { + if (result.isFailed()) + completeLatch.countDown(); + } + }); + + Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); + + // Stop the client, the complete listener must be invoked. + client.stop(); + + Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS)); + } + + @Test + public void testEarlyEOF() throws Exception + { + start(new AbstractHandler() + { + @Override + public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + baseRequest.setHandled(true); + // Promise some content, then flush the headers, then fail to send the content. + response.setContentLength(16); + response.flushBuffer(); + throw new NullPointerException(); + } + }); + + try + { + client.newRequest("localhost", connector.getLocalPort()) + .scheme(scheme) + .timeout(5, TimeUnit.SECONDS) + .send(); + Assert.fail(); + } + catch (ExecutionException x) + { + // Expected. + } + } + + @Test + public void testSmallContentDelimitedByEOFWithSlowRequest() throws Exception + { + testContentDelimitedByEOFWithSlowRequest(1024); + } + + @Test + public void testBigContentDelimitedByEOFWithSlowRequest() throws Exception + { + testContentDelimitedByEOFWithSlowRequest(128 * 1024); + } + + private void testContentDelimitedByEOFWithSlowRequest(int length) throws Exception + { + final byte[] data = new byte[length]; + new Random().nextBytes(data); + start(new AbstractHandler() + { + @Override + public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + baseRequest.setHandled(true); + response.setHeader("Connection", "close"); + response.getOutputStream().write(data); + } + }); + + DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.wrap(new byte[]{0})); + Request request = client.newRequest("localhost", connector.getLocalPort()) + .scheme(scheme) + .content(content); + FutureResponseListener listener = new FutureResponseListener(request); + request.send(listener); + // Wait some time to simulate a slow request. + Thread.sleep(1000); + content.close(); + + ContentResponse response = listener.get(5, TimeUnit.SECONDS); + + Assert.assertEquals(200, response.getStatus()); + Assert.assertArrayEquals(data, response.getContent()); + } } diff --git a/jetty-fcgi/pom.xml b/jetty-fcgi/pom.xml index 3054c2db4af..d309fec8a21 100644 --- a/jetty-fcgi/pom.xml +++ b/jetty-fcgi/pom.xml @@ -1,11 +1,9 @@ - + org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 diff --git a/jetty-http-spi/pom.xml b/jetty-http-spi/pom.xml index ea46cb7d1a9..a21e2e3c9b4 100644 --- a/jetty-http-spi/pom.xml +++ b/jetty-http-spi/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-http-spi diff --git a/jetty-http/pom.xml b/jetty-http/pom.xml index 84f6069bbf9..67d85ac6695 100644 --- a/jetty-http/pom.xml +++ b/jetty-http/pom.xml @@ -3,7 +3,7 @@ jetty-project org.eclipse.jetty - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-http diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java index ef132321252..8a2626803a7 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java @@ -38,7 +38,8 @@ public enum HttpMethod DELETE, TRACE, CONNECT, - MOVE; + MOVE, + PROXY; /* ------------------------------------------------------------ */ /** @@ -48,7 +49,7 @@ public enum HttpMethod * @param limit The first non valid index * @return A HttpMethod if a match or null if no easy match. */ - public static HttpMethod lookAheadGet(byte[] bytes, int position, int limit) + public static HttpMethod lookAheadGet(byte[] bytes, final int position, int limit) { int length=limit-position; if (length<4) @@ -62,6 +63,8 @@ public enum HttpMethod case 'P': if (bytes[position+1]=='O' && bytes[position+2]=='S' && bytes[position+3]=='T' && length>=5 && bytes[position+4]==' ') return POST; + if (bytes[position+1]=='R' && bytes[position+2]=='O' && bytes[position+3]=='X' && length>=6 && bytes[position+4]=='Y' && bytes[position+5]==' ') + return PROXY; if (bytes[position+1]=='U' && bytes[position+2]=='T' && bytes[position+3]==' ') return PUT; break; 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 8443f67de43..ee40a74af16 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 @@ -104,6 +104,7 @@ public class HttpParser SPACE2, REQUEST_VERSION, REASON, + PROXY, HEADER, HEADER_IN_NAME, HEADER_VALUE, @@ -403,7 +404,7 @@ public class HttpParser * otherwise skip white space until something else to parse. */ private boolean quickStart(ByteBuffer buffer) - { + { if (_requestHandler!=null) { _method = HttpMethod.lookAheadGet(buffer); @@ -411,6 +412,7 @@ public class HttpParser { _methodString = _method.asString(); buffer.position(buffer.position()+_methodString.length()+1); + setState(State.SPACE1); return false; } @@ -655,7 +657,29 @@ public class HttpParser version=HttpVersion.lookAheadGet(buffer.array(),buffer.arrayOffset()+buffer.position()-1,buffer.arrayOffset()+buffer.limit()); else version=HttpVersion.CACHE.getBest(buffer,0,buffer.remaining()); - if (version!=null) + if (version==null) + { + if (_method==HttpMethod.PROXY) + { + if (!(_requestHandler instanceof ProxyHandler)) + throw new BadMessage(); + + _uri.flip(); + String protocol=BufferUtil.toString(_uri); + // This is the proxy protocol, so we can assume entire first line is in buffer else 400 + buffer.position(buffer.position()-1); + String sAddr = getProxyField(buffer); + String dAddr = getProxyField(buffer); + int sPort = BufferUtil.takeInt(buffer); + next(buffer); + int dPort = BufferUtil.takeInt(buffer); + next(buffer); + _state=State.START; + ((ProxyHandler)_requestHandler).proxied(protocol,sAddr,dAddr,sPort,dPort); + return false; + } + } + else { int pos = buffer.position()+version.asString().length()-1; if (pos=HttpVersion.HTTP_1_1.getVersion()) { int header_cache = _handler.getHeaderCacheSize(); - if (header_cache>0) - _connectionFields=new ArrayTernaryTrie<>(header_cache); + _connectionFields=new ArrayTernaryTrie<>(header_cache); } setState(State.HEADER); @@ -1334,24 +1357,36 @@ public class HttpParser protected boolean parseContent(ByteBuffer buffer) { + int remaining=buffer.remaining(); + if (remaining==0 && _state==State.CONTENT) + { + long content=_contentLength - _contentPosition; + if (content == 0) + { + setState(State.END); + if (_handler.messageComplete()) + return true; + } + } + // Handle _content byte ch; - while (_state.ordinal() < State.END.ordinal() && buffer.hasRemaining()) + while (_state.ordinal() < State.END.ordinal() && remaining>0) { switch (_state) { case EOF_CONTENT: _contentChunk=buffer.asReadOnlyBuffer(); - _contentPosition += _contentChunk.remaining(); - buffer.position(buffer.position()+_contentChunk.remaining()); + _contentPosition += remaining; + buffer.position(buffer.position()+remaining); if (_handler.content(_contentChunk)) return true; break; case CONTENT: { - long remaining=_contentLength - _contentPosition; - if (remaining == 0) + long content=_contentLength - _contentPosition; + if (content == 0) { setState(State.END); if (_handler.messageComplete()) @@ -1362,25 +1397,25 @@ public class HttpParser _contentChunk=buffer.asReadOnlyBuffer(); // limit content by expected size - if (_contentChunk.remaining() > remaining) + if (remaining > content) { // We can cast remaining to an int as we know that it is smaller than // or equal to length which is already an int. - _contentChunk.limit(_contentChunk.position()+(int)remaining); + _contentChunk.limit(_contentChunk.position()+(int)content); } _contentPosition += _contentChunk.remaining(); buffer.position(buffer.position()+_contentChunk.remaining()); - boolean handle=_handler.content(_contentChunk); + if (_handler.content(_contentChunk)) + return true; + if(_contentPosition == _contentLength) { setState(State.END); if (_handler.messageComplete()) return true; } - if (handle) - return true; } break; } @@ -1440,8 +1475,8 @@ public class HttpParser case CHUNK: { - int remaining=_chunkLength - _chunkPosition; - if (remaining == 0) + int chunk=_chunkLength - _chunkPosition; + if (chunk == 0) { setState(State.CHUNKED_CONTENT); } @@ -1449,13 +1484,13 @@ public class HttpParser { _contentChunk=buffer.asReadOnlyBuffer(); - if (_contentChunk.remaining() > remaining) - _contentChunk.limit(_contentChunk.position()+remaining); - remaining=_contentChunk.remaining(); + if (remaining > chunk) + _contentChunk.limit(_contentChunk.position()+chunk); + chunk=_contentChunk.remaining(); - _contentPosition += remaining; - _chunkPosition += remaining; - buffer.position(buffer.position()+remaining); + _contentPosition += chunk; + _chunkPosition += chunk; + buffer.position(buffer.position()+chunk); if (_handler.content(_contentChunk)) return true; } @@ -1470,7 +1505,10 @@ public class HttpParser default: break; + } + + remaining=buffer.remaining(); } return false; } @@ -1586,6 +1624,11 @@ public class HttpParser public int getHeaderCacheSize(); } + public interface ProxyHandler + { + void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort); + } + public interface RequestHandler extends HttpHandler { /** @@ -1618,4 +1661,20 @@ public class HttpParser { return _connectionFields; } + + private String getProxyField(ByteBuffer buffer) + { + _string.setLength(0); + _length=0; + + while (buffer.hasRemaining()) + { + // process each character + byte ch=next(buffer); + if (ch<=' ') + return _string.toString(); + _string.append((char)ch); + } + throw new BadMessage(); + } } diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java index 38c77af9a0c..6bc6ae23e48 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java @@ -1398,6 +1398,63 @@ public class HttpParserTest } + @Test + public void testProxyProtocol() throws Exception + { + ByteBuffer buffer=BufferUtil + .toBuffer("PROXY TCP4 107.47.45.254 10.0.1.116 27689 80\015\012" + +"GET / HTTP/1.1\015\012" + +"Host: localhost \015\012" + +"Connection: close\015\012"+"\015\012"+"\015\012"); + + Handler handler=new Handler(); + HttpParser parser=new HttpParser((HttpParser.RequestHandler)handler); + parseAll(parser, buffer); + + assertTrue(_headerCompleted); + assertTrue(_messageCompleted); + assertEquals("GET", _methodOrVersion); + assertEquals("/", _uriOrStatus); + assertEquals("HTTP/1.1", _versionOrReason); + assertEquals("PROXY TCP4 107.47.45.254 10.0.1.116 27689 80", handler._proxy); + assertEquals("Host", _hdr[0]); + assertEquals("localhost", _val[0]); + assertEquals("Connection", _hdr[1]); + assertEquals("close", _val[1]); + assertEquals(1, _headers); + } + + @Test + public void testSplitProxyHeaderParseTest() throws Exception + { + Handler handler=new Handler(); + HttpParser parser=new HttpParser((HttpParser.RequestHandler)handler); + + ByteBuffer buffer=BufferUtil.toBuffer("PROXY TCP4 207.47.45.254 10.0.1.116 27689 80\015\012"); + parser.parseNext(buffer); + + buffer=BufferUtil.toBuffer( + "GET / HTTP/1.1\015\012" + +"Host: localhost \015\012" + +"Connection: close\015\012" + +"\015\012" + +"\015\012"); + + parser.parseNext(buffer); + assertTrue(_headerCompleted); + assertTrue(_messageCompleted); + assertEquals("GET", _methodOrVersion); + assertEquals("/", _uriOrStatus); + assertEquals("HTTP/1.1", _versionOrReason); + assertEquals("PROXY TCP4 207.47.45.254 10.0.1.116 27689 80", handler._proxy); + assertEquals("Host", _hdr[0]); + assertEquals("localhost", _val[0]); + assertEquals("Connection", _hdr[1]); + assertEquals("close", _val[1]); + assertEquals(1, _headers); + } + + @Before public void init() { @@ -1429,9 +1486,10 @@ public class HttpParserTest private boolean _headerCompleted; private boolean _messageCompleted; - private class Handler implements HttpParser.RequestHandler, HttpParser.ResponseHandler + private class Handler implements HttpParser.RequestHandler, HttpParser.ResponseHandler, HttpParser.ProxyHandler { private HttpFields fields; + String _proxy; @Override public boolean content(ByteBuffer ref) @@ -1539,5 +1597,11 @@ public class HttpParserTest { return 512; } + + @Override + public void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort) + { + _proxy="PROXY "+protocol+" "+sAddr+" "+dAddr+" "+sPort+" "+dPort; + } } } diff --git a/jetty-io/pom.xml b/jetty-io/pom.xml index e21f1e0e5a4..c5879725d83 100644 --- a/jetty-io/pom.xml +++ b/jetty-io/pom.xml @@ -2,7 +2,7 @@ jetty-project org.eclipse.jetty - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-io 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 6517dbd3427..bfcb1aa18ca 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 @@ -142,12 +142,9 @@ public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint @Override protected void onIdleExpired(TimeoutException timeout) { - boolean output_shutdown=isOutputShutdown(); - boolean input_shutdown=isInputShutdown(); + // Note: Rely on fillInterest to notify onReadTimeout to close connection. _fillInterest.onFail(timeout); _writeFlusher.onFail(timeout); - if (isOpen() && output_shutdown || input_shutdown) - close(); } @Override diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnection.java similarity index 92% rename from jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnection.java rename to jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnection.java index a2fc8511f4a..cd05630f62f 100644 --- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnection.java @@ -16,18 +16,13 @@ // ======================================================================== // -package org.eclipse.jetty.spdy.client; +package org.eclipse.jetty.io; import java.io.IOException; import java.util.Map; import java.util.concurrent.Executor; import javax.net.ssl.SSLEngine; -import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.ClientConnectionFactory; -import org.eclipse.jetty.io.Connection; -import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java b/jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnectionFactory.java similarity index 92% rename from jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java rename to jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnectionFactory.java index 50a1e5b2233..ff3860071f7 100644 --- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnectionFactory.java @@ -16,9 +16,8 @@ // ======================================================================== // -package org.eclipse.jetty.spdy.client; +package org.eclipse.jetty.io; -import org.eclipse.jetty.io.ClientConnectionFactory; public abstract class NegotiatingClientConnectionFactory implements ClientConnectionFactory { 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 d45902e36dd..a4b6f7d2a16 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 @@ -19,11 +19,13 @@ package org.eclipse.jetty.io; import java.io.IOException; +import java.net.Socket; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import java.util.List; +import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.Scheduler; @@ -57,9 +59,11 @@ public class NetworkTrafficSelectChannelEndPoint extends SelectChannelEndPoint if (b.hasRemaining()) { int position = b.position(); + ByteBuffer view=b.slice(); flushed&=super.flush(b); int l=b.position()-position; - notifyOutgoing(b, position, l); + view.limit(view.position()+l); + notifyOutgoing(view); if (!flushed) break; } @@ -67,9 +71,12 @@ public class NetworkTrafficSelectChannelEndPoint extends SelectChannelEndPoint return flushed; } + - public void notifyOpened() + @Override + public void onOpen() { + super.onOpen(); if (listeners != null && !listeners.isEmpty()) { for (NetworkTrafficListener listener : listeners) @@ -86,6 +93,27 @@ public class NetworkTrafficSelectChannelEndPoint extends SelectChannelEndPoint } } + @Override + public void onClose() + { + super.onClose(); + if (listeners != null && !listeners.isEmpty()) + { + for (NetworkTrafficListener listener : listeners) + { + try + { + listener.closed(getSocket()); + } + catch (Exception x) + { + LOG.warn(x); + } + } + } + } + + public void notifyIncoming(ByteBuffer buffer, int read) { if (listeners != null && !listeners.isEmpty() && read > 0) @@ -105,18 +133,16 @@ public class NetworkTrafficSelectChannelEndPoint extends SelectChannelEndPoint } } - public void notifyOutgoing(ByteBuffer buffer, int position, int written) + public void notifyOutgoing(ByteBuffer view) { - if (listeners != null && !listeners.isEmpty() && written > 0) + if (listeners != null && !listeners.isEmpty() && view.hasRemaining()) { + Socket socket=getSocket(); for (NetworkTrafficListener listener : listeners) { try { - ByteBuffer view = buffer.slice(); - view.position(position); - view.limit(position + written); - listener.outgoing(getSocket(), view); + listener.outgoing(socket, view); } catch (Exception x) { @@ -126,21 +152,4 @@ public class NetworkTrafficSelectChannelEndPoint extends SelectChannelEndPoint } } - public void notifyClosed() - { - if (listeners != null && !listeners.isEmpty()) - { - for (NetworkTrafficListener listener : listeners) - { - try - { - listener.closed(getSocket()); - } - catch (Exception x) - { - LOG.warn(x); - } - } - } - } } 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 1db6908fe78..d23be980148 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 @@ -18,6 +18,7 @@ package org.eclipse.jetty.io; +import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.instanceOf; @@ -36,6 +37,8 @@ import org.eclipse.jetty.toolchain.test.AdvancedRunner; import org.eclipse.jetty.toolchain.test.annotation.Slow; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.FutureCallback; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.Scheduler; import org.eclipse.jetty.util.thread.TimerScheduler; import org.junit.After; @@ -235,6 +238,26 @@ public class ByteArrayEndPointTest assertEquals(null, fcb.get()); assertEquals(" more.", endp.getOutputString()); } + + /** + * Simulate AbstractConnection.ReadCallback.failed() + */ + public static class Closer extends FutureCallback + { + private EndPoint endp; + + public Closer(EndPoint endp) + { + this.endp = endp; + } + + @Override + public void failed(Throwable cause) + { + endp.close(); + super.failed(cause); + } + } @Slow @Test @@ -275,7 +298,7 @@ public class ByteArrayEndPointTest assertThat(t.getCause(), instanceOf(TimeoutException.class)); } assertThat(System.currentTimeMillis() - start, greaterThan(idleTimeout / 2)); - assertTrue(endp.isOpen()); + assertThat("Endpoint open", endp.isOpen(), is(true)); // We need to delay the write timeout test below from the read timeout test above. // The reason is that the scheduler thread that fails the endPoint WriteFlusher @@ -298,17 +321,19 @@ public class ByteArrayEndPointTest assertThat(t.getCause(), instanceOf(TimeoutException.class)); } assertThat(System.currentTimeMillis() - start, greaterThan(idleTimeout / 2)); - assertTrue(endp.isOpen()); + assertThat("Endpoint open", endp.isOpen(), is(true)); - // Still no idle close - Thread.sleep(idleTimeout * 2); - assertTrue(endp.isOpen()); + endp.fillInterested(new Closer(endp)); + + // Still no idle close (wait half the time) + Thread.sleep(idleTimeout / 2); + assertThat("Endpoint open", endp.isOpen(), is(true)); // shutdown out endp.shutdownOutput(); - // idle close + // idle close (wait double the time) Thread.sleep(idleTimeout * 2); - assertFalse(endp.isOpen()); + assertThat("Endpoint open", endp.isOpen(), is(false)); } } diff --git a/jetty-jaas/pom.xml b/jetty-jaas/pom.xml index a03618bf51d..ddc80f98e19 100644 --- a/jetty-jaas/pom.xml +++ b/jetty-jaas/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-jaas diff --git a/jetty-jaspi/pom.xml b/jetty-jaspi/pom.xml index 9f72793c877..9f8fc89f2f4 100644 --- a/jetty-jaspi/pom.xml +++ b/jetty-jaspi/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-jaspi diff --git a/jetty-jmx/pom.xml b/jetty-jmx/pom.xml index e19a648c421..56a5cae6286 100644 --- a/jetty-jmx/pom.xml +++ b/jetty-jmx/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-jmx diff --git a/jetty-jmx/src/main/config/modules/jmx.mod b/jetty-jmx/src/main/config/modules/jmx.mod index f8cadb5517d..ee091c706a8 100644 --- a/jetty-jmx/src/main/config/modules/jmx.mod +++ b/jetty-jmx/src/main/config/modules/jmx.mod @@ -2,6 +2,9 @@ # JMX Module # +[depend] +server + [lib] lib/jetty-jmx-${jetty.version}.jar diff --git a/jetty-jndi/pom.xml b/jetty-jndi/pom.xml index f81b7ec5397..c40a6006d68 100644 --- a/jetty-jndi/pom.xml +++ b/jetty-jndi/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-jndi diff --git a/jetty-jsp/pom.xml b/jetty-jsp/pom.xml index 70d3bbc4cac..c9002c22b11 100644 --- a/jetty-jsp/pom.xml +++ b/jetty-jsp/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-jsp diff --git a/jetty-jsp/src/main/config/modules/jsp-impl/glassfish-jsp.mod b/jetty-jsp/src/main/config/modules/jsp-impl/glassfish-jsp.mod new file mode 100644 index 00000000000..130d2b371f4 --- /dev/null +++ b/jetty-jsp/src/main/config/modules/jsp-impl/glassfish-jsp.mod @@ -0,0 +1,8 @@ +# +# Glassfish JSP Module +# +[name] +jsp-impl + +[lib] +lib/jsp/*.jar diff --git a/jetty-jsp/src/main/config/modules/jsp-impl/glassfish-jstl.mod b/jetty-jsp/src/main/config/modules/jsp-impl/glassfish-jstl.mod new file mode 100644 index 00000000000..4b8e6f360c0 --- /dev/null +++ b/jetty-jsp/src/main/config/modules/jsp-impl/glassfish-jstl.mod @@ -0,0 +1,6 @@ +# +# Glassfish JSTL +[name] +jstl-impl + +# This file is empty as glassfish jstl is provided by glassfish jsp diff --git a/jetty-jsp/src/main/config/modules/jsp.mod b/jetty-jsp/src/main/config/modules/jsp.mod deleted file mode 100644 index b67dfe278dc..00000000000 --- a/jetty-jsp/src/main/config/modules/jsp.mod +++ /dev/null @@ -1,14 +0,0 @@ -# -# Jetty JSP Module -# - -[depend] -servlet - -[lib] -lib/jsp/*.jar - -[ini-template] -# JSP Configuration -# To use an non-jdk compiler for JSP compilation uncomment next line -# -Dorg.apache.jasper.compiler.disablejsr199=true diff --git a/jetty-jspc-maven-plugin/pom.xml b/jetty-jspc-maven-plugin/pom.xml index 2f34f4da5b5..2f709b2cd5c 100644 --- a/jetty-jspc-maven-plugin/pom.xml +++ b/jetty-jspc-maven-plugin/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-jspc-maven-plugin @@ -72,9 +72,19 @@ + + org.eclipse.jetty + apache-jsp + ${project.version} + + + org.apache.ant + ant + 1.8.4 + org.eclipse.jetty - jetty-jsp + apache-jstl ${project.version} diff --git a/jetty-jspc-maven-plugin/src/main/java/org/eclipse/jetty/jspc/plugin/JspcMojo.java b/jetty-jspc-maven-plugin/src/main/java/org/eclipse/jetty/jspc/plugin/JspcMojo.java index 1e6d103dc86..ac3e45f3f59 100644 --- a/jetty-jspc-maven-plugin/src/main/java/org/eclipse/jetty/jspc/plugin/JspcMojo.java +++ b/jetty-jspc-maven-plugin/src/main/java/org/eclipse/jetty/jspc/plugin/JspcMojo.java @@ -79,6 +79,7 @@ import org.eclipse.jetty.util.resource.Resource; public class JspcMojo extends AbstractMojo { public static final String END_OF_WEBAPP = ""; + public static final String PRECOMPILED_FLAG = "org.eclipse.jetty.jsp.precompiled"; /** @@ -207,7 +208,7 @@ public class JspcMojo extends AbstractMojo /** * Patterns of jars on the system path that contain tlds. Use | to separate each pattern. * - * @parameter default-value=".*taglibs[^/]*\.jar|.*jstl-impl[^/]*\.jar$ + * @parameter default-value=".*taglibs[^/]*\.jar|.*jstl[^/]*\.jar$ */ private String tldJarNamePatterns; @@ -294,9 +295,9 @@ public class JspcMojo extends AbstractMojo jspc.setWebXmlFragment(webXmlFragment); jspc.setUriroot(webAppSourceDirectory); jspc.setOutputDir(generatedClasses); - jspc.setClassPath(webAppClassPath.toString()); + jspc.setClassPath(sysClassPath+System.getProperty("path.separator")+webAppClassPath.toString()); jspc.setCompile(true); - jspc.setSystemClassPath(sysClassPath); + //jspc.setSystemClassPath(sysClassPath); // JspC#setExtensions() does not exist, so @@ -419,6 +420,10 @@ public class JspcMojo extends AbstractMojo mergedWebXmlWriter.println(line); } } + + //put in a context init-param to flag that the contents have been precompiled + mergedWebXmlWriter.println(""+PRECOMPILED_FLAG+"true"); + // put in the generated fragment try (BufferedReader fragmentWebXmlReader = new BufferedReader( @@ -541,13 +546,16 @@ public class JspcMojo extends AbstractMojo */ private List getSystemJarsWithTlds() throws Exception { + getLog().debug("tld pattern=" + tldJarNamePatterns); final List list = new ArrayList(); List artifactUris = new ArrayList(); Pattern pattern = Pattern.compile(tldJarNamePatterns); for (Iterator iter = pluginArtifacts.iterator(); iter.hasNext(); ) { Artifact pluginArtifact = iter.next(); - artifactUris.add(Resource.newResource(pluginArtifact.getFile()).getURI()); + Resource res = Resource.newResource(pluginArtifact.getFile()); + getLog().debug("scan jar: "+res.getURI()); + artifactUris.add(res.getURI()); } PatternMatcher matcher = new PatternMatcher() diff --git a/jetty-maven-plugin/pom.xml b/jetty-maven-plugin/pom.xml index aed095f49a4..eefa280e905 100644 --- a/jetty-maven-plugin/pom.xml +++ b/jetty-maven-plugin/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-maven-plugin @@ -122,19 +122,19 @@ org.eclipse.jetty - jetty-jsp + apache-jsp ${project.version} - - - javax.transaction - javax.transaction-api - compile - + + org.eclipse.jetty + apache-jstl + ${project.version} + + + javax.transaction + javax.transaction-api + compile + diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java index 818073ef9d0..e4d8348b9f4 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java @@ -62,7 +62,7 @@ public class JettyWebAppContext extends WebAppContext { private static final Logger LOG = Log.getLogger(JettyWebAppContext.class); - private static final String DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN = ".*/javax.servlet-[^/]*\\.jar$|.*/servlet-api-[^/]*\\.jar$"; + private static final String DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN = ".*/javax.servlet-[^/]*\\.jar$|.*/servlet-api-[^/]*\\.jar$|.*javax.servlet.jsp.jstl-[^/]*\\.jar"; private static final String WEB_INF_CLASSES_PREFIX = "/WEB-INF/classes"; private static final String WEB_INF_LIB_PREFIX = "/WEB-INF/lib"; diff --git a/jetty-monitor/pom.xml b/jetty-monitor/pom.xml index d8092919469..2b5611a9a1d 100644 --- a/jetty-monitor/pom.xml +++ b/jetty-monitor/pom.xml @@ -19,7 +19,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-monitor diff --git a/jetty-nosql/pom.xml b/jetty-nosql/pom.xml index 3eb4f2282bf..7c4b1c86801 100644 --- a/jetty-nosql/pom.xml +++ b/jetty-nosql/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-nosql diff --git a/jetty-osgi/jetty-osgi-alpn/pom.xml b/jetty-osgi/jetty-osgi-alpn/pom.xml new file mode 100644 index 00000000000..3698b98bca5 --- /dev/null +++ b/jetty-osgi/jetty-osgi-alpn/pom.xml @@ -0,0 +1,47 @@ + + + org.eclipse.jetty.osgi + jetty-osgi-project + 9.2.0-SNAPSHOT + + 4.0.0 + jetty-osgi-alpn + Jetty :: OSGi ALPN Fragment + jar + + org.eclipse.jetty.osgi.alpn.fragment + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.7 + + + parse-version + + parse-version + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + 2 + ${bundle-symbolic-name};singleton:=true + Jetty OSGi ALPN Fragment + ${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion} + org.eclipse.jetty.alpn;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}" + system.bundle;extension:=framework + + + + + + + diff --git a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml index 4cdcbbad41c..8dfd29da12e 100644 --- a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml +++ b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.osgi jetty-osgi-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-osgi-boot-jsp diff --git a/jetty-osgi/jetty-osgi-boot-warurl/pom.xml b/jetty-osgi/jetty-osgi-boot-warurl/pom.xml index 67c56041ac8..a785f581868 100644 --- a/jetty-osgi/jetty-osgi-boot-warurl/pom.xml +++ b/jetty-osgi/jetty-osgi-boot-warurl/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.osgi jetty-osgi-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/jetty-osgi/jetty-osgi-boot/pom.xml b/jetty-osgi/jetty-osgi-boot/pom.xml index ffd2d9ee3e6..5a02af5fdbf 100644 --- a/jetty-osgi/jetty-osgi-boot/pom.xml +++ b/jetty-osgi/jetty-osgi-boot/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.osgi jetty-osgi-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-osgi-boot diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java index 79e350cacc5..199bde3c358 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java @@ -37,37 +37,81 @@ import org.osgi.framework.Bundle; public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper { private static final Logger LOG = Log.getLogger(BundleClassLoaderHelper.class); + private static enum OSGiContainerType {EquinoxOld, EquinoxLuna, FelixOld, Felix403}; + private static OSGiContainerType osgiContainer; + private static Class Equinox_BundleHost_Class; + private static Class Equinox_EquinoxBundle_Class; + private static Class Felix_BundleImpl_Class; + private static Class Felix_BundleWiring_Class; + //old equinox + private static Method Equinox_BundleHost_getBundleLoader_method; + private static Method Equinox_BundleLoader_createClassLoader_method; + //new equinox + private static Method Equinox_EquinoxBundle_getModuleClassLoader_Method; + + //new felix + private static Method Felix_BundleImpl_Adapt_Method; + //old felix + private static Field Felix_BundleImpl_m_Modules_Field; + private static Field Felix_ModuleImpl_m_ClassLoader_Field; + private static Method Felix_BundleWiring_getClassLoader_Method; - private static boolean identifiedOsgiImpl = false; - - private static boolean isEquinox = false; - - private static boolean isFelix = false; - - private static void init(Bundle bundle) + + private static void checkContainerType (Bundle bundle) { - identifiedOsgiImpl = true; + if (osgiContainer != null) + return; + try { - isEquinox = bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.framework.internal.core.BundleHost") != null; + Equinox_BundleHost_Class = bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.framework.internal.core.BundleHost"); + osgiContainer = OSGiContainerType.EquinoxOld; + return; } - catch (Throwable t) + catch (ClassNotFoundException e) { - isEquinox = false; + LOG.ignore(e); } - if (!isEquinox) + + try { + Equinox_EquinoxBundle_Class = bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.internal.framework.EquinoxBundle"); + osgiContainer = OSGiContainerType.EquinoxLuna; + return; + } + catch (ClassNotFoundException e) + { + LOG.ignore(e); + } + + try + { + //old felix or new felix? + Felix_BundleImpl_Class = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.BundleImpl"); try { - isFelix = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.BundleImpl") != null; + Felix_BundleImpl_Adapt_Method = Felix_BundleImpl_Class.getDeclaredMethod("adapt", new Class[] {Class.class}); + osgiContainer = OSGiContainerType.Felix403; + return; } - catch (Throwable t2) + catch (NoSuchMethodException e) { - isFelix = false; + osgiContainer = OSGiContainerType.FelixOld; + return; } } + catch (ClassNotFoundException e) + { + LOG.warn("Unknown OSGi container type"); + return; + } + } + + + + /** * Assuming the bundle is started. * @@ -77,7 +121,7 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper public ClassLoader getBundleClassLoader(Bundle bundle) { String bundleActivator = (String) bundle.getHeaders().get("Bundle-Activator"); - + if (bundleActivator == null) { bundleActivator = (String) bundle.getHeaders().get("Jetty-ClassInBundle"); @@ -93,80 +137,135 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper LOG.warn(e); } } - // resort to introspection - if (!identifiedOsgiImpl) + + // resort to introspection + return getBundleClassLoaderForContainer(bundle); + } + + /** + * @param bundle + * @return + */ + private ClassLoader getBundleClassLoaderForContainer (Bundle bundle) + { + checkContainerType (bundle); + if (osgiContainer == null) { - init(bundle); - } - if (isEquinox) - { - return internalGetEquinoxBundleClassLoader(bundle); - } - else if (isFelix) - { - return internalGetFelixBundleClassLoader(bundle); + LOG.warn("No classloader for unknown OSGi container type"); + return null; } - LOG.warn("No classloader found for bundle "+bundle.getSymbolicName()); - return null; + switch (osgiContainer) + { + case EquinoxOld: + case EquinoxLuna: + { + return internalGetEquinoxBundleClassLoader(bundle); + } + + case FelixOld: + case Felix403: + { + return internalGetFelixBundleClassLoader(bundle); + } + default: + { + LOG.warn("No classloader found for bundle "+bundle.getSymbolicName()); + return null; + + } + } } + + - private static Method Equinox_BundleHost_getBundleLoader_method; - - private static Method Equinox_BundleLoader_createClassLoader_method; - + /** + * @param bundle + * @return + */ private static ClassLoader internalGetEquinoxBundleClassLoader(Bundle bundle) { - // assume equinox: - try + if (osgiContainer == OSGiContainerType.EquinoxOld) { - if (Equinox_BundleHost_getBundleLoader_method == null) + try { - Equinox_BundleHost_getBundleLoader_method = - bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.framework.internal.core.BundleHost").getDeclaredMethod("getBundleLoader", new Class[] {}); - Equinox_BundleHost_getBundleLoader_method.setAccessible(true); + if (Equinox_BundleHost_getBundleLoader_method == null) + { + Equinox_BundleHost_getBundleLoader_method = + Equinox_BundleHost_Class.getDeclaredMethod("getBundleLoader", new Class[] {}); + Equinox_BundleHost_getBundleLoader_method.setAccessible(true); + } + Object bundleLoader = Equinox_BundleHost_getBundleLoader_method.invoke(bundle, new Object[] {}); + if (Equinox_BundleLoader_createClassLoader_method == null && bundleLoader != null) + { + Equinox_BundleLoader_createClassLoader_method = + bundleLoader.getClass().getClassLoader().loadClass("org.eclipse.osgi.internal.loader.BundleLoader").getDeclaredMethod("createClassLoader", new Class[] {}); + Equinox_BundleLoader_createClassLoader_method.setAccessible(true); + } + return (ClassLoader) Equinox_BundleLoader_createClassLoader_method.invoke(bundleLoader, new Object[] {}); } - Object bundleLoader = Equinox_BundleHost_getBundleLoader_method.invoke(bundle, new Object[] {}); - if (Equinox_BundleLoader_createClassLoader_method == null && bundleLoader != null) + catch (ClassNotFoundException t) { - Equinox_BundleLoader_createClassLoader_method = - bundleLoader.getClass().getClassLoader().loadClass("org.eclipse.osgi.internal.loader.BundleLoader").getDeclaredMethod("createClassLoader", new Class[] {}); - Equinox_BundleLoader_createClassLoader_method.setAccessible(true); + LOG.warn(t); + return null; + } + catch (Throwable t) + { + LOG.warn(t); + return null; } - return (ClassLoader) Equinox_BundleLoader_createClassLoader_method.invoke(bundleLoader, new Object[] {}); } - catch (Throwable t) + + if (osgiContainer == OSGiContainerType.EquinoxLuna) { - LOG.warn(t); + try + { + if (Equinox_EquinoxBundle_getModuleClassLoader_Method == null) + Equinox_EquinoxBundle_getModuleClassLoader_Method = Equinox_EquinoxBundle_Class.getDeclaredMethod("getModuleClassLoader", new Class[] {Boolean.TYPE}); + + Equinox_EquinoxBundle_getModuleClassLoader_Method.setAccessible(true); + return (ClassLoader)Equinox_EquinoxBundle_getModuleClassLoader_Method.invoke(bundle, new Object[] {Boolean.FALSE}); + } + catch (Exception e) + { + LOG.warn(e); + return null; + } } + LOG.warn("No classloader for equinox platform for bundle "+bundle.getSymbolicName()); return null; } - private static Field Felix_BundleImpl_m_modules_field; + - private static Field Felix_ModuleImpl_m_classLoader_field; - - private static Method Felix_adapt_method; - - private static Method Felix_bundle_wiring_getClassLoader_method; - - private static Class Felix_bundleWiringClazz; - - private static Boolean isFelix403 = null; + /** + * @param bundle + * @return + */ private static ClassLoader internalGetFelixBundleClassLoader(Bundle bundle) { - //firstly, try to find classes matching a newer version of felix - initFelix403(bundle); - - if (isFelix403.booleanValue()) + + if (osgiContainer == OSGiContainerType.Felix403) { try { - Object wiring = Felix_adapt_method.invoke(bundle, new Object[] {Felix_bundleWiringClazz}); - ClassLoader cl = (ClassLoader)Felix_bundle_wiring_getClassLoader_method.invoke(wiring); - return cl; + if (Felix_BundleWiring_Class == null) + Felix_BundleWiring_Class = bundle.getClass().getClassLoader().loadClass("org.osgi.framework.wiring.BundleWiring"); + + + Felix_BundleImpl_Adapt_Method.setAccessible(true); + + if (Felix_BundleWiring_getClassLoader_Method == null) + { + Felix_BundleWiring_getClassLoader_Method = Felix_BundleWiring_Class.getDeclaredMethod("getClassLoader"); + Felix_BundleWiring_getClassLoader_Method.setAccessible(true); + } + + + Object wiring = Felix_BundleImpl_Adapt_Method.invoke(bundle, new Object[] {Felix_BundleWiring_Class}); + return (ClassLoader)Felix_BundleWiring_getClassLoader_Method.invoke(wiring); } catch (Exception e) { @@ -176,123 +275,92 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper } - // Fallback to trying earlier versions of felix. - if (Felix_BundleImpl_m_modules_field == null) - { + if (osgiContainer == OSGiContainerType.FelixOld) + { try { - Class bundleImplClazz = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.BundleImpl"); - Felix_BundleImpl_m_modules_field = bundleImplClazz.getDeclaredField("m_modules"); - Felix_BundleImpl_m_modules_field.setAccessible(true); - } - catch (ClassNotFoundException e) - { - LOG.warn(e); - } - catch (NoSuchFieldException e) - { - LOG.warn(e); - } - } + if (Felix_BundleImpl_m_Modules_Field == null) + { + Felix_BundleImpl_m_Modules_Field = Felix_BundleImpl_Class.getDeclaredField("m_modules"); + Felix_BundleImpl_m_Modules_Field.setAccessible(true); + } - // Figure out which version of the modules is exported - Object currentModuleImpl; - try - { - Object[] moduleArray = (Object[]) Felix_BundleImpl_m_modules_field.get(bundle); - currentModuleImpl = moduleArray[moduleArray.length - 1]; - } - catch (Throwable t2) - { - try - { - List moduleArray = (List) Felix_BundleImpl_m_modules_field.get(bundle); - currentModuleImpl = moduleArray.get(moduleArray.size() - 1); - } + // Figure out which version of the modules is exported + Object currentModuleImpl; + + try + { + Object[] moduleArray = (Object[]) Felix_BundleImpl_m_Modules_Field.get(bundle); + currentModuleImpl = moduleArray[moduleArray.length - 1]; + } + catch (Throwable t2) + { + try + { + List moduleArray = (List) Felix_BundleImpl_m_Modules_Field.get(bundle); + currentModuleImpl = moduleArray.get(moduleArray.size() - 1); + } + catch (Exception e) + { + LOG.warn(e); + return null; + } + } + + if (Felix_ModuleImpl_m_ClassLoader_Field == null && currentModuleImpl != null) + { + try + { + Felix_ModuleImpl_m_ClassLoader_Field = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.ModuleImpl").getDeclaredField("m_classLoader"); + Felix_ModuleImpl_m_ClassLoader_Field.setAccessible(true); + } + catch (Exception e) + { + LOG.warn(e); + return null; + } + } + + // first make sure that the classloader is ready: + // the m_classLoader field must be initialized by the + // ModuleImpl.getClassLoader() private method. + ClassLoader cl = null; + try + { + cl = (ClassLoader) Felix_ModuleImpl_m_ClassLoader_Field.get(currentModuleImpl); + if (cl != null) + return cl; + } + catch (Exception e) + { + LOG.warn(e); + return null; + } + + // looks like it was not ready: + // the m_classLoader field must be initialized by the + // ModuleImpl.getClassLoader() private method. + // this call will do that. + try + { + bundle.loadClass("java.lang.Object"); + cl = (ClassLoader) Felix_ModuleImpl_m_ClassLoader_Field.get(currentModuleImpl); + return cl; + } + catch (Exception e) + { + LOG.warn(e); + return null; + } + } catch (Exception e) { LOG.warn(e); return null; } } - - if (Felix_ModuleImpl_m_classLoader_field == null && currentModuleImpl != null) - { - try - { - Felix_ModuleImpl_m_classLoader_field = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.ModuleImpl").getDeclaredField("m_classLoader"); - Felix_ModuleImpl_m_classLoader_field.setAccessible(true); - } - catch (ClassNotFoundException e) - { - LOG.warn(e); - return null; - } - catch (NoSuchFieldException e) - { - LOG.warn(e); - return null; - } - } - // first make sure that the classloader is ready: - // the m_classLoader field must be initialized by the - // ModuleImpl.getClassLoader() private method. - ClassLoader cl = null; - try - { - cl = (ClassLoader) Felix_ModuleImpl_m_classLoader_field.get(currentModuleImpl); - if (cl != null) - return cl; - } - catch (Exception e) - { - LOG.warn(e); - return null; - } - // looks like it was not ready: - // the m_classLoader field must be initialized by the - // ModuleImpl.getClassLoader() private method. - // this call will do that. - try - { - bundle.loadClass("java.lang.Object"); - cl = (ClassLoader) Felix_ModuleImpl_m_classLoader_field.get(currentModuleImpl); - return cl; - } - catch (Exception e) - { - LOG.warn(e); - return null; - } - } - - - private static void initFelix403 (Bundle bundle) - { - //see if the version of Felix is a new one - if (isFelix403 == null) - { - try - { - Class bundleImplClazz = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.BundleImpl"); - Felix_bundleWiringClazz = bundle.getClass().getClassLoader().loadClass("org.osgi.framework.wiring.BundleWiring"); - Felix_adapt_method = bundleImplClazz.getDeclaredMethod("adapt", new Class[] {Class.class}); - Felix_adapt_method.setAccessible(true); - Felix_bundle_wiring_getClassLoader_method = Felix_bundleWiringClazz.getDeclaredMethod("getClassLoader"); - Felix_bundle_wiring_getClassLoader_method.setAccessible(true); - isFelix403 = Boolean.TRUE; - } - catch (ClassNotFoundException e) - { - LOG.warn("Felix 4.x classes not found in environment"); - isFelix403 = Boolean.FALSE; - } - catch (NoSuchMethodException e) - { - LOG.warn("Felix 4.x classes not found in environment"); - isFelix403 = Boolean.FALSE; - } - } + LOG.warn("No classloader for felix platform for bundle "+bundle.getSymbolicName()); + return null; } } diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java index c1d3bcaa732..fed6f8b6cf4 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java @@ -63,7 +63,25 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper // DirZipBundleEntry private static Field ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE = null;// ZipFile + + private static final String[] FILE_BUNDLE_ENTRY_CLASSES = {"org.eclipse.osgi.baseadaptor.bundlefile.FileBundleEntry","org.eclipse.osgi.storage.bundlefile.FileBundleEntry"}; + private static final String[] ZIP_BUNDLE_ENTRY_CLASSES = {"org.eclipse.osgi.baseadaptor.bundlefile.ZipBundleEntry","org.eclipse.osgi.storage.bundlefile.ZipBundleEntry"}; + private static final String[] DIR_ZIP_BUNDLE_ENTRY_CLASSES = {"org.eclipse.osgi.baseadaptor.bundlefile.DirZipBundleEntry","org.eclipse.osgi.storage.bundlefile.DirZipBundleEntry"}; + private static final String[] BUNDLE_URL_CONNECTION_CLASSES = {"org.eclipse.osgi.framework.internal.core.BundleURLConnection", "org.eclipse.osgi.storage.url.BundleURLConnection"}; + + public static boolean match (String name, String... names) + { + if (name == null || names == null) + return false; + boolean matched = false; + for (int i=0; i< names.length && !matched; i++) + if (name.equals(names[i])) + matched = true; + return matched; + } + + /** * Works with equinox, felix, nuxeo and probably more. Not exactly in the * spirit of OSGi but quite necessary to support self-contained webapps and @@ -107,7 +125,8 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper BUNDLE_ENTRY_FIELD.setAccessible(true); } Object bundleEntry = BUNDLE_ENTRY_FIELD.get(con); - if (bundleEntry.getClass().getName().equals("org.eclipse.osgi.baseadaptor.bundlefile.FileBundleEntry")) + + if (match(bundleEntry.getClass().getName(), FILE_BUNDLE_ENTRY_CLASSES)) { if (FILE_FIELD == null) { @@ -117,7 +136,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper File f = (File) FILE_FIELD.get(bundleEntry); return f.getParentFile().getParentFile(); } - else if (bundleEntry.getClass().getName().equals("org.eclipse.osgi.baseadaptor.bundlefile.ZipBundleEntry")) + else if (match(bundleEntry.getClass().getName(), ZIP_BUNDLE_ENTRY_CLASSES)) { url = bundle.getEntry("/"); @@ -144,7 +163,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper ZipFile zipFile = (ZipFile) ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE.get(zipBundleFile); return new File(zipFile.getName()); } - else if (bundleEntry.getClass().getName().equals("org.eclipse.osgi.baseadaptor.bundlefile.DirZipBundleEntry")) + else if (match (bundleEntry.getClass().getName(), DIR_ZIP_BUNDLE_ENTRY_CLASSES)) { // that will not happen as we did ask for the manifest not a // directory. @@ -309,7 +328,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper URLConnection conn = url.openConnection(); conn.setDefaultUseCaches(Resource.getDefaultUseCaches()); - if (BUNDLE_URL_CONNECTION_getLocalURL == null && conn.getClass().getName().equals("org.eclipse.osgi.framework.internal.core.BundleURLConnection")) + if (BUNDLE_URL_CONNECTION_getLocalURL == null && match(conn.getClass().getName(), BUNDLE_URL_CONNECTION_CLASSES)) { BUNDLE_URL_CONNECTION_getLocalURL = conn.getClass().getMethod("getLocalURL", null); BUNDLE_URL_CONNECTION_getLocalURL.setAccessible(true); @@ -340,7 +359,9 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper URLConnection conn = url.openConnection(); conn.setDefaultUseCaches(Resource.getDefaultUseCaches()); - if (BUNDLE_URL_CONNECTION_getFileURL == null && conn.getClass().getName().equals("org.eclipse.osgi.framework.internal.core.BundleURLConnection")) + if (BUNDLE_URL_CONNECTION_getFileURL == null + && + match (conn.getClass().getName(), BUNDLE_URL_CONNECTION_CLASSES)) { BUNDLE_URL_CONNECTION_getFileURL = conn.getClass().getMethod("getFileURL", null); BUNDLE_URL_CONNECTION_getFileURL.setAccessible(true); diff --git a/jetty-osgi/jetty-osgi-httpservice/pom.xml b/jetty-osgi/jetty-osgi-httpservice/pom.xml index 5c97b3931a5..91087d7bcca 100644 --- a/jetty-osgi/jetty-osgi-httpservice/pom.xml +++ b/jetty-osgi/jetty-osgi-httpservice/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.osgi jetty-osgi-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-httpservice @@ -28,6 +28,7 @@ org.eclipse.osgi org.eclipse.osgi + provided javax.servlet diff --git a/jetty-osgi/jetty-osgi-npn/pom.xml b/jetty-osgi/jetty-osgi-npn/pom.xml index b05ac1dc90d..db524d9a3c1 100644 --- a/jetty-osgi/jetty-osgi-npn/pom.xml +++ b/jetty-osgi/jetty-osgi-npn/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.osgi jetty-osgi-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 jetty-osgi-npn diff --git a/jetty-osgi/pom.xml b/jetty-osgi/pom.xml index 7e5db89ed43..6dbd99dcff7 100644 --- a/jetty-osgi/pom.xml +++ b/jetty-osgi/pom.xml @@ -3,7 +3,7 @@ org.eclipse.jetty jetty-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT org.eclipse.jetty.osgi jetty-osgi-project @@ -22,12 +22,23 @@ jetty-osgi-boot jetty-osgi-boot-jsp jetty-osgi-boot-warurl - jetty-osgi-npn jetty-osgi-httpservice test-jetty-osgi-webapp test-jetty-osgi-context test-jetty-osgi + jetty-osgi-alpn + + + npn + + 1.7 + + + jetty-osgi-npn + + + diff --git a/jetty-osgi/test-jetty-osgi-context/pom.xml b/jetty-osgi/test-jetty-osgi-context/pom.xml index 85a0c051075..941b13bfb3c 100644 --- a/jetty-osgi/test-jetty-osgi-context/pom.xml +++ b/jetty-osgi/test-jetty-osgi-context/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.osgi jetty-osgi-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT 4.0.0 test-jetty-osgi-context @@ -21,10 +21,12 @@ org.eclipse.osgi org.eclipse.osgi + provided org.eclipse.osgi org.eclipse.osgi.services + provided org.eclipse.jetty.toolchain diff --git a/jetty-osgi/test-jetty-osgi-webapp/pom.xml b/jetty-osgi/test-jetty-osgi-webapp/pom.xml index 2e4a0c731ce..1a0f37b6655 100644 --- a/jetty-osgi/test-jetty-osgi-webapp/pom.xml +++ b/jetty-osgi/test-jetty-osgi-webapp/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.osgi jetty-osgi-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT ../pom.xml 4.0.0 @@ -21,10 +21,12 @@ org.eclipse.osgi org.eclipse.osgi + provided org.eclipse.osgi org.eclipse.osgi.services + provided diff --git a/jetty-osgi/test-jetty-osgi/pom.xml b/jetty-osgi/test-jetty-osgi/pom.xml index 239b33b1caf..cf6810a052a 100644 --- a/jetty-osgi/test-jetty-osgi/pom.xml +++ b/jetty-osgi/test-jetty-osgi/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.osgi jetty-osgi-project - 9.1.4-SNAPSHOT + 9.2.0-SNAPSHOT ../pom.xml 4.0.0 @@ -14,102 +14,36 @@ ${project.groupId}.boot.test.spdy http://download.eclipse.org/jetty/orbit/ target/distribution - 2.6.0 - 1.4.0 - 1.5.1 - 4.0.3 + 3.4.0 + 1.5.2 1.0 - 1.7.6 + 1.8.5 - - - org.ops4j.pax.swissbox - pax-swissbox-core - ${paxswissbox.version} - test - - - org.ops4j.pax.swissbox - pax-swissbox-extender - ${paxswissbox.version} - test - - - org.ops4j.pax.swissbox - pax-swissbox-lifecycle - ${paxswissbox.version} - test - - - org.ops4j.pax.swissbox - pax-swissbox-framework - ${paxswissbox.version} - test - + org.ops4j.pax.exam pax-exam ${exam.version} test - - org.apache.geronimo.specs - geronimo-atinject_1.0_spec - ${injection.bundle.version} - test - org.ops4j.pax.exam pax-exam-inject ${exam.version} test - - org.apache.aries.spifly - org.apache.aries.spifly.dynamic.bundle - 1.0.0 - test - - - - - org.ops4j.pax.exam pax-exam-container-forked ${exam.version} test - --> - - - org.ops4j.pax.exam - pax-exam-container-paxrunner - ${exam.version} - test - - - - org.ops4j.pax.runner - pax-runner-no-jcl - ${runner.version} - test - - + org.ops4j.pax.exam pax-exam-junit4 @@ -134,28 +68,81 @@ ${url.version} test + + - + + org.eclipse + osgi + 3.9.1-v20140110-1610 + test + + + org.eclipse.osgi + org.eclipse.osgi.services + test + + + + + + org.eclipse.jetty.osgi + jetty-osgi-boot + ${project.version} + test + + + org.eclipse.osgi + org.eclipse.osgi + + + org.eclipse.osgi + org.eclipse.osgi.services + + + + + org.eclipse.jetty.osgi + jetty-osgi-boot-jsp + ${project.version} + test + + + org.eclipse.osgi + org.eclipse.osgi + + + org.eclipse.osgi + org.eclipse.osgi.services + + + + + org.eclipse.jetty.toolchain + jetty-jsp-fragment + 2.3.3 + test + + + org.eclipse.jetty.osgi + jetty-httpservice + ${project.version} + test + + javax.servlet javax.servlet-api @@ -167,36 +154,55 @@ 1.1.1 test - + - org.eclipse.jetty.osgi - jetty-osgi-boot - ${project.version} - provided + org.apache.geronimo.specs + geronimo-atinject_1.0_spec + ${injection.bundle.version} + test - org.eclipse.jetty.osgi - jetty-osgi-boot-jsp - ${project.version} - provided + org.apache.aries.spifly + org.apache.aries.spifly.dynamic.bundle + 1.0.0 + test - org.eclipse.jetty.toolchain - jetty-jsp-fragment - 2.3.3 - provided + org.ow2.asm + asm + 4.1 - org.eclipse.jetty.osgi - jetty-httpservice - ${project.version} - provided + org.ow2.asm + asm-commons + 4.1 + + org.ow2.asm + asm-tree + 4.1 + + + org.eclipse.jetty jetty-annotations runtime + + + org.ow2.asm + asm + + + org.ow2.asm + asm-commons + + + org.ow2.asm + asm-tree + + org.eclipse.jetty @@ -324,14 +330,20 @@ test - org.mortbay.jetty.npn - npn-boot - ${npn.version} + org.mortbay.jetty.alpn + alpn-boot + ${alpn.version} test org.eclipse.jetty.osgi - jetty-osgi-npn + jetty-osgi-alpn + ${project.version} + test + + + org.eclipse.jetty + jetty-alpn-server ${project.version} test @@ -348,7 +360,9 @@ ${project.version} runtime + + javax.servlet servlet-api @@ -371,6 +384,9 @@ servlet runtime + +--> + org.eclipse.jetty test-jetty-webapp @@ -402,9 +418,8 @@ maven-surefire-plugin - - - -Dmortbay-npn-boot=${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn.version}/npn-boot-${npn.version}.jar + + -Dmortbay-alpn-boot=${settings.localRepository}/org/mortbay/jetty/alpn/alpn-boot/${alpn.version}/alpn-boot-${alpn.version}.jar diff --git a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-spdy.xml b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-spdy.xml index 8c06a2cc258..2bc1fe3cecb 100644 --- a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-spdy.xml +++ b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-spdy.xml @@ -4,7 +4,7 @@ - + https @@ -16,7 +16,7 @@ 8192 - + @@ -35,53 +35,6 @@ OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4 - - - - - - - - - - - - - - - - - - - - - - - - - 5000 - 32 - - @@ -95,7 +48,7 @@ - npn + alpn @@ -103,7 +56,7 @@ - + spdy/3 @@ -124,8 +77,6 @@ 65536 - - diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java index 94ecd39df48..0dfc1bb0e18 100644 --- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java +++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java @@ -37,12 +37,13 @@ import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.osgi.boot.OSGiServerConstants; import org.eclipse.jetty.server.handler.ContextHandler; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.ops4j.pax.exam.CoreOptions; import org.ops4j.pax.exam.Option; -import org.ops4j.pax.exam.junit.Configuration; -import org.ops4j.pax.exam.junit.JUnit4TestRunner; +import org.ops4j.pax.exam.Configuration; +import org.ops4j.pax.exam.junit.PaxExam; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; @@ -55,12 +56,11 @@ import org.osgi.framework.ServiceReference; * Tests the ServiceContextProvider. * */ -@RunWith(JUnit4TestRunner.class) +@RunWith(PaxExam.class) public class TestJettyOSGiBootContextAsService { - private static final boolean LOGGING_ENABLED = false; + private static final String LOG_LEVEL = "WARN"; - private static final boolean REMOTE_DEBUGGING = false; @Inject BundleContext bundleContext = null; @@ -69,7 +69,6 @@ public class TestJettyOSGiBootContextAsService public static Option[] configure() { ArrayList