You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by re...@apache.org on 2021/10/30 15:29:00 UTC
[cxf] branch master updated: CXF-8605: Introduce HTTP/2 Transport:
server-side support (#861)
This is an automated email from the ASF dual-hosted git repository.
reta pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/master by this push:
new 965e1bb CXF-8605: Introduce HTTP/2 Transport: server-side support (#861)
965e1bb is described below
commit 965e1bb9e9658f4024f33bf832308f8b8dfce4b6
Author: Andriy Redko <dr...@gmail.com>
AuthorDate: Sat Oct 30 11:28:48 2021 -0400
CXF-8605: Introduce HTTP/2 Transport: server-side support (#861)
---
.../samples/jax_rs/basic_http2_jetty/README.txt | 92 +++++
.../samples/jax_rs/basic_http2_jetty/pom.xml | 136 +++++++
.../src/main/config/KeyREADME.txt | 8 +
.../src/main/config/serviceKeystore.jks | Bin 0 -> 3743 bytes
.../src/main/java/http2demo/common/Customer.java | 43 +++
.../java/http2demo/common/CustomerService.java | 52 +++
.../java/http2demo/server/CustomerServiceImpl.java | 89 +++++
.../src/main/java/http2demo/server/h2/Server.java | 59 +++
.../src/main/java/http2demo/server/h2c/Server.java | 59 +++
.../src/main/resources/ServerConfig.xml | 48 +++
.../samples/jax_rs/basic_http2_netty/README.txt | 92 +++++
.../samples/jax_rs/basic_http2_netty/pom.xml | 100 +++++
.../src/main/config/KeyREADME.txt | 8 +
.../src/main/config/serviceKeystore.jks | Bin 0 -> 3742 bytes
.../src/main/java/http2demo/common/Customer.java | 43 +++
.../java/http2demo/common/CustomerService.java | 52 +++
.../java/http2demo/server/CustomerServiceImpl.java | 89 +++++
.../src/main/java/http2demo/server/h2/Server.java | 59 +++
.../src/main/java/http2demo/server/h2c/Server.java | 59 +++
.../src/main/resources/ServerConfig.xml | 48 +++
.../samples/jax_rs/basic_http2_undertow/README.txt | 93 +++++
.../samples/jax_rs/basic_http2_undertow/pom.xml | 95 +++++
.../src/main/config/KeyREADME.txt | 8 +
.../src/main/config/serviceKeystore.jks | Bin 0 -> 3742 bytes
.../src/main/java/http2demo/common/Customer.java | 43 +++
.../java/http2demo/common/CustomerService.java | 52 +++
.../java/http2demo/server/CustomerServiceImpl.java | 89 +++++
.../src/main/java/http2demo/server/h2/Server.java | 59 +++
.../src/main/java/http2demo/server/h2c/Server.java | 59 +++
.../src/main/resources/ServerConfig.xml | 48 +++
distribution/src/main/release/samples/pom.xml | 8 +-
parent/pom.xml | 10 +
rt/transports/http-jetty/pom.xml | 10 +
.../http_jetty/JettyHTTPServerEngine.java | 42 ++-
rt/transports/http-netty/netty-server/pom.xml | 6 +
.../http/netty/server/NettyHttpServerEngine.java | 27 +-
.../netty/server/NettyHttpServerEngineFactory.java | 2 +-
.../server/NettyHttpServletPipelineFactory.java | 230 +++++++++++-
.../NettyHttpServerEngineBeanDefinitionParser.java | 14 +-
.../http_undertow/UndertowHTTPServerEngine.java | 32 +-
.../transport/http/HttpServerEngineSupport.java | 47 +++
.../transport/https/SSLContextInitParameters.java | 44 +++
.../org/apache/cxf/transport/https/SSLUtils.java | 34 +-
systests/pom.xml | 1 +
systests/{transports => transport-netty}/pom.xml | 207 ++---------
.../http2/netty/AbstractBookServerHttp2.java | 64 ++++
.../netty/AbstractNettyClientServerHttp2Test.java | 118 ++++++
.../org/apache/cxf/systest/http2/netty/Book.java | 69 ++++
.../cxf/systest/http2/netty/BookServerHttp2.java | 47 +++
.../cxf/systest/http2/netty/BookServerHttp2c.java | 45 +++
.../apache/cxf/systest/http2/netty/BookStore.java | 68 ++++
.../cxf/systest/http2/netty/Http2TestClient.java | 401 +++++++++++++++++++++
.../http2/netty/NettyClientServerHttp2Test.java | 48 +++
.../http2/netty/NettyClientServerHttp2cTest.java | 47 +++
.../apache/cxf/systest/http2_netty/server-tls.xml | 41 +++
.../org/apache/cxf/systest/http2_netty/server.xml | 32 ++
.../http2/AbstractBookServerHttp2.java | 66 ++++
.../AbstractUndertowClientServerHttp2Test.java | 120 ++++++
.../http_undertow/http2/BookServerHttp2.java | 45 +++
.../http_undertow/http2/BookServerHttp2c.java | 45 +++
.../http_undertow/http2/Http2TestClient.java | 184 ++++++++++
.../http2/UndertowClientServerHttp2Test.java | 48 +++
.../http2/UndertowClientServerHttp2cTest.java | 48 +++
.../cxf/systest/http_undertow/http2/server-tls.xml | 41 +++
.../cxf/systest/http_undertow/http2/server.xml | 32 ++
systests/transports/pom.xml | 56 +++
.../http2_jetty/AbstractBookServerHttp2.java | 64 ++++
.../AbstractJettyClientServerHttp2Test.java | 119 ++++++
.../org/apache/cxf/systest/http2_jetty/Book.java | 69 ++++
.../cxf/systest/http2_jetty/BookServerHttp2.java | 47 +++
.../cxf/systest/http2_jetty/BookServerHttp2c.java | 45 +++
.../apache/cxf/systest/http2_jetty/BookStore.java | 68 ++++
.../cxf/systest/http2_jetty/Http2TestClient.java | 212 +++++++++++
.../http2_jetty/JettyClientServerHttp2Test.java | 48 +++
.../http2_jetty/JettyClientServerHttp2cTest.java | 47 +++
.../apache/cxf/systest/http2_jetty/server-tls.xml | 41 +++
.../org/apache/cxf/systest/http2_jetty/server.xml | 32 ++
77 files changed, 4610 insertions(+), 243 deletions(-)
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/README.txt b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/README.txt
new file mode 100644
index 0000000..df1bc1f
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/README.txt
@@ -0,0 +1,92 @@
+JAX-RS Basic Demo With HTTPS communications
+===========================================
+
+This demo takes the JAX-RS basic demo a step further
+by doing the communication using HTTP/2 over TLS and
+HTTP/2 over cleartext using Jetty container.
+
+Building and running the demo using Maven
+-----------------------------------------
+From the base directory of this sample (i.e., where this README file is
+located), the Maven pom.xml file can be used to build and run the demo.
+
+Using either UNIX or Windows:
+
+ mvn install
+ mvn -Ph2-server (HTTP/2 over TLS)
+ mvn -Ph2c-server (HTTP/2 over cleartext)
+
+To remove the target dir, run "mvn clean".
+
+Certificates
+------------
+See the src/main/config folder for the sample keys used (don't use
+these keys in production!) as well as scripts used for their creation.
+
+HTTP/2 over cleartext
+------------
+
+- Upgrade from HTTP/1.1 to HTTP/2 (cleartext)
+
+ $ curl http://localhost:9001/customerservice/customers/123 --http2 -i
+
+ HTTP/1.1 101 Switching Protocols
+ HTTP/2 200
+ server: Jetty(9.4.44.v20210927)
+ date: Thu, 14 Oct 2021 01:35:58 GMT
+ content-type: application/xml
+ content-length: 105
+
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Customer><id>123</id><name>John</name></Customer>
+
+- Use HTTP/2 prior knowledge (cleartext)
+
+ $ curl http://localhost:9001/customerservice/customers/123 --http2-prior-knowledge -i
+
+ HTTP/2 200
+ server: Jetty(9.4.44.v20210927)
+ date: Thu, 14 Oct 2021 01:36:44 GMT
+ content-type: application/xml
+ content-length: 105
+
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Customer><id>123</id><name>John</name></Customer>
+
+- Force HTTP/1.1 usage (cleartext)
+
+ $ curl http://localhost:9001/customerservice/customers/123 --http1.1 -i
+
+ HTTP/1.1 200 OK
+ Date: Thu, 14 Oct 2021 01:37:06 GMT
+ Content-Type: application/xml
+ Content-Length: 105
+ Server: Jetty(9.4.44.v20210927)
+
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Customer><id>123</id><name>John</name></Customer>
+
+HTTP/2 over TLS
+------------
+
+- Use HTTP/2
+
+ $ curl https://localhost:9000/customerservice/customers/123 --http2 -ik
+
+ HTTP/2 200
+ server: Jetty(9.4.44.v20210927)
+ date: Thu, 14 Oct 2021 01:25:26 GMT
+ content-type: application/xml
+ content-length: 105
+
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Customer><id>123</id><name>John</name></Customer>
+
+- Force HTTP/1.1 usage
+
+ $ curl https://localhost:9000/customerservice/customers/123 --http1.1 -ik
+
+ HTTP/1.1 200 OK
+ Date: Thu, 14 Oct 2021 01:26:06 GMT
+ Content-Type: application/xml
+ Content-Length: 105
+ Server: Jetty(9.4.44.v20210927)
+
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Customer><id>123</id><name>John</name></Customer>
+
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/pom.xml b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/pom.xml
new file mode 100644
index 0000000..f93cdf2
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/pom.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<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">
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>jax_rs_basic_http2_jetty</artifactId>
+ <name>JAX-RS Basic Demo With HTTP/2 communications</name>
+ <description>JAX-RS Basic Demo With HTTP/2 communications</description>
+ <parent>
+ <groupId>org.apache.cxf.samples</groupId>
+ <artifactId>cxf-samples</artifactId>
+ <version>3.5.0-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+ <profiles>
+ <profile>
+ <id>h2-server</id>
+ <build>
+ <defaultGoal>test</defaultGoal>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>test</phase>
+ <goals>
+ <goal>java</goal>
+ </goals>
+ <configuration>
+ <mainClass>http2demo.server.h2.Server</mainClass>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>h2c-server</id>
+ <build>
+ <defaultGoal>test</defaultGoal>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>test</phase>
+ <goals>
+ <goal>java</goal>
+ </goals>
+ <configuration>
+ <mainClass>http2demo.server.h2c.Server</mainClass>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-transports-http-jetty</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>http2-server</artifactId>
+ <version>${cxf.jetty9.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-alpn-server</artifactId>
+ <version>${cxf.jetty9.version}</version>
+ </dependency>
+ </dependencies>
+
+ <profiles>
+ <profile>
+ <id>jdk8</id>
+ <activation>
+ <jdk>1.8</jdk>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-alpn-openjdk8-server</artifactId>
+ <version>${cxf.jetty9.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ </profile>
+ <profile>
+ <id>jdk9+</id>
+ <activation>
+ <jdk>[9,)</jdk>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-alpn-java-server</artifactId>
+ <version>${cxf.jetty9.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ </profile>
+ </profiles>
+</project>
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/config/KeyREADME.txt b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/config/KeyREADME.txt
new file mode 100644
index 0000000..97f20fa
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/config/KeyREADME.txt
@@ -0,0 +1,8 @@
+# The below scripts show the commands used to generate the self-signed keys for this sample.
+# If you use the below script to create your own keys be sure to change the passwords used here
+# DO NOT USE THE SUPPLIED KEYS IN PRODUCTION--everyone has them!!
+# For production recommended to use keys signed by a third-party certificate authority (CA)
+
+# Create the combination keystore/truststore for the server.
+keytool -genkeypair -keyalg RSA -keysize 4096 -validity 730 -alias myservicekey -keystore serviceKeystore.jks -dname "cn=localhost" -keypass skpass -storepass sspass
+
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/config/serviceKeystore.jks b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/config/serviceKeystore.jks
new file mode 100644
index 0000000..7801717
Binary files /dev/null and b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/config/serviceKeystore.jks differ
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/java/http2demo/common/Customer.java b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/java/http2demo/common/Customer.java
new file mode 100644
index 0000000..81ce3c5
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/java/http2demo/common/Customer.java
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package http2demo.common;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "Customer")
+public class Customer {
+ private long id;
+ private String name;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/java/http2demo/common/CustomerService.java b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/java/http2demo/common/CustomerService.java
new file mode 100644
index 0000000..890ade3
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/java/http2demo/common/CustomerService.java
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package http2demo.common;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Response;
+
+/**
+ * This interface describes a JAX-RS root resource. All the JAXRS annotations (except those overridden) will
+ * be inherited by classes implementing it.
+ */
+@Path("/customerservice/")
+public interface CustomerService {
+
+ @GET
+ @Path("/customers/{id}/")
+ Customer getCustomer(@PathParam("id") String id);
+
+ @PUT
+ @Path("/customers/")
+ Response updateCustomer(Customer customer);
+
+ @POST
+ @Path("/customers/")
+ Response addCustomer(Customer customer);
+
+ @DELETE
+ @Path("/customers/{id}/")
+ Response deleteCustomer(@PathParam("id") String id);
+
+}
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/java/http2demo/server/CustomerServiceImpl.java b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/java/http2demo/server/CustomerServiceImpl.java
new file mode 100644
index 0000000..171d27b
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/java/http2demo/server/CustomerServiceImpl.java
@@ -0,0 +1,89 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package http2demo.server;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.core.Response;
+
+import http2demo.common.Customer;
+import http2demo.common.CustomerService;
+
+public class CustomerServiceImpl implements CustomerService {
+ long currentId = 123;
+ Map<Long, Customer> customers = new HashMap<>();
+
+ public CustomerServiceImpl() {
+ init();
+ }
+
+ public Customer getCustomer(String id) {
+ System.out.println("----invoking getCustomer, Customer id is: " + id);
+ long idNumber = Long.parseLong(id);
+ return customers.get(idNumber);
+ }
+
+ public Response updateCustomer(Customer customer) {
+ System.out.println("----invoking updateCustomer, Customer name is: " + customer.getName());
+ Customer c = customers.get(customer.getId());
+ Response r;
+ if (c != null) {
+ customers.put(customer.getId(), customer);
+ r = Response.ok().build();
+ } else {
+ r = Response.notModified().build();
+ }
+
+ return r;
+ }
+
+ public Response addCustomer(Customer customer) {
+ System.out.println("----invoking addCustomer, Customer name is: " + customer.getName());
+ customer.setId(++currentId);
+
+ customers.put(customer.getId(), customer);
+
+ return Response.ok(customer).build();
+ }
+
+ public Response deleteCustomer(String id) {
+ System.out.println("----invoking deleteCustomer, Customer id is: " + id);
+ long idNumber = Long.parseLong(id);
+ Customer c = customers.get(idNumber);
+
+ Response r;
+ if (c != null) {
+ r = Response.ok().build();
+ customers.remove(idNumber);
+ } else {
+ r = Response.notModified().build();
+ }
+
+ return r;
+ }
+
+ final void init() {
+ Customer c = new Customer();
+ c.setName("John");
+ c.setId(123);
+ customers.put(c.getId(), c);
+ }
+
+}
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/java/http2demo/server/h2/Server.java b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/java/http2demo/server/h2/Server.java
new file mode 100644
index 0000000..d821996
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/java/http2demo/server/h2/Server.java
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package http2demo.server.h2;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.bus.spring.SpringBusFactory;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.transport.http.HttpServerEngineSupport;
+
+import http2demo.server.CustomerServiceImpl;
+
+public class Server {
+
+ static {
+ // set the configuration file
+ SpringBusFactory factory = new SpringBusFactory();
+ Bus bus = factory.createBus("ServerConfig.xml");
+ bus.setProperty(HttpServerEngineSupport.ENABLE_HTTP2, true);
+ BusFactory.setDefaultBus(bus);
+ }
+
+ protected Server() throws Exception {
+ JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+ sf.setResourceClasses(CustomerServiceImpl.class);
+ sf.setResourceProvider(CustomerServiceImpl.class,
+ new SingletonResourceProvider(new CustomerServiceImpl()));
+ sf.setAddress("https://localhost:9000/");
+
+ sf.create();
+ }
+
+ public static void main(String[] args) throws Exception {
+ new Server();
+ System.out.println("Server ready...");
+
+ Thread.sleep(5 * 60 * 1000);
+ System.out.println("Server exiting");
+ System.exit(0);
+ }
+}
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/java/http2demo/server/h2c/Server.java b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/java/http2demo/server/h2c/Server.java
new file mode 100644
index 0000000..d86ef95
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/java/http2demo/server/h2c/Server.java
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package http2demo.server.h2c;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.bus.spring.SpringBusFactory;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.transport.http.HttpServerEngineSupport;
+
+import http2demo.server.CustomerServiceImpl;
+
+public class Server {
+
+ static {
+ // set the configuration file
+ SpringBusFactory factory = new SpringBusFactory();
+ Bus bus = factory.createBus();
+ bus.setProperty(HttpServerEngineSupport.ENABLE_HTTP2, true);
+ BusFactory.setDefaultBus(bus);
+ }
+
+ protected Server() throws Exception {
+ JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+ sf.setResourceClasses(CustomerServiceImpl.class);
+ sf.setResourceProvider(CustomerServiceImpl.class,
+ new SingletonResourceProvider(new CustomerServiceImpl()));
+ sf.setAddress("http://localhost:9001/");
+
+ sf.create();
+ }
+
+ public static void main(String[] args) throws Exception {
+ new Server();
+ System.out.println("Server ready...");
+
+ Thread.sleep(5 * 60 * 1000);
+ System.out.println("Server exiting");
+ System.exit(0);
+ }
+}
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/resources/ServerConfig.xml b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/resources/ServerConfig.xml
new file mode 100644
index 0000000..6ef1e90
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_jetty/src/main/resources/ServerConfig.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!--
+ ** This file configures the Server which exposes the REST endpoint.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:sec="http://cxf.apache.org/configuration/security"
+ xmlns:http="http://cxf.apache.org/transports/http/configuration"
+ xmlns:httpj="http://cxf.apache.org/transports/http-jetty/configuration"
+ xsi:schemaLocation="http://cxf.apache.org/configuration/security
+ http://cxf.apache.org/schemas/configuration/security.xsd
+ http://cxf.apache.org/transports/http/configuration
+ http://cxf.apache.org/schemas/configuration/http-conf.xsd
+ http://cxf.apache.org/transports/http-jetty/configuration
+ http://cxf.apache.org/schemas/configuration/http-jetty.xsd
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd">
+ <httpj:engine-factory bus="cxf">
+ <httpj:engine port="9000">
+ <httpj:tlsServerParameters>
+ <sec:keyManagers keyPassword="skpass">
+ <sec:keyStore file="src/main/config/serviceKeystore.jks" password="sspass" type="JKS"/>
+ </sec:keyManagers>
+ <sec:trustManagers>
+ <sec:keyStore file="src/main/config/serviceKeystore.jks" password="sspass" type="JKS"/>
+ </sec:trustManagers>
+ </httpj:tlsServerParameters>
+ </httpj:engine>
+ </httpj:engine-factory>
+</beans>
\ No newline at end of file
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_netty/README.txt b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/README.txt
new file mode 100644
index 0000000..cfc5b91
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/README.txt
@@ -0,0 +1,92 @@
+JAX-RS Basic Demo With HTTPS communications
+===========================================
+
+This demo takes the JAX-RS basic demo a step further
+by doing the communication using HTTP/2 over TLS and
+HTTP/2 over cleartext using Netty container.
+
+Building and running the demo using Maven
+-----------------------------------------
+From the base directory of this sample (i.e., where this README file is
+located), the Maven pom.xml file can be used to build and run the demo.
+
+Using either UNIX or Windows:
+
+ mvn install
+ mvn -Ph2-server (HTTP/2 over TLS)
+ mvn -Ph2c-server (HTTP/2 over cleartext)
+
+To remove the target dir, run "mvn clean".
+
+Certificates
+------------
+See the src/main/config folder for the sample keys used (don't use
+these keys in production!) as well as scripts used for their creation.
+
+HTTP/2 over cleartext
+------------
+
+- Upgrade from HTTP/1.1 to HTTP/2 (cleartext)
+
+ $ curl http://localhost:9001/customerservice/customers/123 --http2 -i
+
+ HTTP/1.1 101 Switching Protocols
+ connection: upgrade
+ upgrade: h2c
+
+ HTTP/2 200
+ content-type: application/xml
+ date: Thu, 14 Oct 2021 01:48:35 GMT
+ content-length: 105
+
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Customer><id>123</id><name>John</name></Customer>
+
+- Use HTTP/2 prior knowledge (cleartext)
+
+ $ curl http://localhost:9001/customerservice/customers/123 --http2-prior-knowledge -i
+
+ HTTP/2 200
+ content-type: application/xml
+ date: Thu, 14 Oct 2021 01:49:08 GMT
+ content-length: 105
+
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Customer><id>123</id><name>John</name></Customer>
+
+- Force HTTP/1.1 usage (cleartext)
+
+ $ curl http://localhost:9001/customerservice/customers/123 --http1.1 -i
+
+ HTTP/1.1 200 OK
+ Content-Type: application/xml
+ Date: Thu, 14 Oct 2021 01:49:35 GMT
+ content-length: 105
+ connection: keep-alive
+
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Customer><id>123</id><name>John</name></Customer>
+
+HTTP/2 over TLS
+------------
+
+- Use HTTP/2
+
+ $ curl https://localhost:9000/customerservice/customers/123 --http2 -ik
+
+ HTTP/2 200
+ content-type: application/xml
+ date: Thu, 14 Oct 2021 01:46:59 GMT
+ content-length: 105
+
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Customer><id>123</id><name>John</name></Customer>
+
+- Force HTTP/1.1 usage
+
+ $ curl https://localhost:9000/customerservice/customers/123 --http1.1 -ik
+
+ HTTP/1.1 200 OK
+ Content-Type: application/xml
+ Date: Thu, 14 Oct 2021 01:47:51 GMT
+ content-length: 105
+ connection: keep-alive
+
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Customer><id>123</id><name>John</name></Customer>
+
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_netty/pom.xml b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/pom.xml
new file mode 100644
index 0000000..fd53303
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/pom.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<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">
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>jax_rs_basic_http2_netty</artifactId>
+ <name>JAX-RS Basic Demo With HTTP/2 communications</name>
+ <description>JAX-RS Basic Demo With HTTP/2 communications</description>
+ <parent>
+ <groupId>org.apache.cxf.samples</groupId>
+ <artifactId>cxf-samples</artifactId>
+ <version>3.5.0-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+ <profiles>
+ <profile>
+ <id>h2-server</id>
+ <build>
+ <defaultGoal>test</defaultGoal>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>test</phase>
+ <goals>
+ <goal>java</goal>
+ </goals>
+ <configuration>
+ <mainClass>http2demo.server.h2.Server</mainClass>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>h2c-server</id>
+ <build>
+ <defaultGoal>test</defaultGoal>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>test</phase>
+ <goals>
+ <goal>java</goal>
+ </goals>
+ <configuration>
+ <mainClass>http2demo.server.h2c.Server</mainClass>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-transports-http-netty-server</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-codec-http2</artifactId>
+ <version>${cxf.netty.version}</version>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/config/KeyREADME.txt b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/config/KeyREADME.txt
new file mode 100644
index 0000000..97f20fa
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/config/KeyREADME.txt
@@ -0,0 +1,8 @@
+# The below scripts show the commands used to generate the self-signed keys for this sample.
+# If you use the below script to create your own keys be sure to change the passwords used here
+# DO NOT USE THE SUPPLIED KEYS IN PRODUCTION--everyone has them!!
+# For production recommended to use keys signed by a third-party certificate authority (CA)
+
+# Create the combination keystore/truststore for the server.
+keytool -genkeypair -keyalg RSA -keysize 4096 -validity 730 -alias myservicekey -keystore serviceKeystore.jks -dname "cn=localhost" -keypass skpass -storepass sspass
+
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/config/serviceKeystore.jks b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/config/serviceKeystore.jks
new file mode 100644
index 0000000..e3dd4b4
Binary files /dev/null and b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/config/serviceKeystore.jks differ
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/java/http2demo/common/Customer.java b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/java/http2demo/common/Customer.java
new file mode 100644
index 0000000..81ce3c5
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/java/http2demo/common/Customer.java
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package http2demo.common;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "Customer")
+public class Customer {
+ private long id;
+ private String name;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/java/http2demo/common/CustomerService.java b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/java/http2demo/common/CustomerService.java
new file mode 100644
index 0000000..890ade3
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/java/http2demo/common/CustomerService.java
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package http2demo.common;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Response;
+
+/**
+ * This interface describes a JAX-RS root resource. All the JAXRS annotations (except those overridden) will
+ * be inherited by classes implementing it.
+ */
+@Path("/customerservice/")
+public interface CustomerService {
+
+ @GET
+ @Path("/customers/{id}/")
+ Customer getCustomer(@PathParam("id") String id);
+
+ @PUT
+ @Path("/customers/")
+ Response updateCustomer(Customer customer);
+
+ @POST
+ @Path("/customers/")
+ Response addCustomer(Customer customer);
+
+ @DELETE
+ @Path("/customers/{id}/")
+ Response deleteCustomer(@PathParam("id") String id);
+
+}
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/java/http2demo/server/CustomerServiceImpl.java b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/java/http2demo/server/CustomerServiceImpl.java
new file mode 100644
index 0000000..171d27b
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/java/http2demo/server/CustomerServiceImpl.java
@@ -0,0 +1,89 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package http2demo.server;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.core.Response;
+
+import http2demo.common.Customer;
+import http2demo.common.CustomerService;
+
+public class CustomerServiceImpl implements CustomerService {
+ long currentId = 123;
+ Map<Long, Customer> customers = new HashMap<>();
+
+ public CustomerServiceImpl() {
+ init();
+ }
+
+ public Customer getCustomer(String id) {
+ System.out.println("----invoking getCustomer, Customer id is: " + id);
+ long idNumber = Long.parseLong(id);
+ return customers.get(idNumber);
+ }
+
+ public Response updateCustomer(Customer customer) {
+ System.out.println("----invoking updateCustomer, Customer name is: " + customer.getName());
+ Customer c = customers.get(customer.getId());
+ Response r;
+ if (c != null) {
+ customers.put(customer.getId(), customer);
+ r = Response.ok().build();
+ } else {
+ r = Response.notModified().build();
+ }
+
+ return r;
+ }
+
+ public Response addCustomer(Customer customer) {
+ System.out.println("----invoking addCustomer, Customer name is: " + customer.getName());
+ customer.setId(++currentId);
+
+ customers.put(customer.getId(), customer);
+
+ return Response.ok(customer).build();
+ }
+
+ public Response deleteCustomer(String id) {
+ System.out.println("----invoking deleteCustomer, Customer id is: " + id);
+ long idNumber = Long.parseLong(id);
+ Customer c = customers.get(idNumber);
+
+ Response r;
+ if (c != null) {
+ r = Response.ok().build();
+ customers.remove(idNumber);
+ } else {
+ r = Response.notModified().build();
+ }
+
+ return r;
+ }
+
+ final void init() {
+ Customer c = new Customer();
+ c.setName("John");
+ c.setId(123);
+ customers.put(c.getId(), c);
+ }
+
+}
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/java/http2demo/server/h2/Server.java b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/java/http2demo/server/h2/Server.java
new file mode 100644
index 0000000..d821996
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/java/http2demo/server/h2/Server.java
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package http2demo.server.h2;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.bus.spring.SpringBusFactory;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.transport.http.HttpServerEngineSupport;
+
+import http2demo.server.CustomerServiceImpl;
+
+public class Server {
+
+ static {
+ // set the configuration file
+ SpringBusFactory factory = new SpringBusFactory();
+ Bus bus = factory.createBus("ServerConfig.xml");
+ bus.setProperty(HttpServerEngineSupport.ENABLE_HTTP2, true);
+ BusFactory.setDefaultBus(bus);
+ }
+
+ protected Server() throws Exception {
+ JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+ sf.setResourceClasses(CustomerServiceImpl.class);
+ sf.setResourceProvider(CustomerServiceImpl.class,
+ new SingletonResourceProvider(new CustomerServiceImpl()));
+ sf.setAddress("https://localhost:9000/");
+
+ sf.create();
+ }
+
+ public static void main(String[] args) throws Exception {
+ new Server();
+ System.out.println("Server ready...");
+
+ Thread.sleep(5 * 60 * 1000);
+ System.out.println("Server exiting");
+ System.exit(0);
+ }
+}
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/java/http2demo/server/h2c/Server.java b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/java/http2demo/server/h2c/Server.java
new file mode 100644
index 0000000..d86ef95
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/java/http2demo/server/h2c/Server.java
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package http2demo.server.h2c;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.bus.spring.SpringBusFactory;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.transport.http.HttpServerEngineSupport;
+
+import http2demo.server.CustomerServiceImpl;
+
+public class Server {
+
+ static {
+ // set the configuration file
+ SpringBusFactory factory = new SpringBusFactory();
+ Bus bus = factory.createBus();
+ bus.setProperty(HttpServerEngineSupport.ENABLE_HTTP2, true);
+ BusFactory.setDefaultBus(bus);
+ }
+
+ protected Server() throws Exception {
+ JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+ sf.setResourceClasses(CustomerServiceImpl.class);
+ sf.setResourceProvider(CustomerServiceImpl.class,
+ new SingletonResourceProvider(new CustomerServiceImpl()));
+ sf.setAddress("http://localhost:9001/");
+
+ sf.create();
+ }
+
+ public static void main(String[] args) throws Exception {
+ new Server();
+ System.out.println("Server ready...");
+
+ Thread.sleep(5 * 60 * 1000);
+ System.out.println("Server exiting");
+ System.exit(0);
+ }
+}
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/resources/ServerConfig.xml b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/resources/ServerConfig.xml
new file mode 100644
index 0000000..8506180
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_netty/src/main/resources/ServerConfig.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!--
+ ** This file configures the Server which exposes the REST endpoint.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:sec="http://cxf.apache.org/configuration/security"
+ xmlns:http="http://cxf.apache.org/transports/http/configuration"
+ xmlns:httpj="http://cxf.apache.org/transports/http-netty-server/configuration"
+ xsi:schemaLocation="http://cxf.apache.org/configuration/security
+ http://cxf.apache.org/schemas/configuration/security.xsd
+ http://cxf.apache.org/transports/http/configuration
+ http://cxf.apache.org/schemas/configuration/http-conf.xsd
+ http://cxf.apache.org/transports/http-netty-server/configuration
+ http://cxf.apache.org/schemas/configuration/http-netty-server.xsd
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd">
+ <httpj:engine-factory bus="cxf">
+ <httpj:engine port="9000">
+ <httpj:tlsServerParameters>
+ <sec:keyManagers keyPassword="skpass">
+ <sec:keyStore file="src/main/config/serviceKeystore.jks" password="sspass" type="JKS"/>
+ </sec:keyManagers>
+ <sec:trustManagers>
+ <sec:keyStore file="src/main/config/serviceKeystore.jks" password="sspass" type="JKS"/>
+ </sec:trustManagers>
+ </httpj:tlsServerParameters>
+ </httpj:engine>
+ </httpj:engine-factory>
+</beans>
\ No newline at end of file
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/README.txt b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/README.txt
new file mode 100644
index 0000000..bb2cb62
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/README.txt
@@ -0,0 +1,93 @@
+JAX-RS Basic Demo With HTTPS communications
+===========================================
+
+This demo takes the JAX-RS basic demo a step further
+by doing the communication using HTTP/2 over TLS and
+HTTP/2 over cleartext using Undertow container.
+
+Building and running the demo using Maven
+-----------------------------------------
+From the base directory of this sample (i.e., where this README file is
+located), the Maven pom.xml file can be used to build and run the demo.
+
+Using either UNIX or Windows:
+
+ mvn install
+ mvn -Ph2-server (HTTP/2 over TLS)
+ mvn -Ph2c-server (HTTP/2 over cleartext)
+
+To remove the target dir, run "mvn clean".
+
+Certificates
+------------
+See the src/main/config folder for the sample keys used (don't use
+these keys in production!) as well as scripts used for their creation.
+
+HTTP/2 over cleartext
+------------
+
+- Upgrade from HTTP/1.1 to HTTP/2 (cleartext)
+
+ $ curl http://localhost:9001/customerservice/customers/123 --http2 -i
+
+ HTTP/1.1 101 Switching Protocols
+ Connection: Upgrade
+ Upgrade: h2c
+ Date: Thu, 14 Oct 2021 00:51:50 GMT
+
+ HTTP/2 200
+ content-type: application/xml
+ content-length: 105
+ date: Thu, 14 Oct 2021 00:51:50 GMT
+
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Customer><id>123</id><name>John</name></Customer>
+
+- Use HTTP/2 prior knowledge (cleartext)
+
+ $ curl http://localhost:9001/customerservice/customers/123 --http2-prior-knowledge -i
+
+ HTTP/2 200
+ content-type: application/xml
+ content-length: 105
+ date: Thu, 14 Oct 2021 00:55:35 GMT
+
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Customer><id>123</id><name>John</name></Customer>
+
+- Force HTTP/1.1 usage (cleartext)
+
+ $ curl http://localhost:9001/customerservice/customers/123 --http1.1 -i
+
+ HTTP/1.1 200 OK
+ Connection: keep-alive
+ Content-Type: application/xml
+ Content-Length: 105
+ Date: Thu, 14 Oct 2021 00:50:50 GMT
+
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Customer><id>123</id><name>John</name></Customer>
+
+HTTP/2 over TLS
+------------
+
+- Use HTTP/2
+
+ $ curl https://localhost:9000/customerservice/customers/123 --http2 -ik
+
+ HTTP/2 200
+ content-type: application/xml
+ content-length: 105
+ date: Thu, 14 Oct 2021 01:04:34 GMT
+
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Customer><id>123</id><name>John</name></Customer>
+
+- Force HTTP/1.1 usage
+
+ $ curl https://localhost:9000/customerservice/customers/123 --http1.1 -ik
+
+ HTTP/1.1 200 OK
+ Connection: keep-alive
+ Content-Type: application/xml
+ Content-Length: 105
+ Date: Thu, 14 Oct 2021 01:06:34 GMT
+
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Customer><id>123</id><name>John</name></Customer>
+
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/pom.xml b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/pom.xml
new file mode 100644
index 0000000..ea57f3a
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/pom.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<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">
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>jax_rs_basic_http2_undertow</artifactId>
+ <name>JAX-RS Basic Demo With HTTP/2 communications</name>
+ <description>JAX-RS Basic Demo With HTTP/2 communications</description>
+ <parent>
+ <groupId>org.apache.cxf.samples</groupId>
+ <artifactId>cxf-samples</artifactId>
+ <version>3.5.0-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+ <profiles>
+ <profile>
+ <id>h2-server</id>
+ <build>
+ <defaultGoal>test</defaultGoal>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>test</phase>
+ <goals>
+ <goal>java</goal>
+ </goals>
+ <configuration>
+ <mainClass>http2demo.server.h2.Server</mainClass>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>h2c-server</id>
+ <build>
+ <defaultGoal>test</defaultGoal>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>test</phase>
+ <goals>
+ <goal>java</goal>
+ </goals>
+ <configuration>
+ <mainClass>http2demo.server.h2c.Server</mainClass>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-transports-http-undertow</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/config/KeyREADME.txt b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/config/KeyREADME.txt
new file mode 100644
index 0000000..97f20fa
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/config/KeyREADME.txt
@@ -0,0 +1,8 @@
+# The below scripts show the commands used to generate the self-signed keys for this sample.
+# If you use the below script to create your own keys be sure to change the passwords used here
+# DO NOT USE THE SUPPLIED KEYS IN PRODUCTION--everyone has them!!
+# For production recommended to use keys signed by a third-party certificate authority (CA)
+
+# Create the combination keystore/truststore for the server.
+keytool -genkeypair -keyalg RSA -keysize 4096 -validity 730 -alias myservicekey -keystore serviceKeystore.jks -dname "cn=localhost" -keypass skpass -storepass sspass
+
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/config/serviceKeystore.jks b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/config/serviceKeystore.jks
new file mode 100644
index 0000000..8d40c61
Binary files /dev/null and b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/config/serviceKeystore.jks differ
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/java/http2demo/common/Customer.java b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/java/http2demo/common/Customer.java
new file mode 100644
index 0000000..81ce3c5
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/java/http2demo/common/Customer.java
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package http2demo.common;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "Customer")
+public class Customer {
+ private long id;
+ private String name;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/java/http2demo/common/CustomerService.java b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/java/http2demo/common/CustomerService.java
new file mode 100644
index 0000000..890ade3
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/java/http2demo/common/CustomerService.java
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package http2demo.common;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Response;
+
+/**
+ * This interface describes a JAX-RS root resource. All the JAXRS annotations (except those overridden) will
+ * be inherited by classes implementing it.
+ */
+@Path("/customerservice/")
+public interface CustomerService {
+
+ @GET
+ @Path("/customers/{id}/")
+ Customer getCustomer(@PathParam("id") String id);
+
+ @PUT
+ @Path("/customers/")
+ Response updateCustomer(Customer customer);
+
+ @POST
+ @Path("/customers/")
+ Response addCustomer(Customer customer);
+
+ @DELETE
+ @Path("/customers/{id}/")
+ Response deleteCustomer(@PathParam("id") String id);
+
+}
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/java/http2demo/server/CustomerServiceImpl.java b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/java/http2demo/server/CustomerServiceImpl.java
new file mode 100644
index 0000000..171d27b
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/java/http2demo/server/CustomerServiceImpl.java
@@ -0,0 +1,89 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package http2demo.server;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.core.Response;
+
+import http2demo.common.Customer;
+import http2demo.common.CustomerService;
+
+public class CustomerServiceImpl implements CustomerService {
+ long currentId = 123;
+ Map<Long, Customer> customers = new HashMap<>();
+
+ public CustomerServiceImpl() {
+ init();
+ }
+
+ public Customer getCustomer(String id) {
+ System.out.println("----invoking getCustomer, Customer id is: " + id);
+ long idNumber = Long.parseLong(id);
+ return customers.get(idNumber);
+ }
+
+ public Response updateCustomer(Customer customer) {
+ System.out.println("----invoking updateCustomer, Customer name is: " + customer.getName());
+ Customer c = customers.get(customer.getId());
+ Response r;
+ if (c != null) {
+ customers.put(customer.getId(), customer);
+ r = Response.ok().build();
+ } else {
+ r = Response.notModified().build();
+ }
+
+ return r;
+ }
+
+ public Response addCustomer(Customer customer) {
+ System.out.println("----invoking addCustomer, Customer name is: " + customer.getName());
+ customer.setId(++currentId);
+
+ customers.put(customer.getId(), customer);
+
+ return Response.ok(customer).build();
+ }
+
+ public Response deleteCustomer(String id) {
+ System.out.println("----invoking deleteCustomer, Customer id is: " + id);
+ long idNumber = Long.parseLong(id);
+ Customer c = customers.get(idNumber);
+
+ Response r;
+ if (c != null) {
+ r = Response.ok().build();
+ customers.remove(idNumber);
+ } else {
+ r = Response.notModified().build();
+ }
+
+ return r;
+ }
+
+ final void init() {
+ Customer c = new Customer();
+ c.setName("John");
+ c.setId(123);
+ customers.put(c.getId(), c);
+ }
+
+}
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/java/http2demo/server/h2/Server.java b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/java/http2demo/server/h2/Server.java
new file mode 100644
index 0000000..d821996
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/java/http2demo/server/h2/Server.java
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package http2demo.server.h2;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.bus.spring.SpringBusFactory;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.transport.http.HttpServerEngineSupport;
+
+import http2demo.server.CustomerServiceImpl;
+
+public class Server {
+
+ static {
+ // set the configuration file
+ SpringBusFactory factory = new SpringBusFactory();
+ Bus bus = factory.createBus("ServerConfig.xml");
+ bus.setProperty(HttpServerEngineSupport.ENABLE_HTTP2, true);
+ BusFactory.setDefaultBus(bus);
+ }
+
+ protected Server() throws Exception {
+ JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+ sf.setResourceClasses(CustomerServiceImpl.class);
+ sf.setResourceProvider(CustomerServiceImpl.class,
+ new SingletonResourceProvider(new CustomerServiceImpl()));
+ sf.setAddress("https://localhost:9000/");
+
+ sf.create();
+ }
+
+ public static void main(String[] args) throws Exception {
+ new Server();
+ System.out.println("Server ready...");
+
+ Thread.sleep(5 * 60 * 1000);
+ System.out.println("Server exiting");
+ System.exit(0);
+ }
+}
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/java/http2demo/server/h2c/Server.java b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/java/http2demo/server/h2c/Server.java
new file mode 100644
index 0000000..d86ef95
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/java/http2demo/server/h2c/Server.java
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package http2demo.server.h2c;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.bus.spring.SpringBusFactory;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.transport.http.HttpServerEngineSupport;
+
+import http2demo.server.CustomerServiceImpl;
+
+public class Server {
+
+ static {
+ // set the configuration file
+ SpringBusFactory factory = new SpringBusFactory();
+ Bus bus = factory.createBus();
+ bus.setProperty(HttpServerEngineSupport.ENABLE_HTTP2, true);
+ BusFactory.setDefaultBus(bus);
+ }
+
+ protected Server() throws Exception {
+ JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+ sf.setResourceClasses(CustomerServiceImpl.class);
+ sf.setResourceProvider(CustomerServiceImpl.class,
+ new SingletonResourceProvider(new CustomerServiceImpl()));
+ sf.setAddress("http://localhost:9001/");
+
+ sf.create();
+ }
+
+ public static void main(String[] args) throws Exception {
+ new Server();
+ System.out.println("Server ready...");
+
+ Thread.sleep(5 * 60 * 1000);
+ System.out.println("Server exiting");
+ System.exit(0);
+ }
+}
diff --git a/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/resources/ServerConfig.xml b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/resources/ServerConfig.xml
new file mode 100644
index 0000000..fff566f
--- /dev/null
+++ b/distribution/src/main/release/samples/jax_rs/basic_http2_undertow/src/main/resources/ServerConfig.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!--
+ ** This file configures the Server which exposes the REST endpoint.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:sec="http://cxf.apache.org/configuration/security"
+ xmlns:http="http://cxf.apache.org/transports/http/configuration"
+ xmlns:httpj="http://cxf.apache.org/transports/http-undertow/configuration"
+ xsi:schemaLocation="http://cxf.apache.org/configuration/security
+ http://cxf.apache.org/schemas/configuration/security.xsd
+ http://cxf.apache.org/transports/http/configuration
+ http://cxf.apache.org/schemas/configuration/http-conf.xsd
+ http://cxf.apache.org/transports/http-undertow/configuration
+ http://cxf.apache.org/schemas/configuration/http-undertow.xsd
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd">
+ <httpj:engine-factory bus="cxf">
+ <httpj:engine port="9000">
+ <httpj:tlsServerParameters>
+ <sec:keyManagers keyPassword="skpass">
+ <sec:keyStore file="src/main/config/serviceKeystore.jks" password="sspass" type="JKS"/>
+ </sec:keyManagers>
+ <sec:trustManagers>
+ <sec:keyStore file="src/main/config/serviceKeystore.jks" password="sspass" type="JKS"/>
+ </sec:trustManagers>
+ </httpj:tlsServerParameters>
+ </httpj:engine>
+ </httpj:engine-factory>
+</beans>
\ No newline at end of file
diff --git a/distribution/src/main/release/samples/pom.xml b/distribution/src/main/release/samples/pom.xml
index 409eb38..754c6297 100644
--- a/distribution/src/main/release/samples/pom.xml
+++ b/distribution/src/main/release/samples/pom.xml
@@ -30,9 +30,10 @@
<!-- don't deploy the samples, kind of pointless -->
<maven.deploy.skip>true</maven.deploy.skip>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <spring.boot.version>2.5.0</spring.boot.version>
+ <spring.boot.version>2.5.5</spring.boot.version>
<spring.cloud.eureka.version>3.0.3</spring.cloud.eureka.version>
- <cxf.jetty9.version>9.4.43.v20210629</cxf.jetty9.version>
+ <cxf.jetty9.version>9.4.44.v20210927</cxf.jetty9.version>
+ <cxf.netty.version>4.1.69.Final</cxf.netty.version>
<cxf.httpcomponents.client.version>4.5.13</cxf.httpcomponents.client.version>
<cxf.swagger.ui.version>3.52.1</cxf.swagger.ui.version>
<cxf.tika.version>2.1.0</cxf.tika.version>
@@ -147,6 +148,9 @@
<module>jaxws_graalvm_dynamic/client</module>
<module>jaxws_graalvm_dynamic/server</module>
<module>jax_rs/graalvm_basic</module>
+ <module>jax_rs/basic_http2_undertow</module>
+ <module>jax_rs/basic_http2_netty</module>
+ <module>jax_rs/basic_http2_jetty</module>
</modules>
<dependencyManagement>
<dependencies>
diff --git a/parent/pom.xml b/parent/pom.xml
index f2c17b7..97e5429 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -1159,6 +1159,16 @@
<version>${cxf.jetty.version}</version>
</dependency>
<dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>http2-server</artifactId>
+ <version>${cxf.jetty.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-alpn-server</artifactId>
+ <version>${cxf.jetty.version}</version>
+ </dependency>
+ <dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
<version>${cxf.undertow.version}</version>
diff --git a/rt/transports/http-jetty/pom.xml b/rt/transports/http-jetty/pom.xml
index d17d88a..a8c0ff9 100644
--- a/rt/transports/http-jetty/pom.xml
+++ b/rt/transports/http-jetty/pom.xml
@@ -128,6 +128,16 @@
<artifactId>jetty-http</artifactId>
</dependency>
<dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>http2-server</artifactId>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-alpn-server</artifactId>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>runtime</scope>
diff --git a/rt/transports/http-jetty/src/main/java/org/apache/cxf/transport/http_jetty/JettyHTTPServerEngine.java b/rt/transports/http-jetty/src/main/java/org/apache/cxf/transport/http_jetty/JettyHTTPServerEngine.java
index 45212be..ffc8819 100644
--- a/rt/transports/http-jetty/src/main/java/org/apache/cxf/transport/http_jetty/JettyHTTPServerEngine.java
+++ b/rt/transports/http-jetty/src/main/java/org/apache/cxf/transport/http_jetty/JettyHTTPServerEngine.java
@@ -54,7 +54,11 @@ import org.apache.cxf.configuration.security.ClientAuthentication;
import org.apache.cxf.helpers.JavaUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.transport.HttpUriMapper;
+import org.apache.cxf.transport.http.HttpServerEngineSupport;
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.server.AbstractConnector;
import org.eclipse.jetty.server.ConnectionFactory;
@@ -84,7 +88,7 @@ import org.eclipse.jetty.util.thread.ThreadPool;
* work off of a designated port. The port will be enabled for
* "http" or "https" depending upon its successful configuration.
*/
-public class JettyHTTPServerEngine implements ServerEngine {
+public class JettyHTTPServerEngine implements ServerEngine, HttpServerEngineSupport {
public static final String DO_NOT_CHECK_URL_PROP = "org.apache.cxf.transports.http_jetty.DontCheckUrl";
private static final Logger LOG = LogUtils.getL7dLogger(JettyHTTPServerEngine.class);
@@ -145,15 +149,12 @@ public class JettyHTTPServerEngine implements ServerEngine {
/**
* This constructor is called by the JettyHTTPServerEngineFactory.
*/
- public JettyHTTPServerEngine(
- Container.Listener mBeanContainer,
- String host,
- int port) {
+ public JettyHTTPServerEngine(Container.Listener mBeanContainer, String host, int port) {
this.host = host;
this.port = port;
this.mBeanContainer = mBeanContainer;
}
-
+
public JettyHTTPServerEngine() {
}
@@ -398,7 +399,7 @@ public class JettyHTTPServerEngine implements ServerEngine {
addServerMBean();
if (connector == null) {
- connector = createConnector(getHost(), getPort());
+ connector = createConnector(getHost(), getPort(), handler.getBus());
if (LOG.isLoggable(Level.FINER)) {
logConnector((ServerConnector)connector);
}
@@ -621,7 +622,7 @@ public class JettyHTTPServerEngine implements ServerEngine {
}
- private Connector createConnector(String hosto, int porto) {
+ private Connector createConnector(String hosto, int porto, final Bus bus) {
// now we just use the SelectChannelConnector as the default connector
SslContextFactory sslcf = null;
if (tlsServerParameters != null) {
@@ -648,7 +649,7 @@ public class JettyHTTPServerEngine implements ServerEngine {
// unparsable version
}
- ServerConnector result = (ServerConnector)createConnectorJetty(sslcf, hosto, porto, major, minor);
+ ServerConnector result = (ServerConnector)createConnectorJetty(sslcf, hosto, porto, major, minor, bus);
try {
result.setPort(porto);
@@ -665,7 +666,8 @@ public class JettyHTTPServerEngine implements ServerEngine {
return result;
}
- AbstractConnector createConnectorJetty(SslContextFactory sslcf, String hosto, int porto, int major, int minor) {
+ AbstractConnector createConnectorJetty(SslContextFactory sslcf, String hosto, int porto,
+ int major, int minor, final Bus bus) {
final AbstractConnector result;
try {
HttpConfiguration httpConfig = new HttpConfiguration();
@@ -678,10 +680,26 @@ public class JettyHTTPServerEngine implements ServerEngine {
if (tlsServerParameters != null) {
httpConfig.addCustomizer(new org.eclipse.jetty.server.SecureRequestCustomizer());
- SslConnectionFactory scf = new SslConnectionFactory(sslcf, "HTTP/1.1");
- connectionFactories.add(scf);
+
+ if (!isHttp2Enabled(bus)) {
+ final SslConnectionFactory scf = new SslConnectionFactory(sslcf, httpFactory.getProtocol());
+ connectionFactories.add(scf);
+ } else {
+ // The ALPN processors are application specific (as per Jetty docs) and are pluggable as
+ // additional dependency.
+ final ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
+ alpn.setDefaultProtocol(httpFactory.getProtocol());
+
+ final SslConnectionFactory scf = new SslConnectionFactory(sslcf, alpn.getProtocol());
+ connectionFactories.add(scf);
+ connectionFactories.add(alpn);
+ connectionFactories.add(new HTTP2ServerConnectionFactory(httpConfig));
+ }
+
String proto = (major > 9 || (major == 9 && minor >= 3)) ? "SSL" : "SSL-HTTP/1.1";
result.setDefaultProtocol(proto);
+ } else if (isHttp2Enabled(bus)) {
+ connectionFactories.add(new HTTP2CServerConnectionFactory(httpConfig));
}
connectionFactories.add(httpFactory);
result.setConnectionFactories(connectionFactories);
diff --git a/rt/transports/http-netty/netty-server/pom.xml b/rt/transports/http-netty/netty-server/pom.xml
index 3807fdd..9e65f55 100644
--- a/rt/transports/http-netty/netty-server/pom.xml
+++ b/rt/transports/http-netty/netty-server/pom.xml
@@ -62,6 +62,12 @@
<version>${cxf.netty.version}</version>
</dependency>
<dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-codec-http2</artifactId>
+ <version>${cxf.netty.version}</version>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
<!-- we don't implement the servlet 3.0 specification here -->
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-servlet_2.5_spec</artifactId>
diff --git a/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/NettyHttpServerEngine.java b/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/NettyHttpServerEngine.java
index 182d997..c198fb2 100644
--- a/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/NettyHttpServerEngine.java
+++ b/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/NettyHttpServerEngine.java
@@ -29,11 +29,13 @@ import java.util.logging.Logger;
import javax.annotation.PostConstruct;
+import org.apache.cxf.Bus;
import org.apache.cxf.common.i18n.Message;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.configuration.jsse.TLSServerParameters;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.transport.HttpUriMapper;
+import org.apache.cxf.transport.http.HttpServerEngineSupport;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
@@ -44,7 +46,7 @@ import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;
-public class NettyHttpServerEngine implements ServerEngine {
+public class NettyHttpServerEngine implements ServerEngine, HttpServerEngineSupport {
private static final Logger LOG =
LogUtils.getL7dLogger(NettyHttpServerEngine.class);
@@ -95,22 +97,37 @@ public class NettyHttpServerEngine implements ServerEngine {
private EventLoopGroup bossGroup;
private EventLoopGroup workerGroup;
private EventExecutorGroup applicationExecutor;
+
+ private Bus bus;
+
public NettyHttpServerEngine() {
}
- public NettyHttpServerEngine(
- String host,
- int port) {
+ @Deprecated
+ public NettyHttpServerEngine(String host, int port) {
+ this(host, port, null);
+ }
+
+ public NettyHttpServerEngine(String host, int port, Bus bus) {
this.host = host;
this.port = port;
+ this.bus = bus;
}
@PostConstruct
public void finalizeConfig() {
// need to check if we need to any other thing other than Setting the TLSServerParameter
}
+
+ public void setBus(Bus bus) {
+ this.bus = bus;
+ }
+
+ public Bus getBus() {
+ return bus;
+ }
/**
* This method is used to programmatically set the TLSServerParameters.
@@ -163,7 +180,7 @@ public class NettyHttpServerEngine implements ServerEngine {
new NettyHttpServletPipelineFactory(
tlsServerParameters, sessionSupport,
maxChunkContentSize, handlerMap,
- this, applicationExecutor);
+ this, applicationExecutor, isHttp2Enabled(bus));
// Start the servletPipeline's timer
servletPipeline.start();
bootstrap.childHandler(servletPipeline);
diff --git a/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/NettyHttpServerEngineFactory.java b/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/NettyHttpServerEngineFactory.java
index 93bb2ca..7c3712f 100644
--- a/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/NettyHttpServerEngineFactory.java
+++ b/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/NettyHttpServerEngineFactory.java
@@ -151,7 +151,7 @@ public class NettyHttpServerEngineFactory implements BusLifeCycleListener {
NettyHttpServerEngine ref = portMap.get(port);
if (ref == null) {
- ref = new NettyHttpServerEngine(host, port);
+ ref = new NettyHttpServerEngine(host, port, factory.getBus());
if (tlsParams != null) {
ref.setTlsServerParameters(tlsParams);
}
diff --git a/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/NettyHttpServletPipelineFactory.java b/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/NettyHttpServletPipelineFactory.java
index f92a16e..cdc9350 100644
--- a/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/NettyHttpServletPipelineFactory.java
+++ b/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/NettyHttpServletPipelineFactory.java
@@ -19,31 +19,64 @@
package org.apache.cxf.transport.http.netty.server;
+import java.util.Arrays;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
+import javax.net.ssl.TrustManager;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.configuration.jsse.TLSServerParameters;
+import org.apache.cxf.configuration.security.ClientAuthentication;
import org.apache.cxf.transport.http.netty.server.interceptor.ChannelInterceptor;
import org.apache.cxf.transport.http.netty.server.interceptor.HttpSessionInterceptor;
import org.apache.cxf.transport.http.netty.server.session.DefaultHttpSessionStore;
import org.apache.cxf.transport.http.netty.server.session.HttpSessionStore;
+import org.apache.cxf.transport.https.SSLContextInitParameters;
import org.apache.cxf.transport.https.SSLUtils;
import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
+import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.HttpContentCompressor;
+import io.netty.handler.codec.http.HttpMessage;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
+import io.netty.handler.codec.http.HttpServerCodec;
+import io.netty.handler.codec.http.HttpServerUpgradeHandler;
+import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodec;
+import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory;
+import io.netty.handler.codec.http2.CleartextHttp2ServerUpgradeHandler;
+import io.netty.handler.codec.http2.Http2CodecUtil;
+import io.netty.handler.codec.http2.Http2FrameCodecBuilder;
+import io.netty.handler.codec.http2.Http2MultiplexHandler;
+import io.netty.handler.codec.http2.Http2SecurityUtil;
+import io.netty.handler.codec.http2.Http2ServerUpgradeCodec;
+import io.netty.handler.codec.http2.Http2StreamFrameToHttpObjectCodec;
+import io.netty.handler.ssl.ApplicationProtocolConfig;
+import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
+import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
+import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
+import io.netty.handler.ssl.ApplicationProtocolNames;
+import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
+import io.netty.handler.ssl.ClientAuth;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslHandler;
+import io.netty.handler.ssl.SslProvider;
+import io.netty.handler.ssl.SupportedCipherSuiteFilter;
import io.netty.handler.timeout.IdleStateHandler;
+import io.netty.util.AsciiString;
+import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.ImmediateEventExecutor;
@@ -68,6 +101,8 @@ public class NettyHttpServletPipelineFactory extends ChannelInitializer<Channel>
private final EventExecutorGroup applicationExecutor;
private final NettyHttpServerEngine nettyHttpServerEngine;
+
+ private final boolean enableHttp2; /* enable HTTP2 support */
/**
* @deprecated use {@link #NettyHttpServletPipelineFactory(TLSServerParameters, boolean, int, Map,
@@ -83,9 +118,18 @@ public class NettyHttpServletPipelineFactory extends ChannelInitializer<Channel>
}
public NettyHttpServletPipelineFactory(TLSServerParameters tlsServerParameters,
+ boolean supportSession, int maxChunkContentSize,
+ Map<String, NettyHttpContextHandler> handlerMap,
+ NettyHttpServerEngine engine, EventExecutorGroup applicationExecutor) {
+ this(tlsServerParameters, supportSession, maxChunkContentSize, handlerMap, engine,
+ applicationExecutor, false);
+ }
+
+ public NettyHttpServletPipelineFactory(TLSServerParameters tlsServerParameters,
boolean supportSession, int maxChunkContentSize,
Map<String, NettyHttpContextHandler> handlerMap,
- NettyHttpServerEngine engine, EventExecutorGroup applicationExecutor) {
+ NettyHttpServerEngine engine, EventExecutorGroup applicationExecutor,
+ boolean enableHttp2) {
this.supportSession = supportSession;
this.watchdog = new HttpSessionWatchdog();
this.handlerMap = handlerMap;
@@ -93,6 +137,7 @@ public class NettyHttpServletPipelineFactory extends ChannelInitializer<Channel>
this.maxChunkContentSize = maxChunkContentSize;
this.nettyHttpServerEngine = engine;
this.applicationExecutor = applicationExecutor;
+ this.enableHttp2 = enableHttp2;
}
public Map<String, NettyHttpContextHandler> getHttpContextHandlerMap() {
@@ -139,12 +184,12 @@ public class NettyHttpServletPipelineFactory extends ChannelInitializer<Channel>
return handler;
}
- protected ChannelPipeline getDefaulHttpChannelPipeline(Channel channel) throws Exception {
+ protected ChannelPipeline getDefaultHttpChannelPipeline(Channel channel) throws Exception {
// Create a default pipeline implementation.
ChannelPipeline pipeline = channel.pipeline();
- SslHandler sslHandler = configureServerSSLOnDemand();
+ SslHandler sslHandler = configureServerHttpSSLOnDemand();
if (sslHandler != null) {
LOG.log(Level.FINE,
"Server SSL handler configured and added as an interceptor against the ChannelPipeline: {}",
@@ -152,6 +197,18 @@ public class NettyHttpServletPipelineFactory extends ChannelInitializer<Channel>
pipeline.addLast("ssl", sslHandler);
}
+ configureDefaultHttpPipeline(pipeline);
+
+ return pipeline;
+ }
+
+ protected void configureDefaultHttp2Pipeline(ChannelPipeline pipeline) {
+ pipeline
+ .addLast(Http2FrameCodecBuilder.forServer().build())
+ .addLast(new Http2MultiplexHandler(createHttp2ChannelInitializer()));
+ }
+
+ protected void configureDefaultHttpPipeline(ChannelPipeline pipeline) {
pipeline.addLast("decoder", new HttpRequestDecoder());
pipeline.addLast("encoder", new HttpResponseEncoder());
pipeline.addLast("aggregator", new HttpObjectAggregator(maxChunkContentSize));
@@ -159,20 +216,74 @@ public class NettyHttpServletPipelineFactory extends ChannelInitializer<Channel>
// Remove the following line if you don't want automatic content
// compression.
pipeline.addLast("deflater", new HttpContentCompressor());
+
// Set up the idle handler
pipeline.addLast("idle", new IdleStateHandler(nettyHttpServerEngine.getReadIdleTime(),
nettyHttpServerEngine.getWriteIdleTime(), 0));
-
- return pipeline;
}
- private SslHandler configureServerSSLOnDemand() throws Exception {
+ private SslHandler configureServerHttpSSLOnDemand() throws Exception {
if (tlsServerParameters != null) {
SSLEngine sslEngine = SSLUtils.createServerSSLEngine(tlsServerParameters);
return new SslHandler(sslEngine);
}
return null;
}
+
+ private SslContext configureServerHttp2SSLOnDemand() throws Exception {
+ if (tlsServerParameters != null) {
+ final SSLContextInitParameters initParams = SSLUtils.getSSLContextInitParameters(tlsServerParameters);
+ // Use only JDK provider for now, leaving OpenSsl as an option
+ final SslProvider provider = SslProvider.JDK;
+
+ final KeyManager[] keyManagers = initParams.getKeyManagers();
+ if (keyManagers == null || keyManagers.length == 0) {
+ throw new IllegalStateException("No KeyManagers are configured, unable "
+ + "to create Netty's SslContext instance");
+ }
+
+ final String[] cipherSuites = org.apache.cxf.configuration.jsse.SSLUtils
+ .getCiphersuitesToInclude(
+ tlsServerParameters.getCipherSuites(),
+ tlsServerParameters.getCipherSuitesFilter(),
+ SSLContext.getDefault().getDefaultSSLParameters().getCipherSuites(),
+ Http2SecurityUtil.CIPHERS.toArray(new String[] {}),
+ LOG);
+
+ final SslContextBuilder builder = SslContextBuilder
+ .forServer(keyManagers[0]) /* only first is used, as with SSLContext::init*/
+ .sslProvider(provider)
+ .ciphers(Arrays.asList(cipherSuites), SupportedCipherSuiteFilter.INSTANCE)
+ .applicationProtocolConfig(
+ new ApplicationProtocolConfig(
+ Protocol.ALPN,
+ // NO_ADVERTISE is currently the only mode supported by both OpenSsl and JDK providers.
+ SelectorFailureBehavior.NO_ADVERTISE,
+ // ACCEPT is currently the only mode supported by both OpenSsl and JDK providers.
+ SelectedListenerFailureBehavior.ACCEPT,
+ ApplicationProtocolNames.HTTP_2,
+ ApplicationProtocolNames.HTTP_1_1
+ ));
+
+ final TrustManager[] trustManagers = initParams.getTrustManagers();
+ if (trustManagers != null && trustManagers.length > 0) {
+ builder.trustManager(trustManagers[0]);
+ }
+
+ final ClientAuthentication clientAuth = tlsServerParameters.getClientAuthentication();
+ if (clientAuth != null) {
+ if (clientAuth.isSetRequired() && clientAuth.isRequired()) {
+ builder.clientAuth(ClientAuth.REQUIRE);
+ } else if (clientAuth.isSetWant() && clientAuth.isWant()) {
+ builder.clientAuth(ClientAuth.OPTIONAL);
+ }
+ }
+
+ return builder.build();
+ }
+
+ return null;
+ }
private class HttpSessionWatchdog implements Runnable {
@@ -203,12 +314,115 @@ public class NettyHttpServletPipelineFactory extends ChannelInitializer<Channel>
}
}
+
+ /**
+ * Application negotiation handler to select either HTTP 1.1 or HTTP 2 protocol, based
+ * on client/server ALPN negotiations.
+ */
+ private class Http2OrHttpHandler extends ApplicationProtocolNegotiationHandler {
+ protected Http2OrHttpHandler() {
+ super(ApplicationProtocolNames.HTTP_1_1);
+ }
+
+ @Override
+ protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception {
+ if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
+ configureDefaultHttp2Pipeline(ctx.pipeline());
+ } else if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) {
+ configureDefaultHttpPipeline(ctx.pipeline());
+ ctx.pipeline().addLast(applicationExecutor, "handler", getServletHandler());
+ } else {
+ throw new IllegalStateException("Unknown application protocol: " + protocol);
+ }
+ }
+ }
@Override
protected void initChannel(Channel ch) throws Exception {
- ChannelPipeline pipeline = getDefaulHttpChannelPipeline(ch);
+ if (!enableHttp2) {
+ final ChannelPipeline pipeline = getDefaultHttpChannelPipeline(ch);
+ pipeline.addLast(applicationExecutor, "handler", this.getServletHandler());
+ } else {
+ getDefaultHttp2ChannelPipeline(ch);
+ }
+ }
+
+ protected ChannelPipeline getDefaultHttp2ChannelPipeline(Channel channel) throws Exception {
+ // Create a default pipeline implementation with HTTP/2 support
+ ChannelPipeline pipeline = channel.pipeline();
+
+ SslContext sslCtx = configureServerHttp2SSLOnDemand();
+ if (sslCtx != null) {
+ final SslHandler sslHandler = sslCtx.newHandler(channel.alloc());
- pipeline.addLast(applicationExecutor, "handler", this.getServletHandler());
+ LOG.log(Level.FINE,
+ "Server SSL handler configured and added as an interceptor against the ChannelPipeline: {}",
+ sslHandler);
+
+ pipeline.addLast(sslHandler, new Http2OrHttpHandler());
+ return pipeline;
+ }
+
+ final UpgradeCodecFactory upgradeCodecFactory = new UpgradeCodecFactory() {
+ @Override
+ public UpgradeCodec newUpgradeCodec(CharSequence protocol) {
+ if (AsciiString.contentEquals(Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME, protocol)) {
+ return new Http2ServerUpgradeCodec(
+ Http2FrameCodecBuilder.forServer().build(),
+ new Http2MultiplexHandler(createHttp2ChannelInitializer()));
+ } else {
+ return null;
+ }
+ }
+ };
+
+ final HttpServerCodec sourceCodec = new HttpServerCodec();
+ final HttpServerUpgradeHandler upgradeHandler = new HttpServerUpgradeHandler(sourceCodec, upgradeCodecFactory);
+ final CleartextHttp2ServerUpgradeHandler cleartextUpgradeHandler = new CleartextHttp2ServerUpgradeHandler(
+ sourceCodec, upgradeHandler, createHttp2ChannelInitializerPriorKnowledge());
+
+ pipeline.addLast(cleartextUpgradeHandler);
+ pipeline.addLast(new SimpleChannelInboundHandler<HttpMessage>() {
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, HttpMessage msg) throws Exception {
+ // If this handler is hit then no upgrade has been attempted and the client is just talking HTTP
+ final ChannelPipeline pipeline = ctx.pipeline();
+
+ pipeline.addAfter(applicationExecutor, ctx.name(), "handler", getServletHandler());
+ pipeline.replace(this, "aggregator", new HttpObjectAggregator(maxChunkContentSize));
+
+ // Remove the following line if you don't want automatic content compression.
+ pipeline.addLast("deflater", new HttpContentCompressor());
+
+ // Set up the idle handler
+ pipeline.addLast("idle", new IdleStateHandler(nettyHttpServerEngine.getReadIdleTime(),
+ nettyHttpServerEngine.getWriteIdleTime(), 0));
+
+ ctx.fireChannelRead(ReferenceCountUtil.retain(msg));
+ }
+ });
+
+ return pipeline;
}
+ private ChannelInitializer<Channel> createHttp2ChannelInitializer() {
+ return new ChannelInitializer<Channel>() {
+ @Override
+ protected void initChannel(Channel childChannel) throws Exception {
+ childChannel.pipeline()
+ .addLast(new Http2StreamFrameToHttpObjectCodec(true))
+ .addLast("aggregator", new HttpObjectAggregator(maxChunkContentSize))
+ .addLast(applicationExecutor, getServletHandler());
+ }
+ };
+ }
+
+ private ChannelInitializer<Channel> createHttp2ChannelInitializerPriorKnowledge() {
+ return new ChannelInitializer<Channel>() {
+ @Override
+ protected void initChannel(Channel childChannel) throws Exception {
+ configureDefaultHttp2Pipeline(childChannel.pipeline());
+ }
+ };
+ }
}
diff --git a/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/spring/NettyHttpServerEngineBeanDefinitionParser.java b/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/spring/NettyHttpServerEngineBeanDefinitionParser.java
index 2ef975e..8e8daf9 100644
--- a/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/spring/NettyHttpServerEngineBeanDefinitionParser.java
+++ b/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/spring/NettyHttpServerEngineBeanDefinitionParser.java
@@ -229,7 +229,6 @@ public class NettyHttpServerEngineBeanDefinitionParser extends AbstractBeanDefin
String threadingRef;
String tlsRef;
- Bus bus;
NettyHttpServerEngineFactory factory;
public SpringNettyHttpServerEngine(
@@ -237,8 +236,7 @@ public class NettyHttpServerEngineBeanDefinitionParser extends AbstractBeanDefin
Bus b,
String host,
int port) {
- super(host, port);
- bus = b;
+ super(host, port, b);
factory = fac;
}
@@ -247,15 +245,15 @@ public class NettyHttpServerEngineBeanDefinitionParser extends AbstractBeanDefin
}
public void setBus(Bus b) {
- bus = b;
- if (null != bus && null == factory) {
- factory = bus.getExtension(NettyHttpServerEngineFactory.class);
+ super.setBus(b);
+ if (null != getBus() && null == factory) {
+ factory = getBus().getExtension(NettyHttpServerEngineFactory.class);
}
}
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
- if (bus == null) {
- bus = BusWiringBeanFactoryPostProcessor.addDefaultBus(ctx);
+ if (getBus() == null) {
+ setBus(BusWiringBeanFactoryPostProcessor.addDefaultBus(ctx));
}
}
diff --git a/rt/transports/http-undertow/src/main/java/org/apache/cxf/transport/http_undertow/UndertowHTTPServerEngine.java b/rt/transports/http-undertow/src/main/java/org/apache/cxf/transport/http_undertow/UndertowHTTPServerEngine.java
index e684f6f..b8971ba 100644
--- a/rt/transports/http-undertow/src/main/java/org/apache/cxf/transport/http_undertow/UndertowHTTPServerEngine.java
+++ b/rt/transports/http-undertow/src/main/java/org/apache/cxf/transport/http_undertow/UndertowHTTPServerEngine.java
@@ -43,6 +43,7 @@ import org.apache.cxf.common.util.SystemPropertyAction;
import org.apache.cxf.configuration.jsse.TLSServerParameters;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.transport.HttpUriMapper;
+import org.apache.cxf.transport.http.HttpServerEngineSupport;
import org.apache.cxf.transport.https.AliasedX509ExtendedKeyManager;
import org.xnio.Options;
import org.xnio.Sequence;
@@ -64,10 +65,14 @@ import io.undertow.servlet.handlers.ServletPathMatches;
import io.undertow.util.CopyOnWriteMap;
-public class UndertowHTTPServerEngine implements ServerEngine {
+public class UndertowHTTPServerEngine implements ServerEngine, HttpServerEngineSupport {
public static final String DO_NOT_CHECK_URL_PROP = "org.apache.cxf.transports.http_undertow.DontCheckUrl";
+ /**
+ * Please use {@link HttpServerEngineSupport#ENABLE_HTTP2} instead.
+ */
+ @Deprecated
public static final String ENABLE_HTTP2_PROP = "org.apache.cxf.transports.http_undertow.EnableHttp2";
public static final String ENABLE_RECORD_REQUEST_START_TIME_PROP =
@@ -183,6 +188,17 @@ public class UndertowHTTPServerEngine implements ServerEngine {
servantCount = servantCount + 1;
}
+ @Override
+ public boolean isHttp2Enabled(Bus bus) {
+ Object prop = null;
+ if (bus != null) {
+ prop = bus.getProperty(ENABLE_HTTP2_PROP);
+ }
+ if (prop == null) {
+ prop = SystemPropertyAction.getPropertyOrNull(ENABLE_HTTP2_PROP);
+ }
+ return PropertyUtils.isTrue(prop) || HttpServerEngineSupport.super.isHttp2Enabled(bus);
+ }
private ServletContext buildServletContext(String contextName)
throws ServletException {
@@ -203,7 +219,7 @@ public class UndertowHTTPServerEngine implements ServerEngine {
private Undertow createServer(URL url, UndertowHTTPHandler undertowHTTPHandler) throws Exception {
Undertow.Builder result = Undertow.builder();
result.setServerOption(UndertowOptions.IDLE_TIMEOUT, getMaxIdleTime());
- if (this.shouldEnableHttp2(undertowHTTPHandler.getBus())) {
+ if (this.isHttp2Enabled(undertowHTTPHandler.getBus())) {
result.setServerOption(UndertowOptions.ENABLE_HTTP2, Boolean.TRUE);
}
if (this.shouldEnableRecordRequestStartTime(undertowHTTPHandler.getBus())) {
@@ -313,18 +329,6 @@ public class UndertowHTTPServerEngine implements ServerEngine {
return !PropertyUtils.isTrue(prop);
}
- private boolean shouldEnableHttp2(Bus bus) {
-
- Object prop = null;
- if (bus != null) {
- prop = bus.getProperty(ENABLE_HTTP2_PROP);
- }
- if (prop == null) {
- prop = SystemPropertyAction.getPropertyOrNull(ENABLE_HTTP2_PROP);
- }
- return PropertyUtils.isTrue(prop);
- }
-
private boolean shouldEnableRecordRequestStartTime(Bus bus) {
Object prop = null;
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpServerEngineSupport.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpServerEngineSupport.java
new file mode 100644
index 0000000..fd4762e
--- /dev/null
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpServerEngineSupport.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.transport.http;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.util.PropertyUtils;
+import org.apache.cxf.common.util.SystemPropertyAction;
+
+/**
+ * Support class for HTTP server engines: holds common properties and commonly
+ * used methods, shared across all HTTP server engine implementations (Tomcat, Jetty,
+ * Undertow, Netty, ...).
+ */
+public interface HttpServerEngineSupport {
+ String ENABLE_HTTP2 = "org.apache.cxf.transports.http2.enabled";
+
+ default boolean isHttp2Enabled(Bus bus) {
+ Object value = null;
+
+ if (bus != null) {
+ value = bus.getProperty(ENABLE_HTTP2);
+ }
+
+ if (value == null) {
+ value = SystemPropertyAction.getPropertyOrNull(ENABLE_HTTP2);
+ }
+
+ return PropertyUtils.isTrue(value);
+ }
+}
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/SSLContextInitParameters.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/SSLContextInitParameters.java
new file mode 100644
index 0000000..2182962
--- /dev/null
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/SSLContextInitParameters.java
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.transport.https;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.TrustManager;
+
+public final class SSLContextInitParameters {
+ private KeyManager[] keyManagers;
+ private TrustManager[] trustManagers;
+
+ public void setKeyManagers(KeyManager[] keyManagers) {
+ this.keyManagers = keyManagers;
+ }
+
+ public KeyManager[] getKeyManagers() {
+ return keyManagers;
+ }
+
+ public void setTrustManagers(TrustManager[] trustManagers) {
+ this.trustManagers = trustManagers;
+ }
+
+ public TrustManager[] getTrustManagers() {
+ return trustManagers;
+ }
+}
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/SSLUtils.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/SSLUtils.java
index 49242ac..f43253f 100644
--- a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/SSLUtils.java
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/SSLUtils.java
@@ -59,16 +59,11 @@ public final class SSLUtils {
}
return verifier;
}
-
- public static SSLContext getSSLContext(TLSParameterBase parameters) throws GeneralSecurityException {
- // TODO do we need to cache the context
- String provider = parameters.getJsseProvider();
-
- String protocol = parameters.getSecureSocketProtocol() != null ? parameters
- .getSecureSocketProtocol() : "TLS";
-
- SSLContext ctx = provider == null ? SSLContext.getInstance(protocol) : SSLContext
- .getInstance(protocol, provider);
+
+ public static SSLContextInitParameters getSSLContextInitParameters(TLSParameterBase parameters)
+ throws GeneralSecurityException {
+
+ final SSLContextInitParameters contextParameters = new SSLContextInitParameters();
KeyManager[] keyManagers = parameters.getKeyManagers();
if (keyManagers == null && parameters instanceof TLSClientParameters) {
@@ -80,8 +75,25 @@ public final class SSLUtils {
if (trustManagers == null && parameters instanceof TLSClientParameters) {
trustManagers = org.apache.cxf.configuration.jsse.SSLUtils.getDefaultTrustStoreManagers(LOG);
}
+
+ contextParameters.setKeyManagers(configuredKeyManagers);
+ contextParameters.setTrustManagers(trustManagers);
+
+ return contextParameters;
+ }
+
+ public static SSLContext getSSLContext(TLSParameterBase parameters) throws GeneralSecurityException {
+ // TODO do we need to cache the context
+ String provider = parameters.getJsseProvider();
+
+ String protocol = parameters.getSecureSocketProtocol() != null ? parameters
+ .getSecureSocketProtocol() : "TLS";
+
+ SSLContext ctx = provider == null ? SSLContext.getInstance(protocol) : SSLContext
+ .getInstance(protocol, provider);
- ctx.init(configuredKeyManagers, trustManagers, parameters.getSecureRandom());
+ final SSLContextInitParameters initParams = getSSLContextInitParameters(parameters);
+ ctx.init(initParams.getKeyManagers(), initParams.getTrustManagers(), parameters.getSecureRandom());
if (parameters instanceof TLSClientParameters && ctx.getClientSessionContext() != null) {
ctx.getClientSessionContext().setSessionTimeout(((TLSClientParameters)parameters).getSslCacheTimeout());
diff --git a/systests/pom.xml b/systests/pom.xml
index 5ab0ee1..eb29fa8 100644
--- a/systests/pom.xml
+++ b/systests/pom.xml
@@ -55,5 +55,6 @@
<module>rs-sse</module>
<module>microprofile</module>
<module>spring-boot</module>
+ <module>transport-netty</module>
</modules>
</project>
diff --git a/systests/transports/pom.xml b/systests/transport-netty/pom.xml
similarity index 51%
copy from systests/transports/pom.xml
copy to systests/transport-netty/pom.xml
index a7f4ec6..f5aa5f5 100644
--- a/systests/transports/pom.xml
+++ b/systests/transport-netty/pom.xml
@@ -26,14 +26,15 @@
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.cxf.systests</groupId>
- <artifactId>cxf-systests-transports</artifactId>
- <name>Apache CXF Transport System Tests</name>
- <description>Apache CXF Transport System Tests</description>
+ <artifactId>cxf-systests-transport-netty</artifactId>
+ <name>Apache CXF Netty Transport System Tests</name>
+ <description>Apache CXF Netty Transport System Tests</description>
<url>https://cxf.apache.org</url>
+
<properties>
- <cxf.surefire.fork.vmargs>-Djdk.http.auth.tunneling.disabledSchemes=""</cxf.surefire.fork.vmargs>
- <cxf.module.name>org.apache.cxf.systests.transport</cxf.module.name>
+ <cxf.module.name>org.apache.cxf.systests.transport.netty</cxf.module.name>
</properties>
+
<build>
<testSourceDirectory>${basedir}/src/test/java</testSourceDirectory>
<testResources>
@@ -103,63 +104,38 @@
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-frontend-jaxws</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-frontend-js</artifactId>
+ <artifactId>cxf-rt-transports-http</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-bindings-soap</artifactId>
+ <artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-databinding-aegis</artifactId>
+ <artifactId>cxf-rt-rs-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-transports-http</artifactId>
- <version>${project.version}</version>
+ <groupId>com.fasterxml.jackson.jaxrs</groupId>
+ <artifactId>jackson-jaxrs-json-provider</artifactId>
+ <scope>test</scope>
</dependency>
<dependency>
- <groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-transports-http-hc</artifactId>
- <version>${project.version}</version>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>jcl-over-slf4j</artifactId>
- </exclusion>
- </exclusions>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-annotations</artifactId>
+ <scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-transports-udp</artifactId>
+ <artifactId>cxf-rt-rs-extension-providers</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-servlet</artifactId>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-webapp</artifactId>
- </dependency>
- <dependency>
<groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-transports-http-jetty</artifactId>
+ <artifactId>cxf-rt-transports-http-netty-server</artifactId>
<version>${project.version}</version>
- <exclusions>
- <exclusion>
- <groupId>javax.servlet</groupId>
- <artifactId>javax.servlet-api</artifactId>
- </exclusion>
- </exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
@@ -173,188 +149,55 @@
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-transports-local</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-ws-policy</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-frontend-jaxrs</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-features-logging</artifactId>
+ <artifactId>cxf-testutils</artifactId>
<version>${project.version}</version>
+ <scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-testutils</artifactId>
<version>${project.version}</version>
<scope>test</scope>
+ <classifier>keys</classifier>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
- <artifactId>cxf-testutils</artifactId>
+ <artifactId>cxf-rt-features-logging</artifactId>
<version>${project.version}</version>
<scope>test</scope>
- <classifier>keys</classifier>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
- <scope>test</scope>
<version>${cxf.spring.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-webmvc</artifactId>
<scope>test</scope>
- <version>${cxf.spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
- <scope>test</scope>
<version>${cxf.spring.version}</version>
+ <scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
- <scope>test</scope>
<version>${cxf.spring.version}</version>
- </dependency>
- <dependency>
- <groupId>httpunit</groupId>
- <artifactId>httpunit</artifactId>
- <version>1.7</version>
<scope>test</scope>
- <exclusions>
- <exclusion>
- <groupId>javax.servlet</groupId>
- <artifactId>servlet-api</artifactId>
- </exclusion>
- <exclusion>
- <groupId>xerces</groupId>
- <artifactId>xercesImpl</artifactId>
- </exclusion>
- <exclusion>
- <groupId>xerces</groupId>
- <artifactId>xmlParserAPIs</artifactId>
- </exclusion>
- </exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
- <groupId>org.springframework</groupId>
- <artifactId>${cxf.spring.mock}</artifactId>
- <version>${cxf.spring.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.security</groupId>
- <artifactId>spring-security-config</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework.security</groupId>
- <artifactId>spring-security-web</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <version>${cxf.spring.boot.version}</version>
- <scope>test</scope>
- <exclusions>
- <exclusion>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-logging</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- <version>${cxf.spring.boot.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-rs-client</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.apache.geronimo.specs</groupId>
- <artifactId>geronimo-j2ee-connector_1.5_spec</artifactId>
- </dependency>
- <!-- make http://java.sun.com/dtd/web-app_2_3.dtd et al locally available during the tests -->
- <dependency>
- <groupId>${cxf.servlet-api.group}</groupId>
- <artifactId>${cxf.servlet-api.artifact}</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mozilla</groupId>
- <artifactId>rhino</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <!-- This needs to be here as httpunit requires some classes that are only in 1.7R2 or lower -->
- <groupId>rhino</groupId>
- <artifactId>js</artifactId>
- <version>1.7R2</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.codehaus.jettison</groupId>
- <artifactId>jettison</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.littleshoot</groupId>
- <artifactId>littleproxy</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.glassfish.jaxb</groupId>
- <artifactId>jaxb-xjc</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bctls-jdk15on</artifactId>
- <version>${cxf.bcprov.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.junit.vintage</groupId>
- <artifactId>junit-vintage-engine</artifactId>
- <version>${cxf.junit5.version}</version>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-codec-http2</artifactId>
+ <version>${cxf.netty.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
-
- <profiles>
- <profile>
- <id>jdk16</id>
- <activation>
- <jdk>[16,)</jdk>
- </activation>
- <properties>
- <cxf.surefire.fork.vmargs>--add-opens java.xml/com.sun.org.apache.xerces.internal.dom=ALL-UNNAMED -Djdk.http.auth.tunneling.disabledSchemes=""</cxf.surefire.fork.vmargs>
- </properties>
- </profile>
- </profiles>
</project>
diff --git a/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/AbstractBookServerHttp2.java b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/AbstractBookServerHttp2.java
new file mode 100644
index 0000000..1a07c9d
--- /dev/null
+++ b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/AbstractBookServerHttp2.java
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http2.netty;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.bus.spring.SpringBusFactory;
+import org.apache.cxf.customer.book.Book;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.jaxrs.provider.StreamingResponseProvider;
+import org.apache.cxf.testutil.common.AbstractBusTestServerBase;
+import org.apache.cxf.transport.http.HttpServerEngineSupport;
+
+abstract class AbstractBookServerHttp2 extends AbstractBusTestServerBase {
+ org.apache.cxf.endpoint.Server server;
+
+ private final String port;
+ private final String context;
+ private final String scheme;
+
+ AbstractBookServerHttp2(String port, String context, String scheme) {
+ this.port = port;
+ this.context = context;
+ this.scheme = scheme;
+ }
+
+ protected void run() {
+ SpringBusFactory factory = new SpringBusFactory();
+ Bus bus = factory.createBus(context);
+ bus.setProperty(HttpServerEngineSupport.ENABLE_HTTP2, true);
+ setBus(bus);
+ JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+ sf.setBus(bus);
+ sf.setResourceClasses(BookStore.class);
+ sf.setProvider(new StreamingResponseProvider<Book>());
+ sf.setResourceProvider(BookStore.class,
+ new SingletonResourceProvider(new BookStore(), true));
+ sf.setAddress(scheme + "://localhost:" + port + "/http2");
+ server = sf.create();
+ }
+
+ public void tearDown() throws Exception {
+ server.stop();
+ server.destroy();
+ server = null;
+ }
+}
diff --git a/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/AbstractNettyClientServerHttp2Test.java b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/AbstractNettyClientServerHttp2Test.java
new file mode 100644
index 0000000..1263377
--- /dev/null
+++ b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/AbstractNettyClientServerHttp2Test.java
@@ -0,0 +1,118 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http2.netty;
+
+import javax.ws.rs.core.Response;
+
+import org.apache.cxf.configuration.jsse.TLSClientParameters;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.systest.http2.netty.Http2TestClient.ClientResponse;
+import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
+import org.apache.cxf.transport.http.HTTPConduit;
+import org.apache.cxf.transport.https.InsecureTrustManager;
+
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+abstract class AbstractNettyClientServerHttp2Test extends AbstractBusClientServerTestBase {
+ @Test
+ public void testBookNotFoundWithHttp2() throws Exception {
+ final Http2TestClient client = new Http2TestClient(isSecure());
+
+ final ClientResponse response = client
+ .request(getAddress())
+ .accept("text/plain")
+ .path(getContext() + "/web/bookstore/notFound")
+ .http2()
+ .get();
+
+ assertThat(response.getResponseCode(), equalTo(404));
+ assertThat(response.getProtocol(), equalTo("HTTP/2.0"));
+ }
+
+ @Test
+ public void testBookWithHttp2() throws Exception {
+ final Http2TestClient client = new Http2TestClient(isSecure());
+
+ final ClientResponse response = client
+ .request(getAddress())
+ .accept("text/plain")
+ .path(getContext() + "/web/bookstore/booknames")
+ .http2()
+ .get();
+
+ assertThat(response.getResponseCode(), equalTo(200));
+ assertThat(response.getProtocol(), equalTo("HTTP/2.0"));
+ assertEquals("CXF in Action", response.getBody());
+ }
+
+ @Test
+ public void testGetBookStreamHttp2() throws Exception {
+ final Http2TestClient client = new Http2TestClient(isSecure());
+
+ final ClientResponse response = client
+ .request(getAddress())
+ .accept("application/xml")
+ .path(getContext() + "/web/bookstore/bookstream")
+ .http2()
+ .get();
+
+ assertThat(response.getResponseCode(), equalTo(200));
+ assertThat(response.getProtocol(), equalTo("HTTP/2.0"));
+ assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ + "<Book><id>1</id><name>Book1</name></Book>", response.getBody());
+ }
+
+ @Test
+ public void testBookWithHttp() throws Exception {
+ final WebClient wc = WebClient
+ .create(getAddress() + getContext() + "/web/bookstore/booknames")
+ .accept("text/plain");
+
+ if (isSecure()) {
+ final HTTPConduit conduit = WebClient.getConfig(wc).getHttpConduit();
+ TLSClientParameters params = conduit.getTlsClientParameters();
+
+ if (params == null) {
+ params = new TLSClientParameters();
+ conduit.setTlsClientParameters(params);
+ }
+
+ // Create TrustManager instance which trusts all clients and servers
+ params.setTrustManagers(InsecureTrustManager.getNoOpX509TrustManagers());
+ params.setDisableCNCheck(true);
+ }
+
+ try (Response resp = wc.get()) {
+ assertThat(resp.getStatus(), equalTo(200));
+ assertEquals("CXF in Action", resp.readEntity(String.class));
+ }
+ }
+
+ protected abstract String getAddress();
+ protected abstract String getContext();
+
+ protected boolean isSecure() {
+ return getAddress().startsWith("https");
+ }
+}
\ No newline at end of file
diff --git a/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/Book.java b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/Book.java
new file mode 100644
index 0000000..9d95c5d
--- /dev/null
+++ b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/Book.java
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http2.netty;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
+
+@JsonTypeInfo(use = Id.CLASS, include = As.PROPERTY, property = "class")
+@XmlRootElement(name = "Book")
+public class Book {
+ private String name;
+ private long id;
+
+ public Book() {
+ }
+
+ public Book(String name, long id) {
+ this.name = name;
+ this.id = id;
+ }
+
+ public void setName(String n) {
+ name = n;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setId(long i) {
+ id = i;
+ }
+ public long getId() {
+ return id;
+ }
+
+ @PUT
+ public void cloneState(Book book) {
+ id = book.getId();
+ name = book.getName();
+ }
+
+ @GET
+ public Book retrieveState() {
+ return this;
+ }
+}
diff --git a/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/BookServerHttp2.java b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/BookServerHttp2.java
new file mode 100644
index 0000000..51be2c2
--- /dev/null
+++ b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/BookServerHttp2.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http2.netty;
+
+public class BookServerHttp2 extends AbstractBookServerHttp2 {
+ public static final String PORT = allocatePort(BookServerHttp2.class);
+
+ org.apache.cxf.endpoint.Server server;
+
+ public BookServerHttp2() {
+ this(PORT);
+ }
+
+ public BookServerHttp2(String port) {
+ super(port, "org/apache/cxf/systest/http2_netty/server-tls.xml", "https");
+ }
+
+ public static void main(String[] args) {
+ try {
+ BookServerHttp2 s = new BookServerHttp2();
+ s.start();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ System.exit(-1);
+ } finally {
+ System.out.println("done!");
+ }
+ }
+
+}
diff --git a/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/BookServerHttp2c.java b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/BookServerHttp2c.java
new file mode 100644
index 0000000..73ca51c
--- /dev/null
+++ b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/BookServerHttp2c.java
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http2.netty;
+
+public class BookServerHttp2c extends AbstractBookServerHttp2 {
+ public static final String PORT = allocatePort(BookServerHttp2c.class);
+
+ public BookServerHttp2c() {
+ this(PORT);
+ }
+
+ public BookServerHttp2c(String port) {
+ super(port, "org/apache/cxf/systest/http2_netty/server.xml", "http");
+ }
+
+ public static void main(String[] args) {
+ try {
+ BookServerHttp2c s = new BookServerHttp2c();
+ s.start();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ System.exit(-1);
+ } finally {
+ System.out.println("done!");
+ }
+ }
+
+}
diff --git a/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/BookStore.java b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/BookStore.java
new file mode 100644
index 0000000..4b4e1ca
--- /dev/null
+++ b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/BookStore.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http2.netty;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+import org.apache.cxf.jaxrs.ext.StreamingResponse;
+
+@Path("/web/bookstore")
+public class BookStore {
+ private static ExecutorService executor = Executors.newSingleThreadExecutor();
+
+ @GET
+ @Path("/booknames")
+ @Produces("text/plain")
+ public byte[] getBookName() {
+ return "CXF in Action".getBytes();
+ }
+
+ @GET
+ @Path("/bookstream")
+ @Produces("application/xml")
+ public StreamingResponse<Book> getBookStream() {
+ return new StreamingResponse<Book>() {
+ public void writeTo(final StreamingResponse.Writer<Book> out) throws IOException {
+ out.write(new Book("Book1", 1));
+ executor.execute(new Runnable() {
+ public void run() {
+ try {
+ for (int i = 2; i <= 5; i++) {
+ Thread.sleep(500);
+ out.write(new Book("Book" + i, i));
+ out.getEntityStream().flush();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+ };
+ }
+}
+
+
diff --git a/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/Http2TestClient.java b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/Http2TestClient.java
new file mode 100644
index 0000000..32e4e64
--- /dev/null
+++ b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/Http2TestClient.java
@@ -0,0 +1,401 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http2.netty;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.core.MediaType;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.ChannelPromise;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.http.DefaultFullHttpRequest;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.FullHttpResponse;
+import io.netty.handler.codec.http.HttpClientCodec;
+import io.netty.handler.codec.http.HttpClientUpgradeHandler;
+import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpVersion;
+import io.netty.handler.codec.http2.DefaultHttp2Connection;
+import io.netty.handler.codec.http2.DelegatingDecompressorFrameListener;
+import io.netty.handler.codec.http2.Http2ClientUpgradeCodec;
+import io.netty.handler.codec.http2.Http2Connection;
+import io.netty.handler.codec.http2.Http2ConnectionHandler;
+import io.netty.handler.codec.http2.Http2SecurityUtil;
+import io.netty.handler.codec.http2.Http2Settings;
+import io.netty.handler.codec.http2.HttpConversionUtil;
+import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder;
+import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder;
+import io.netty.handler.ssl.ApplicationProtocolConfig;
+import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
+import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
+import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
+import io.netty.handler.ssl.ApplicationProtocolNames;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslProvider;
+import io.netty.handler.ssl.SupportedCipherSuiteFilter;
+import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
+import io.netty.util.internal.PlatformDependent;
+
+/**
+ * TODO: Use CXF client once https://issues.apache.org/jira/browse/CXF-8606 is dones
+ */
+public class Http2TestClient implements AutoCloseable {
+ private final SslContext ssl;
+
+ public Http2TestClient(boolean secure) throws Exception {
+ if (secure) {
+ ssl = SslContext.newClientContext(
+ SslProvider.JDK,
+ null,
+ InsecureTrustManagerFactory.INSTANCE,
+ Http2SecurityUtil.CIPHERS,
+ SupportedCipherSuiteFilter.INSTANCE,
+ new ApplicationProtocolConfig(
+ Protocol.ALPN,
+ SelectorFailureBehavior.FATAL_ALERT,
+ SelectedListenerFailureBehavior.FATAL_ALERT,
+ ApplicationProtocolNames.HTTP_2,
+ ApplicationProtocolNames.HTTP_1_1),
+ 0, 0);
+ } else {
+ ssl = null;
+ }
+ }
+
+ public static class ClientResponse {
+ private String body;
+ private String protocol;
+ private int responseCode;
+
+ public ClientResponse(int responseCode, String protocol) {
+ this.responseCode = responseCode;
+ this.protocol = protocol;
+ }
+
+ public void setBody(String body) {
+ this.body = body;
+ }
+
+ public String getBody() {
+ return body;
+ }
+
+ public void setResponseCode(int rc) {
+ this.responseCode = rc;
+ }
+
+ public int getResponseCode() {
+ return responseCode;
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+ }
+
+ public class RequestBuilder {
+ private final String address;
+ private String path = "";
+ private String accept = MediaType.WILDCARD;
+ private HttpVersion version = HttpVersion.HTTP_1_1;
+
+ public RequestBuilder(final String address) {
+ this.address = address;
+ }
+
+ public RequestBuilder path(final String p) {
+ this.path = p;
+ return this;
+ }
+
+
+ public RequestBuilder accept(final String a) {
+ this.accept = a;
+ return this;
+ }
+
+ public RequestBuilder http2() {
+ version = null;
+ return this;
+ }
+
+ public ClientResponse get() throws Exception {
+ return request(address, path, version, HttpMethod.GET, accept);
+ }
+ }
+
+ public RequestBuilder request(final String address) throws IOException {
+ return new RequestBuilder(address);
+ }
+
+ public ClientResponse request(final String address, final String path,
+ final HttpVersion version, final HttpMethod method, final String accept)
+ throws Exception {
+
+ final URI uri = URI.create(address);
+
+ final Http2ClientInitializer initializer = new Http2ClientInitializer(Integer.MAX_VALUE);
+ final NioEventLoopGroup worker = new NioEventLoopGroup();
+
+ final Bootstrap bootstrap = new Bootstrap();
+ bootstrap.group(worker);
+ bootstrap.channel(NioSocketChannel.class);
+ bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
+ bootstrap.remoteAddress(uri.getHost(), uri.getPort());
+ bootstrap.handler(initializer);
+
+ final Channel channel = bootstrap.connect().syncUninterruptibly().channel();
+ final HttpResponseHandler responseHandler = initializer.getResponseHandler();
+ final Http2SettingsHandler http2SettingsHandler = initializer.getSettingsHandler();
+
+ try {
+ final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, method, path);
+ request.headers().add(HttpHeaderNames.HOST, uri.getHost());
+ request.headers().add(HttpHeaderNames.ACCEPT, accept);
+ request.headers().add(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), uri.getScheme());
+
+ http2SettingsHandler.awaitSettings(5, TimeUnit.SECONDS);
+ responseHandler.put(3, channel.write(request), channel.newPromise());
+
+ channel.flush();
+ responseHandler.awaitResponses(15, TimeUnit.SECONDS);
+ } finally {
+ channel.close().awaitUninterruptibly();
+ worker.shutdownGracefully();
+ }
+
+
+ final List<ClientResponse> responses = responseHandler.responses();
+ if (responses.size() != 1) {
+ throw new IllegalStateException("Expected exactly one response, but got 0 or more");
+ }
+
+ return responses.get(0);
+ }
+
+ @Override
+ public void close() throws Exception {
+ }
+
+ private class Http2SettingsHandler extends SimpleChannelInboundHandler<Http2Settings> {
+ private ChannelPromise promise;
+
+ Http2SettingsHandler(ChannelPromise promise) {
+ this.promise = promise;
+ }
+
+ /**
+ * Wait for this handler to be added after the upgrade to HTTP/2, and for initial preface
+ * handshake to complete.
+ */
+ void awaitSettings(long timeout, TimeUnit unit) throws Exception {
+ if (!promise.awaitUninterruptibly(timeout, unit)) {
+ throw new IllegalStateException("Timed out waiting for settings");
+ }
+ if (!promise.isSuccess()) {
+ throw new RuntimeException(promise.cause());
+ }
+ }
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, Http2Settings msg) throws Exception {
+ promise.setSuccess();
+ ctx.pipeline().remove(this);
+ }
+ }
+
+ private class HttpResponseHandler extends SimpleChannelInboundHandler<FullHttpResponse> {
+ private final Map<Integer, Entry<ChannelFuture, ChannelPromise>> streamidPromiseMap;
+ private final List<ClientResponse> responses = new CopyOnWriteArrayList<>();
+
+ HttpResponseHandler() {
+ streamidPromiseMap = PlatformDependent.newConcurrentHashMap();
+ }
+
+ Entry<ChannelFuture, ChannelPromise> put(int streamId, ChannelFuture writeFuture, ChannelPromise promise) {
+ return streamidPromiseMap.put(streamId, new SimpleEntry<>(writeFuture, promise));
+ }
+
+ void awaitResponses(long timeout, TimeUnit unit) {
+ final Iterator<Entry<Integer, Entry<ChannelFuture, ChannelPromise>>> itr = streamidPromiseMap
+ .entrySet()
+ .iterator();
+
+ while (itr.hasNext()) {
+ final Entry<Integer, Entry<ChannelFuture, ChannelPromise>> entry = itr.next();
+
+ final ChannelFuture writeFuture = entry.getValue().getKey();
+ if (!writeFuture.awaitUninterruptibly(timeout, unit)) {
+ throw new IllegalStateException("Timed out waiting to write for stream id " + entry.getKey());
+ }
+
+ if (!writeFuture.isSuccess()) {
+ throw new RuntimeException(writeFuture.cause());
+ }
+
+ final ChannelPromise promise = entry.getValue().getValue();
+ if (!promise.awaitUninterruptibly(timeout, unit)) {
+ throw new IllegalStateException("Timed out waiting for response on stream id " + entry.getKey());
+ }
+
+ if (!promise.isSuccess()) {
+ throw new RuntimeException(promise.cause());
+ }
+
+ itr.remove();
+ }
+ }
+
+ List<ClientResponse> responses() {
+ return responses;
+ }
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception {
+ Integer streamId = msg.headers().getInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text());
+ if (streamId == null) {
+ System.err.println("HttpResponseHandler unexpected message received: " + msg);
+ return;
+ }
+
+ final Entry<ChannelFuture, ChannelPromise> entry = streamidPromiseMap.get(streamId);
+ if (entry == null) {
+ System.err.println("Message received for unknown stream id " + streamId);
+ } else {
+ final ByteBuf content = msg.content();
+ final ClientResponse response = new ClientResponse(msg.status().code(), "HTTP/2.0");
+
+ if (content.isReadable()) {
+ int contentLength = content.readableBytes();
+ byte[] arr = new byte[contentLength];
+ content.readBytes(arr);
+ response.setBody(new String(arr));
+ }
+
+ responses.add(response);
+ entry.getValue().setSuccess();
+ }
+ }
+ }
+
+ private class Http2ClientInitializer extends ChannelInitializer<SocketChannel> {
+ private final int maxContentLength;
+ private HttpResponseHandler responseHandler;
+ private Http2SettingsHandler settingsHandler;
+ private Http2ConnectionHandler connectionHandler;
+
+ Http2ClientInitializer(int maxContentLength) {
+ this.maxContentLength = maxContentLength;
+ }
+
+ @Override
+ public void initChannel(SocketChannel ch) throws Exception {
+ final Http2Connection connection = new DefaultHttp2Connection(false);
+
+ responseHandler = new HttpResponseHandler();
+ settingsHandler = new Http2SettingsHandler(ch.newPromise());
+
+ connectionHandler = new HttpToHttp2ConnectionHandlerBuilder()
+ .connection(connection)
+ .frameListener(new DelegatingDecompressorFrameListener(connection,
+ new InboundHttp2ToHttpAdapterBuilder(connection)
+ .maxContentLength(maxContentLength)
+ .propagateSettings(true)
+ .build()))
+ .build();
+
+ if (ssl != null) {
+ ch.pipeline().addLast(ssl.newHandler(ch.alloc()));
+ ch.pipeline().addLast(connectionHandler);
+ ch.pipeline().addLast(settingsHandler);
+ ch.pipeline().addLast(responseHandler);
+ } else {
+ final HttpClientCodec sourceCodec = new HttpClientCodec();
+ final Http2ClientUpgradeCodec upgradeCodec = new Http2ClientUpgradeCodec(connectionHandler);
+ final HttpClientUpgradeHandler upgradeHandler = new HttpClientUpgradeHandler(sourceCodec,
+ upgradeCodec, 65536);
+
+ ch.pipeline().addLast(sourceCodec);
+ ch.pipeline().addLast(upgradeHandler);
+ ch.pipeline().addLast(new UpgradeRequestHandler(settingsHandler, responseHandler));
+ }
+ }
+
+ HttpResponseHandler getResponseHandler() {
+ return responseHandler;
+ }
+
+ Http2SettingsHandler getSettingsHandler() {
+ return settingsHandler;
+ }
+ }
+
+ /**
+ * A handler that triggers the cleartext upgrade to HTTP/2 by sending an
+ * initial HTTP request.
+ */
+ private class UpgradeRequestHandler extends ChannelInboundHandlerAdapter {
+ private final Http2SettingsHandler settingsHandler;
+ private final HttpResponseHandler responseHandler;
+
+ UpgradeRequestHandler(final Http2SettingsHandler settingsHandler, final HttpResponseHandler responseHandler) {
+ this.settingsHandler = settingsHandler;
+ this.responseHandler = responseHandler;
+ }
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) throws Exception {
+ final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
+ request.headers().add(HttpHeaderNames.HOST, "localhost");
+ request.headers().add(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http");
+
+ ctx.writeAndFlush(request);
+ ctx.fireChannelActive();
+
+ ctx.pipeline().remove(this);
+ ctx.pipeline().addLast(settingsHandler);
+ ctx.pipeline().addLast(responseHandler);
+ }
+ }
+}
diff --git a/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/NettyClientServerHttp2Test.java b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/NettyClientServerHttp2Test.java
new file mode 100644
index 0000000..d29fe7a
--- /dev/null
+++ b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/NettyClientServerHttp2Test.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http2.netty;
+
+import org.apache.cxf.jaxrs.model.AbstractResourceInfo;
+
+import org.junit.BeforeClass;
+
+import static org.junit.Assert.assertTrue;
+
+public class NettyClientServerHttp2Test extends AbstractNettyClientServerHttp2Test {
+ private static final String PORT = BookServerHttp2.PORT;
+
+ @BeforeClass
+ public static void startServers() throws Exception {
+ AbstractResourceInfo.clearAllMaps();
+ assertTrue("server did not launch correctly", launchServer(new BookServerHttp2()));
+ createStaticBus();
+ }
+
+
+ @Override
+ protected String getAddress() {
+ return "https://localhost:" + PORT;
+ }
+
+ @Override
+ protected String getContext() {
+ return "/http2";
+ }
+}
diff --git a/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/NettyClientServerHttp2cTest.java b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/NettyClientServerHttp2cTest.java
new file mode 100644
index 0000000..b7d7f5a
--- /dev/null
+++ b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/NettyClientServerHttp2cTest.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http2.netty;
+
+import org.apache.cxf.jaxrs.model.AbstractResourceInfo;
+
+import org.junit.BeforeClass;
+
+import static org.junit.Assert.assertTrue;
+
+public class NettyClientServerHttp2cTest extends AbstractNettyClientServerHttp2Test {
+ private static final String PORT = BookServerHttp2c.PORT;
+
+ @BeforeClass
+ public static void startServers() throws Exception {
+ AbstractResourceInfo.clearAllMaps();
+ assertTrue("server did not launch correctly", launchServer(new BookServerHttp2c()));
+ createStaticBus();
+ }
+
+ @Override
+ protected String getAddress() {
+ return "http://localhost:" + PORT;
+ }
+
+ @Override
+ protected String getContext() {
+ return "/http2";
+ }
+}
diff --git a/systests/transport-netty/src/test/resources/org/apache/cxf/systest/http2_netty/server-tls.xml b/systests/transport-netty/src/test/resources/org/apache/cxf/systest/http2_netty/server-tls.xml
new file mode 100644
index 0000000..dd9ebf7
--- /dev/null
+++ b/systests/transport-netty/src/test/resources/org/apache/cxf/systest/http2_netty/server-tls.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:http-netty="http://cxf.apache.org/transports/http-netty-server/configuration"
+ xmlns:sec="http://cxf.apache.org/configuration/security"
+ xsi:schemaLocation="http://cxf.apache.org/transports/http-netty-server/configuration
+ http://cxf.apache.org/schemas/configuration/http-netty-server.xsd
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://cxf.apache.org/configuration/security
+ http://cxf.apache.org/schemas/configuration/security.xsd">
+ <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
+ <http-netty:engine-factory bus="cxf">
+ <http-netty:engine port="${testutil.ports.org.apache.cxf.systest.http2.netty.BookServerHttp2}">
+ <http-netty:tlsServerParameters>
+ <sec:keyManagers keyPassword="password">
+ <sec:keyStore type="jks" password="password" resource="keys/Bethal.jks"/>
+ </sec:keyManagers>
+ <sec:clientAuthentication want="false" required="false"/>
+ </http-netty:tlsServerParameters>
+ </http-netty:engine>
+ </http-netty:engine-factory>
+</beans>
\ No newline at end of file
diff --git a/systests/transport-netty/src/test/resources/org/apache/cxf/systest/http2_netty/server.xml b/systests/transport-netty/src/test/resources/org/apache/cxf/systest/http2_netty/server.xml
new file mode 100644
index 0000000..5ff63d1
--- /dev/null
+++ b/systests/transport-netty/src/test/resources/org/apache/cxf/systest/http2_netty/server.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:http-netty="http://cxf.apache.org/transports/http-netty-server/configuration"
+ xsi:schemaLocation="http://cxf.apache.org/transports/http-netty-server/configuration
+ http://cxf.apache.org/schemas/configuration/http-netty-server.xsd
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd">
+ <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
+ <http-netty:engine-factory bus="cxf">
+ <http-netty:engine port="${testutil.ports.org.apache.cxf.systest.http2.netty.BookServerHttp2c}">
+ </http-netty:engine>
+ </http-netty:engine-factory>
+</beans>
\ No newline at end of file
diff --git a/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/AbstractBookServerHttp2.java b/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/AbstractBookServerHttp2.java
new file mode 100644
index 0000000..e6601db
--- /dev/null
+++ b/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/AbstractBookServerHttp2.java
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http_undertow.http2;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.bus.spring.SpringBusFactory;
+import org.apache.cxf.customer.book.Book;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.jaxrs.provider.StreamingResponseProvider;
+import org.apache.cxf.systest.http_undertow.websocket.BookStorePerRequest;
+import org.apache.cxf.systest.http_undertow.websocket.BookStoreWebSocket;
+import org.apache.cxf.testutil.common.AbstractBusTestServerBase;
+import org.apache.cxf.transport.http.HttpServerEngineSupport;
+
+abstract class AbstractBookServerHttp2 extends AbstractBusTestServerBase {
+ org.apache.cxf.endpoint.Server server;
+
+ private final String port;
+ private final String scheme;
+ private final String context;
+
+ AbstractBookServerHttp2(String port, String context, String scheme) {
+ this.port = port;
+ this.context = context;
+ this.scheme = scheme;
+ }
+
+ protected void run() {
+ SpringBusFactory factory = new SpringBusFactory();
+ Bus bus = factory.createBus(context);
+ bus.setProperty(HttpServerEngineSupport.ENABLE_HTTP2, true);
+ setBus(bus);
+ JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+ sf.setBus(bus);
+ sf.setResourceClasses(BookStoreWebSocket.class, BookStorePerRequest.class);
+ sf.setProvider(new StreamingResponseProvider<Book>());
+ sf.setResourceProvider(BookStoreWebSocket.class,
+ new SingletonResourceProvider(new BookStoreWebSocket(), true));
+ sf.setAddress(scheme + "://localhost:" + port + "/http2");
+ server = sf.create();
+ }
+
+ public void tearDown() throws Exception {
+ server.stop();
+ server.destroy();
+ server = null;
+ }
+}
diff --git a/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/AbstractUndertowClientServerHttp2Test.java b/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/AbstractUndertowClientServerHttp2Test.java
new file mode 100644
index 0000000..c3b3470
--- /dev/null
+++ b/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/AbstractUndertowClientServerHttp2Test.java
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http_undertow.http2;
+
+import javax.ws.rs.core.Response;
+
+import org.apache.cxf.configuration.jsse.TLSClientParameters;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
+import org.apache.cxf.transport.http.HTTPConduit;
+import org.apache.cxf.transport.https.InsecureTrustManager;
+
+import io.undertow.client.ClientResponse;
+import io.undertow.util.Protocols;
+
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+abstract class AbstractUndertowClientServerHttp2Test extends AbstractBusClientServerTestBase {
+ @Test
+ public void testBookNotFoundWithHttp2() throws Exception {
+ final Http2TestClient client = new Http2TestClient(isSecure());
+
+ final ClientResponse response = client
+ .request(getAddress())
+ .accept("text/plain")
+ .path(getContext() + "/web/bookstore/notFound")
+ .http2()
+ .get();
+
+ assertThat(response.getResponseCode(), equalTo(404));
+ assertThat(response.getProtocol(), equalTo(Protocols.HTTP_2_0));
+ }
+
+ @Test
+ public void testBookWithHttp2() throws Exception {
+ final Http2TestClient client = new Http2TestClient(isSecure());
+
+ final ClientResponse response = client
+ .request(getAddress())
+ .accept("text/plain")
+ .path(getContext() + "/web/bookstore/booknames")
+ .http2()
+ .get();
+
+ assertThat(response.getResponseCode(), equalTo(200));
+ assertThat(response.getProtocol(), equalTo(Protocols.HTTP_2_0));
+ assertEquals("CXF in Action", client.getBody(response));
+ }
+
+ @Test
+ public void testGetBookStreamHttp2() throws Exception {
+ final Http2TestClient client = new Http2TestClient(isSecure());
+
+ final ClientResponse response = client
+ .request(getAddress())
+ .accept("application/json")
+ .path(getContext() + "/web/bookstore/bookstream")
+ .http2()
+ .get();
+
+ assertThat(response.getResponseCode(), equalTo(200));
+ assertThat(response.getProtocol(), equalTo(Protocols.HTTP_2_0));
+ assertEquals("{\"Book\":{\"id\":1,\"name\":\"WebSocket1\"}}",
+ client.getBody(response));
+ }
+
+ @Test
+ public void testBookWithHttp() throws Exception {
+ final WebClient wc = WebClient
+ .create(getAddress() + getContext() + "/web/bookstore/booknames")
+ .accept("text/plain");
+
+ if (isSecure()) {
+ final HTTPConduit conduit = WebClient.getConfig(wc).getHttpConduit();
+ TLSClientParameters params = conduit.getTlsClientParameters();
+
+ if (params == null) {
+ params = new TLSClientParameters();
+ conduit.setTlsClientParameters(params);
+ }
+
+ // Create TrustManager instance which trusts all clients and servers
+ params.setTrustManagers(InsecureTrustManager.getNoOpX509TrustManagers());
+ params.setDisableCNCheck(true);
+ }
+
+ try (Response resp = wc.get()) {
+ assertThat(resp.getStatus(), equalTo(200));
+ assertEquals("CXF in Action", resp.readEntity(String.class));
+ }
+ }
+
+ protected abstract String getAddress();
+ protected abstract String getContext();
+
+ protected boolean isSecure() {
+ return getAddress().startsWith("https");
+ }
+}
diff --git a/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/BookServerHttp2.java b/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/BookServerHttp2.java
new file mode 100644
index 0000000..a1f7872c
--- /dev/null
+++ b/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/BookServerHttp2.java
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http_undertow.http2;
+
+public class BookServerHttp2 extends AbstractBookServerHttp2 {
+ public static final String PORT = allocatePort(BookServerHttp2.class);
+
+ public BookServerHttp2() {
+ this(PORT);
+ }
+
+ public BookServerHttp2(String port) {
+ super(port, "org/apache/cxf/systest/http_undertow/http2/server-tls.xml", "https");
+ }
+
+ public static void main(String[] args) {
+ try {
+ BookServerHttp2 s = new BookServerHttp2();
+ s.start();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ System.exit(-1);
+ } finally {
+ System.out.println("done!");
+ }
+ }
+
+}
diff --git a/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/BookServerHttp2c.java b/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/BookServerHttp2c.java
new file mode 100644
index 0000000..d3e02b4
--- /dev/null
+++ b/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/BookServerHttp2c.java
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http_undertow.http2;
+
+public class BookServerHttp2c extends AbstractBookServerHttp2 {
+ public static final String PORT = allocatePort(BookServerHttp2c.class);
+
+ public BookServerHttp2c() {
+ this(PORT);
+ }
+
+ public BookServerHttp2c(String port) {
+ super(port, "org/apache/cxf/systest/http_undertow/http2/server.xml", "http");
+ }
+
+ public static void main(String[] args) {
+ try {
+ BookServerHttp2c s = new BookServerHttp2c();
+ s.start();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ System.exit(-1);
+ } finally {
+ System.out.println("done!");
+ }
+ }
+
+}
diff --git a/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/Http2TestClient.java b/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/Http2TestClient.java
new file mode 100644
index 0000000..6d8ed7d
--- /dev/null
+++ b/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/Http2TestClient.java
@@ -0,0 +1,184 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http_undertow.http2;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.concurrent.CompletableFuture;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.cxf.transport.https.InsecureTrustManager;
+import org.xnio.IoUtils;
+import org.xnio.OptionMap;
+import org.xnio.Options;
+import org.xnio.Xnio;
+import org.xnio.XnioWorker;
+
+import io.undertow.UndertowOptions;
+import io.undertow.client.ClientCallback;
+import io.undertow.client.ClientConnection;
+import io.undertow.client.ClientExchange;
+import io.undertow.client.ClientRequest;
+import io.undertow.client.ClientResponse;
+import io.undertow.client.UndertowClient;
+import io.undertow.connector.ByteBufferPool;
+import io.undertow.protocols.ssl.UndertowXnioSsl;
+import io.undertow.server.DefaultByteBufferPool;
+import io.undertow.util.AttachmentKey;
+import io.undertow.util.Headers;
+import io.undertow.util.HttpString;
+import io.undertow.util.Methods;
+import io.undertow.util.StringReadChannelListener;
+
+/**
+ * TODO: Use CXF client once https://issues.apache.org/jira/browse/CXF-8606 is dones
+ */
+public class Http2TestClient {
+ private static final int BUFFER_SIZE = Integer.getInteger("test.bufferSize", 1024 * 16 - 20);
+ private static final AttachmentKey<String> RESPONSE_BODY = AttachmentKey.create(String.class);
+
+ private final UndertowClient client;
+ private final UndertowXnioSsl ssl;
+ private final XnioWorker worker;
+ private final ByteBufferPool pool;
+
+ public Http2TestClient(boolean secure) throws Exception {
+ client = UndertowClient.getInstance();
+
+ final Xnio xnio = Xnio.getInstance();
+ worker = xnio.createWorker(null,
+ OptionMap.builder()
+ .set(Options.WORKER_IO_THREADS, 8)
+ .set(Options.TCP_NODELAY, true)
+ .set(Options.KEEP_ALIVE, true)
+ .set(Options.WORKER_NAME, "TestClient")
+ .getMap());
+
+ pool = new DefaultByteBufferPool(true, BUFFER_SIZE, 1000, 10, 100);
+ if (secure) {
+ final SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
+ sslContext.init(new KeyManager[] {}, InsecureTrustManager.getNoOpX509TrustManagers(), null);
+ ssl = new UndertowXnioSsl(xnio, OptionMap.EMPTY, sslContext);
+ } else {
+ ssl = null;
+ }
+ }
+
+ public class RequestBuilder {
+ private final String address;
+ private String path = "";
+ private String accept = MediaType.WILDCARD;
+ private OptionMap options = OptionMap.EMPTY;
+
+ public RequestBuilder(final String address) {
+ this.address = address;
+ }
+
+ public RequestBuilder path(final String p) {
+ this.path = p;
+ return this;
+ }
+
+
+ public RequestBuilder accept(final String a) {
+ this.accept = a;
+ return this;
+ }
+
+ public RequestBuilder http2() {
+ options = OptionMap.create(UndertowOptions.ENABLE_HTTP2, true);
+ return this;
+ }
+
+ public ClientResponse get() throws IOException {
+ return request(address, path, options, Methods.GET, accept);
+ }
+ }
+
+ public RequestBuilder request(final String address) throws IOException {
+ return new RequestBuilder(address);
+ }
+
+ public ClientResponse request(final String address, final String path, final OptionMap options,
+ final HttpString method, final String accept) throws IOException {
+
+ final ClientConnection connection = client
+ .connect(URI.create(address), worker, ssl, pool, options)
+ .get();
+
+ try {
+ final ClientRequest request = new ClientRequest()
+ .setMethod(method)
+ .setPath(path);
+
+ request.getRequestHeaders().put(Headers.ACCEPT, accept);
+ request.getRequestHeaders().put(Headers.HOST, "localhost");
+
+ final CompletableFuture<ClientResponse> future = new CompletableFuture<ClientResponse>();
+ connection.sendRequest(request, createClientCallback(future));
+ return future.join();
+ } finally {
+ IoUtils.safeClose(connection);
+ }
+ }
+
+ private ClientCallback<ClientExchange> createClientCallback(final CompletableFuture<ClientResponse> future) {
+ return new ClientCallback<ClientExchange>() {
+ @Override
+ public void completed(ClientExchange result) {
+ result.setResponseListener(new ClientCallback<ClientExchange>() {
+ @Override
+ public void completed(final ClientExchange result) {
+ new StringReadChannelListener(result.getConnection().getBufferPool()) {
+ @Override
+ protected void stringDone(String string) {
+ result.getResponse().putAttachment(RESPONSE_BODY, string);
+ future.complete(result.getResponse());
+ }
+
+ @Override
+ protected void error(IOException e) {
+ future.completeExceptionally(e);
+ }
+ }.setup(result.getResponseChannel());
+ }
+
+ @Override
+ public void failed(IOException e) {
+ future.completeExceptionally(e);
+ }
+ });
+ }
+
+ @Override
+ public void failed(IOException e) {
+ future.completeExceptionally(e);
+ }
+ };
+ }
+
+ public String getBody(ClientResponse response) {
+ return response.getAttachment(RESPONSE_BODY);
+ }
+
+}
diff --git a/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/UndertowClientServerHttp2Test.java b/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/UndertowClientServerHttp2Test.java
new file mode 100644
index 0000000..1babfdc
--- /dev/null
+++ b/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/UndertowClientServerHttp2Test.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http_undertow.http2;
+
+
+import org.apache.cxf.jaxrs.model.AbstractResourceInfo;
+
+import org.junit.BeforeClass;
+
+import static org.junit.Assert.assertTrue;
+
+public class UndertowClientServerHttp2Test extends AbstractUndertowClientServerHttp2Test {
+ private static final String PORT = BookServerHttp2.PORT;
+
+ @BeforeClass
+ public static void startServers() throws Exception {
+ AbstractResourceInfo.clearAllMaps();
+ assertTrue("server did not launch correctly", launchServer(new BookServerHttp2()));
+ createStaticBus();
+ }
+
+ @Override
+ protected String getAddress() {
+ return "https://localhost:" + PORT;
+ }
+
+ @Override
+ protected String getContext() {
+ return "/http2";
+ }
+}
diff --git a/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/UndertowClientServerHttp2cTest.java b/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/UndertowClientServerHttp2cTest.java
new file mode 100644
index 0000000..b5b752d
--- /dev/null
+++ b/systests/transport-undertow/src/test/java/org/apache/cxf/systest/http_undertow/http2/UndertowClientServerHttp2cTest.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http_undertow.http2;
+
+
+import org.apache.cxf.jaxrs.model.AbstractResourceInfo;
+
+import org.junit.BeforeClass;
+
+import static org.junit.Assert.assertTrue;
+
+public class UndertowClientServerHttp2cTest extends AbstractUndertowClientServerHttp2Test {
+ private static final String PORT = BookServerHttp2c.PORT;
+
+ @BeforeClass
+ public static void startServers() throws Exception {
+ AbstractResourceInfo.clearAllMaps();
+ assertTrue("server did not launch correctly", launchServer(new BookServerHttp2c()));
+ createStaticBus();
+ }
+
+ @Override
+ protected String getAddress() {
+ return "http://localhost:" + PORT;
+ }
+
+ @Override
+ protected String getContext() {
+ return "/http2";
+ }
+}
diff --git a/systests/transport-undertow/src/test/resources/org/apache/cxf/systest/http_undertow/http2/server-tls.xml b/systests/transport-undertow/src/test/resources/org/apache/cxf/systest/http_undertow/http2/server-tls.xml
new file mode 100644
index 0000000..cbd3a67
--- /dev/null
+++ b/systests/transport-undertow/src/test/resources/org/apache/cxf/systest/http_undertow/http2/server-tls.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:http-undertow="http://cxf.apache.org/transports/http-undertow/configuration"
+ xmlns:sec="http://cxf.apache.org/configuration/security"
+ xsi:schemaLocation="http://cxf.apache.org/transports/http-undertow/configuration
+ http://cxf.apache.org/schemas/configuration/http-undertow.xsd
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://cxf.apache.org/configuration/security
+ http://cxf.apache.org/schemas/configuration/security.xsd">
+ <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
+ <http-undertow:engine-factory bus="cxf">
+ <http-undertow:engine port="${testutil.ports.org.apache.cxf.systest.http_undertow.http2.BookServerHttp2}">
+ <http-undertow:tlsServerParameters>
+ <sec:keyManagers keyPassword="password">
+ <sec:keyStore type="jks" password="password" resource="keys/Bethal.jks"/>
+ </sec:keyManagers>
+ <sec:clientAuthentication want="false" required="false"/>
+ </http-undertow:tlsServerParameters>
+ </http-undertow:engine>
+ </http-undertow:engine-factory>
+</beans>
\ No newline at end of file
diff --git a/systests/transport-undertow/src/test/resources/org/apache/cxf/systest/http_undertow/http2/server.xml b/systests/transport-undertow/src/test/resources/org/apache/cxf/systest/http_undertow/http2/server.xml
new file mode 100644
index 0000000..a35af1a
--- /dev/null
+++ b/systests/transport-undertow/src/test/resources/org/apache/cxf/systest/http_undertow/http2/server.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:http-undertow="http://cxf.apache.org/transports/http-undertow/configuration"
+ xsi:schemaLocation="http://cxf.apache.org/transports/http-undertow/configuration
+ http://cxf.apache.org/schemas/configuration/http-undertow.xsd
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd">
+ <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
+ <http-undertow:engine-factory bus="cxf">
+ <http-undertow:engine port="${testutil.ports.org.apache.cxf.systest.http_undertow.http2.BookServerHttp2c}">
+ </http-undertow:engine>
+ </http-undertow:engine-factory>
+</beans>
\ No newline at end of file
diff --git a/systests/transports/pom.xml b/systests/transports/pom.xml
index a7f4ec6..62c617f 100644
--- a/systests/transports/pom.xml
+++ b/systests/transports/pom.xml
@@ -344,10 +344,66 @@
<version>${cxf.junit5.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>http2-server</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-alpn-server</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>http2-client</artifactId>
+ <version>${cxf.jetty9.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<profiles>
<profile>
+ <id>jdk8</id>
+ <activation>
+ <jdk>1.8</jdk>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-alpn-openjdk8-server</artifactId>
+ <version>${cxf.jetty9.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-alpn-openjdk8-client</artifactId>
+ <version>${cxf.jetty9.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ </profile>
+ <profile>
+ <id>jdk9+</id>
+ <activation>
+ <jdk>[9,)</jdk>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-alpn-java-server</artifactId>
+ <version>${cxf.jetty9.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-alpn-java-client</artifactId>
+ <version>${cxf.jetty9.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ </profile>
+ <profile>
<id>jdk16</id>
<activation>
<jdk>[16,)</jdk>
diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/AbstractBookServerHttp2.java b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/AbstractBookServerHttp2.java
new file mode 100644
index 0000000..83d999b
--- /dev/null
+++ b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/AbstractBookServerHttp2.java
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http2_jetty;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.bus.spring.SpringBusFactory;
+import org.apache.cxf.customer.book.Book;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.jaxrs.provider.StreamingResponseProvider;
+import org.apache.cxf.testutil.common.AbstractBusTestServerBase;
+import org.apache.cxf.transport.http.HttpServerEngineSupport;
+
+abstract class AbstractBookServerHttp2 extends AbstractBusTestServerBase {
+ org.apache.cxf.endpoint.Server server;
+
+ private final String port;
+ private final String context;
+ private final String scheme;
+
+ AbstractBookServerHttp2(String port, String context, String scheme) {
+ this.port = port;
+ this.context = context;
+ this.scheme = scheme;
+ }
+
+ protected void run() {
+ SpringBusFactory factory = new SpringBusFactory();
+ Bus bus = factory.createBus(context);
+ bus.setProperty(HttpServerEngineSupport.ENABLE_HTTP2, true);
+ setBus(bus);
+ JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+ sf.setBus(bus);
+ sf.setResourceClasses(BookStore.class);
+ sf.setProvider(new StreamingResponseProvider<Book>());
+ sf.setResourceProvider(BookStore.class,
+ new SingletonResourceProvider(new BookStore(), true));
+ sf.setAddress(scheme + "://localhost:" + port + "/http2");
+ server = sf.create();
+ }
+
+ public void tearDown() throws Exception {
+ server.stop();
+ server.destroy();
+ server = null;
+ }
+}
diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/AbstractJettyClientServerHttp2Test.java b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/AbstractJettyClientServerHttp2Test.java
new file mode 100644
index 0000000..08eba61
--- /dev/null
+++ b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/AbstractJettyClientServerHttp2Test.java
@@ -0,0 +1,119 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http2_jetty;
+
+import javax.ws.rs.core.Response;
+
+import org.apache.cxf.configuration.jsse.TLSClientParameters;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.systest.http2_jetty.Http2TestClient.ClientResponse;
+import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
+import org.apache.cxf.transport.http.HTTPConduit;
+import org.apache.cxf.transport.https.InsecureTrustManager;
+import org.eclipse.jetty.http.HttpVersion;
+
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+abstract class AbstractJettyClientServerHttp2Test extends AbstractBusClientServerTestBase {
+ @Test
+ public void testBookNotFoundWithHttp2() throws Exception {
+ final Http2TestClient client = new Http2TestClient(isSecure());
+
+ final ClientResponse response = client
+ .request(getAddress())
+ .accept("text/plain")
+ .path(getContext() + "/web/bookstore/notFound")
+ .http2()
+ .get();
+
+ assertThat(response.getResponseCode(), equalTo(404));
+ assertThat(response.getProtocol(), equalTo(HttpVersion.HTTP_2));
+ }
+
+ @Test
+ public void testBookWithHttp2() throws Exception {
+ final Http2TestClient client = new Http2TestClient(isSecure());
+
+ final ClientResponse response = client
+ .request(getAddress())
+ .accept("text/plain")
+ .path(getContext() + "/web/bookstore/booknames")
+ .http2()
+ .get();
+
+ assertThat(response.getResponseCode(), equalTo(200));
+ assertThat(response.getProtocol(), equalTo(HttpVersion.HTTP_2));
+ assertEquals("CXF in Action", response.getBody());
+ }
+
+ @Test
+ public void testGetBookStreamHttp2() throws Exception {
+ final Http2TestClient client = new Http2TestClient(isSecure());
+
+ final ClientResponse response = client
+ .request(getAddress())
+ .accept("application/xml")
+ .path(getContext() + "/web/bookstore/bookstream")
+ .http2()
+ .get();
+
+ assertThat(response.getResponseCode(), equalTo(200));
+ assertThat(response.getProtocol(), equalTo(HttpVersion.HTTP_2));
+ assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ + "<Book><id>1</id><name>Book1</name></Book>", response.getBody());
+ }
+
+ @Test
+ public void testBookWithHttp() throws Exception {
+ final WebClient wc = WebClient
+ .create(getAddress() + getContext() + "/web/bookstore/booknames")
+ .accept("text/plain");
+
+ if (isSecure()) {
+ final HTTPConduit conduit = WebClient.getConfig(wc).getHttpConduit();
+ TLSClientParameters params = conduit.getTlsClientParameters();
+
+ if (params == null) {
+ params = new TLSClientParameters();
+ conduit.setTlsClientParameters(params);
+ }
+
+ // Create TrustManager instance which trusts all clients and servers
+ params.setTrustManagers(InsecureTrustManager.getNoOpX509TrustManagers());
+ params.setDisableCNCheck(true);
+ }
+
+ try (Response resp = wc.get()) {
+ assertThat(resp.getStatus(), equalTo(200));
+ assertEquals("CXF in Action", resp.readEntity(String.class));
+ }
+ }
+
+ protected abstract String getAddress();
+ protected abstract String getContext();
+
+ protected boolean isSecure() {
+ return getAddress().startsWith("https");
+ }
+}
\ No newline at end of file
diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/Book.java b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/Book.java
new file mode 100644
index 0000000..fd80aac
--- /dev/null
+++ b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/Book.java
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http2_jetty;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
+
+@JsonTypeInfo(use = Id.CLASS, include = As.PROPERTY, property = "class")
+@XmlRootElement(name = "Book")
+public class Book {
+ private String name;
+ private long id;
+
+ public Book() {
+ }
+
+ public Book(String name, long id) {
+ this.name = name;
+ this.id = id;
+ }
+
+ public void setName(String n) {
+ name = n;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setId(long i) {
+ id = i;
+ }
+ public long getId() {
+ return id;
+ }
+
+ @PUT
+ public void cloneState(Book book) {
+ id = book.getId();
+ name = book.getName();
+ }
+
+ @GET
+ public Book retrieveState() {
+ return this;
+ }
+}
diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/BookServerHttp2.java b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/BookServerHttp2.java
new file mode 100644
index 0000000..1980e96
--- /dev/null
+++ b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/BookServerHttp2.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http2_jetty;
+
+public class BookServerHttp2 extends AbstractBookServerHttp2 {
+ public static final String PORT = allocatePort(BookServerHttp2.class);
+
+ org.apache.cxf.endpoint.Server server;
+
+ public BookServerHttp2() {
+ this(PORT);
+ }
+
+ public BookServerHttp2(String port) {
+ super(port, "org/apache/cxf/systest/http2_jetty/server-tls.xml", "https");
+ }
+
+ public static void main(String[] args) {
+ try {
+ BookServerHttp2 s = new BookServerHttp2();
+ s.start();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ System.exit(-1);
+ } finally {
+ System.out.println("done!");
+ }
+ }
+
+}
diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/BookServerHttp2c.java b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/BookServerHttp2c.java
new file mode 100644
index 0000000..74d7303
--- /dev/null
+++ b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/BookServerHttp2c.java
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http2_jetty;
+
+public class BookServerHttp2c extends AbstractBookServerHttp2 {
+ public static final String PORT = allocatePort(BookServerHttp2c.class);
+
+ public BookServerHttp2c() {
+ this(PORT);
+ }
+
+ public BookServerHttp2c(String port) {
+ super(port, "org/apache/cxf/systest/http2_jetty/server.xml", "http");
+ }
+
+ public static void main(String[] args) {
+ try {
+ BookServerHttp2c s = new BookServerHttp2c();
+ s.start();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ System.exit(-1);
+ } finally {
+ System.out.println("done!");
+ }
+ }
+
+}
diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/BookStore.java b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/BookStore.java
new file mode 100644
index 0000000..5aa5bdd
--- /dev/null
+++ b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/BookStore.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http2_jetty;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+import org.apache.cxf.jaxrs.ext.StreamingResponse;
+
+@Path("/web/bookstore")
+public class BookStore {
+ private static ExecutorService executor = Executors.newSingleThreadExecutor();
+
+ @GET
+ @Path("/booknames")
+ @Produces("text/plain")
+ public byte[] getBookName() {
+ return "CXF in Action".getBytes();
+ }
+
+ @GET
+ @Path("/bookstream")
+ @Produces("application/xml")
+ public StreamingResponse<Book> getBookStream() {
+ return new StreamingResponse<Book>() {
+ public void writeTo(final StreamingResponse.Writer<Book> out) throws IOException {
+ out.write(new Book("Book1", 1));
+ executor.execute(new Runnable() {
+ public void run() {
+ try {
+ for (int i = 2; i <= 5; i++) {
+ Thread.sleep(500);
+ out.write(new Book("Book" + i, i));
+ out.getEntityStream().flush();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+ };
+ }
+}
+
+
diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/Http2TestClient.java b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/Http2TestClient.java
new file mode 100644
index 0000000..70a388a
--- /dev/null
+++ b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/Http2TestClient.java
@@ -0,0 +1,212 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http2_jetty;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.ws.rs.ClientErrorException;
+import javax.ws.rs.core.MediaType;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+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.api.server.ServerSessionListener;
+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.FuturePromise;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+/**
+ * TODO: Use CXF client once https://issues.apache.org/jira/browse/CXF-8606 is dones
+ */
+public class Http2TestClient implements AutoCloseable {
+ private final HTTP2Client client;
+ private final SslContextFactory sslContextFactory;
+
+ public Http2TestClient(boolean secure) throws Exception {
+ client = new HTTP2Client();
+ if (secure) {
+ sslContextFactory = new SslContextFactory.Client(true);
+ client.addBean(sslContextFactory);
+ } else {
+ sslContextFactory = null;
+ }
+ client.start();
+ }
+
+ public static class ClientResponse {
+ private String body;
+ private HttpVersion protocol;
+ private int responseCode;
+
+ public void setBody(String body) {
+ this.body = body;
+ }
+
+ public String getBody() {
+ return body;
+ }
+
+ public void setResponseCode(int rc) {
+ this.responseCode = rc;
+ }
+
+ public int getResponseCode() {
+ return responseCode;
+ }
+
+ public HttpVersion getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(HttpVersion protocol) {
+ this.protocol = protocol;
+ }
+ }
+
+ public class RequestBuilder {
+ private final String address;
+ private String path = "";
+ private String accept = MediaType.WILDCARD;
+ private HttpVersion version = HttpVersion.HTTP_1_1;
+
+ public RequestBuilder(final String address) {
+ this.address = address;
+ }
+
+ public RequestBuilder path(final String p) {
+ this.path = p;
+ return this;
+ }
+
+
+ public RequestBuilder accept(final String a) {
+ this.accept = a;
+ return this;
+ }
+
+ public RequestBuilder http2() {
+ version = HttpVersion.HTTP_2;
+ return this;
+ }
+
+ public ClientResponse get() throws InterruptedException, ExecutionException, TimeoutException {
+ return request(address, path, version, "GET", accept);
+ }
+ }
+
+ public RequestBuilder request(final String address) throws IOException {
+ return new RequestBuilder(address);
+ }
+
+ public ClientResponse request(final String address, final String path,
+ final HttpVersion version, final String method, final String accept)
+ throws InterruptedException, ExecutionException, TimeoutException {
+
+ final URI uri = URI.create(address);
+ final FuturePromise<Session> sessionPromise = new FuturePromise<>();
+
+ client.connect(sslContextFactory, new InetSocketAddress(uri.getHost(), uri.getPort()),
+ new ServerSessionListener.Adapter(), sessionPromise);
+ final Session session = sessionPromise.get();
+
+ final HttpFields requestFields = new HttpFields();
+ requestFields.add(HttpHeader.ACCEPT, accept);
+ requestFields.add(HttpHeader.HOST, "localhost");
+
+ final MetaData.Request request = new MetaData.Request(method, new HttpURI(address + path),
+ version, requestFields);
+
+ final CompletableFuture<ClientResponse> future = new CompletableFuture<>();
+ final Stream.Listener responseListener = new ResponseListener(future);
+
+ final HeadersFrame headersFrame = new HeadersFrame(request, null, true);
+ session.newStream(headersFrame, new FuturePromise<>(), responseListener);
+ return future.get(5, TimeUnit.SECONDS);
+ }
+
+ @Override
+ public void close() throws Exception {
+ client.stop();
+ }
+
+ private final class ResponseListener extends Stream.Listener.Adapter {
+ private final ClientResponse response = new ClientResponse();
+ private final CompletableFuture<ClientResponse> future;
+
+ ResponseListener(final CompletableFuture<ClientResponse> f) {
+ this.future = f;
+ }
+
+ @Override
+ public void onHeaders(Stream stream, HeadersFrame frame) {
+ final MetaData metaData = frame.getMetaData();
+ response.setProtocol(metaData.getHttpVersion());
+ if (metaData.isResponse()) {
+ final int status = ((MetaData.Response)metaData).getStatus();
+ response.setResponseCode(status);
+ // Unsuccessful response
+ if (status >= 400) {
+ future.complete(response);
+ }
+ }
+ super.onHeaders(stream, frame);
+ }
+
+ @Override
+ public void onData(Stream stream, DataFrame frame, Callback callback) {
+ byte[] bytes = new byte[frame.getData().remaining()];
+ frame.getData().get(bytes);
+ response.setBody(new String(bytes));
+ future.complete(response);
+ super.onData(stream, frame, callback);
+ }
+
+ @Override
+ public void onTimeout(Stream stream, Throwable x) {
+ future.completeExceptionally(x);
+ super.onTimeout(stream, x);
+ }
+
+ @Override
+ public void onFailure(Stream stream, int error, String reason, Throwable failure, Callback callback) {
+ future.completeExceptionally(failure);
+ super.onFailure(stream, error, reason, failure, callback);
+ }
+
+ @Override
+ public void onFailure(Stream stream, int error, String reason, Callback callback) {
+ future.completeExceptionally(new ClientErrorException(reason, error));
+ super.onFailure(stream, error, reason, callback);
+ }
+ }
+}
diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/JettyClientServerHttp2Test.java b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/JettyClientServerHttp2Test.java
new file mode 100644
index 0000000..a707024
--- /dev/null
+++ b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/JettyClientServerHttp2Test.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http2_jetty;
+
+import org.apache.cxf.jaxrs.model.AbstractResourceInfo;
+
+import org.junit.BeforeClass;
+
+import static org.junit.Assert.assertTrue;
+
+public class JettyClientServerHttp2Test extends AbstractJettyClientServerHttp2Test {
+ private static final String PORT = BookServerHttp2.PORT;
+
+ @BeforeClass
+ public static void startServers() throws Exception {
+ AbstractResourceInfo.clearAllMaps();
+ assertTrue("server did not launch correctly", launchServer(new BookServerHttp2()));
+ createStaticBus();
+ }
+
+
+ @Override
+ protected String getAddress() {
+ return "https://localhost:" + PORT;
+ }
+
+ @Override
+ protected String getContext() {
+ return "/http2";
+ }
+}
diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/JettyClientServerHttp2cTest.java b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/JettyClientServerHttp2cTest.java
new file mode 100644
index 0000000..805367c
--- /dev/null
+++ b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/JettyClientServerHttp2cTest.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.http2_jetty;
+
+import org.apache.cxf.jaxrs.model.AbstractResourceInfo;
+
+import org.junit.BeforeClass;
+
+import static org.junit.Assert.assertTrue;
+
+public class JettyClientServerHttp2cTest extends AbstractJettyClientServerHttp2Test {
+ private static final String PORT = BookServerHttp2c.PORT;
+
+ @BeforeClass
+ public static void startServers() throws Exception {
+ AbstractResourceInfo.clearAllMaps();
+ assertTrue("server did not launch correctly", launchServer(new BookServerHttp2c()));
+ createStaticBus();
+ }
+
+ @Override
+ protected String getAddress() {
+ return "http://localhost:" + PORT;
+ }
+
+ @Override
+ protected String getContext() {
+ return "/http2";
+ }
+}
diff --git a/systests/transports/src/test/resources/org/apache/cxf/systest/http2_jetty/server-tls.xml b/systests/transports/src/test/resources/org/apache/cxf/systest/http2_jetty/server-tls.xml
new file mode 100644
index 0000000..5a0cba8
--- /dev/null
+++ b/systests/transports/src/test/resources/org/apache/cxf/systest/http2_jetty/server-tls.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:http-jetty="http://cxf.apache.org/transports/http-jetty/configuration"
+ xmlns:sec="http://cxf.apache.org/configuration/security"
+ xsi:schemaLocation="http://cxf.apache.org/transports/http-jetty/configuration
+ http://cxf.apache.org/schemas/configuration/http-jetty.xsd
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://cxf.apache.org/configuration/security
+ http://cxf.apache.org/schemas/configuration/security.xsd">
+ <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
+ <http-jetty:engine-factory bus="cxf">
+ <http-jetty:engine port="${testutil.ports.org.apache.cxf.systest.http2_jetty.BookServerHttp2}">
+ <http-jetty:tlsServerParameters>
+ <sec:keyManagers keyPassword="password">
+ <sec:keyStore type="jks" password="password" resource="keys/Bethal.jks"/>
+ </sec:keyManagers>
+ <sec:clientAuthentication want="false" required="false"/>
+ </http-jetty:tlsServerParameters>
+ </http-jetty:engine>
+ </http-jetty:engine-factory>
+</beans>
\ No newline at end of file
diff --git a/systests/transports/src/test/resources/org/apache/cxf/systest/http2_jetty/server.xml b/systests/transports/src/test/resources/org/apache/cxf/systest/http2_jetty/server.xml
new file mode 100644
index 0000000..c44bf1e
--- /dev/null
+++ b/systests/transports/src/test/resources/org/apache/cxf/systest/http2_jetty/server.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:http-jetty="http://cxf.apache.org/transports/http-jetty/configuration"
+ xsi:schemaLocation="http://cxf.apache.org/transports/http-jetty/configuration
+ http://cxf.apache.org/schemas/configuration/http-jetty.xsd
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd">
+ <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
+ <http-jetty:engine-factory bus="cxf">
+ <http-jetty:engine port="${testutil.ports.org.apache.cxf.systest.http2_jetty.BookServerHttp2c}">
+ </http-jetty:engine>
+ </http-jetty:engine-factory>
+</beans>
\ No newline at end of file