001// Licensed under the Apache License, Version 2.0 (the "License");
002// you may not use this file except in compliance with the License.
003// You may obtain a copy of the License at
004//
005// http://www.apache.org/licenses/LICENSE-2.0
006//
007// Unless required by applicable law or agreed to in writing, software
008// distributed under the License is distributed on an "AS IS" BASIS,
009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
010// See the License for the specific language governing permissions and
011// limitations under the License.
012
013package org.apache.tapestry5.http.internal.services;
014
015import java.io.UnsupportedEncodingException;
016import java.util.Collections;
017import java.util.Enumeration;
018import java.util.List;
019import java.util.Locale;
020
021import javax.servlet.http.HttpServletRequest;
022
023import org.apache.tapestry5.commons.util.CollectionFactory;
024import org.apache.tapestry5.http.services.Request;
025import org.apache.tapestry5.http.services.Session;
026
027/**
028 * Basic implementation of {@link org.apache.tapestry5.http.services.Request} that wraps around an
029 * {@link javax.servlet.http.HttpServletRequest}. This is not threadsafe, nor should it need to be (each Request is
030 * handled by its own Thread).
031 */
032public class RequestImpl implements Request
033{
034    /**
035     * @deprecated Use {@link Request#REQUESTED_WITH_HEADER} instead
036     */
037    static final String REQUESTED_WITH_HEADER = Request.REQUESTED_WITH_HEADER;
038
039    static final String XML_HTTP_REQUEST = "XMLHttpRequest";
040
041    /**
042     * @deprecated Use {@link Request#X_FORWARDED_PROTO_HEADER} instead
043     */
044    static final String X_FORWARDED_PROTO_HEADER = Request.X_FORWARDED_PROTO_HEADER;
045    static final String X_FORWARDED_PROTO_HTTPS = "https";
046
047    private final HttpServletRequest request;
048
049    private final String applicationCharset;
050
051    private final TapestrySessionFactory sessionFactory;
052
053    private boolean encodingSet;
054
055    private Session session;
056
057    public RequestImpl(
058            HttpServletRequest request,
059            String applicationCharset,
060            TapestrySessionFactory sessionFactory)
061    {
062        this.request = request;
063        this.applicationCharset = applicationCharset;
064        this.sessionFactory = sessionFactory;
065    }
066
067    public List<String> getParameterNames()
068    {
069        setupEncoding();
070
071        return toList(request.getParameterNames());
072    }
073
074    public List<String> getHeaderNames()
075    {
076        return toList(request.getHeaderNames());
077    }
078
079    public String getParameter(String name)
080    {
081        setupEncoding();
082
083        return request.getParameter(name);
084    }
085
086    public String[] getParameters(String name)
087    {
088        setupEncoding();
089
090        return request.getParameterValues(name);
091    }
092
093    public String getHeader(String name)
094    {
095        return request.getHeader(name);
096    }
097
098    public String getPath()
099    {
100        String pathInfo = request.getPathInfo();
101
102        if (pathInfo == null)
103            return request.getServletPath();
104
105        // Websphere 6.1 is a bit wonky (see TAPESTRY-1713), and tends to return the empty string
106        // for the servlet path, and return the true path in pathInfo.
107
108        return pathInfo.length() == 0 ? "/" : pathInfo;
109    }
110
111    public String getContextPath()
112    {
113        return request.getContextPath();
114    }
115
116
117    public boolean isSessionInvalidated()
118    {
119        // Double check to ensure that the session exists, but don't create it.
120        if (session == null)
121        {
122            session = sessionFactory.getSession(false);
123        }
124
125        return session != null && session.isInvalidated();
126    }
127
128    public Session getSession(boolean create)
129    {
130        if (session != null && session.isInvalidated())
131        {
132            session = null;
133        }
134
135        if (session == null)
136        {
137            // TAP5-1489 - Re-storage of session attributes at end of request should be configurable
138            session = sessionFactory.getSession(create);
139        }
140
141        return session;
142    }
143
144    public Locale getLocale()
145    {
146        return request.getLocale();
147    }
148
149    public long getDateHeader(String name)
150    {
151        return request.getDateHeader(name);
152    }
153
154    private void setupEncoding()
155    {
156        if (encodingSet)
157        {
158            return;
159        }
160
161        // The request may specify an encoding, which is better than the application, which can only
162        // guess at what the client is doing!
163
164        String requestEncoding = request.getCharacterEncoding();
165
166        try
167        {
168            request.setCharacterEncoding(requestEncoding != null ? requestEncoding : applicationCharset);
169        } catch (UnsupportedEncodingException ex)
170        {
171            throw new RuntimeException(ex);
172        }
173
174        encodingSet = true;
175    }
176
177    public boolean isXHR()
178    {
179        return XML_HTTP_REQUEST.equals(request.getHeader(Request.REQUESTED_WITH_HEADER));
180    }
181
182    public boolean isSecure()
183    {
184        return request.isSecure() ||
185                X_FORWARDED_PROTO_HTTPS.equals(request.getHeader(Request.X_FORWARDED_PROTO_HEADER));
186    }
187
188    public boolean isRequestedSessionIdValid()
189    {
190        return request.isRequestedSessionIdValid();
191    }
192
193    public Object getAttribute(String name)
194    {
195        return request.getAttribute(name);
196    }
197
198    public List<String> getAttributeNames()
199    {
200        setupEncoding();
201
202        return toList(request.getAttributeNames());
203    }
204
205    public void setAttribute(String name, Object value)
206    {
207        request.setAttribute(name, value);
208    }
209
210    public String getMethod()
211    {
212        return request.getMethod();
213    }
214
215    public String getServerName()
216    {
217        return request.getServerName();
218    }
219
220    public int getLocalPort()
221    {
222        return request.getLocalPort();
223    }
224
225    /**
226     * @since 5.2.5
227     */
228    public int getServerPort()
229    {
230        return request.getServerPort();
231    }
232
233    public String getRemoteHost()
234    {
235        return request.getRemoteHost();
236    }
237    
238    /**
239     * Converts an enumeration (of Strings) into a sorted list of Strings.
240     */
241    @SuppressWarnings("rawtypes")
242    private static List<String> toList(Enumeration e)
243    {
244        List<String> result = CollectionFactory.newList();
245
246        while (e.hasMoreElements())
247        {
248            String name = (String) e.nextElement();
249
250            result.add(name);
251        }
252
253        Collections.sort(result);
254
255        return result;
256    }
257
258
259}