Merged branch 'master' into '431642'.
This commit is contained in:
commit
04f4e6fb5b
46
VERSION.txt
46
VERSION.txt
|
@ -1,4 +1,48 @@
|
|||
jetty-9.1.4-SNAPSHOT
|
||||
jetty-9.2.0-SNAPSHOT
|
||||
|
||||
jetty-9.1.4.v20140401 - 01 April 2014
|
||||
+ 414206 Rewrite rules re-encode requestURI
|
||||
+ 414885 Don't expose JDT classes by default
|
||||
+ 417022 Access current HttpConnection from Request not ThreadLocal
|
||||
+ 423619 set Request timestamp on startRequest
|
||||
+ 423982 removed duplicate UrlResource toString
|
||||
+ 424107 Jetty should not finish chunked encoding on exception.
|
||||
+ 425991 added qml mime type
|
||||
+ 426897 improved ContainerLifeCycle javadoc
|
||||
+ 427185 Add org.objectweb.asm. as serverClass
|
||||
+ 427204 jetty-start / startup incorrectly requires directory in jetty.base
|
||||
+ 427368 start.sh fails quietly on command line error
|
||||
+ 428594 File upload with onMessage and InputStream fails
|
||||
+ 428595 JSR-356 / ClientContainer does not support SSL
|
||||
+ 428597 javax-websocket-client-impl and javax-websocket-server-impl jars
|
||||
Manifests do not export packages for OSGI
|
||||
+ 428817 jetty-start / Allow for property to configure deploy manager
|
||||
`webapps` directory
|
||||
+ 429180 Make requestlog filename parameterized
|
||||
+ 429357 JDBCSessionManager.Session.removeAttribute don't set dirty flag if
|
||||
attribute already removed
|
||||
+ 429409 osgi] jetty.websocket.servlet must import jetty.websocket.server
|
||||
+ 429487 Runner code cleanups
|
||||
+ 429616 Use UTF-8 encoding for XML
|
||||
+ 429779 masked zero length websocket frame gives NullPointerException during
|
||||
streaming read
|
||||
+ 430088 OnMessage*Callable decoding of streaming binary or text is not thread
|
||||
safe
|
||||
+ 430242 added SharedBlockingCallback to support threadsafe blocking
|
||||
+ 430273 Cancel async timeout breaks volatile link to avoid race with slow
|
||||
expire
|
||||
+ 430341 add apache jsp and jstl optional modules
|
||||
+ 430490 Added JETTY_SHELL 426738 Fixed JETTY_HOME comments
|
||||
+ 430649 test form encoding
|
||||
+ 430654 closing client connections can hang worker threads.
|
||||
+ 430808 OutputStreamContentProvider violates OutputStream contract.
|
||||
+ 430822 jetty-start / make soLingerTime configurable via property
|
||||
+ 430823 jetty-start / make NeedClientAuth (ssl) configurable via property
|
||||
+ 430824 jetty-start / use of jetty-logging.xml prevents configuration of
|
||||
ThreadPool in jetty.xml
|
||||
+ 431103 Complete listener not called if request times out before processing
|
||||
exchange.
|
||||
+ 431592 do not resolved forwarded-for address
|
||||
|
||||
jetty-9.1.3.v20140225 - 25 February 2014
|
||||
+ 373952 Ensure MongoSessionManager un/binds session attributes on refresh
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -0,0 +1,10 @@
|
|||
#
|
||||
# Apache JSP Module
|
||||
#
|
||||
|
||||
[name]
|
||||
jsp-impl
|
||||
|
||||
[lib]
|
||||
lib/apache-jsp/*.jar
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
org.eclipse.jetty.apache.jsp.JettyJasperInitializer
|
|
@ -0,0 +1 @@
|
|||
org.eclipse.jetty.apache.jsp.JuliLog
|
|
@ -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>
|
|
@ -0,0 +1,8 @@
|
|||
#
|
||||
# Apache JSTL
|
||||
#
|
||||
[name]
|
||||
jstl-impl
|
||||
|
||||
[lib]
|
||||
lib/apache-jstl/*.jar
|
|
@ -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.
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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/
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
// }
|
||||
}
|
|
@ -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));
|
||||
// }
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
|
@ -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
|
||||
{
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
# JMX Module
|
||||
#
|
||||
|
||||
[depend]
|
||||
server
|
||||
|
||||
[lib]
|
||||
lib/jetty-jmx-${jetty.version}.jar
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#
|
||||
# Glassfish JSP Module
|
||||
#
|
||||
[name]
|
||||
jsp-impl
|
||||
|
||||
[lib]
|
||||
lib/jsp/*.jar
|
|
@ -0,0 +1,6 @@
|
|||
#
|
||||
# Glassfish JSTL
|
||||
[name]
|
||||
jstl-impl
|
||||
|
||||
# This file is empty as glassfish jstl is provided by glassfish jsp
|
|
@ -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
|
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue