View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.core5.testing.nio;
29  
30  import static org.hamcrest.MatcherAssert.assertThat;
31  import static org.junit.jupiter.api.Assertions.assertEquals;
32  import static org.junit.jupiter.api.Assertions.assertFalse;
33  import static org.junit.jupiter.api.Assertions.assertTrue;
34  
35  import java.net.InetSocketAddress;
36  import java.util.concurrent.ExecutionException;
37  import java.util.concurrent.Future;
38  
39  import org.apache.hc.core5.http.ContentType;
40  import org.apache.hc.core5.http.HttpHost;
41  import org.apache.hc.core5.http.HttpResponse;
42  import org.apache.hc.core5.http.HttpStatus;
43  import org.apache.hc.core5.http.Message;
44  import org.apache.hc.core5.http.Method;
45  import org.apache.hc.core5.http.URIScheme;
46  import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer;
47  import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer;
48  import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer;
49  import org.apache.hc.core5.http.nio.ssl.BasicServerTlsStrategy;
50  import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
51  import org.apache.hc.core5.http.nio.support.BasicRequestProducer;
52  import org.apache.hc.core5.http.nio.support.BasicResponseConsumer;
53  import org.apache.hc.core5.http2.impl.nio.ProtocolNegotiationException;
54  import org.apache.hc.core5.http2.impl.nio.bootstrap.H2MultiplexingRequester;
55  import org.apache.hc.core5.http2.ssl.H2ClientTlsStrategy;
56  import org.apache.hc.core5.http2.ssl.H2ServerTlsStrategy;
57  import org.apache.hc.core5.reactor.IOReactorConfig;
58  import org.apache.hc.core5.reactor.ListenerEndpoint;
59  import org.apache.hc.core5.testing.SSLTestContexts;
60  import org.apache.hc.core5.testing.nio.extension.H2AsyncServerResource;
61  import org.apache.hc.core5.testing.nio.extension.H2MultiplexingRequesterResource;
62  import org.apache.hc.core5.util.Timeout;
63  import org.hamcrest.CoreMatchers;
64  import org.junit.jupiter.api.Test;
65  import org.junit.jupiter.api.extension.RegisterExtension;
66  
67  public abstract class H2AlpnTest {
68  
69      private static final Timeout TIMEOUT = Timeout.ofMinutes(1);
70  
71      private final boolean strictALPN;
72      private final boolean h2Allowed;
73  
74      @RegisterExtension
75      private final H2AsyncServerResource serverResource;
76      @RegisterExtension
77      private final H2MultiplexingRequesterResource clientResource;
78  
79      public H2AlpnTest(final boolean strictALPN, final boolean h2Allowed) throws Exception {
80          this.strictALPN = strictALPN;
81          this.h2Allowed = h2Allowed;
82  
83          final TlsStrategy serverTlsStrategy = h2Allowed ?
84                  new H2ServerTlsStrategy(SSLTestContexts.createServerSSLContext()) :
85                  new BasicServerTlsStrategy(SSLTestContexts.createServerSSLContext());
86  
87          this.serverResource = new H2AsyncServerResource(bootstrap -> bootstrap
88                  .setIOReactorConfig(
89                          IOReactorConfig.custom()
90                                  .setSoTimeout(TIMEOUT)
91                                  .build())
92                  .setTlsStrategy(serverTlsStrategy)
93                  .register("*", () -> new EchoHandler(2048))
94          );
95  
96          final TlsStrategy clientTlsStrategy = new H2ClientTlsStrategy(SSLTestContexts.createClientSSLContext());
97  
98          this.clientResource = new H2MultiplexingRequesterResource(bootstrap -> bootstrap
99                  .setIOReactorConfig(IOReactorConfig.custom()
100                         .setSoTimeout(TIMEOUT)
101                         .build())
102                 .setTlsStrategy(clientTlsStrategy)
103                 .setStrictALPNHandshake(strictALPN)
104         );
105     }
106 
107     @Test
108     public void testALPN() throws Exception {
109         final HttpAsyncServer server = serverResource.start();
110         final Future<ListenerEndpoint> future = server.listen(new InetSocketAddress(0), URIScheme.HTTPS);
111         final ListenerEndpoint listener = future.get();
112         final InetSocketAddress address = (InetSocketAddress) listener.getAddress();
113         final H2MultiplexingRequester requester = clientResource.start();
114 
115         final HttpHost target = new HttpHost(URIScheme.HTTPS.id, "localhost", address.getPort());
116         final Future<Message<HttpResponse, String>> resultFuture1 = requester.execute(
117             new BasicRequestProducer(Method.POST, target, "/stuff",
118                 new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)),
119             new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null);
120         final Message<HttpResponse, String> message1;
121         try {
122             message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
123         } catch (final ExecutionException e) {
124             final Throwable cause = e.getCause();
125             assertFalse(h2Allowed, "h2 negotiation was enabled, but h2 was not negotiated");
126             assertTrue(cause instanceof ProtocolNegotiationException);
127             assertEquals("ALPN: missing application protocol", cause.getMessage());
128             assertTrue(strictALPN, "strict ALPN mode was not enabled, but the client negotiator still threw");
129             return;
130         }
131 
132         assertTrue(h2Allowed, "h2 negotiation was disabled, but h2 was negotiated");
133         assertThat(message1, CoreMatchers.notNullValue());
134         final HttpResponse response1 = message1.getHead();
135         assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
136         final String body1 = message1.getBody();
137         assertThat(body1, CoreMatchers.equalTo("some stuff"));
138     }
139 
140 }