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