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