1 /*
2  * JBoss, Home of Professional Open Source.
3  * Copyright 2014 Red Hat, Inc., and individual contributors
4  * as indicated by the @author tags.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */

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 /**
108  * @author Stuart Douglas
109  */

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     //I don't think these really belong here, but there is not really anywhere else for them
127     //maybe we should move them into a separate class
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//no extension
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()); //not much else we can do here
277                 } else {
278                     String filePath = file.toAbsolutePath().toString().substring(base.toAbsolutePath().toString().length());
279                     filePath = filePath.replace('\\', '/'); //for windows systems
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             //should never happen, as the resource loader should return null in this case
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                 //UNDERTOW-373 even though the resource does not exist we still need to return a path
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         //TODO: actually make this work
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     /**
835      * Gets the session with the specified ID if it exists
836      *
837      * @param sessionId The session ID
838      * @return The session
839      */

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, thisfalse);
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, thisfalse);
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                     //this is a cross context request
873                     //we need to make sure there is a top level session
874                     final HttpSessionImpl topLevel = originalServletContext.getSession(originalServletContext, exchange, true);
875                     //override the session id to just return the same ID as the top level session
876
877                     c = new SessionConfig() {
878                         @Override
879                         public void setSessionId(HttpServerExchange exchange, String sessionId) {
880                             //noop
881                         }
882
883                         @Override
884                         public void clearSession(HttpServerExchange exchange, String sessionId) {
885                             //noop
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                     //first we check if there is a session with this id already
905                     //this can happen with a shared session manager
906                     session = sessionManager.getSession(exchange, c);
907                     if (session != null) {
908                         httpSession = SecurityActions.forSession(session, thisfalse);
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, thistrue);
934                     exchange.putAttachment(sessionAttachmentKey, httpSession);
935                 }
936             }
937         }
938         return httpSession;
939     }
940
941     /**
942      * Gets the session
943      *
944      * @param create
945      * @return
946      */

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                 //it is valid for the charset to be enclosed in quotes
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             //strip any trailing semicolon
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     /**
1201      * This is a bit of a hack to make sure than an invalidated session ID is not re-used. It also allows {@link io.undertow.servlet.handlers.ServletRequestContext#getOverridenSessionId()} to be used.
1202      */

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