Merged branch 'master' into '431642'.

This commit is contained in:
Simone Bordet 2014-04-18 15:08:53 +02:00
commit 04f4e6fb5b
300 changed files with 10918 additions and 3463 deletions

View File

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

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

95
apache-jsp/pom.xml Normal file
View File

@ -0,0 +1,95 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apache-jsp</artifactId>
<name>Jetty :: Apache JSP</name>
<url>http://www.eclipse.org/jetty</url>
<packaging>jar</packaging>
<properties>
<bundle-symbolic-name>${project.groupId}.${project.artifactId}</bundle-symbolic-name>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>generate-manifest</id>
<goals>
<goal>manifest</goal>
</goals>
<configuration>
<instructions>
<Export-Package>org.eclipse.jetty.apache.jsp.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"</Export-Package>
<Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)"</Require-Capability>
<Provide-Capability>osgi.serviceloader; osgi.serviceloader=javax.servlet.ServletContainerInitializer</Provide-Capability>
<_nouses>true</_nouses>
</instructions>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptorRefs>
<descriptorRef>config</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Schemas -->
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-schemas</artifactId>
</dependency>
<!-- servlet api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<!-- JSP Api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
</dependency>
<!-- JSP Impl -->
<dependency>
<groupId>org.mortbay.jasper</groupId>
<artifactId>apache-jsp</artifactId>
</dependency>
<!-- Eclipse Java Compiler (for JSP Compilation) -->
<dependency>
<groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>org.eclipse.jdt.core</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,10 @@
#
# Apache JSP Module
#
[name]
jsp-impl
[lib]
lib/apache-jsp/*.jar

View File

@ -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<String> 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<URL> tldUrls = (Collection<URL>)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);
}
}

View File

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

View File

@ -0,0 +1 @@
org.eclipse.jetty.apache.jsp.JettyJasperInitializer

View File

@ -0,0 +1 @@
org.eclipse.jetty.apache.jsp.JuliLog

49
apache-jstl/pom.xml Normal file
View File

@ -0,0 +1,49 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apache-jstl</artifactId>
<name>Apache :: JSTL module</name>
<url>http://tomcat.apache.org/taglibs/standard/</url>
<packaging>jar</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptorRefs>
<descriptorRef>config</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<!-- JSTL Api -->
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-spec</artifactId>
</dependency>
<!-- JSTL Impl -->
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-impl</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,8 @@
#
# Apache JSTL
#
[name]
jstl-impl
[lib]
lib/apache-jstl/*.jar

View File

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

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>example-async-rest</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty.example-async-rest</groupId>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>example-async-rest</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty.example-async-rest</groupId>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.examples</groupId>
<artifactId>examples-parent</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.examples</groupId>
<artifactId>examples-parent</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -21,7 +21,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.eclipse.jetty.examples</groupId>

View File

@ -0,0 +1,76 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-parent</artifactId>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-alpn-client</artifactId>
<name>Jetty :: ALPN Client</name>
<description>Jetty ALPN client services</description>
<url>http://www.eclipse.org/jetty</url>
<properties>
<bundle-symbolic-name>${project.groupId}.alpn.client</bundle-symbolic-name>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>manifest</goal>
</goals>
<configuration>
<instructions>
<Import-Package>org.eclipse.jetty.alpn;resolution:=optional</Import-Package>
</instructions>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<!-- always include the sources to be able to prepare the eclipse-jetty-SDK feature
with a snapshot. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<configuration>
<onlyAnalyze>org.eclipse.jetty.alpn.*</onlyAnalyze>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.alpn</groupId>
<artifactId>alpn-api</artifactId>
<version>${alpn.api.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

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

View File

@ -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<String, Object> context) throws IOException
{
return new ALPNClientConnection(endPoint, executor, getClientConnectionFactory(),
(SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY), context, protocol);
}
}

View File

@ -0,0 +1,94 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-parent</artifactId>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-alpn-server</artifactId>
<name>Jetty :: ALPN Server</name>
<description>Jetty ALPN server services</description>
<url>http://www.eclipse.org/jetty</url>
<properties>
<bundle-symbolic-name>${project.groupId}.alpn.server</bundle-symbolic-name>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>manifest</goal>
</goals>
<configuration>
<instructions>
<Import-Package>org.eclipse.jetty.alpn,*</Import-Package>
<_nouses>true</_nouses>
</instructions>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptorRefs>
<descriptorRef>config</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
<!-- always include the sources to be able to prepare the eclipse-jetty-SDK feature
with a snapshot. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<configuration>
<onlyAnalyze>org.eclipse.jetty.alpn.*</onlyAnalyze>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.alpn</groupId>
<artifactId>alpn-api</artifactId>
<version>${alpn.api.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure id="protonego" class="org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory">
<Arg name="protocols">
<Array type="String">
<Item>spdy/3</Item>
<Item>spdy/2</Item>
<Item>http/1.1</Item>
</Array>
</Arg>
<Set name="defaultProtocol">http/1.1</Set>
</Configure>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<String> protocols, String defaultProtocol)
{
super(connector, endPoint, engine, protocols, defaultProtocol);
ALPN.put(engine, this);
}
@Override
public void unsupported()
{
select(Collections.<String>emptyList());
}
@Override
public String select(List<String> clientProtocols)
{
List<String> 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();
}
}

View File

@ -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<String> protocols, String defaultProtocol)
{
return new ALPNServerConnection(connector, endPoint, engine, protocols, defaultProtocol);
}
}

17
jetty-alpn/pom.xml Normal file
View File

@ -0,0 +1,17 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-alpn-parent</artifactId>
<packaging>pom</packaging>
<name>Jetty :: ALPN :: Parent</name>
<description>Jetty ALPN services parent</description>
<url>http://www.eclipse.org/jetty</url>
<modules>
<module>jetty-alpn-server</module>
<module>jetty-alpn-client</module>
</modules>
</project>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-annotations</artifactId>
@ -43,8 +43,8 @@
</goals>
<configuration>
<instructions>
<Import-Package>javax.servlet.*;version="[2.6.0,3.2)",*</Import-Package>
<Require-Capability>osgi.serviceloader; filter:="(osgi.serviceloader=javax.servlet.ServletContainerInitializer)";cardinality:=multiple, osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)"</Require-Capability>
<Import-Package>javax.servlet.*;version="[2.6.0,3.2)",org.objectweb.asm.*;version=4,*</Import-Package>
<Require-Capability>osgi.serviceloader; filter:="(osgi.serviceloader=javax.servlet.ServletContainerInitializer)";resolution:=optional;cardinality:=multiple, osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)"</Require-Capability>
</instructions>
</configuration>
</execution>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-ant</artifactId>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

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

View File

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

View File

@ -62,7 +62,7 @@ public abstract class HttpSender implements AsyncContentProvider.Listener
private final AtomicReference<RequestState> requestState = new AtomicReference<>(RequestState.QUEUED);
private final AtomicReference<SenderState> 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();
}

View File

@ -129,6 +129,15 @@ public abstract class MultiplexHttpDestination<C extends Connection> 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)
{

View File

@ -140,8 +140,11 @@ public abstract class PoolingHttpDestination<C extends Connection> 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())

View File

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

View File

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

View File

@ -38,6 +38,7 @@ import org.eclipse.jetty.util.BufferUtil;
public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
{
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

View File

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

View File

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

View File

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

View File

@ -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.<Response.ResponseListener>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());
// }
}

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-continuation</artifactId>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-deploy</artifactId>

View File

@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<artifactId>jetty-distribution</artifactId>
<name>Jetty :: Distribution Assemblies</name>
@ -302,7 +302,7 @@
<configuration>
<includeGroupIds>org.eclipse.jetty</includeGroupIds>
<excludeGroupIds>org.eclipse.jetty.orbit,org.eclipse.jetty.spdy,org.eclipse.jetty.websocket,org.eclipse.jetty.fcgi,org.eclipse.jetty.toolchain,org.apache.taglibs</excludeGroupIds>
<excludeArtifactIds>jetty-all,jetty-jsp,jetty-start,jetty-monitor</excludeArtifactIds>
<excludeArtifactIds>jetty-all,jetty-jsp,apache-jsp,jetty-start,jetty-monitor</excludeArtifactIds>
<includeTypes>jar</includeTypes>
<outputDirectory>${assembly-directory}/lib</outputDirectory>
</configuration>
@ -457,6 +457,20 @@
<outputDirectory>${assembly-directory}/lib/jsp</outputDirectory>
</configuration>
</execution>
<execution>
<id>copy-apache-jsp-deps</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeGroupIds>org.eclipse.jetty,org.eclipse.jetty.toolchain,javax.servlet.jsp,org.mortbay.jasper,org.mortbay.jasper,org.eclipse.jetty.orbit</includeGroupIds>
<includeArtifactIds>apache-jsp,javax.servlet.jsp-api,apache-el,org.eclipse.jdt.core</includeArtifactIds>
<includeTypes>jar</includeTypes>
<prependGroupId>true</prependGroupId>
<outputDirectory>${assembly-directory}/lib/apache-jsp</outputDirectory>
</configuration>
</execution>
<execution>
<id>copy-jstl-api</id>
<phase>generate-resources</phase>
@ -484,6 +498,20 @@
<outputDirectory>${assembly-directory}/lib/jsp</outputDirectory>
</configuration>
</execution>
<execution>
<id>copy-apache-jstl-deps</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<excludeGroupIds>org.glassfish.web</excludeGroupIds>
<includeArtifactIds>taglibs-standard-spec,taglibs-standard-impl</includeArtifactIds>
<prependGroupId>true</prependGroupId>
<includeTypes>jar</includeTypes>
<outputDirectory>${assembly-directory}/lib/apache-jstl</outputDirectory>
</configuration>
</execution>
<execution>
<id>copy-jaspi-deps</id>
<phase>generate-resources</phase>
@ -526,7 +554,7 @@
<argument>jetty.home=${assembly-directory}</argument>
<argument>jetty.base=${assembly-directory}</argument>
<argument>--add-to-start=server,deploy,websocket,ext,resources</argument>
<argument>--add-to-startd=jsp,http</argument>
<argument>--add-to-startd=jsp,jstl,http</argument>
</arguments>
</configuration>
<goals>
@ -541,8 +569,8 @@
<arguments>
<argument>jetty.home=${assembly-directory}</argument>
<argument>jetty.base=${assembly-directory}/demo-base</argument>
<argument>--add-to-start=server,continuation,deploy,ext,resources,client,annotations,jndi,servlets</argument>
<argument>--add-to-startd-ini=jsp,http,https</argument>
<argument>--add-to-start=server,continuation,deploy,websocket,ext,resources,client,annotations,jndi,servlets</argument>
<argument>--add-to-startd-ini=jsp,jstl,http,https</argument>
</arguments>
</configuration>
<goals>
@ -667,6 +695,11 @@
<artifactId>jetty-monitor</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-quickstart</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-start</artifactId>
@ -697,6 +730,16 @@
<artifactId>jetty-jsp</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>apache-jsp</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>apache-jstl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-plus</artifactId>
@ -765,6 +808,11 @@
<version>${project.version}</version>
<type>war</type>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.example-async-rest</groupId>
<artifactId>example-async-rest-webapp</artifactId>

View File

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

View File

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

View File

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

View File

@ -1,11 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty.fcgi</groupId>
<artifactId>fcgi-parent</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

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

View File

@ -69,7 +69,7 @@ public class HttpClientTransportOverFCGI extends AbstractHttpClientTransport
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> 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<Connection> promise = (Promise<Connection>)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);

View File

@ -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<HttpConnectionOverFCGI> fcgiDestination =
(PoolingHttpDestination<HttpConnectionOverFCGI>)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
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>fcgi-parent</artifactId>
<groupId>org.eclipse.jetty.fcgi</groupId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>org.eclipse.jetty.fcgi</groupId>
<artifactId>fcgi-parent</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>fcgi-parent</artifactId>
<groupId>org.eclipse.jetty.fcgi</groupId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -1,11 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.eclipse.jetty.fcgi</groupId>
<artifactId>fcgi-parent</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

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

View File

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

View File

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

View File

@ -1,11 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-http-spi</artifactId>

View File

@ -3,7 +3,7 @@
<parent>
<artifactId>jetty-project</artifactId>
<groupId>org.eclipse.jetty</groupId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-http</artifactId>

View File

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

View File

@ -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<buffer.limit())
@ -715,8 +739,7 @@ public class HttpParser
if (_connectionFields==null && _version.getVersion()>=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<T> extends HttpHandler<T>
{
/**
@ -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();
}
}

View File

@ -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<ByteBuffer>, HttpParser.ResponseHandler<ByteBuffer>
private class Handler implements HttpParser.RequestHandler<ByteBuffer>, HttpParser.ResponseHandler<ByteBuffer>, 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;
}
}
}

View File

@ -2,7 +2,7 @@
<parent>
<artifactId>jetty-project</artifactId>
<groupId>org.eclipse.jetty</groupId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-io</artifactId>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-jaas</artifactId>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-jaspi</artifactId>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-jmx</artifactId>

View File

@ -2,6 +2,9 @@
# JMX Module
#
[depend]
server
[lib]
lib/jetty-jmx-${jetty.version}.jar

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-jndi</artifactId>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-jsp</artifactId>

View File

@ -0,0 +1,8 @@
#
# Glassfish JSP Module
#
[name]
jsp-impl
[lib]
lib/jsp/*.jar

View File

@ -0,0 +1,6 @@
#
# Glassfish JSTL
[name]
jstl-impl
# This file is empty as glassfish jstl is provided by glassfish jsp

View File

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

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-jspc-maven-plugin</artifactId>
@ -72,9 +72,19 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>apache-jsp</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.8.4</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jsp</artifactId>
<artifactId>apache-jstl</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>

View File

@ -79,6 +79,7 @@ import org.eclipse.jetty.util.resource.Resource;
public class JspcMojo extends AbstractMojo
{
public static final String END_OF_WEBAPP = "</web-app>";
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("<context-param><param-name>"+PRECOMPILED_FLAG+"</param-name><param-value>true</param-value></context-param>");
// put in the generated fragment
try (BufferedReader fragmentWebXmlReader = new BufferedReader(
@ -541,13 +546,16 @@ public class JspcMojo extends AbstractMojo
*/
private List<URL> getSystemJarsWithTlds() throws Exception
{
getLog().debug("tld pattern=" + tldJarNamePatterns);
final List<URL> list = new ArrayList<URL>();
List<URI> artifactUris = new ArrayList<URI>();
Pattern pattern = Pattern.compile(tldJarNamePatterns);
for (Iterator<Artifact> 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()

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-maven-plugin</artifactId>
@ -122,19 +122,19 @@
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jsp</artifactId>
<artifactId>apache-jsp</artifactId>
<version>${project.version}</version>
</dependency>
<!-- dependency>
<groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>javax.activation</artifactId>
<scope>compile</scope>
</dependency -->
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>apache-jstl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
<reporting>
<plugins>

View File

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

View File

@ -19,7 +19,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-monitor</artifactId>

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.4-SNAPSHOT</version>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-nosql</artifactId>

View File

@ -0,0 +1,47 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
<version>9.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-osgi-alpn</artifactId>
<name>Jetty :: OSGi ALPN Fragment</name>
<packaging>jar</packaging>
<properties>
<bundle-symbolic-name>org.eclipse.jetty.osgi.alpn.fragment</bundle-symbolic-name>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>parse-version</id>
<goals>
<goal>parse-version</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Bundle-ManifestVersion>2</Bundle-ManifestVersion>
<Bundle-SymbolicName>${bundle-symbolic-name};singleton:=true</Bundle-SymbolicName>
<Bundle-Name>Jetty OSGi ALPN Fragment</Bundle-Name>
<Bundle-Version>${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}</Bundle-Version>
<Export-Package>org.eclipse.jetty.alpn;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"</Export-Package>
<Fragment-Host>system.bundle;extension:=framework</Fragment-Host>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>

Some files were not shown because too many files have changed in this diff Show More