1
18 package io.undertow.servlet.spec;
19
20 import io.undertow.Version;
21 import io.undertow.server.HandlerWrapper;
22 import io.undertow.server.HttpServerExchange;
23 import io.undertow.server.handlers.cache.LRUCache;
24 import io.undertow.server.handlers.resource.Resource;
25 import io.undertow.server.session.PathParameterSessionConfig;
26 import io.undertow.server.session.Session;
27 import io.undertow.server.session.SessionConfig;
28 import io.undertow.server.session.SessionManager;
29 import io.undertow.server.session.SslSessionConfig;
30 import io.undertow.servlet.UndertowServletLogger;
31 import io.undertow.servlet.UndertowServletMessages;
32 import io.undertow.servlet.api.Deployment;
33 import io.undertow.servlet.api.DeploymentInfo;
34 import io.undertow.servlet.api.DeploymentManager;
35 import io.undertow.servlet.api.FilterInfo;
36 import io.undertow.servlet.api.HttpMethodSecurityInfo;
37 import io.undertow.servlet.api.InstanceFactory;
38 import io.undertow.servlet.api.ListenerInfo;
39 import io.undertow.servlet.api.SecurityInfo;
40 import io.undertow.servlet.api.ServletContainer;
41 import io.undertow.servlet.api.ServletInfo;
42 import io.undertow.servlet.api.ServletSecurityInfo;
43 import io.undertow.servlet.api.SessionConfigWrapper;
44 import io.undertow.servlet.api.ThreadSetupHandler;
45 import io.undertow.servlet.api.TransportGuaranteeType;
46 import io.undertow.servlet.core.ApplicationListeners;
47 import io.undertow.servlet.core.ManagedListener;
48 import io.undertow.servlet.core.ManagedServlet;
49 import io.undertow.servlet.handlers.ServletChain;
50 import io.undertow.servlet.handlers.ServletHandler;
51 import io.undertow.servlet.handlers.ServletRequestContext;
52 import io.undertow.servlet.util.EmptyEnumeration;
53 import io.undertow.servlet.util.ImmediateInstanceFactory;
54 import io.undertow.servlet.util.IteratorEnumeration;
55 import io.undertow.util.AttachmentKey;
56 import io.undertow.util.CanonicalPathUtils;
57
58 import javax.annotation.security.DeclareRoles;
59 import javax.annotation.security.RunAs;
60 import javax.servlet.DispatcherType;
61 import javax.servlet.Filter;
62 import javax.servlet.FilterRegistration;
63 import javax.servlet.MultipartConfigElement;
64 import javax.servlet.ReadListener;
65 import javax.servlet.RequestDispatcher;
66 import javax.servlet.Servlet;
67 import javax.servlet.ServletContext;
68 import javax.servlet.ServletContextListener;
69 import javax.servlet.ServletException;
70 import javax.servlet.ServletRegistration;
71 import javax.servlet.ServletRequest;
72 import javax.servlet.SessionTrackingMode;
73 import javax.servlet.WriteListener;
74 import javax.servlet.annotation.HttpMethodConstraint;
75 import javax.servlet.annotation.MultipartConfig;
76 import javax.servlet.annotation.ServletSecurity;
77 import javax.servlet.descriptor.JspConfigDescriptor;
78 import java.io.BufferedInputStream;
79 import java.io.File;
80 import java.io.FileInputStream;
81 import java.io.FileNotFoundException;
82 import java.io.IOException;
83 import java.io.InputStream;
84 import java.net.MalformedURLException;
85 import java.net.URL;
86 import java.nio.file.Files;
87 import java.nio.file.Path;
88 import java.security.AccessController;
89 import java.security.PrivilegedAction;
90 import java.util.Arrays;
91 import java.util.Collections;
92 import java.util.EnumSet;
93 import java.util.Enumeration;
94 import java.util.EventListener;
95 import java.util.HashMap;
96 import java.util.HashSet;
97 import java.util.List;
98 import java.util.Locale;
99 import java.util.Map;
100 import java.util.Set;
101 import java.util.concurrent.ConcurrentHashMap;
102 import java.util.concurrent.ConcurrentMap;
103
104 import static io.undertow.servlet.core.ApplicationListeners.ListenerState.NO_LISTENER;
105 import static io.undertow.servlet.core.ApplicationListeners.ListenerState.PROGRAMATIC_LISTENER;
106
107
110 public class ServletContextImpl implements ServletContext {
111
112 private final ServletContainer servletContainer;
113 private final Deployment deployment;
114 private volatile DeploymentInfo deploymentInfo;
115 private final ConcurrentMap<String, Object> attributes;
116 private final SessionCookieConfigImpl sessionCookieConfig;
117 private final AttachmentKey<HttpSessionImpl> sessionAttachmentKey = AttachmentKey.create(HttpSessionImpl.class);
118 private volatile Set<SessionTrackingMode> sessionTrackingModes = new HashSet<>(Arrays.asList(new SessionTrackingMode[]{SessionTrackingMode.COOKIE, SessionTrackingMode.URL}));
119 private volatile Set<SessionTrackingMode> defaultSessionTrackingModes = new HashSet<>(Arrays.asList(new SessionTrackingMode[]{SessionTrackingMode.COOKIE, SessionTrackingMode.URL}));
120 private volatile SessionConfig sessionConfig;
121 private volatile boolean initialized = false;
122 private int filterMappingUrlPatternInsertPosition = 0;
123 private int filterMappingServletNameInsertPosition = 0;
124 private final LRUCache<String, ContentTypeInfo> contentTypeCache;
125
126
127
128 private volatile ThreadSetupHandler.Action<Void, WriteListener> onWritePossibleTask;
129 private volatile ThreadSetupHandler.Action<Void, Runnable> runnableTask;
130 private volatile ThreadSetupHandler.Action<Void, ReadListener> onDataAvailableTask;
131 private volatile ThreadSetupHandler.Action<Void, ReadListener> onAllDataReadTask;
132 private volatile ThreadSetupHandler.Action<Void, ThreadSetupHandler.Action<Void, Object>> invokeActionTask;
133 private volatile int defaultSessionTimeout;
134
135 public ServletContextImpl(final ServletContainer servletContainer, final Deployment deployment) {
136 this.servletContainer = servletContainer;
137 this.deployment = deployment;
138 this.deploymentInfo = deployment.getDeploymentInfo();
139 sessionCookieConfig = new SessionCookieConfigImpl(this);
140 sessionCookieConfig.setPath(deploymentInfo.getContextPath());
141 if (deploymentInfo.getServletContextAttributeBackingMap() == null) {
142 this.attributes = new ConcurrentHashMap<>();
143 } else {
144 this.attributes = deploymentInfo.getServletContextAttributeBackingMap();
145 }
146 attributes.putAll(deployment.getDeploymentInfo().getServletContextAttributes());
147 this.contentTypeCache = new LRUCache<>(deployment.getDeploymentInfo().getContentTypeCacheSize(), -1, true);
148 this.defaultSessionTimeout = deploymentInfo.getDefaultSessionTimeout() / 60;
149 }
150
151 public void initDone() {
152 initialized = true;
153 Set<SessionTrackingMode> trackingMethods = sessionTrackingModes;
154 SessionConfig sessionConfig = sessionCookieConfig;
155 if (trackingMethods != null && !trackingMethods.isEmpty()) {
156 if (sessionTrackingModes.contains(SessionTrackingMode.SSL)) {
157 sessionConfig = new SslSessionConfig(deployment.getSessionManager());
158 } else {
159 if (sessionTrackingModes.contains(SessionTrackingMode.COOKIE) && sessionTrackingModes.contains(SessionTrackingMode.URL)) {
160 sessionCookieConfig.setFallback(new PathParameterSessionConfig(sessionCookieConfig.getName().toLowerCase(Locale.ENGLISH)));
161 } else if (sessionTrackingModes.contains(SessionTrackingMode.URL)) {
162 sessionConfig = new PathParameterSessionConfig(sessionCookieConfig.getName().toLowerCase(Locale.ENGLISH));
163 }
164 }
165 }
166 SessionConfigWrapper wrapper = deploymentInfo.getSessionConfigWrapper();
167 if (wrapper != null) {
168 sessionConfig = wrapper.wrap(sessionConfig, deployment);
169 }
170 this.sessionConfig = new ServletContextSessionConfig(sessionConfig);
171 this.onWritePossibleTask = deployment.createThreadSetupAction(new ThreadSetupHandler.Action<Void, WriteListener>() {
172 @Override
173 public Void call(HttpServerExchange exchange, WriteListener context) throws Exception {
174 context.onWritePossible();
175 return null;
176 }
177 });
178 this.runnableTask = new ThreadSetupHandler.Action<Void, Runnable>() {
179 @Override
180 public Void call(HttpServerExchange exchange, Runnable context) throws Exception {
181 context.run();
182 return null;
183 }
184 };
185 this.onDataAvailableTask = deployment.createThreadSetupAction(new ThreadSetupHandler.Action<Void, ReadListener>() {
186 @Override
187 public Void call(HttpServerExchange exchange, ReadListener context) throws Exception {
188 context.onDataAvailable();
189 return null;
190 }
191 });
192 this.onAllDataReadTask = deployment.createThreadSetupAction(new ThreadSetupHandler.Action<Void, ReadListener>() {
193 @Override
194 public Void call(HttpServerExchange exchange, ReadListener context) throws Exception {
195 context.onAllDataRead();
196 return null;
197 }
198 });
199 this.invokeActionTask = deployment.createThreadSetupAction(new ThreadSetupHandler.Action<Void, ThreadSetupHandler.Action<Void, Object>>() {
200 @Override
201 public Void call(HttpServerExchange exchange, ThreadSetupHandler.Action<Void, Object> context) throws Exception {
202 context.call(exchange, null);
203 return null;
204 }
205 });
206 }
207
208 @Override
209 public String getContextPath() {
210 String contextPath = deploymentInfo.getContextPath();
211 if (contextPath.equals("/")) {
212 return "";
213 }
214 return contextPath;
215 }
216
217 @Override
218 public ServletContext getContext(final String uripath) {
219 DeploymentManager deploymentByPath = servletContainer.getDeploymentByPath(uripath);
220 if (deploymentByPath == null) {
221 return null;
222 }
223 return deploymentByPath.getDeployment().getServletContext();
224 }
225
226 @Override
227 public int getMajorVersion() {
228 return deploymentInfo.getContainerMajorVersion();
229 }
230
231 @Override
232 public int getMinorVersion() {
233 return deploymentInfo.getContainerMinorVersion();
234 }
235
236 @Override
237 public int getEffectiveMajorVersion() {
238 return deploymentInfo.getMajorVersion();
239 }
240
241 @Override
242 public int getEffectiveMinorVersion() {
243 return deploymentInfo.getMinorVersion();
244 }
245
246 @Override
247 public String getMimeType(final String file) {
248 if(file == null) {
249 return null;
250 }
251 String lower = file.toLowerCase(Locale.ENGLISH);
252 int pos = lower.lastIndexOf('.');
253 if (pos == -1) {
254 return null;
255 }
256 return deployment.getMimeExtensionMappings().get(lower.substring(pos + 1));
257 }
258
259 @Override
260 public Set<String> getResourcePaths(final String path) {
261 final Resource resource;
262 try {
263 resource = deploymentInfo.getResourceManager().getResource(path);
264 } catch (IOException e) {
265 return null;
266 }
267 if (resource == null || !resource.isDirectory()) {
268 return null;
269 }
270 final Set<String> resources = new HashSet<>();
271 for (Resource res : resource.list()) {
272 Path file = res.getFilePath();
273 if (file != null) {
274 Path base = res.getResourceManagerRootPath();
275 if (base == null) {
276 resources.add(file.toString());
277 } else {
278 String filePath = file.toAbsolutePath().toString().substring(base.toAbsolutePath().toString().length());
279 filePath = filePath.replace('\\', '/');
280 if (Files.isDirectory(file)) {
281 filePath = filePath + "/";
282 }
283 resources.add(filePath);
284 }
285 }
286 }
287 return resources;
288 }
289
290 @Override
291 public URL getResource(final String path) throws MalformedURLException {
292 if (!path.startsWith("/")) {
293 throw UndertowServletMessages.MESSAGES.pathMustStartWithSlash(path);
294 }
295 Resource resource = null;
296 try {
297 resource = deploymentInfo.getResourceManager().getResource(path);
298 } catch (IOException e) {
299 return null;
300 }
301 if (resource == null) {
302 return null;
303 }
304 return resource.getUrl();
305 }
306
307 @Override
308 public InputStream getResourceAsStream(final String path) {
309 Resource resource = null;
310 try {
311 resource = deploymentInfo.getResourceManager().getResource(path);
312 } catch (IOException e) {
313 return null;
314 }
315 if (resource == null) {
316 return null;
317 }
318 try {
319 if (resource.getFile() != null) {
320 return new BufferedInputStream(new FileInputStream(resource.getFile()));
321 } else {
322 return new BufferedInputStream(resource.getUrl().openStream());
323 }
324 } catch (FileNotFoundException e) {
325
326 return null;
327 } catch (IOException e) {
328 return null;
329 }
330 }
331
332 @Override
333 public RequestDispatcher getRequestDispatcher(final String path) {
334 return new RequestDispatcherImpl(path, this);
335 }
336
337 @Override
338 public RequestDispatcher getNamedDispatcher(final String name) {
339 ServletChain chain = deployment.getServletPaths().getServletHandlerByName(name);
340 if (chain != null) {
341 return new RequestDispatcherImpl(chain, this);
342 } else {
343 return null;
344 }
345 }
346
347 @Override
348 public Servlet getServlet(final String name) throws ServletException {
349 return deployment.getServletPaths().getServletHandlerByName(name).getManagedServlet().getServlet().getInstance();
350 }
351
352 @Override
353 public Enumeration<Servlet> getServlets() {
354 return EmptyEnumeration.instance();
355 }
356
357 @Override
358 public Enumeration<String> getServletNames() {
359 return EmptyEnumeration.instance();
360 }
361
362 @Override
363 public void log(final String msg) {
364 UndertowServletLogger.ROOT_LOGGER.info(msg);
365 }
366
367 @Override
368 public void log(final Exception exception, final String msg) {
369 UndertowServletLogger.ROOT_LOGGER.error(msg, exception);
370 }
371
372 @Override
373 public void log(final String message, final Throwable throwable) {
374 UndertowServletLogger.ROOT_LOGGER.error(message, throwable);
375 }
376
377 @Override
378 public String getRealPath(final String path) {
379 if (path == null) {
380 return null;
381 }
382 String canonicalPath = CanonicalPathUtils.canonicalize(path);
383 Resource resource;
384 try {
385 resource = deploymentInfo.getResourceManager().getResource(canonicalPath);
386
387 if (resource == null) {
388
389 Resource deploymentRoot = deploymentInfo.getResourceManager().getResource("/");
390 if(deploymentRoot == null) {
391 return null;
392 }
393 Path root = deploymentRoot.getFilePath();
394 if(root == null) {
395 return null;
396 }
397 if(!canonicalPath.startsWith("/")) {
398 canonicalPath = "/" + canonicalPath;
399 }
400 if(File.separatorChar != '/') {
401 canonicalPath = canonicalPath.replace('/', File.separatorChar);
402 }
403 return root.toAbsolutePath().toString() + canonicalPath;
404 }
405 } catch (IOException e) {
406 return null;
407 }
408 Path file = resource.getFilePath();
409 if (file == null) {
410 return null;
411 }
412 return file.toAbsolutePath().toString();
413 }
414
415 @Override
416 public String getServerInfo() {
417 return deploymentInfo.getServerName() + " - " + Version.getVersionString();
418 }
419
420 @Override
421 public String getInitParameter(final String name) {
422 if (name == null) {
423 throw UndertowServletMessages.MESSAGES.nullName();
424 }
425 return deploymentInfo.getInitParameters().get(name);
426 }
427
428 @Override
429 public Enumeration<String> getInitParameterNames() {
430 return new IteratorEnumeration<>(deploymentInfo.getInitParameters().keySet().iterator());
431 }
432
433 @Override
434 public boolean setInitParameter(final String name, final String value) {
435 if(name == null) {
436 throw UndertowServletMessages.MESSAGES.paramCannotBeNullNPE("name");
437 }
438 if (deploymentInfo.getInitParameters().containsKey(name)) {
439 return false;
440 }
441 deploymentInfo.addInitParameter(name, value);
442 return true;
443 }
444
445 @Override
446 public Object getAttribute(final String name) {
447 if (name == null) {
448 throw UndertowServletMessages.MESSAGES.nullName();
449 }
450 return attributes.get(name);
451 }
452
453 @Override
454 public Enumeration<String> getAttributeNames() {
455 return new IteratorEnumeration<>(attributes.keySet().iterator());
456 }
457
458 @Override
459 public void setAttribute(final String name, final Object object) {
460 if (name == null) {
461 throw UndertowServletMessages.MESSAGES.nullName();
462 }
463 if (object == null) {
464 Object existing = attributes.remove(name);
465 if (deployment.getApplicationListeners() != null) {
466 if (existing != null) {
467 deployment.getApplicationListeners().servletContextAttributeRemoved(name, existing);
468 }
469 }
470 } else {
471 Object existing = attributes.put(name, object);
472 if (deployment.getApplicationListeners() != null) {
473 if (existing != null) {
474 deployment.getApplicationListeners().servletContextAttributeReplaced(name, existing);
475 } else {
476 deployment.getApplicationListeners().servletContextAttributeAdded(name, object);
477 }
478 }
479 }
480 }
481
482 @Override
483 public void removeAttribute(final String name) {
484 Object exiting = attributes.remove(name);
485 deployment.getApplicationListeners().servletContextAttributeRemoved(name, exiting);
486 }
487
488 @Override
489 public String getServletContextName() {
490 return deploymentInfo.getDisplayName();
491 }
492
493 @Override
494 public ServletRegistration.Dynamic addServlet(final String servletName, final String className) {
495 return addServlet(servletName, className, Collections.emptyList());
496 }
497
498 public ServletRegistration.Dynamic addServlet(final String servletName, final String className, List<HandlerWrapper> wrappers) {
499 ensureNotProgramaticListener();
500 ensureNotInitialized();
501 ensureServletNameNotNull(servletName);
502 try {
503 if (deploymentInfo.getServlets().containsKey(servletName)) {
504 return null;
505 }
506 Class<? extends Servlet> servletClass=(Class<? extends Servlet>) deploymentInfo.getClassLoader().loadClass(className);
507 ServletInfo servlet = new ServletInfo(servletName, servletClass, deploymentInfo.getClassIntrospecter().createInstanceFactory(servletClass));
508 for(HandlerWrapper i : wrappers) {
509 servlet.addHandlerChainWrapper(i);
510 }
511 readServletAnnotations(servlet);
512 deploymentInfo.addServlet(servlet);
513 ServletHandler handler = deployment.getServlets().addServlet(servlet);
514 return new ServletRegistrationImpl(servlet, handler.getManagedServlet(), deployment);
515 } catch (ClassNotFoundException e) {
516 throw UndertowServletMessages.MESSAGES.cannotLoadClass(className, e);
517 } catch (NoSuchMethodException e) {
518 throw UndertowServletMessages.MESSAGES.couldNotCreateFactory(className,e);
519 }
520 }
521
522 @Override
523 public ServletRegistration.Dynamic addServlet(final String servletName, final Servlet servlet) {
524 ensureNotProgramaticListener();
525 ensureNotInitialized();
526 ensureServletNameNotNull(servletName);
527 if (deploymentInfo.getServlets().containsKey(servletName)) {
528 return null;
529 }
530 ServletInfo s = new ServletInfo(servletName, servlet.getClass(), new ImmediateInstanceFactory<>(servlet));
531 readServletAnnotations(s);
532 deploymentInfo.addServlet(s);
533 ServletHandler handler = deployment.getServlets().addServlet(s);
534 return new ServletRegistrationImpl(s, handler.getManagedServlet(), deployment);
535 }
536
537 @Override
538 public ServletRegistration.Dynamic addServlet(final String servletName, final Class<? extends Servlet> servletClass){
539 ensureNotProgramaticListener();
540 ensureNotInitialized();
541 ensureServletNameNotNull(servletName);
542 if (deploymentInfo.getServlets().containsKey(servletName)) {
543 return null;
544 }
545 try {
546 ServletInfo servlet = new ServletInfo(servletName, servletClass, deploymentInfo.getClassIntrospecter().createInstanceFactory(servletClass));
547 readServletAnnotations(servlet);
548 deploymentInfo.addServlet(servlet);
549 ServletHandler handler = deployment.getServlets().addServlet(servlet);
550 return new ServletRegistrationImpl(servlet, handler.getManagedServlet(), deployment);
551 } catch (NoSuchMethodException e) {
552 throw UndertowServletMessages.MESSAGES.couldNotCreateFactory(servletClass.getName(),e);
553 }
554 }
555
556 private void ensureServletNameNotNull(String servletName) {
557 if(servletName == null) {
558 throw UndertowServletMessages.MESSAGES.servletNameNull();
559 }
560 }
561
562 @Override
563 public <T extends Servlet> T createServlet(final Class<T> clazz) throws ServletException {
564 ensureNotProgramaticListener();
565 try {
566 return deploymentInfo.getClassIntrospecter().createInstanceFactory(clazz).createInstance().getInstance();
567 } catch (Exception e) {
568 throw UndertowServletMessages.MESSAGES.couldNotInstantiateComponent(clazz.getName(), e);
569 }
570 }
571
572 @Override
573 public ServletRegistration getServletRegistration(final String servletName) {
574 ensureNotProgramaticListener();
575 final ManagedServlet servlet = deployment.getServlets().getManagedServlet(servletName);
576 if (servlet == null) {
577 return null;
578 }
579 return new ServletRegistrationImpl(servlet.getServletInfo(), servlet, deployment);
580 }
581
582 @Override
583 public Map<String, ? extends ServletRegistration> getServletRegistrations() {
584 ensureNotProgramaticListener();
585 final Map<String, ServletRegistration> ret = new HashMap<>();
586 for (Map.Entry<String, ServletHandler> entry : deployment.getServlets().getServletHandlers().entrySet()) {
587 ret.put(entry.getKey(), new ServletRegistrationImpl(entry.getValue().getManagedServlet().getServletInfo(), entry.getValue().getManagedServlet(), deployment));
588 }
589 return ret;
590 }
591
592 @Override
593 public FilterRegistration.Dynamic addFilter(final String filterName, final String className) {
594 ensureNotProgramaticListener();
595 ensureNotInitialized();
596 if (deploymentInfo.getFilters().containsKey(filterName)) {
597 return null;
598 }
599 try {
600 Class<? extends Filter> filterClass=(Class<? extends Filter>) deploymentInfo.getClassLoader().loadClass(className);
601 FilterInfo filter = new FilterInfo(filterName, filterClass, deploymentInfo.getClassIntrospecter().createInstanceFactory(filterClass));
602 deploymentInfo.addFilter(filter);
603 deployment.getFilters().addFilter(filter);
604 return new FilterRegistrationImpl(filter, deployment, this);
605 } catch (ClassNotFoundException e) {
606 throw UndertowServletMessages.MESSAGES.cannotLoadClass(className, e);
607 }catch (NoSuchMethodException e) {
608 throw UndertowServletMessages.MESSAGES.couldNotCreateFactory(className,e);
609 }
610 }
611
612 @Override
613 public FilterRegistration.Dynamic addFilter(final String filterName, final Filter filter) {
614 ensureNotProgramaticListener();
615 ensureNotInitialized();
616
617 if (deploymentInfo.getFilters().containsKey(filterName)) {
618 return null;
619 }
620 FilterInfo f = new FilterInfo(filterName, filter.getClass(), new ImmediateInstanceFactory<>(filter));
621 deploymentInfo.addFilter(f);
622 deployment.getFilters().addFilter(f);
623 return new FilterRegistrationImpl(f, deployment, this);
624
625 }
626
627 @Override
628 public FilterRegistration.Dynamic addFilter(final String filterName, final Class<? extends Filter> filterClass) {
629 ensureNotProgramaticListener();
630 ensureNotInitialized();
631 if (deploymentInfo.getFilters().containsKey(filterName)) {
632 return null;
633 }
634 try {
635 FilterInfo filter = new FilterInfo(filterName, filterClass,deploymentInfo.getClassIntrospecter().createInstanceFactory(filterClass));
636 deploymentInfo.addFilter(filter);
637 deployment.getFilters().addFilter(filter);
638 return new FilterRegistrationImpl(filter, deployment, this);
639 } catch (NoSuchMethodException e) {
640 throw UndertowServletMessages.MESSAGES.couldNotCreateFactory(filterClass.getName(),e);
641 }
642 }
643
644 @Override
645 public <T extends Filter> T createFilter(final Class<T> clazz) throws ServletException {
646 ensureNotProgramaticListener();
647 try {
648 return deploymentInfo.getClassIntrospecter().createInstanceFactory(clazz).createInstance().getInstance();
649 } catch (Exception e) {
650 throw UndertowServletMessages.MESSAGES.couldNotInstantiateComponent(clazz.getName(), e);
651 }
652 }
653
654 @Override
655 public FilterRegistration getFilterRegistration(final String filterName) {
656 ensureNotProgramaticListener();
657 final FilterInfo filterInfo = deploymentInfo.getFilters().get(filterName);
658 if (filterInfo == null) {
659 return null;
660 }
661 return new FilterRegistrationImpl(filterInfo, deployment, this);
662 }
663
664 @Override
665 public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
666 ensureNotProgramaticListener();
667 final Map<String, FilterRegistration> ret = new HashMap<>();
668 for (Map.Entry<String, FilterInfo> entry : deploymentInfo.getFilters().entrySet()) {
669 ret.put(entry.getKey(), new FilterRegistrationImpl(entry.getValue(), deployment, this));
670 }
671 return ret;
672 }
673
674 @Override
675 public SessionCookieConfigImpl getSessionCookieConfig() {
676 ensureNotProgramaticListener();
677 return sessionCookieConfig;
678 }
679
680 @Override
681 public void setSessionTrackingModes(final Set<SessionTrackingMode> sessionTrackingModes) {
682 ensureNotProgramaticListener();
683 ensureNotInitialized();
684 if (sessionTrackingModes.size() > 1 && sessionTrackingModes.contains(SessionTrackingMode.SSL)) {
685 throw UndertowServletMessages.MESSAGES.sslCannotBeCombinedWithAnyOtherMethod();
686 }
687 this.sessionTrackingModes = new HashSet<>(sessionTrackingModes);
688
689 }
690
691 @Override
692 public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
693 ensureNotProgramaticListener();
694 return defaultSessionTrackingModes;
695 }
696
697 @Override
698 public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
699 ensureNotProgramaticListener();
700 return Collections.unmodifiableSet(sessionTrackingModes);
701 }
702
703 @Override
704 public void addListener(final String className) {
705 try {
706 Class<? extends EventListener> clazz = (Class<? extends EventListener>) deploymentInfo.getClassLoader().loadClass(className);
707 addListener(clazz);
708 } catch (ClassNotFoundException e) {
709 throw new IllegalArgumentException(e);
710 }
711 }
712
713 @Override
714 public <T extends EventListener> void addListener(final T t) {
715 ensureNotInitialized();
716 ensureNotProgramaticListener();
717 if (ApplicationListeners.listenerState() != NO_LISTENER
718 && ServletContextListener.class.isAssignableFrom(t.getClass())) {
719 throw UndertowServletMessages.MESSAGES.cannotAddServletContextListener();
720 }
721 ListenerInfo listener = new ListenerInfo(t.getClass(), new ImmediateInstanceFactory<EventListener>(t));
722 deploymentInfo.addListener(listener);
723 deployment.getApplicationListeners().addListener(new ManagedListener(listener, true));
724 }
725
726 @Override
727 public void addListener(final Class<? extends EventListener> listenerClass) {
728 ensureNotInitialized();
729 ensureNotProgramaticListener();
730 if (ApplicationListeners.listenerState() != NO_LISTENER
731 && ServletContextListener.class.isAssignableFrom(listenerClass)) {
732 throw UndertowServletMessages.MESSAGES.cannotAddServletContextListener();
733 }
734 InstanceFactory<? extends EventListener> factory = null;
735 try {
736 factory = deploymentInfo.getClassIntrospecter().createInstanceFactory(listenerClass);
737 } catch (Exception e) {
738 throw new IllegalArgumentException(e);
739 }
740 final ListenerInfo listener = new ListenerInfo(listenerClass, factory);
741 deploymentInfo.addListener(listener);
742 deployment.getApplicationListeners().addListener(new ManagedListener(listener, true));
743 }
744
745 @Override
746 public <T extends EventListener> T createListener(final Class<T> clazz) throws ServletException {
747 ensureNotProgramaticListener();
748 if (!ApplicationListeners.isListenerClass(clazz)) {
749 throw UndertowServletMessages.MESSAGES.listenerMustImplementListenerClass(clazz);
750 }
751 try {
752 return deploymentInfo.getClassIntrospecter().createInstanceFactory(clazz).createInstance().getInstance();
753 } catch (Exception e) {
754 throw UndertowServletMessages.MESSAGES.couldNotInstantiateComponent(clazz.getName(), e);
755 }
756 }
757
758 @Override
759 public JspConfigDescriptor getJspConfigDescriptor() {
760 return deploymentInfo.getJspConfigDescriptor();
761 }
762
763 @Override
764 public ClassLoader getClassLoader() {
765 return deploymentInfo.getClassLoader();
766 }
767
768 @Override
769 public void declareRoles(final String... roleNames) {
770 }
771
772 @Override
773 public ServletRegistration.Dynamic addJspFile(String servletName, String jspFile) {
774 if(servletName == null || servletName.isEmpty()) {
775 throw UndertowServletMessages.MESSAGES.paramCannotBeNull("servletName");
776 }
777 return addServlet(servletName, "org.apache.jasper.servlet.JspServlet", Collections.singletonList(handler -> exchange -> {
778 ServletRequest request = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY).getServletRequest();
779 request.setAttribute(System.getProperty("org.apache.jasper.Constants.JSP_FILE", "org.apache.catalina.jsp_file"), jspFile);
780 handler.handleRequest(exchange);
781 }));
782 }
783
784 @Override
785 public int getSessionTimeout() {
786 return defaultSessionTimeout;
787 }
788
789 @Override
790 public void setSessionTimeout(int sessionTimeout) {
791 ensureNotInitialized();
792 ensureNotProgramaticListener();
793 this.defaultSessionTimeout = sessionTimeout;
794 deployment.getSessionManager().setDefaultSessionTimeout(sessionTimeout * 60);
795 }
796
797 @Override
798 public String getRequestCharacterEncoding() {
799 String enconding = deploymentInfo.getDefaultRequestEncoding();
800 if(enconding != null) {
801 return enconding;
802 }
803 return deploymentInfo.getDefaultEncoding();
804 }
805
806 @Override
807 public void setRequestCharacterEncoding(String encoding) {
808 ensureNotInitialized();
809 ensureNotProgramaticListener();
810 deploymentInfo.setDefaultRequestEncoding(encoding);
811 }
812
813 @Override
814 public String getResponseCharacterEncoding() {
815 String enconding = deploymentInfo.getDefaultResponseEncoding();
816 if(enconding != null) {
817 return enconding;
818 }
819 return deploymentInfo.getDefaultEncoding();
820 }
821
822 @Override
823 public void setResponseCharacterEncoding(String encoding) {
824 ensureNotInitialized();
825 ensureNotProgramaticListener();
826 deploymentInfo.setDefaultResponseEncoding(encoding);
827 }
828
829 @Override
830 public String getVirtualServerName() {
831 return deployment.getDeploymentInfo().getHostName();
832 }
833
834
840 public HttpSessionImpl getSession(final String sessionId) {
841 final SessionManager sessionManager = deployment.getSessionManager();
842 Session session = sessionManager.getSession(sessionId);
843 if (session != null) {
844 return SecurityActions.forSession(session, this, false);
845 }
846 return null;
847 }
848
849 public HttpSessionImpl getSession(final ServletContextImpl originalServletContext, final HttpServerExchange exchange, boolean create) {
850 SessionConfig c = originalServletContext.getSessionConfig();
851 HttpSessionImpl httpSession = exchange.getAttachment(sessionAttachmentKey);
852 if (httpSession != null && httpSession.isInvalid()) {
853 exchange.removeAttachment(sessionAttachmentKey);
854 httpSession = null;
855 }
856 if (httpSession == null) {
857 final SessionManager sessionManager = deployment.getSessionManager();
858 Session session = sessionManager.getSession(exchange, c);
859 if (session != null) {
860 httpSession = SecurityActions.forSession(session, this, false);
861 exchange.putAttachment(sessionAttachmentKey, httpSession);
862 } else if (create) {
863
864 String existing = c.findSessionId(exchange);
865 Boolean isRequestedSessionIdSaved = exchange.getAttachment(HttpServletRequestImpl.REQUESTED_SESSION_ID_SET);
866 if (isRequestedSessionIdSaved == null || !isRequestedSessionIdSaved) {
867 exchange.putAttachment(HttpServletRequestImpl.REQUESTED_SESSION_ID_SET, Boolean.TRUE);
868 exchange.putAttachment(HttpServletRequestImpl.REQUESTED_SESSION_ID, existing);
869 }
870
871 if (originalServletContext != this) {
872
873
874 final HttpSessionImpl topLevel = originalServletContext.getSession(originalServletContext, exchange, true);
875
876
877 c = new SessionConfig() {
878 @Override
879 public void setSessionId(HttpServerExchange exchange, String sessionId) {
880
881 }
882
883 @Override
884 public void clearSession(HttpServerExchange exchange, String sessionId) {
885
886 }
887
888 @Override
889 public String findSessionId(HttpServerExchange exchange) {
890 return topLevel.getId();
891 }
892
893 @Override
894 public SessionCookieSource sessionCookieSource(HttpServerExchange exchange) {
895 return SessionCookieSource.NONE;
896 }
897
898 @Override
899 public String rewriteUrl(String originalUrl, String sessionId) {
900 return null;
901 }
902 };
903
904
905
906 session = sessionManager.getSession(exchange, c);
907 if (session != null) {
908 httpSession = SecurityActions.forSession(session, this, false);
909 exchange.putAttachment(sessionAttachmentKey, httpSession);
910 }
911 } else if (existing != null) {
912 if(deploymentInfo.isCheckOtherSessionManagers()) {
913 boolean found = false;
914 for (String deploymentName : deployment.getServletContainer().listDeployments()) {
915 DeploymentManager deployment = this.deployment.getServletContainer().getDeployment(deploymentName);
916 if (deployment != null) {
917 if (deployment.getDeployment().getSessionManager().getSession(existing) != null) {
918 found = true;
919 break;
920 }
921 }
922 }
923 if (!found) {
924 c.clearSession(exchange, existing);
925 }
926 } else {
927 c.clearSession(exchange, existing);
928 }
929 }
930
931 if (httpSession == null) {
932 final Session newSession = sessionManager.createSession(exchange, c);
933 httpSession = SecurityActions.forSession(newSession, this, true);
934 exchange.putAttachment(sessionAttachmentKey, httpSession);
935 }
936 }
937 }
938 return httpSession;
939 }
940
941
947 public HttpSessionImpl getSession(final HttpServerExchange exchange, boolean create) {
948 return getSession(this, exchange, create);
949 }
950
951 public void updateSessionAccessTime(final HttpServerExchange exchange) {
952 HttpSessionImpl httpSession = getSession(exchange, false);
953 if (httpSession != null) {
954 Session underlyingSession;
955 if (System.getSecurityManager() == null) {
956 underlyingSession = httpSession.getSession();
957 } else {
958 underlyingSession = AccessController.doPrivileged(new HttpSessionImpl.UnwrapSessionAction(httpSession));
959 }
960 underlyingSession.requestDone(exchange);
961 }
962 }
963
964 public Deployment getDeployment() {
965 return deployment;
966 }
967
968 private void ensureNotInitialized() {
969 if (initialized) {
970 throw UndertowServletMessages.MESSAGES.servletContextAlreadyInitialized();
971 }
972 }
973
974 private void ensureNotProgramaticListener() {
975 if (ApplicationListeners.listenerState() == PROGRAMATIC_LISTENER) {
976 throw UndertowServletMessages.MESSAGES.cannotCallFromProgramaticListener();
977 }
978 }
979
980 boolean isInitialized() {
981 return initialized;
982 }
983
984 public SessionConfig getSessionConfig() {
985 return sessionConfig;
986 }
987
988 public void destroy() {
989 attributes.clear();
990 deploymentInfo = null;
991 }
992
993 private void readServletAnnotations(ServletInfo servlet) {
994 if (System.getSecurityManager() == null) {
995 new ReadServletAnnotationsTask(servlet, deploymentInfo).run();
996 } else {
997 AccessController.doPrivileged(new ReadServletAnnotationsTask(servlet, deploymentInfo));
998 }
999 }
1000
1001 public void setDefaultSessionTrackingModes(HashSet<SessionTrackingMode> sessionTrackingModes) {
1002 this.defaultSessionTrackingModes = sessionTrackingModes;
1003 this.sessionTrackingModes = sessionTrackingModes;
1004 }
1005
1006 void invokeOnWritePossible(HttpServerExchange exchange, WriteListener listener) {
1007 try {
1008 this.onWritePossibleTask.call(exchange, listener);
1009 } catch (Exception e) {
1010 throw new RuntimeException(e);
1011 }
1012 }
1013
1014 void invokeOnAllDataRead(HttpServerExchange exchange, ReadListener listener) {
1015 try {
1016 this.onAllDataReadTask.call(exchange, listener);
1017 } catch (Exception e) {
1018 throw new RuntimeException(e);
1019 }
1020 }
1021
1022 void invokeOnDataAvailable(HttpServerExchange exchange, ReadListener listener) {
1023 try {
1024 this.onDataAvailableTask.call(exchange, listener);
1025 } catch (Exception e) {
1026 throw new RuntimeException(e);
1027 }
1028 }
1029
1030 void invokeAction(HttpServerExchange exchange, ThreadSetupHandler.Action<Void, Object> listener) {
1031 try {
1032 this.invokeActionTask.call(exchange, listener);
1033 } catch (Exception e) {
1034 throw new RuntimeException(e);
1035 }
1036 }
1037
1038 void invokeRunnable(HttpServerExchange exchange, Runnable runnable) {
1039 final boolean setupRequired = SecurityActions.currentServletRequestContext() == null;
1040 if(setupRequired) {
1041 try {
1042 this.runnableTask.call(exchange, runnable);
1043 } catch (Exception e) {
1044 throw new RuntimeException(e);
1045 }
1046 } else {
1047 runnable.run();
1048 }
1049 }
1050 private static final class ReadServletAnnotationsTask implements PrivilegedAction<Void> {
1051
1052 private final ServletInfo servletInfo;
1053 private final DeploymentInfo deploymentInfo;
1054
1055 private ReadServletAnnotationsTask(ServletInfo servletInfo, DeploymentInfo deploymentInfo) {
1056 this.servletInfo = servletInfo;
1057 this.deploymentInfo = deploymentInfo;
1058 }
1059
1060 @Override
1061 public Void run() {
1062 final ServletSecurity security = servletInfo.getServletClass().getAnnotation(ServletSecurity.class);
1063 if (security != null) {
1064
1065 ServletSecurityInfo servletSecurityInfo = new ServletSecurityInfo()
1066 .setEmptyRoleSemantic(security.value().value() == ServletSecurity.EmptyRoleSemantic.DENY ? SecurityInfo.EmptyRoleSemantic.DENY : SecurityInfo.EmptyRoleSemantic.PERMIT)
1067 .setTransportGuaranteeType(security.value().transportGuarantee() == ServletSecurity.TransportGuarantee.CONFIDENTIAL ? TransportGuaranteeType.CONFIDENTIAL : TransportGuaranteeType.NONE)
1068 .addRolesAllowed(security.value().rolesAllowed());
1069 for (HttpMethodConstraint constraint : security.httpMethodConstraints()) {
1070 servletSecurityInfo.addHttpMethodSecurityInfo(new HttpMethodSecurityInfo()
1071 .setMethod(constraint.value()))
1072 .setEmptyRoleSemantic(constraint.emptyRoleSemantic() == ServletSecurity.EmptyRoleSemantic.DENY ? SecurityInfo.EmptyRoleSemantic.DENY : SecurityInfo.EmptyRoleSemantic.PERMIT)
1073 .setTransportGuaranteeType(constraint.transportGuarantee() == ServletSecurity.TransportGuarantee.CONFIDENTIAL ? TransportGuaranteeType.CONFIDENTIAL : TransportGuaranteeType.NONE)
1074 .addRolesAllowed(constraint.rolesAllowed());
1075 }
1076 servletInfo.setServletSecurityInfo(servletSecurityInfo);
1077 }
1078 final MultipartConfig multipartConfig = servletInfo.getServletClass().getAnnotation(MultipartConfig.class);
1079 if (multipartConfig != null) {
1080 servletInfo.setMultipartConfig(new MultipartConfigElement(multipartConfig.location(), multipartConfig.maxFileSize(), multipartConfig.maxRequestSize(), multipartConfig.fileSizeThreshold()));
1081 }
1082 final RunAs runAs = servletInfo.getServletClass().getAnnotation(RunAs.class);
1083 if (runAs != null) {
1084 servletInfo.setRunAs(runAs.value());
1085 }
1086 final DeclareRoles declareRoles = servletInfo.getServletClass().getAnnotation(DeclareRoles.class);
1087 if (declareRoles != null) {
1088 deploymentInfo.addSecurityRoles(declareRoles.value());
1089 }
1090 return null;
1091 }
1092 }
1093
1094 void addMappingForServletNames(FilterInfo filterInfo, final EnumSet<DispatcherType> dispatcherTypes, final boolean isMatchAfter, final String... servletNames) {
1095 DeploymentInfo deploymentInfo = deployment.getDeploymentInfo();
1096
1097 for (final String servlet : servletNames) {
1098 if (isMatchAfter) {
1099 if (dispatcherTypes == null || dispatcherTypes.isEmpty()) {
1100 deploymentInfo.addFilterServletNameMapping(filterInfo.getName(), servlet, DispatcherType.REQUEST);
1101 } else {
1102 for (final DispatcherType dispatcher : dispatcherTypes) {
1103 deploymentInfo.addFilterServletNameMapping(filterInfo.getName(), servlet, dispatcher);
1104 }
1105 }
1106 } else {
1107 if (dispatcherTypes == null || dispatcherTypes.isEmpty()) {
1108 deploymentInfo.insertFilterServletNameMapping(filterMappingServletNameInsertPosition++, filterInfo.getName(), servlet, DispatcherType.REQUEST);
1109 } else {
1110 for (final DispatcherType dispatcher : dispatcherTypes) {
1111 deploymentInfo.insertFilterServletNameMapping(filterMappingServletNameInsertPosition++, filterInfo.getName(), servlet, dispatcher);
1112 }
1113 }
1114 }
1115 }
1116 deployment.getServletPaths().invalidate();
1117 }
1118
1119 void addMappingForUrlPatterns(FilterInfo filterInfo, final EnumSet<DispatcherType> dispatcherTypes, final boolean isMatchAfter, final String... urlPatterns) {
1120 DeploymentInfo deploymentInfo = deployment.getDeploymentInfo();
1121 for (final String url : urlPatterns) {
1122 if (isMatchAfter) {
1123 if (dispatcherTypes == null || dispatcherTypes.isEmpty()) {
1124 deploymentInfo.addFilterUrlMapping(filterInfo.getName(), url, DispatcherType.REQUEST);
1125 } else {
1126 for (final DispatcherType dispatcher : dispatcherTypes) {
1127 deploymentInfo.addFilterUrlMapping(filterInfo.getName(), url, dispatcher);
1128 }
1129 }
1130 } else {
1131 if (dispatcherTypes == null || dispatcherTypes.isEmpty()) {
1132 deploymentInfo.insertFilterUrlMapping(filterMappingUrlPatternInsertPosition++, filterInfo.getName(), url, DispatcherType.REQUEST);
1133 } else {
1134 for (final DispatcherType dispatcher : dispatcherTypes) {
1135 deploymentInfo.insertFilterUrlMapping(filterMappingUrlPatternInsertPosition++, filterInfo.getName(), url, dispatcher);
1136 }
1137 }
1138 }
1139 }
1140 deployment.getServletPaths().invalidate();
1141 }
1142
1143 ContentTypeInfo parseContentType(String type) {
1144 ContentTypeInfo existing = contentTypeCache.get(type);
1145 if(existing != null) {
1146 return existing;
1147 }
1148 String contentType = type;
1149 String charset = null;
1150
1151 int split = type.indexOf(";");
1152 if (split != -1) {
1153 int pos = type.indexOf("charset=");
1154 if (pos != -1) {
1155 int i = pos + "charset=".length();
1156 do {
1157 char c = type.charAt(i);
1158 if (c == ' ' || c == '\t' || c == ';') {
1159 break;
1160 }
1161 ++i;
1162 } while (i < type.length());
1163 charset = type.substring(pos + "charset=".length(), i);
1164
1165 if (charset.startsWith("\"") && charset.endsWith("\"") && charset.length() > 1) {
1166 charset = charset.substring(1, charset.length() - 1);
1167 }
1168
1169 int charsetStart = pos;
1170 while (type.charAt(--charsetStart) != ';' && charsetStart > 0) {
1171 }
1172 StringBuilder contentTypeBuilder = new StringBuilder();
1173 contentTypeBuilder.append(type.substring(0, charsetStart));
1174 if (i != type.length()) {
1175 contentTypeBuilder.append(type.substring(i));
1176 }
1177 contentType = contentTypeBuilder.toString();
1178 }
1179
1180 for (int i = contentType.length() - 1; i >= 0; --i) {
1181 char c = contentType.charAt(i);
1182 if (c == ' ' || c == '\t') {
1183 continue;
1184 }
1185 if (c == ';') {
1186 contentType = contentType.substring(0, i);
1187 }
1188 break;
1189 }
1190 }
1191 if(charset == null) {
1192 existing = new ContentTypeInfo(contentType, null, contentType);
1193 } else {
1194 existing = new ContentTypeInfo(contentType + ";charset=" + charset, charset, contentType);
1195 }
1196 contentTypeCache.add(type, existing);
1197 return existing;
1198 }
1199
1200
1203 static final class ServletContextSessionConfig implements SessionConfig {
1204
1205 private final AttachmentKey<String> INVALIDATED = AttachmentKey.create(String.class);
1206
1207 private final SessionConfig delegate;
1208
1209 private ServletContextSessionConfig(SessionConfig delegate) {
1210 this.delegate = delegate;
1211 }
1212
1213 @Override
1214 public void setSessionId(HttpServerExchange exchange, String sessionId) {
1215 delegate.setSessionId(exchange, sessionId);
1216 }
1217
1218 @Override
1219 public void clearSession(HttpServerExchange exchange, String sessionId) {
1220 exchange.putAttachment(INVALIDATED, sessionId);
1221 Boolean isRequestedSessionIdSaved = exchange.getAttachment(HttpServletRequestImpl.REQUESTED_SESSION_ID_SET);
1222 if (isRequestedSessionIdSaved == null || !isRequestedSessionIdSaved) {
1223 exchange.putAttachment(HttpServletRequestImpl.REQUESTED_SESSION_ID_SET, Boolean.TRUE);
1224 exchange.putAttachment(HttpServletRequestImpl.REQUESTED_SESSION_ID, sessionId);
1225 }
1226 delegate.clearSession(exchange, sessionId);
1227 }
1228
1229 @Override
1230 public String findSessionId(HttpServerExchange exchange) {
1231 String invalidated = exchange.getAttachment(INVALIDATED);
1232 ServletRequestContext src = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
1233 final String current;
1234 if(src.getOverridenSessionId() == null) {
1235 current = delegate.findSessionId(exchange);
1236 } else {
1237 current = src.getOverridenSessionId();
1238 }
1239 if(invalidated == null) {
1240 return current;
1241 }
1242 if(invalidated.equals(current)) {
1243 return null;
1244 }
1245 return current;
1246 }
1247
1248 @Override
1249 public SessionCookieSource sessionCookieSource(HttpServerExchange exchange) {
1250 return delegate.sessionCookieSource(exchange);
1251 }
1252
1253 @Override
1254 public String rewriteUrl(String originalUrl, String sessionId) {
1255 return delegate.rewriteUrl(originalUrl, sessionId);
1256 }
1257
1258 public SessionConfig getDelegate() {
1259 return delegate;
1260 }
1261 }
1262 }
1263