1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.core5.testing.nio;
29
30 import java.io.IOException;
31 import java.net.InetSocketAddress;
32 import java.util.concurrent.Future;
33
34 import org.apache.hc.core5.function.Supplier;
35 import org.apache.hc.core5.http.ContentType;
36 import org.apache.hc.core5.http.EntityDetails;
37 import org.apache.hc.core5.http.Header;
38 import org.apache.hc.core5.http.HttpException;
39 import org.apache.hc.core5.http.HttpHost;
40 import org.apache.hc.core5.http.HttpRequest;
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.HttpAsyncRequester;
47 import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer;
48 import org.apache.hc.core5.http.nio.AsyncDataConsumer;
49 import org.apache.hc.core5.http.nio.AsyncEntityProducer;
50 import org.apache.hc.core5.http.nio.AsyncFilterChain;
51 import org.apache.hc.core5.http.nio.AsyncFilterHandler;
52 import org.apache.hc.core5.http.nio.AsyncPushProducer;
53 import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
54 import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer;
55 import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer;
56 import org.apache.hc.core5.http.nio.support.BasicRequestProducer;
57 import org.apache.hc.core5.http.nio.support.BasicResponseConsumer;
58 import org.apache.hc.core5.http.protocol.HttpContext;
59 import org.apache.hc.core5.http.protocol.UriPatternMatcher;
60 import org.apache.hc.core5.http2.HttpVersionPolicy;
61 import org.apache.hc.core5.http2.impl.nio.bootstrap.H2RequesterBootstrap;
62 import org.apache.hc.core5.http2.impl.nio.bootstrap.H2ServerBootstrap;
63 import org.apache.hc.core5.http2.ssl.H2ClientTlsStrategy;
64 import org.apache.hc.core5.io.CloseMode;
65 import org.apache.hc.core5.reactor.IOReactorConfig;
66 import org.apache.hc.core5.reactor.ListenerEndpoint;
67 import org.apache.hc.core5.testing.SSLTestContexts;
68 import org.apache.hc.core5.testing.classic.LoggingConnPoolListener;
69 import org.apache.hc.core5.util.Timeout;
70 import org.hamcrest.CoreMatchers;
71 import org.hamcrest.MatcherAssert;
72 import org.junit.Rule;
73 import org.junit.Test;
74 import org.junit.rules.ExternalResource;
75 import org.slf4j.Logger;
76 import org.slf4j.LoggerFactory;
77
78 public class H2ServerBootstrapFiltersTest {
79
80 private final Logger log = LoggerFactory.getLogger(getClass());
81
82 private static final Timeout TIMEOUT = Timeout.ofMinutes(1);
83
84 private HttpAsyncServer server;
85
86 @Rule
87 public ExternalResource serverResource = new ExternalResource() {
88
89 @Override
90 protected void before() throws Throwable {
91 log.debug("Starting up test server");
92 server = H2ServerBootstrap.bootstrap()
93 .setLookupRegistry(new UriPatternMatcher<Supplier<AsyncServerExchangeHandler>>())
94 .setVersionPolicy(HttpVersionPolicy.NEGOTIATE)
95 .setIOReactorConfig(
96 IOReactorConfig.custom()
97 .setSoTimeout(TIMEOUT)
98 .build())
99 .setStreamListener(LoggingHttp1StreamListener.INSTANCE_SERVER)
100 .setStreamListener(LoggingH2StreamListener.INSTANCE)
101 .setIOSessionDecorator(LoggingIOSessionDecorator.INSTANCE)
102 .setExceptionCallback(LoggingExceptionCallback.INSTANCE)
103 .setIOSessionListener(LoggingIOSessionListener.INSTANCE)
104 .register("*", new Supplier<AsyncServerExchangeHandler>() {
105
106 @Override
107 public AsyncServerExchangeHandler get() {
108 return new EchoHandler(2048);
109 }
110
111 })
112 .addFilterLast("test-filter", new AsyncFilterHandler() {
113
114 @Override
115 public AsyncDataConsumer handle(
116 final HttpRequest request,
117 final EntityDetails entityDetails,
118 final HttpContext context,
119 final AsyncFilterChain.ResponseTrigger responseTrigger,
120 final AsyncFilterChain chain) throws HttpException, IOException {
121 return chain.proceed(request, entityDetails, context, new AsyncFilterChain.ResponseTrigger() {
122
123 @Override
124 public void sendInformation(
125 final HttpResponse response) throws HttpException, IOException {
126 responseTrigger.sendInformation(response);
127 }
128
129 @Override
130 public void submitResponse(
131 final HttpResponse response,
132 final AsyncEntityProducer entityProducer) throws HttpException, IOException {
133 response.setHeader("X-Test-Filter", "active");
134 responseTrigger.submitResponse(response, entityProducer);
135 }
136
137 @Override
138 public void pushPromise(
139 final HttpRequest promise,
140 final AsyncPushProducer responseProducer) throws HttpException, IOException {
141 responseTrigger.pushPromise(promise, responseProducer);
142 }
143
144 });
145 }
146 })
147 .create();
148 }
149
150 @Override
151 protected void after() {
152 log.debug("Shutting down test server");
153 if (server != null) {
154 server.close(CloseMode.GRACEFUL);
155 }
156 }
157
158 };
159
160 private HttpAsyncRequester requester;
161
162 @Rule
163 public ExternalResource clientResource = new ExternalResource() {
164
165 @Override
166 protected void before() throws Throwable {
167 log.debug("Starting up test client");
168 requester = H2RequesterBootstrap.bootstrap()
169 .setVersionPolicy(HttpVersionPolicy.NEGOTIATE)
170 .setIOReactorConfig(IOReactorConfig.custom()
171 .setSoTimeout(TIMEOUT)
172 .build())
173 .setTlsStrategy(new H2ClientTlsStrategy(SSLTestContexts.createClientSSLContext()))
174 .setStreamListener(LoggingHttp1StreamListener.INSTANCE_CLIENT)
175 .setStreamListener(LoggingH2StreamListener.INSTANCE)
176 .setConnPoolListener(LoggingConnPoolListener.INSTANCE)
177 .setIOSessionDecorator(LoggingIOSessionDecorator.INSTANCE)
178 .setExceptionCallback(LoggingExceptionCallback.INSTANCE)
179 .setIOSessionListener(LoggingIOSessionListener.INSTANCE)
180 .create();
181 }
182
183 @Override
184 protected void after() {
185 log.debug("Shutting down test client");
186 if (requester != null) {
187 requester.close(CloseMode.GRACEFUL);
188 }
189 }
190
191 };
192
193 @Test
194 public void testSequentialRequests() throws Exception {
195 server.start();
196 final Future<ListenerEndpoint> future = server.listen(new InetSocketAddress(0), URIScheme.HTTP);
197 final ListenerEndpoint listener = future.get();
198 final InetSocketAddress address = (InetSocketAddress) listener.getAddress();
199 requester.start();
200
201 final HttpHost target = new HttpHost("http", "localhost", address.getPort());
202 final Future<Message<HttpResponse, String>> resultFuture = requester.execute(
203 new BasicRequestProducer(Method.POST, target, "/stuff",
204 new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)),
205 new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null);
206 final Message<HttpResponse, String> message = resultFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
207 MatcherAssert.assertThat(message, CoreMatchers.notNullValue());
208 final HttpResponse response = message.getHead();
209 MatcherAssert.assertThat(response.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
210 final Header testFilterHeader = response.getHeader("X-Test-Filter");
211 MatcherAssert.assertThat(testFilterHeader, CoreMatchers.notNullValue());
212 }
213
214 }