1 /*
2  * Copyright (C) 2008 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.inject.servlet;
18 
19 import static com.google.inject.servlet.ManagedServletPipeline.REQUEST_DISPATCHER_REQUEST;
20 import static org.easymock.EasyMock.anyObject;
21 import static org.easymock.EasyMock.createMock;
22 import static org.easymock.EasyMock.expect;
23 import static org.easymock.EasyMock.replay;
24 import static org.easymock.EasyMock.verify;
25 
26 import com.google.common.collect.Sets;
27 import com.google.inject.Binding;
28 import com.google.inject.Injector;
29 import com.google.inject.Key;
30 import com.google.inject.spi.BindingScopingVisitor;
31 import java.io.IOException;
32 import java.util.HashMap;
33 import javax.servlet.ServletException;
34 import javax.servlet.http.HttpServlet;
35 import javax.servlet.http.HttpServletRequest;
36 import javax.servlet.http.HttpServletResponse;
37 import junit.framework.TestCase;
38 
39 /**
40  * Ensures servlet spec compliance for CGI-style variables and general path/pattern matching.
41  *
42  * @author Dhanji R. Prasanna (dhanji@gmail com)
43  */
44 public class ServletDefinitionPathsTest extends TestCase {
45 
46   // Data-driven test.
testServletPathMatching()47   public final void testServletPathMatching() throws IOException, ServletException {
48     servletPath("/index.html", "*.html", "/index.html");
49     servletPath("/somewhere/index.html", "*.html", "/somewhere/index.html");
50     servletPath("/somewhere/index.html", "/*", "");
51     servletPath("/index.html", "/*", "");
52     servletPath("/", "/*", "");
53     servletPath("//", "/*", "");
54     servletPath("/////", "/*", "");
55     servletPath("", "/*", "");
56     servletPath("/thing/index.html", "/thing/*", "/thing");
57     servletPath("/thing/wing/index.html", "/thing/*", "/thing");
58   }
59 
servletPath( final String requestPath, String mapping, final String expectedServletPath)60   private void servletPath(
61       final String requestPath, String mapping, final String expectedServletPath)
62       throws IOException, ServletException {
63 
64     Injector injector = createMock(Injector.class);
65     Binding binding = createMock(Binding.class);
66     HttpServletRequest request = createMock(HttpServletRequest.class);
67     HttpServletResponse response = createMock(HttpServletResponse.class);
68 
69     expect(binding.acceptScopingVisitor((BindingScopingVisitor) anyObject())).andReturn(true);
70     expect(injector.getBinding(Key.get(HttpServlet.class))).andReturn(binding);
71 
72     final boolean[] run = new boolean[1];
73     //get an instance of this servlet
74     expect(injector.getInstance(Key.get(HttpServlet.class)))
75         .andReturn(
76             new HttpServlet() {
77 
78               @Override
79               protected void service(
80                   HttpServletRequest servletRequest, HttpServletResponse httpServletResponse)
81                   throws ServletException, IOException {
82 
83                 final String path = servletRequest.getServletPath();
84                 assertEquals(
85                     String.format("expected [%s] but was [%s]", expectedServletPath, path),
86                     expectedServletPath,
87                     path);
88                 run[0] = true;
89               }
90             });
91 
92     expect(request.getServletPath()).andReturn(requestPath);
93 
94     replay(injector, binding, request);
95 
96     ServletDefinition servletDefinition =
97         new ServletDefinition(
98             Key.get(HttpServlet.class),
99             UriPatternType.get(UriPatternType.SERVLET, mapping),
100             new HashMap<String, String>(),
101             null);
102 
103     servletDefinition.init(null, injector, Sets.<HttpServlet>newIdentityHashSet());
104     servletDefinition.doService(request, response);
105 
106     assertTrue("Servlet did not run!", run[0]);
107 
108     verify(injector, binding, request);
109   }
110 
111   // Data-driven test.
testPathInfoWithServletStyleMatching()112   public final void testPathInfoWithServletStyleMatching() throws IOException, ServletException {
113     pathInfoWithServletStyleMatching("/path/index.html", "/path", "/*", "/index.html", "");
114     pathInfoWithServletStyleMatching(
115         "/path//hulaboo///index.html", "/path", "/*", "/hulaboo/index.html", "");
116     pathInfoWithServletStyleMatching("/path/", "/path", "/*", "/", "");
117     pathInfoWithServletStyleMatching("/path////////", "/path", "/*", "/", "");
118 
119     // a servlet mapping of /thing/*
120     pathInfoWithServletStyleMatching("/path/thing////////", "/path", "/thing/*", "/", "/thing");
121     pathInfoWithServletStyleMatching("/path/thing/stuff", "/path", "/thing/*", "/stuff", "/thing");
122     pathInfoWithServletStyleMatching(
123         "/path/thing/stuff.html", "/path", "/thing/*", "/stuff.html", "/thing");
124     pathInfoWithServletStyleMatching("/path/thing", "/path", "/thing/*", null, "/thing");
125 
126     // see external issue 372
127     pathInfoWithServletStyleMatching(
128         "/path/some/path/of.jsp", "/path", "/thing/*", null, "/some/path/of.jsp");
129 
130     // *.xx style mapping
131     pathInfoWithServletStyleMatching("/path/thing.thing", "/path", "*.thing", null, "/thing.thing");
132     pathInfoWithServletStyleMatching("/path///h.thing", "/path", "*.thing", null, "/h.thing");
133     pathInfoWithServletStyleMatching(
134         "/path///...//h.thing", "/path", "*.thing", null, "/.../h.thing");
135     pathInfoWithServletStyleMatching("/path/my/h.thing", "/path", "*.thing", null, "/my/h.thing");
136 
137     // Encoded URLs
138     pathInfoWithServletStyleMatching("/path/index%2B.html", "/path", "/*", "/index+.html", "");
139     pathInfoWithServletStyleMatching(
140         "/path/a%20file%20with%20spaces%20in%20name.html",
141         "/path", "/*", "/a file with spaces in name.html", "");
142     pathInfoWithServletStyleMatching(
143         "/path/Tam%C3%A1s%20nem%20m%C3%A1s.html", "/path", "/*", "/Tamás nem más.html", "");
144   }
145 
pathInfoWithServletStyleMatching( final String requestUri, final String contextPath, String mapping, final String expectedPathInfo, final String servletPath)146   private void pathInfoWithServletStyleMatching(
147       final String requestUri,
148       final String contextPath,
149       String mapping,
150       final String expectedPathInfo,
151       final String servletPath)
152       throws IOException, ServletException {
153 
154     Injector injector = createMock(Injector.class);
155     Binding binding = createMock(Binding.class);
156     HttpServletRequest request = createMock(HttpServletRequest.class);
157     HttpServletResponse response = createMock(HttpServletResponse.class);
158 
159     expect(binding.acceptScopingVisitor((BindingScopingVisitor) anyObject())).andReturn(true);
160     expect(injector.getBinding(Key.get(HttpServlet.class))).andReturn(binding);
161 
162     final boolean[] run = new boolean[1];
163     //get an instance of this servlet
164     expect(injector.getInstance(Key.get(HttpServlet.class)))
165         .andReturn(
166             new HttpServlet() {
167 
168               @Override
169               protected void service(
170                   HttpServletRequest servletRequest, HttpServletResponse httpServletResponse)
171                   throws ServletException, IOException {
172 
173                 final String path = servletRequest.getPathInfo();
174 
175                 if (null == expectedPathInfo) {
176                   assertNull(
177                       String.format("expected [%s] but was [%s]", expectedPathInfo, path), path);
178                 } else {
179                   assertEquals(
180                       String.format("expected [%s] but was [%s]", expectedPathInfo, path),
181                       expectedPathInfo,
182                       path);
183                 }
184 
185                 //assert memoizer
186                 //noinspection StringEquality
187                 assertSame("memo field did not work", path, servletRequest.getPathInfo());
188 
189                 run[0] = true;
190               }
191             });
192 
193     expect(request.getRequestURI()).andReturn(requestUri);
194 
195     expect(request.getServletPath()).andReturn(servletPath).anyTimes();
196 
197     expect(request.getContextPath()).andReturn(contextPath);
198 
199     expect(request.getAttribute(REQUEST_DISPATCHER_REQUEST)).andReturn(null);
200 
201     replay(injector, binding, request);
202 
203     ServletDefinition servletDefinition =
204         new ServletDefinition(
205             Key.get(HttpServlet.class),
206             UriPatternType.get(UriPatternType.SERVLET, mapping),
207             new HashMap<String, String>(),
208             null);
209 
210     servletDefinition.init(null, injector, Sets.<HttpServlet>newIdentityHashSet());
211     servletDefinition.doService(request, response);
212 
213     assertTrue("Servlet did not run!", run[0]);
214 
215     verify(injector, binding, request);
216   }
217 
218   // Data-driven test.
testPathInfoWithRegexMatching()219   public final void testPathInfoWithRegexMatching() throws IOException, ServletException {
220     // first a mapping of /*
221     pathInfoWithRegexMatching("/path/index.html", "/path", "/(.)*", "/index.html", "");
222     pathInfoWithRegexMatching(
223         "/path//hulaboo///index.html", "/path", "/(.)*", "/hulaboo/index.html", "");
224     pathInfoWithRegexMatching("/path/", "/path", "/(.)*", "/", "");
225     pathInfoWithRegexMatching("/path////////", "/path", "/(.)*", "/", "");
226 
227     // a servlet mapping of /thing/*
228     pathInfoWithRegexMatching("/path/thing////////", "/path", "/thing/(.)*", "/", "/thing");
229     pathInfoWithRegexMatching("/path/thing/stuff", "/path", "/thing/(.)*", "/stuff", "/thing");
230     pathInfoWithRegexMatching(
231         "/path/thing/stuff.html", "/path", "/thing/(.)*", "/stuff.html", "/thing");
232     pathInfoWithRegexMatching("/path/thing", "/path", "/thing/(.)*", null, "/thing");
233 
234     // *.xx style mapping
235     pathInfoWithRegexMatching("/path/thing.thing", "/path", ".*\\.thing", null, "/thing.thing");
236     pathInfoWithRegexMatching("/path///h.thing", "/path", ".*\\.thing", null, "/h.thing");
237     pathInfoWithRegexMatching("/path///...//h.thing", "/path", ".*\\.thing", null, "/.../h.thing");
238     pathInfoWithRegexMatching("/path/my/h.thing", "/path", ".*\\.thing", null, "/my/h.thing");
239 
240     // path
241     pathInfoWithRegexMatching(
242         "/path/test.com/com.test.MyServletModule",
243         "",
244         "/path/[^/]+/(.*)",
245         "com.test.MyServletModule",
246         "/path/test.com/com.test.MyServletModule");
247 
248     // Encoded URLs
249     pathInfoWithRegexMatching("/path/index%2B.html", "/path", "/(.)*", "/index+.html", "");
250     pathInfoWithRegexMatching(
251         "/path/a%20file%20with%20spaces%20in%20name.html",
252         "/path", "/(.)*", "/a file with spaces in name.html", "");
253     pathInfoWithRegexMatching(
254         "/path/Tam%C3%A1s%20nem%20m%C3%A1s.html", "/path", "/(.)*", "/Tamás nem más.html", "");
255   }
256 
pathInfoWithRegexMatching( final String requestUri, final String contextPath, String mapping, final String expectedPathInfo, final String servletPath)257   public final void pathInfoWithRegexMatching(
258       final String requestUri,
259       final String contextPath,
260       String mapping,
261       final String expectedPathInfo,
262       final String servletPath)
263       throws IOException, ServletException {
264 
265     Injector injector = createMock(Injector.class);
266     Binding binding = createMock(Binding.class);
267     HttpServletRequest request = createMock(HttpServletRequest.class);
268     HttpServletResponse response = createMock(HttpServletResponse.class);
269 
270     expect(binding.acceptScopingVisitor((BindingScopingVisitor) anyObject())).andReturn(true);
271     expect(injector.getBinding(Key.get(HttpServlet.class))).andReturn(binding);
272 
273     final boolean[] run = new boolean[1];
274     //get an instance of this servlet
275     expect(injector.getInstance(Key.get(HttpServlet.class)))
276         .andReturn(
277             new HttpServlet() {
278 
279               @Override
280               protected void service(
281                   HttpServletRequest servletRequest, HttpServletResponse httpServletResponse)
282                   throws ServletException, IOException {
283 
284                 final String path = servletRequest.getPathInfo();
285 
286                 if (null == expectedPathInfo) {
287                   assertNull(
288                       String.format("expected [%s] but was [%s]", expectedPathInfo, path), path);
289                 } else {
290                   assertEquals(
291                       String.format("expected [%s] but was [%s]", expectedPathInfo, path),
292                       expectedPathInfo,
293                       path);
294                 }
295 
296                 //assert memoizer
297                 //noinspection StringEquality
298                 assertSame("memo field did not work", path, servletRequest.getPathInfo());
299 
300                 run[0] = true;
301               }
302             });
303 
304     expect(request.getRequestURI()).andReturn(requestUri);
305 
306     expect(request.getServletPath()).andReturn(servletPath).anyTimes();
307 
308     expect(request.getContextPath()).andReturn(contextPath);
309 
310     expect(request.getAttribute(REQUEST_DISPATCHER_REQUEST)).andReturn(null);
311 
312     replay(injector, binding, request);
313 
314     ServletDefinition servletDefinition =
315         new ServletDefinition(
316             Key.get(HttpServlet.class),
317             UriPatternType.get(UriPatternType.REGEX, mapping),
318             new HashMap<String, String>(),
319             null);
320 
321     servletDefinition.init(null, injector, Sets.<HttpServlet>newIdentityHashSet());
322     servletDefinition.doService(request, response);
323 
324     assertTrue("Servlet did not run!", run[0]);
325 
326     verify(injector, binding, request);
327   }
328 }
329