Issue #1859 - Jetty http2 client idle_timeout when trying to get the session after connected to Jetty HTTP2 server.

Implemented an integration test case that shows
HTTP2Client usage from within a web application.
This commit is contained in:
Simone Bordet 2017-09-30 22:16:45 +02:00
parent 3eaba140ef
commit a6e28b5ce0
8 changed files with 476 additions and 0 deletions

View File

@ -38,5 +38,6 @@
<module>test-servlet-spec</module>
<module>test-jaas-webapp</module>
<module>test-jndi-webapp</module>
<module>test-http2-webapp</module>
</modules>
</project>

View File

@ -0,0 +1,175 @@
<?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">
<parent>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-webapps-parent</artifactId>
<version>9.4.8-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-http2-webapp</artifactId>
<name>Test :: Jetty HTTP2 Webapp</name>
<packaging>war</packaging>
<properties>
<bundle-symbolic-name>${project.groupId}.http2</bundle-symbolic-name>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-webapp</id>
<phase>pre-integration-test</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<type>war</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/webapp</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>jdk8</id>
<activation>
<jdk>[1.8,1.9)</jdk>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-alpn-boot</id>
<phase>pre-integration-test</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.mortbay.jetty.alpn</groupId>
<artifactId>alpn-boot</artifactId>
<version>${alpn.version}</version>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/alpn</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<argLine>-Xbootclasspath/p:${project.build.directory}/alpn/alpn-boot-${alpn.version}.jar</argLine>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-openjdk8-client</artifactId>
<version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty.alpn</groupId>
<artifactId>alpn-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-openjdk8-server</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
<profile>
<id>jdk9</id>
<activation>
<jdk>[1.9,)</jdk>
</activation>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-java-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-java-server</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-client</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-server</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,138 @@
//
// ========================================================================
// Copyright (c) 1995-2017 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.test.webapp;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.ssl.SslContextFactory;
public class HTTP1Servlet extends HttpServlet
{
private SslContextFactory sslContextFactory;
private HTTP2Client http2Client;
@Override
public void init() throws ServletException
{
try
{
sslContextFactory = new SslContextFactory(true);
http2Client = new HTTP2Client();
http2Client.addBean(sslContextFactory);
http2Client.start();
}
catch (Exception x)
{
throw new ServletException(x);
}
}
@Override
public void destroy()
{
try
{
http2Client.stop();
}
catch (Exception x)
{
x.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String host = "localhost";
int port = request.getServerPort();
String contextPath = request.getContextPath();
ServletOutputStream output = response.getOutputStream();
AsyncContext asyncContext = request.startAsync();
http2Client.connect(sslContextFactory, new InetSocketAddress(host, port), new Session.Listener.Adapter(), new Promise<Session>()
{
@Override
public void succeeded(Session session)
{
HttpURI uri = new HttpURI(request.getScheme(), host, port, contextPath + "/h2");
MetaData.Request metaData = new MetaData.Request(HttpMethod.GET.asString(), uri, HttpVersion.HTTP_2, new HttpFields());
HeadersFrame frame = new HeadersFrame(metaData, null, true);
session.newStream(frame, new Promise.Adapter<Stream>()
{
@Override
public void failed(Throwable x)
{
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
response.setHeader("X-Failure", "stream");
asyncContext.complete();
}
}, new Stream.Listener.Adapter()
{
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
try
{
ByteBuffer buffer = frame.getData();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
output.write(bytes);
callback.succeeded();
if (frame.isEndStream())
asyncContext.complete();
}
catch (IOException x)
{
asyncContext.complete();
}
}
});
}
@Override
public void failed(Throwable x)
{
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
response.setHeader("X-Failure", "session");
asyncContext.complete();
}
});
}
}

View File

@ -0,0 +1,35 @@
//
// ========================================================================
// Copyright (c) 1995-2017 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.test.webapp;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HTTP2Servlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
response.getOutputStream().print("ok");
}
}

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>h1</servlet-name>
<servlet-class>org.eclipse.jetty.test.webapp.HTTP1Servlet</servlet-class>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>h1</servlet-name>
<url-pattern>/h1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>h2</servlet-name>
<servlet-class>org.eclipse.jetty.test.webapp.HTTP2Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>h2</servlet-name>
<url-pattern>/h2</url-pattern>
</servlet-mapping>
</web-app>

View File

@ -0,0 +1,96 @@
//
// ========================================================================
// Copyright (c) 1995-2017 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.test.webapp;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http2.HTTP2Cipher;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.Assert;
import org.junit.Test;
public class HTTP2FromWebAppIT
{
@Test
public void testHTTP2FromWebApp() throws Exception
{
Server server = new Server();
SslContextFactory serverTLS = new SslContextFactory();
serverTLS.setKeyStorePath("src/test/resources/keystore.jks");
serverTLS.setKeyStorePassword("storepwd");
serverTLS.setCipherComparator(new HTTP2Cipher.CipherComparator());
HttpConfiguration httpsConfig = new HttpConfiguration();
httpsConfig.addCustomizer(new SecureRequestCustomizer());
HttpConnectionFactory h1 = new HttpConnectionFactory(httpsConfig);
ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
alpn.setDefaultProtocol(h1.getProtocol());
SslConnectionFactory ssl = new SslConnectionFactory(serverTLS, alpn.getProtocol());
HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpsConfig);
ServerConnector connector = new ServerConnector(server, ssl, alpn, h2, h1);
server.addConnector(connector);
String contextPath = "/http2_from_webapp";
WebAppContext context = new WebAppContext("target/webapp", contextPath);
server.setHandler(context);
server.start();
try
{
SslContextFactory clientTLS = new SslContextFactory(true);
HttpClient client = new HttpClient(clientTLS);
client.start();
try
{
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(HttpScheme.HTTPS.asString())
.path(contextPath + "/h1")
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals("ok", response.getContentAsString());
}
finally
{
client.stop();
}
}
finally
{
server.stop();
}
}
}

View File

@ -0,0 +1,3 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.eclipse.jetty.LEVEL=INFO
#org.eclipse.jetty.alpn.LEVEL=DEBUG