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
19 package io.undertow.servlet.handlers;
20
21 import java.net.InetAddress;
22 import java.net.InetSocketAddress;
23 import java.util.List;
24
25 import io.undertow.UndertowMessages;
26 import io.undertow.server.HttpServerExchange;
27 import io.undertow.servlet.api.Deployment;
28 import io.undertow.servlet.api.ServletStackTraces;
29 import io.undertow.servlet.api.TransportGuaranteeType;
30 import io.undertow.servlet.api.SingleConstraintMatch;
31 import io.undertow.servlet.spec.HttpServletRequestImpl;
32 import io.undertow.servlet.spec.HttpServletResponseImpl;
33 import io.undertow.servlet.spec.HttpSessionImpl;
34 import io.undertow.servlet.spec.ServletContextImpl;
35 import io.undertow.util.AttachmentKey;
36 import io.undertow.util.Headers;
37
38 import javax.servlet.DispatcherType;
39 import javax.servlet.ServletRequest;
40 import javax.servlet.ServletResponse;
41
42 /**
43  * All the information that servlet needs to attach to the exchange.
44  * <p>
45  * This is all stored under this class, rather than using individual attachments, as
46  * this approach has significant performance advantages.
47  * <p>
48  * The {@link ServletInitialHandler} also pushed this information to the {@link #CURRENT}
49  * thread local, which allows it to be access even if the request or response have been
50  * wrapped with non-compliant wrapper classes.
51  *
52  * @author Stuart Douglas
53  */

