1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.forgerock.http.bindings;
18
19 import static io.swagger.models.Scheme.HTTP;
20 import static io.swagger.models.Scheme.HTTPS;
21 import static java.lang.String.format;
22 import static java.util.Arrays.asList;
23 import static org.assertj.core.api.Assertions.assertThat;
24 import static org.assertj.core.api.Assertions.fail;
25 import static org.forgerock.http.Applications.describedHttpApplication;
26 import static org.forgerock.http.Applications.simpleHttpApplication;
27 import static org.forgerock.http.filter.TransactionIdInboundFilter.SYSPROP_TRUST_TRANSACTION_HEADER;
28 import static org.forgerock.http.handler.Handlers.chainOf;
29 import static org.forgerock.http.protocol.Response.newResponsePromise;
30 import static org.forgerock.json.JsonValue.json;
31 import static org.forgerock.json.test.assertj.AssertJJsonValueAssert.assertThat;
32 import static org.mockito.Mockito.mock;
33 import static org.mockito.Mockito.verify;
34 import static org.mockito.Mockito.verifyNoMoreInteractions;
35 import static org.mockito.Mockito.when;
36
37 import java.util.List;
38
39 import org.assertj.core.api.SoftAssertionError;
40 import org.assertj.core.api.SoftAssertions;
41 import org.forgerock.http.ApiProducer;
42 import org.forgerock.http.Client;
43 import org.forgerock.http.DescribedHttpApplication;
44 import org.forgerock.http.Handler;
45 import org.forgerock.http.HttpApplication;
46 import org.forgerock.http.HttpApplicationException;
47 import org.forgerock.http.handler.DescribableHandler;
48 import org.forgerock.http.handler.HttpClientHandler;
49 import org.forgerock.http.header.CookieHeader;
50 import org.forgerock.http.header.SetCookieHeader;
51 import org.forgerock.http.protocol.Cookie;
52 import org.forgerock.http.protocol.Request;
53 import org.forgerock.http.protocol.Response;
54 import org.forgerock.http.protocol.Status;
55 import org.forgerock.http.routing.UriRouterContext;
56 import org.forgerock.http.session.Session;
57 import org.forgerock.http.session.SessionContext;
58 import org.forgerock.http.swagger.OpenApiRequestFilter;
59 import org.forgerock.http.swagger.SwaggerApiProducer;
60 import org.forgerock.services.TransactionId;
61 import org.forgerock.services.context.ClientContext;
62 import org.forgerock.services.context.Context;
63 import org.forgerock.services.context.TransactionIdContext;
64 import org.forgerock.util.promise.NeverThrowsException;
65 import org.forgerock.util.promise.Promise;
66 import org.testng.annotations.AfterMethod;
67 import org.testng.annotations.BeforeMethod;
68 import org.testng.annotations.Test;
69
70 import io.swagger.models.Info;
71 import io.swagger.models.Operation;
72 import io.swagger.models.Path;
73 import io.swagger.models.Swagger;
74
75
76
77
78 public abstract class BindingTest {
79
80 private int port;
81
82
83
84
85 protected abstract void createServer();
86
87
88
89
90
91 protected abstract void stopServer() throws Exception;
92
93
94
95
96
97
98 protected abstract int startServer() throws Exception;
99
100
101
102
103
104
105 protected abstract void addApplication(HttpApplication application) throws Exception;
106
107
108
109
110
111 @BeforeMethod
112 public final void setUp() throws Exception {
113 createServer();
114 }
115
116
117
118
119
120 @AfterMethod
121 public final void tearDown() throws Exception {
122 stopServer();
123 port = 0;
124 }
125
126
127
128
129
130 @Test
131 public void testDescribedHttpApplicationLifecycle() throws Exception {
132 final DescribedHttpApplication application = mock(DescribedHttpApplication.class);
133 when(application.start()).thenReturn(mock(DescribableHandler.class));
134 addApplication(application);
135 port = startServer();
136 verify(application).getBufferFactory();
137 verify(application).start();
138 verify(application).getApiProducer();
139 verifyNoMoreInteractions(application);
140
141 stopServer();
142 verify(application).stop();
143 verifyNoMoreInteractions(application);
144 }
145
146
147
148
149
150 @Test
151 public void testHttpApplicationLifecycle() throws Exception {
152 final HttpApplication application = mock(HttpApplication.class);
153 addApplication(application);
154 port = startServer();
155 verify(application).getBufferFactory();
156 verify(application).start();
157 verifyNoMoreInteractions(application);
158
159 stopServer();
160 verify(application).stop();
161 verifyNoMoreInteractions(application);
162 }
163
164
165
166
167
168 @Test
169 public void testAnswerWith500IfHttpApplicationFailedToStart() throws Exception {
170 final HttpApplication application = mock(HttpApplication.class);
171 addApplication(application);
172
173 when(application.start()).thenThrow(new HttpApplicationException("Unable to start the HttpApplication"));
174 port = startServer();
175
176 try (final HttpClientHandler handler = new HttpClientHandler()) {
177 final Client client = new Client(handler);
178 final Request request = new Request()
179 .setMethod("GET")
180 .setUri(format("http://localhost:%d/test", port));
181 final Response response = client.send(request).get();
182 assertThat(response.getStatus()).isEqualTo(Status.INTERNAL_SERVER_ERROR);
183 }
184 }
185
186
187
188
189
190 @Test
191 public void testRequest() throws Exception {
192 HttpApplication application = simpleHttpApplication(new TestHandler(), null);
193 addApplication(application);
194 port = startServer();
195
196 try (final HttpClientHandler handler = new HttpClientHandler()) {
197 final Client client = new Client(handler);
198 final Request request = new Request()
199 .setMethod("POST")
200 .setUri(format("http://localhost:%d/test", port));
201 request.getHeaders().add("X-WhateverHeader", "Whatever Value");
202 request.getEntity().setString("Hello");
203
204 final Response response = client.send(request).get();
205 assertThat(response.getEntity().toString()).isEqualTo("HELLO");
206 assertThat(response.getHeaders().get("X-WhateverHeader").getFirstValue()).isEqualTo("Whatever Value");
207 }
208 }
209
210
211
212
213
214 @Test
215 public void testRequestApi() throws Exception {
216 DescribableHandler testHandler = chainOf(new TestHandler(), new OpenApiRequestFilter());
217 HttpApplication application = describedHttpApplication(testHandler, null,
218 new SwaggerApiProducer(new Info(), "", "", asList(HTTP, HTTPS)));
219 addApplication(application);
220
221 port = startServer();
222
223 try (final HttpClientHandler handler = new HttpClientHandler()) {
224 final Client client = new Client(handler);
225 final Request request = new Request()
226 .setMethod("GET")
227 .setUri(format("http://localhost:%d/test?_api", port));
228 request.getHeaders().add("X-WhateverHeader", "Whatever Value");
229
230 final Response response = client.send(request).get();
231 assertThat(json(response.getEntity().getJson())).isObject()
232 .hasObject("paths")
233 .hasObject("test")
234 .hasObject("post")
235 .hasArray("produces")
236 .containsExactly("text/plain");
237 }
238 }
239
240
241
242
243
244 @Test
245 public void testSession() throws Exception {
246 HttpApplication application = simpleHttpApplication(new TestSessionHandler(), null);
247 addApplication(application);
248 port = startServer();
249
250 try (final HttpClientHandler handler = new HttpClientHandler()) {
251 final Client client = new Client(handler);
252 final Request populate = new Request()
253 .setMethod("POST")
254 .setUri(format("http://localhost:%d/populate", port));
255
256 Response response = client.send(populate).get();
257 assertThat(response.getStatus()).isEqualTo(Status.OK);
258 final List<Cookie> sessionCookie = response.getHeaders().get(SetCookieHeader.class).getCookies();
259
260 final Request check = new Request()
261 .setMethod("POST")
262 .setUri(format("http://localhost:%d/check", port));
263 check.getHeaders().put(new CookieHeader(sessionCookie));
264
265 response = client.send(check).get();
266 assertThat(response.getEntity().toString()).isEqualTo("OK");
267 }
268 }
269
270
271
272
273
274 @Test
275 public void testTransactionContext() throws Exception {
276 Handler handler = new Handler() {
277 @Override
278 public Promise<Response, NeverThrowsException> handle(Context context, Request request) {
279 if (!context.containsContext(TransactionIdContext.class)) {
280 return newResponsePromise(new Response(Status.EXPECTATION_FAILED));
281 }
282 Response response = new Response(Status.OK);
283 TransactionId transactionId = context.asContext(TransactionIdContext.class).getTransactionId();
284 response.setEntity(transactionId.getValue());
285 return newResponsePromise(response);
286 }
287 };
288 HttpApplication application = simpleHttpApplication(handler, null);
289 addApplication(application);
290
291 String previousPropertyValue = System.setProperty(SYSPROP_TRUST_TRANSACTION_HEADER, "true");
292 port = startServer();
293
294 try (final HttpClientHandler httpClientHandler = new HttpClientHandler()) {
295 final Client client = new Client(httpClientHandler);
296 final Request request = new Request()
297 .setMethod("GET")
298 .setUri(format("http://localhost:%d/", port));
299 request.getHeaders().add("X-ForgeRock-TransactionId", "test-transaction-id");
300
301 Response response = client.send(request).get();
302 assertThat(response.getStatus()).isEqualTo(Status.OK);
303 assertThat(response.getEntity().toString()).isEqualTo("test-transaction-id");
304 } finally {
305 if (previousPropertyValue == null) {
306 System.clearProperty(SYSPROP_TRUST_TRANSACTION_HEADER);
307 } else {
308 System.setProperty(SYSPROP_TRUST_TRANSACTION_HEADER, previousPropertyValue);
309 }
310 }
311 }
312
313 private final class TestHandler implements DescribableHandler {
314
315 @Override
316 public Promise<Response, NeverThrowsException> handle(Context context, Request request) {
317 final SoftAssertions softly = new SoftAssertions();
318 try {
319 softly.assertThat(request.getMethod()).isEqualTo("POST");
320 softly.assertThat(request.getUri().getPath()).isEqualTo("/test");
321 softly.assertThat(request.getEntity().toString()).isEqualTo("Hello");
322 softly.assertThat(request.getHeaders().get("X-WhateverHeader").getFirstValue())
323 .isEqualTo("Whatever Value");
324 softly.assertThat(context.asContext(UriRouterContext.class)).isNotNull();
325 softly.assertThat(context.asContext(UriRouterContext.class).getMatchedUri()).isEmpty();
326 softly.assertThat(context.asContext(UriRouterContext.class).getOriginalUri().toString())
327 .isEqualTo(format("http://localhost:%d/test", port));
328 softly.assertThat(context.asContext(SessionContext.class)).isNotNull();
329 softly.assertThat(context.asContext(SessionContext.class).getSession()).isNotNull();
330 softly.assertThat(context.asContext(ClientContext.class)).isNotNull();
331 softly.assertThat(context.asContext(ClientContext.class).getLocalPort())
332 .isEqualTo(port);
333 softly.assertAll();
334
335 final Response response = new Response(Status.OK);
336 response.getHeaders().addAll(request.getHeaders().asMapOfHeaders());
337 response.setEntity(request.getEntity().toString().toUpperCase());
338 return newResponsePromise(response);
339 } catch (SoftAssertionError e) {
340 return newResponsePromise(new Response(Status.INTERNAL_SERVER_ERROR)
341 .setEntity(e.getMessage()).setCause(new Exception(e)));
342 }
343 }
344
345 @Override
346 public Swagger api(ApiProducer<Swagger> producer) {
347 return null;
348 }
349
350 @Override
351 public Swagger handleApiRequest(Context context, Request request) {
352 return new Swagger().path("test", new Path().post(new Operation().produces("text/plain")));
353 }
354
355 @Override
356 public void addDescriptorListener(Listener listener) {
357
358 }
359
360 @Override
361 public void removeDescriptorListener(Listener listener) {
362
363 }
364 }
365
366 private final class TestSessionHandler implements Handler {
367 @Override
368 public Promise<Response, NeverThrowsException> handle(Context context, Request request) {
369 final Session session = context.asContext(SessionContext.class).getSession();
370 try {
371 if (request.getUri().toASCIIString().endsWith("/populate")) {
372 assertThat(session.isEmpty()).isTrue();
373 assertThat(session.size()).isEqualTo(0);
374 assertThat(session.containsKey("sessionKey")).isFalse();
375 assertThat(session.containsValue("sessionValue")).isFalse();
376 assertThat(session.put("sessionKey", "sessionValue")).isNull();
377 } else if (request.getUri().toASCIIString().endsWith("/check")) {
378 assertThat(session.get("sessionKey")).isEqualTo("sessionValue");
379 assertThat(session.isEmpty()).isFalse();
380 assertThat(session.size()).isEqualTo(1);
381 assertThat(session.containsKey("sessionKey")).isTrue();
382 assertThat(session.containsValue("sessionValue")).isTrue();
383 } else {
384 fail("Unsupported URI: " + request.getUri().toString());
385 }
386
387 final Response response = new Response(Status.OK);
388 response.setEntity("OK");
389 return newResponsePromise(response);
390 } catch (AssertionError e) {
391 return newResponsePromise(new Response(Status.INTERNAL_SERVER_ERROR)
392 .setEntity(e.getMessage()).setCause(new Exception(e)));
393 }
394 }
395 }
396 }