1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.forgerock.http.grizzly;
18
19 import static org.forgerock.http.handler.Handlers.asDescribableHandler;
20 import static org.forgerock.http.handler.Handlers.chainOf;
21 import static org.forgerock.http.handler.Handlers.internalServerErrorHandler;
22 import static org.forgerock.http.io.IO.newBranchingInputStream;
23 import static org.forgerock.http.io.IO.newTemporaryStorage;
24 import static org.forgerock.http.protocol.Responses.newInternalServerError;
25 import static org.forgerock.http.routing.UriRouterContext.uriRouterContext;
26 import static org.forgerock.util.Utils.closeSilently;
27
28 import io.swagger.v3.oas.models.OpenAPI;
29 import java.io.File;
30 import java.io.IOException;
31 import java.net.URISyntaxException;
32 import java.security.cert.X509Certificate;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import org.forgerock.http.ApiProducer;
36 import org.forgerock.http.DescribedHttpApplication;
37 import org.forgerock.http.Handler;
38 import org.forgerock.http.HttpApplication;
39 import org.forgerock.http.HttpApplicationException;
40 import org.forgerock.http.filter.TransactionIdInboundFilter;
41 import org.forgerock.http.handler.DescribableHandler;
42 import org.forgerock.http.io.Buffer;
43 import org.forgerock.http.io.IO;
44 import org.forgerock.http.routing.UriRouterContext;
45 import org.forgerock.http.session.SessionContext;
46 import org.forgerock.http.util.CaseInsensitiveSet;
47 import org.forgerock.http.util.Uris;
48 import org.forgerock.services.context.AttributesContext;
49 import org.forgerock.services.context.ClientContext;
50 import org.forgerock.services.context.Context;
51 import org.forgerock.services.context.RequestAuditContext;
52 import org.forgerock.services.context.RootContext;
53 import org.forgerock.util.Factory;
54 import org.forgerock.util.promise.ResultHandler;
55 import org.forgerock.util.promise.RuntimeExceptionHandler;
56 import org.glassfish.grizzly.http.server.HttpHandler;
57 import org.glassfish.grizzly.http.server.Request;
58 import org.glassfish.grizzly.http.server.Response;
59 import org.glassfish.grizzly.http.server.util.Globals;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62
63
64
65
66
67
68
69 final class HandlerAdapter extends HttpHandler {
70
71
72 private static final CaseInsensitiveSet NON_ENTITY_METHODS = new CaseInsensitiveSet(
73 Arrays.asList("GET", "HEAD", "TRACE"));
74
75 private static final Logger LOGGER = LoggerFactory.getLogger(HandlerAdapter.class);
76
77 private final HttpApplication httpApplication;
78 private final Factory<Buffer> storage;
79 private DescribableHandler describedHandler;
80
81 HandlerAdapter(HttpApplication httpApplication) {
82 this.httpApplication = httpApplication;
83 final Factory<Buffer> applicationStorage = httpApplication.getBufferFactory();
84 this.storage = applicationStorage != null
85 ? applicationStorage
86 : newTemporaryStorage(new File(System.getProperty("java.io.tmpdir")));
87 }
88
89 @Override
90 public void start() {
91 super.start();
92 try {
93 describedHandler = chainOf(httpApplication.start(), new TransactionIdInboundFilter());
94 if (httpApplication instanceof DescribedHttpApplication) {
95 ApiProducer<OpenAPI> apiProducer = ((DescribedHttpApplication) httpApplication).getApiProducer();
96 describedHandler.api(apiProducer);
97 }
98 } catch (HttpApplicationException e) {
99 LOGGER.error("Error while starting the application.", e);
100 describedHandler = asDescribableHandler(internalServerErrorHandler(e));
101 }
102 }
103
104 @Override
105 public void destroy() {
106 httpApplication.stop();
107 describedHandler = null;
108 super.destroy();
109 }
110
111 @Override
112 public void service(final Request request, final Response response) throws Exception {
113 final org.forgerock.http.protocol.Request chfRequest = toChfRequest(request);
114 final RootContext rootContext = new RootContext();
115 final SessionContext sessionContext = new SessionContext(rootContext, new SessionAdapter(request.getSession()));
116 final UriRouterContext uriRouterContext = createRouterContext(sessionContext, request, chfRequest);
117 final AttributesContext attributesContext = new AttributesContext(new RequestAuditContext(uriRouterContext));
118 final ClientContext context = createClientContext(attributesContext, request);
119
120
121 request.getContext().suspend();
122
123
124 response.suspend();
125
126 describedHandler.handle(context, chfRequest)
127 .thenOnResult(new ResultHandler<org.forgerock.http.protocol.Response>() {
128 @Override
129 public void handleResult(org.forgerock.http.protocol.Response chfResponse) {
130 writeResponse(chfResponse, response, sessionContext);
131 }
132 })
133 .thenOnRuntimeException(new RuntimeExceptionHandler() {
134 @Override
135 public void handleRuntimeException(RuntimeException e) {
136 LOGGER.error("RuntimeException caught", e);
137 writeResponse(
138 newInternalServerError(e),
139 response, sessionContext);
140 }
141 })
142 .thenAlways(new Runnable() {
143 @Override
144 public void run() {
145 response.resume();
146 }
147 });
148 }
149
150 private void writeResponse(final org.forgerock.http.protocol.Response chfResponse, final Response grizzlyResponse,
151 final SessionContext sessionContext) {
152 try {
153 grizzlyResponse.setStatus(chfResponse.getStatus().getCode());
154 sessionContext.getSession().save(chfResponse);
155
156
157 for (String name : chfResponse.getHeaders().keySet()) {
158 for (String value : chfResponse.getHeaders().get(name).getValues()) {
159 if (value != null && !value.isEmpty()) {
160 grizzlyResponse.addHeader(name, value);
161 }
162 }
163 }
164 IO.stream(chfResponse.getEntity().getRawContentInputStream(), grizzlyResponse.getOutputStream());
165 } catch (IOException e) {
166 LOGGER.trace("Failed to write response", e);
167 } finally {
168 closeSilently(chfResponse);
169 }
170 }
171
172 private org.forgerock.http.protocol.Request toChfRequest(Request req) throws URISyntaxException {
173
174 org.forgerock.http.protocol.Request request = new org.forgerock.http.protocol.Request();
175 request.setMethod(req.getMethod().toString());
176
177
178
179
180
181 request.setUri(Uris.createNonStrict(req.getScheme(), null, req.getServerName(), req.getServerPort(),
182 req.getRequestURI(), req.getQueryString(), null));
183
184
185 for (String e : req.getHeaderNames()) {
186 final ArrayList<String> values = new ArrayList<>(1);
187 for (String value : req.getHeaders(e)) {
188 values.add(value);
189 }
190 request.getHeaders().add(e, values);
191 }
192
193
194 if ((req.getContentLength() > 0 || req.getHeader("Transfer-Encoding") != null)
195 && !NON_ENTITY_METHODS.contains(request.getMethod())) {
196 request.setEntity(newBranchingInputStream(req.getInputStream(), storage));
197 }
198
199 return request;
200 }
201
202 private UriRouterContext createRouterContext(Context parent, Request req,
203 org.forgerock.http.protocol.Request request) {
204 return uriRouterContext(parent).matchedUri("").remainingUri(req.getRequestURI())
205 .originalUri(request.getUri().asURI()).build();
206 }
207
208 private ClientContext createClientContext(Context parent, Request req) {
209 return ClientContext.buildExternalClientContext(parent)
210 .remoteUser(req.getRemoteUser())
211 .remoteAddress(req.getRemoteAddr())
212 .remotePort(req.getRemotePort())
213 .secure("https".equalsIgnoreCase(req.getScheme()))
214 .certificates((X509Certificate[]) req.getAttribute(Globals.CERTIFICATES_ATTR))
215 .userAgent(req.getHeader("User-Agent"))
216 .localAddress(req.getLocalAddr())
217 .localPort(req.getLocalPort())
218 .build();
219 }
220
221 }