54 public class ServletRequestContext {
55
56     private static final RuntimePermission GET_CURRENT_REQUEST = new RuntimePermission("io.undertow.servlet.GET_CURRENT_REQUEST");
57     private static final RuntimePermission SET_CURRENT_REQUEST = new RuntimePermission("io.undertow.servlet.SET_CURRENT_REQUEST");
58
59     private static final ThreadLocal<ServletRequestContext> CURRENT = new ThreadLocal<>();
60
61     public static void setCurrentRequestContext(ServletRequestContext servletRequestContext) {
62         SecurityManager sm = System.getSecurityManager();
63         if(sm != null) {
64             sm.checkPermission(SET_CURRENT_REQUEST);
65         }
66         CURRENT.set(servletRequestContext);
67     }
68
69     public static void clearCurrentServletAttachments() {
70         SecurityManager sm = System.getSecurityManager();
71         if(sm != null) {
72             sm.checkPermission(SET_CURRENT_REQUEST);
73         }
74         CURRENT.remove();
75     }
76
77     /**
78      * Gets the {@link ServletRequestContext} assigned to the current thread.
79      *
80      * @return The current {@link ServletRequestContext} based on the calling thread
81      * @throws IllegalStateException if the calling thread does not have a {@link ServletRequestContext} set
82      * @see ServletRequestContext#current()
83      */

84     public static ServletRequestContext requireCurrent() {
85         ServletRequestContext attachments = current();
86         if (attachments == null) {
87             throw UndertowMessages.MESSAGES.noRequestActive();
88         }
89         return attachments;
90     }
91
92     /**
93      * Gets the current threads {@link ServletRequestContext} if set, otherwise null.
94      *
95      * @return The current {@link ServletRequestContext} based on the calling thread, or null if unavailable
96      */

97     public static ServletRequestContext current() {
98         SecurityManager sm = System.getSecurityManager();
99         if(sm != null) {
100             sm.checkPermission(GET_CURRENT_REQUEST);
101         }
102         return CURRENT.get();
103     }
104
105     public static final AttachmentKey<ServletRequestContext> ATTACHMENT_KEY = AttachmentKey.create(ServletRequestContext.class);
106
107     private final Deployment deployment;
108     private final HttpServletRequestImpl originalRequest;
109     private final HttpServletResponseImpl originalResponse;
110     private final ServletPathMatch originalServletPathMatch;
111     private ServletResponse servletResponse;
112     private ServletRequest servletRequest;
113     private DispatcherType dispatcherType;
114
115     private ServletChain currentServlet;
116     private ServletPathMatch servletPathMatch;
117
118     private List<SingleConstraintMatch> requiredConstrains;
119     private TransportGuaranteeType transportGuarenteeType;
120     private HttpSessionImpl session;
121
122     private ServletContextImpl currentServletContext;
123     private String overridenSessionId;
124
125     /**
126      * If this is true the request is running inside the context of ServletInitialHandler
127      */

128     private boolean runningInsideHandler = false;
129     private int errorCode = -1;
130     private String errorMessage;
131     private boolean asyncSupported = true;
132
133     public ServletRequestContext(final Deployment deployment, final HttpServletRequestImpl originalRequest, final HttpServletResponseImpl originalResponse, final ServletPathMatch originalServletPathMatch) {
134         this.deployment = deployment;
135         this.originalRequest = originalRequest;
136         this.originalResponse = originalResponse;
137         this.servletRequest = originalRequest;
138         this.servletResponse = originalResponse;
139         this.originalServletPathMatch = originalServletPathMatch;
140         this.currentServletContext = deployment.getServletContext();
141     }
142
143     public Deployment getDeployment() {
144         return deployment;
145     }
146
147     public ServletChain getCurrentServlet() {
148         return currentServlet;
149     }
150
151     public void setCurrentServlet(ServletChain currentServlet) {
152         this.currentServlet = currentServlet;
153     }
154
155     public ServletPathMatch getServletPathMatch() {
156         return servletPathMatch;
157     }
158
159     public void setServletPathMatch(ServletPathMatch servletPathMatch) {
160         this.servletPathMatch = servletPathMatch;
161     }
162
163     public List<SingleConstraintMatch> getRequiredConstrains() {
164         return requiredConstrains;
165     }
166
167     public void setRequiredConstrains(List<SingleConstraintMatch> requiredConstrains) {
168         this.requiredConstrains = requiredConstrains;
169     }
170
171     public TransportGuaranteeType getTransportGuarenteeType() {
172         return transportGuarenteeType;
173     }
174
175     public void setTransportGuarenteeType(TransportGuaranteeType transportGuarenteeType) {
176         this.transportGuarenteeType = transportGuarenteeType;
177     }
178
179     public ServletResponse getServletResponse() {
180         return servletResponse;
181     }
182
183     public void setServletResponse(ServletResponse servletResponse) {
184         this.servletResponse = servletResponse;
185     }
186
187     public ServletRequest getServletRequest() {
188         return servletRequest;
189     }
190
191     public void setServletRequest(ServletRequest servletRequest) {
192         this.servletRequest = servletRequest;
193     }
194
195     public DispatcherType getDispatcherType() {
196         return dispatcherType;
197     }
198
199     public void setDispatcherType(DispatcherType dispatcherType) {
200         this.dispatcherType = dispatcherType;
201     }
202
203     public HttpServletRequestImpl getOriginalRequest() {
204         return originalRequest;
205     }
206
207     public HttpServletResponseImpl getOriginalResponse() {
208         return originalResponse;
209     }
210
211     public HttpSessionImpl getSession() {
212         return session;
213     }
214
215     public void setSession(final HttpSessionImpl session) {
216         this.session = session;
217     }
218
219     public HttpServerExchange getExchange() {
220         return originalRequest.getExchange();
221     }
222
223     public ServletPathMatch getOriginalServletPathMatch() {
224         return originalServletPathMatch;
225     }
226
227     public ServletContextImpl getCurrentServletContext() {
228         return currentServletContext;
229     }
230
231     public void setCurrentServletContext(ServletContextImpl currentServletContext) {
232         this.currentServletContext = currentServletContext;
233     }
234
235     public boolean displayStackTraces() {
236         ServletStackTraces mode = deployment.getDeploymentInfo().getServletStackTraces();
237         if (mode == ServletStackTraces.NONE) {
238             return false;
239         } else if (mode == ServletStackTraces.ALL) {
240             return true;
241         } else {
242             InetSocketAddress localAddress = getExchange().getSourceAddress();
243             if(localAddress == null) {
244                 return false;
245             }
246             InetAddress address = localAddress.getAddress();
247             if(address == null) {
248                 return false;
249             }
250             if(!address.isLoopbackAddress()) {
251                 return false;
252             }
253             return !getExchange().getRequestHeaders().contains(Headers.X_FORWARDED_FOR);
254         }
255
256     }
257
258     public void setError(int sc, String msg) {
259         this.errorCode = sc;
260         this.errorMessage = msg;
261     }
262
263     public int getErrorCode() {
264         return errorCode;
265     }
266
267     public String getErrorMessage() {
268         return errorMessage;
269     }
270
271     public boolean isRunningInsideHandler() {
272         return runningInsideHandler;
273     }
274
275     public void setRunningInsideHandler(boolean runningInsideHandler) {
276         this.runningInsideHandler = runningInsideHandler;
277     }
278
279     public boolean isAsyncSupported() {
280         return asyncSupported;
281     }
282
283     public String getOverridenSessionId() {
284         return overridenSessionId;
285     }
286
287     public void setOverridenSessionId(String overridenSessionId) {
288         this.overridenSessionId = overridenSessionId;
289     }
290
291     public void setAsyncSupported(boolean asyncSupported) {
292         this.asyncSupported = asyncSupported;
293     }
294 }
295