1
18
19 package io.undertow.servlet.handlers;
20
21 import io.undertow.UndertowLogger;
22 import io.undertow.UndertowMessages;
23 import io.undertow.server.DefaultByteBufferPool;
24 import io.undertow.server.HttpHandler;
25 import io.undertow.server.HttpServerExchange;
26 import io.undertow.server.HttpUpgradeListener;
27 import io.undertow.server.SSLSessionInfo;
28 import io.undertow.server.ServerConnection;
29 import io.undertow.server.XnioBufferPoolAdaptor;
30 import io.undertow.servlet.api.Deployment;
31 import io.undertow.servlet.api.ExceptionHandler;
32 import io.undertow.servlet.api.LoggingExceptionHandler;
33 import io.undertow.servlet.api.ServletDispatcher;
34 import io.undertow.servlet.api.ThreadSetupHandler;
35 import io.undertow.servlet.core.ApplicationListeners;
36 import io.undertow.servlet.core.ServletBlockingHttpExchange;
37 import io.undertow.servlet.spec.AsyncContextImpl;
38 import io.undertow.servlet.spec.HttpServletRequestImpl;
39 import io.undertow.servlet.spec.HttpServletResponseImpl;
40 import io.undertow.servlet.spec.RequestDispatcherImpl;
41 import io.undertow.servlet.spec.ServletContextImpl;
42 import io.undertow.util.HttpString;
43 import io.undertow.util.Protocols;
44 import io.undertow.util.StatusCodes;
45 import org.xnio.ChannelListener;
46 import org.xnio.Option;
47 import org.xnio.OptionMap;
48 import io.undertow.connector.ByteBufferPool;
49 import org.xnio.Pool;
50 import org.xnio.StreamConnection;
51 import org.xnio.XnioIoThread;
52 import org.xnio.XnioWorker;
53 import org.xnio.channels.ConnectedChannel;
54 import org.xnio.conduits.ConduitStreamSinkChannel;
55 import org.xnio.conduits.ConduitStreamSourceChannel;
56 import org.xnio.conduits.StreamSinkConduit;
57
58 import javax.servlet.DispatcherType;
59 import javax.servlet.ServletException;
60 import javax.servlet.ServletRequest;
61 import javax.servlet.ServletResponse;
62 import javax.servlet.http.HttpServletRequest;
63 import javax.servlet.http.HttpServletResponse;
64 import java.io.IOException;
65 import java.net.SocketAddress;
66 import java.nio.ByteBuffer;
67 import java.security.AccessController;
68 import java.security.PrivilegedExceptionAction;
69 import java.util.Map;
70 import java.util.concurrent.Executor;
71
72
78 public class ServletInitialHandler implements HttpHandler, ServletDispatcher {
79
80 private static final RuntimePermission PERMISSION = new RuntimePermission("io.undertow.servlet.CREATE_INITIAL_HANDLER");
81
82 private final HttpHandler next;
83
84
85 private final ThreadSetupHandler.Action<Object, ServletRequestContext> firstRequestHandler;
86
87 private final ServletContextImpl servletContext;
88
89 private final ApplicationListeners listeners;
90
91 private final ServletPathMatches paths;
92
93 private final ExceptionHandler exceptionHandler;
94 private final HttpHandler dispatchHandler = new HttpHandler() {
95 @Override
96 public void handleRequest(final HttpServerExchange exchange) throws Exception {
97 final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
98 if (System.getSecurityManager() == null) {
99 dispatchRequest(exchange, servletRequestContext, servletRequestContext.getOriginalServletPathMatch().getServletChain(), DispatcherType.REQUEST);
100 } else {
101
102 AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
103 @Override
104 public Object run() throws Exception {
105 dispatchRequest(exchange, servletRequestContext, servletRequestContext.getOriginalServletPathMatch().getServletChain(), DispatcherType.REQUEST);
106 return null;
107 }
108 });
109 }
110 }
111 };
112
113 public ServletInitialHandler(final ServletPathMatches paths, final HttpHandler next, final Deployment deployment, final ServletContextImpl servletContext) {
114 this.next = next;
115 this.servletContext = servletContext;
116 this.paths = paths;
117 this.listeners = servletContext.getDeployment().getApplicationListeners();
118 SecurityManager sm = System.getSecurityManager();
119 if(sm != null) {
120
121
122 sm.checkPermission(PERMISSION);
123 }
124 ExceptionHandler handler = servletContext.getDeployment().getDeploymentInfo().getExceptionHandler();
125 if(handler != null) {
126 this.exceptionHandler = handler;
127 } else {
128 this.exceptionHandler = LoggingExceptionHandler.DEFAULT;
129 }
130 this.firstRequestHandler = deployment.createThreadSetupAction(new ThreadSetupHandler.Action<Object, ServletRequestContext>() {
131 @Override
132 public Object call(HttpServerExchange exchange, ServletRequestContext context) throws Exception {
133 handleFirstRequest(exchange, context);
134 return null;
135 }
136 });
137 }
138
139 @Override
140 public void handleRequest(final HttpServerExchange exchange) throws Exception {
141 final String path = exchange.getRelativePath();
142 if(isForbiddenPath(path)) {
143 exchange.setStatusCode(StatusCodes.NOT_FOUND);
144 return;
145 }
146 final ServletPathMatch info = paths.getServletHandlerByPath(path);
147 if (info.getType() == ServletPathMatch.Type.REWRITE) {
148
149
150 exchange.setRelativePath(info.getRewriteLocation());
151 exchange.setRequestPath(exchange.getResolvedPath() + info.getRewriteLocation());
152 }
153 final HttpServletResponseImpl response = new HttpServletResponseImpl(exchange, servletContext);
154 final HttpServletRequestImpl request = new HttpServletRequestImpl(exchange, servletContext);
155 final ServletRequestContext servletRequestContext = new ServletRequestContext(servletContext.getDeployment(), request, response, info);
156
157 if (info.getServletChain().getManagedServlet().getMaxRequestSize() > 0) {
158 exchange.setMaxEntitySize(info.getServletChain().getManagedServlet().getMaxRequestSize());
159 }
160 exchange.putAttachment(ServletRequestContext.ATTACHMENT_KEY, servletRequestContext);
161
162 exchange.startBlocking(new ServletBlockingHttpExchange(exchange));
163 servletRequestContext.setServletPathMatch(info);
164
165 Executor executor = info.getServletChain().getExecutor();
166 if (executor == null) {
167 executor = servletContext.getDeployment().getExecutor();
168 }
169
170 if (exchange.isInIoThread() || executor != null) {
171
172 exchange.dispatch(executor, dispatchHandler);
173 } else {
174 dispatchRequest(exchange, servletRequestContext, info.getServletChain(), DispatcherType.REQUEST);
175 }
176 }
177
178 private boolean isForbiddenPath(String path) {
179 return path.equalsIgnoreCase("/meta-inf/")
180 || path.regionMatches(true, 0, "/web-inf/", 0, "/web-inf/".length());
181 }
182
183 public void dispatchToPath(final HttpServerExchange exchange, final ServletPathMatch pathInfo, final DispatcherType dispatcherType) throws Exception {
184 final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
185 servletRequestContext.setServletPathMatch(pathInfo);
186 dispatchRequest(exchange, servletRequestContext, pathInfo.getServletChain(), dispatcherType);
187 }
188
189 @Override
190 public void dispatchToServlet(final HttpServerExchange exchange, final ServletChain servletchain, final DispatcherType dispatcherType) throws Exception {
191 final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
192
193 DispatcherType oldDispatch = servletRequestContext.getDispatcherType();
194 ServletChain oldChain = servletRequestContext.getCurrentServlet();
195 try {
196 dispatchRequest(exchange, servletRequestContext, servletchain, dispatcherType);
197 } finally {
198 servletRequestContext.setDispatcherType(oldDispatch);
199 servletRequestContext.setCurrentServlet(oldChain);
200 }
201 }
202
203 @Override
204 public void dispatchMockRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException {
205
206 final DefaultByteBufferPool bufferPool = new DefaultByteBufferPool(false, 1024, 0, 0);
207 MockServerConnection connection = new MockServerConnection(bufferPool);
208 HttpServerExchange exchange = new HttpServerExchange(connection);
209 exchange.setRequestScheme(request.getScheme());
210 exchange.setRequestMethod(new HttpString(request.getMethod()));
211 exchange.setProtocol(Protocols.HTTP_1_0);
212 exchange.setResolvedPath(request.getContextPath());
213 String relative;
214 if (request.getPathInfo() == null) {
215 relative = request.getServletPath();
216 } else {
217 relative = request.getServletPath() + request.getPathInfo();
218 }
219 exchange.setRelativePath(relative);
220 final ServletPathMatch info = paths.getServletHandlerByPath(request.getServletPath());
221 final HttpServletResponseImpl oResponse = new HttpServletResponseImpl(exchange, servletContext);
222 final HttpServletRequestImpl oRequest = new HttpServletRequestImpl(exchange, servletContext);
223 final ServletRequestContext servletRequestContext = new ServletRequestContext(servletContext.getDeployment(), oRequest, oResponse, info);
224 servletRequestContext.setServletRequest(request);
225 servletRequestContext.setServletResponse(response);
226
227 if (info.getServletChain().getManagedServlet().getMaxRequestSize() > 0) {
228 exchange.setMaxEntitySize(info.getServletChain().getManagedServlet().getMaxRequestSize());
229 }
230 exchange.putAttachment(ServletRequestContext.ATTACHMENT_KEY, servletRequestContext);
231
232 exchange.startBlocking(new ServletBlockingHttpExchange(exchange));
233 servletRequestContext.setServletPathMatch(info);
234
235 try {
236 dispatchRequest(exchange, servletRequestContext, info.getServletChain(), DispatcherType.REQUEST);
237 } catch (Exception e) {
238 if (e instanceof RuntimeException) {
239 throw (RuntimeException) e;
240 }
241 throw new ServletException(e);
242 }
243 }
244
245 private void dispatchRequest(final HttpServerExchange exchange, final ServletRequestContext servletRequestContext, final ServletChain servletChain, final DispatcherType dispatcherType) throws Exception {
246 servletRequestContext.setDispatcherType(dispatcherType);
247 servletRequestContext.setCurrentServlet(servletChain);
248 if (dispatcherType == DispatcherType.REQUEST || dispatcherType == DispatcherType.ASYNC) {
249 firstRequestHandler.call(exchange, servletRequestContext);
250 } else {
251 next.handleRequest(exchange);
252 }
253 }
254
255 private void handleFirstRequest(final HttpServerExchange exchange, ServletRequestContext servletRequestContext) throws Exception {
256 ServletRequest request = servletRequestContext.getServletRequest();
257 ServletResponse response = servletRequestContext.getServletResponse();
258
259
260 Map<String, String> attrs = exchange.getAttachment(HttpServerExchange.REQUEST_ATTRIBUTES);
261 if(attrs != null) {
262 for(Map.Entry<String, String> entry : attrs.entrySet()) {
263 request.setAttribute(entry.getKey(), entry.getValue());
264 }
265 }
266 servletRequestContext.setRunningInsideHandler(true);
267 try {
268 listeners.requestInitialized(request);
269 next.handleRequest(exchange);
270 AsyncContextImpl asyncContextInternal = servletRequestContext.getOriginalRequest().getAsyncContextInternal();
271 if(asyncContextInternal != null && asyncContextInternal.isCompletedBeforeInitialRequestDone()) {
272 asyncContextInternal.handleCompletedBeforeInitialRequestDone();
273 }
274
275 if(servletRequestContext.getErrorCode() > 0) {
276 servletRequestContext.getOriginalResponse().doErrorDispatch(servletRequestContext.getErrorCode(), servletRequestContext.getErrorMessage());
277 }
278 } catch (Throwable t) {
279
280 servletRequestContext.setRunningInsideHandler(false);
281 AsyncContextImpl asyncContextInternal = servletRequestContext.getOriginalRequest().getAsyncContextInternal();
282 if(asyncContextInternal != null && asyncContextInternal.isCompletedBeforeInitialRequestDone()) {
283 asyncContextInternal.handleCompletedBeforeInitialRequestDone();
284 }
285 if(asyncContextInternal != null) {
286 asyncContextInternal.initialRequestFailed();
287 }
288
289 boolean handled = exceptionHandler.handleThrowable(exchange, request, response, t);
290
291 if(handled) {
292 exchange.endExchange();
293 } else if (request.isAsyncStarted() || request.getDispatcherType() == DispatcherType.ASYNC) {
294 exchange.unDispatch();
295 servletRequestContext.getOriginalRequest().getAsyncContextInternal().handleError(t);
296 } else {
297 if (!exchange.isResponseStarted()) {
298 response.reset();
299 exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR);
300 exchange.getResponseHeaders().clear();
301 String location = servletContext.getDeployment().getErrorPages().getErrorLocation(t);
302 if (location == null) {
303 location = servletContext.getDeployment().getErrorPages().getErrorLocation(StatusCodes.INTERNAL_SERVER_ERROR);
304 }
305 if (location != null) {
306 RequestDispatcherImpl dispatcher = new RequestDispatcherImpl(location, servletContext);
307 try {
308 dispatcher.error(servletRequestContext, request, response, servletRequestContext.getOriginalServletPathMatch().getServletChain().getManagedServlet().getServletInfo().getName(), t);
309 } catch (Exception e) {
310 UndertowLogger.REQUEST_LOGGER.exceptionGeneratingErrorPage(e, location);
311 }
312 } else {
313 if (servletRequestContext.displayStackTraces()) {
314 ServletDebugPageHandler.handleRequest(exchange, servletRequestContext, t);
315 } else {
316 servletRequestContext.getOriginalResponse().doErrorDispatch(StatusCodes.INTERNAL_SERVER_ERROR, StatusCodes.INTERNAL_SERVER_ERROR_STRING);
317 }
318 }
319 }
320 }
321
322 } finally {
323 servletRequestContext.setRunningInsideHandler(false);
324 listeners.requestDestroyed(request);
325 }
326
327 if (!exchange.isDispatched() && !(exchange.getConnection() instanceof MockServerConnection)) {
328 servletRequestContext.getOriginalResponse().responseDone();
329 }
330 if(!exchange.isDispatched()) {
331 AsyncContextImpl ctx = servletRequestContext.getOriginalRequest().getAsyncContextInternal();
332 if(ctx != null) {
333 ctx.complete();
334 }
335 }
336 }
337
338 public HttpHandler getNext() {
339 return next;
340 }
341
342 private static class MockServerConnection extends ServerConnection {
343 private final ByteBufferPool bufferPool;
344 private SSLSessionInfo sslSessionInfo;
345 private XnioBufferPoolAdaptor poolAdaptor;
346 private MockServerConnection(ByteBufferPool bufferPool) {
347 this.bufferPool = bufferPool;
348 }
349
350 @Override
351 public Pool<ByteBuffer> getBufferPool() {
352 if(poolAdaptor == null) {
353 poolAdaptor = new XnioBufferPoolAdaptor(getByteBufferPool());
354 }
355 return poolAdaptor;
356 }
357
358
359 @Override
360 public ByteBufferPool getByteBufferPool() {
361 return bufferPool;
362 }
363
364 @Override
365 public XnioWorker getWorker() {
366 return null;
367 }
368
369 @Override
370 public XnioIoThread getIoThread() {
371 return null;
372 }
373
374 @Override
375 public HttpServerExchange sendOutOfBandResponse(HttpServerExchange exchange) {
376 throw UndertowMessages.MESSAGES.outOfBandResponseNotSupported();
377 }
378
379 @Override
380 public boolean isContinueResponseSupported() {
381 return false;
382 }
383
384 @Override
385 public void terminateRequestChannel(HttpServerExchange exchange) {
386
387 }
388
389 @Override
390 public boolean isOpen() {
391 return true;
392 }
393
394 @Override
395 public boolean supportsOption(Option<?> option) {
396 return false;
397 }
398
399 @Override
400 public <T> T getOption(Option<T> option) throws IOException {
401 return null;
402 }
403
404 @Override
405 public <T> T setOption(Option<T> option, T value) throws IllegalArgumentException, IOException {
406 return null;
407 }
408
409 @Override
410 public void close() throws IOException {
411 }
412
413 @Override
414 public SocketAddress getPeerAddress() {
415 return null;
416 }
417
418 @Override
419 public <A extends SocketAddress> A getPeerAddress(Class<A> type) {
420 return null;
421 }
422
423 @Override
424 public ChannelListener.Setter<? extends ConnectedChannel> getCloseSetter() {
425 return null;
426 }
427
428 @Override
429 public SocketAddress getLocalAddress() {
430 return null;
431 }
432
433 @Override
434 public <A extends SocketAddress> A getLocalAddress(Class<A> type) {
435 return null;
436 }
437
438 @Override
439 public OptionMap getUndertowOptions() {
440 return OptionMap.EMPTY;
441 }
442
443 @Override
444 public int getBufferSize() {
445 return 1024;
446 }
447
448 @Override
449 public SSLSessionInfo getSslSessionInfo() {
450 return sslSessionInfo;
451 }
452
453 @Override
454 public void setSslSessionInfo(SSLSessionInfo sessionInfo) {
455 sslSessionInfo = sessionInfo;
456 }
457
458 @Override
459 public void addCloseListener(CloseListener listener) {
460 }
461
462 @Override
463 public StreamConnection upgradeChannel() {
464 return null;
465 }
466
467 @Override
468 public ConduitStreamSinkChannel getSinkChannel() {
469 return null;
470 }
471
472 @Override
473 public ConduitStreamSourceChannel getSourceChannel() {
474 return new ConduitStreamSourceChannel(null, null);
475 }
476
477 @Override
478 protected StreamSinkConduit getSinkConduit(HttpServerExchange exchange, StreamSinkConduit conduit) {
479 return conduit;
480 }
481
482 @Override
483 protected boolean isUpgradeSupported() {
484 return false;
485 }
486
487 @Override
488 protected boolean isConnectSupported() {
489 return false;
490 }
491
492 @Override
493 protected void exchangeComplete(HttpServerExchange exchange) {
494 }
495
496 @Override
497 protected void setUpgradeListener(HttpUpgradeListener upgradeListener) {
498
499 }
500
501 @Override
502 protected void setConnectListener(HttpUpgradeListener connectListener) {
503
504 }
505
506 @Override
507 protected void maxEntitySizeUpdated(HttpServerExchange exchange) {
508 }
509
510 @Override
511 public String getTransportProtocol() {
512 return "mock";
513 }
514
515 @Override
516 public boolean isRequestTrailerFieldsSupported() {
517 return false;
518 }
519 }
520
521 }
522