1/**
2 * Copyright (C) 2011 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
17package com.google.inject.servlet;
18
19import static org.easymock.EasyMock.createControl;
20import static org.easymock.EasyMock.expect;
21
22import com.google.inject.Guice;
23import com.google.inject.Inject;
24import com.google.inject.Injector;
25import com.google.inject.Key;
26import com.google.inject.Scopes;
27import com.google.inject.name.Named;
28import com.google.inject.name.Names;
29
30import junit.framework.TestCase;
31
32import org.easymock.IMocksControl;
33
34import java.io.IOException;
35
36import javax.servlet.FilterChain;
37import javax.servlet.FilterConfig;
38import javax.servlet.ServletContext;
39import javax.servlet.ServletException;
40import javax.servlet.ServletRequest;
41import javax.servlet.ServletResponse;
42import javax.servlet.http.HttpServlet;
43import javax.servlet.http.HttpServletRequest;
44import javax.servlet.http.HttpServletResponse;
45
46/** Tests to make sure that servlets with a context path are handled right. */
47public class ContextPathTest extends TestCase {
48
49  @Inject @Named("foo")
50  private TestServlet fooServlet;
51
52  @Inject @Named("bar")
53  private TestServlet barServlet;
54
55  private IMocksControl globalControl;
56  private Injector injector;
57  private ServletContext servletContext;
58  private FilterConfig filterConfig;
59  private GuiceFilter guiceFilter;
60
61  @Override
62  public final void setUp() throws Exception {
63    injector = Guice.createInjector(new ServletModule() {
64      @Override
65      protected void configureServlets() {
66        bind(TestServlet.class).annotatedWith(Names.named("foo"))
67            .to(TestServlet.class).in(Scopes.SINGLETON);
68        bind(TestServlet.class).annotatedWith(Names.named("bar"))
69            .to(TestServlet.class).in(Scopes.SINGLETON);
70        serve("/foo/*").with(Key.get(TestServlet.class, Names.named("foo")));
71        serve("/bar/*").with(Key.get(TestServlet.class, Names.named("bar")));
72        // TODO: add a filter(..) call and validate it is correct
73      }
74    });
75    injector.injectMembers(this);
76
77    assertNotNull(fooServlet);
78    assertNotNull(barServlet);
79    assertNotSame(fooServlet, barServlet);
80
81    globalControl = createControl();
82    servletContext = globalControl.createMock(ServletContext.class);
83    filterConfig = globalControl.createMock(FilterConfig.class);
84
85    expect(servletContext.getAttribute(GuiceServletContextListener.INJECTOR_NAME))
86        .andReturn(injector).anyTimes();
87    expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes();
88
89    globalControl.replay();
90
91    guiceFilter = new GuiceFilter();
92    guiceFilter.init(filterConfig);
93  }
94
95  @Override
96  public final void tearDown() {
97    assertNotNull(fooServlet);
98    assertNotNull(barServlet);
99
100    fooServlet = null;
101    barServlet = null;
102
103    guiceFilter.destroy();
104    guiceFilter = null;
105
106    injector = null;
107
108    filterConfig = null;
109    servletContext = null;
110
111    globalControl.verify();
112  }
113
114  public void testSimple() throws Exception {
115    IMocksControl testControl = createControl();
116    TestFilterChain testFilterChain = new TestFilterChain();
117    HttpServletRequest req = testControl.createMock(HttpServletRequest.class);
118    HttpServletResponse res = testControl.createMock(HttpServletResponse.class);
119
120    expect(req.getMethod()).andReturn("GET").anyTimes();
121    expect(req.getRequestURI()).andReturn("/bar/foo").anyTimes();
122    expect(req.getServletPath()).andReturn("/bar/foo").anyTimes();
123    expect(req.getContextPath()).andReturn("").anyTimes();
124
125    testControl.replay();
126
127    guiceFilter.doFilter(req, res, testFilterChain);
128
129    assertFalse(testFilterChain.isTriggered());
130    assertFalse(fooServlet.isTriggered());
131    assertTrue(barServlet.isTriggered());
132
133    testControl.verify();
134  }
135
136  //
137  // each of the following "runTest" calls takes three path parameters:
138  //
139  // The value of "getRequestURI()"
140  // The value of "getServletPath()"
141  // The value of "getContextPath()"
142  //
143  // these values have been captured using a filter in Apache Tomcat 6.0.32
144  // and are used for real-world values that a servlet container would send into
145  // the GuiceFilter.
146  //
147  // the remaining three booleans are:
148  //
149  // True if the request gets passed down the filter chain
150  // True if the request hits the "foo" servlet
151  // True if the request hits the "bar" sevlet
152  //
153  // After adjusting the request URI for the web app deployment location, all
154  // calls
155  // should always produce the same result.
156  //
157
158  // ROOT Web app, using Tomcat default servlet
159  public void testRootDefault() throws Exception {
160    // fetching /. Should go up the filter chain (only mappings on /foo/* and /bar/*).
161    runTest("/", "/", "", true, false, false);
162    // fetching /bar/. Should hit the bar servlet
163    runTest("/bar/", "/bar/", "", false, false, true);
164    // fetching /foo/xxx. Should hit the foo servlet
165    runTest("/foo/xxx", "/foo/xxx", "", false, true, false);
166    // fetching /xxx. Should go up the chain
167    runTest("/xxx", "/xxx", "", true, false, false);
168  }
169
170  // ROOT Web app, using explicit backing servlet mounted at /*
171  public void testRootExplicit() throws Exception {
172    // fetching /. Should go up the filter chain (only mappings on /foo/* and /bar/*).
173    runTest("/", "", "", true, false, false);
174    // fetching /bar/. Should hit the bar servlet
175    runTest("/bar/", "", "", false, false, true);
176    // fetching /foo/xxx. Should hit the foo servlet
177    runTest("/foo/xxx", "", "", false, true, false);
178    // fetching /xxx. Should go up the chain
179    runTest("/xxx", "", "", true, false, false);
180  }
181
182  // ROOT Web app, using two backing servlets, mounted at /bar/* and /foo/*
183  public void testRootSpecific() throws Exception {
184    // fetching /. Should go up the filter chain (only mappings on /foo/* and /bar/*).
185    runTest("/", "/", "", true, false, false);
186    // fetching /bar/. Should hit the bar servlet
187    runTest("/bar/", "/bar", "", false, false, true);
188    // fetching /foo/xxx. Should hit the foo servlet
189    runTest("/foo/xxx", "/foo", "", false, true, false);
190    // fetching /xxx. Should go up the chain
191    runTest("/xxx", "/xxx", "", true, false, false);
192  }
193
194  // Web app located at /webtest, using Tomcat default servlet
195  public void testWebtestDefault() throws Exception {
196    // fetching /. Should go up the filter chain (only mappings on /foo/* and /bar/*).
197    runTest("/webtest/", "/", "/webtest", true, false, false);
198    // fetching /bar/. Should hit the bar servlet
199    runTest("/webtest/bar/", "/bar/", "/webtest", false, false, true);
200    // fetching /foo/xxx. Should hit the foo servlet
201    runTest("/webtest/foo/xxx", "/foo/xxx", "/webtest", false, true, false);
202    // fetching /xxx. Should go up the chain
203    runTest("/webtest/xxx", "/xxx", "/webtest", true, false, false);
204  }
205
206  // Web app located at /webtest, using explicit backing servlet mounted at /*
207  public void testWebtestExplicit() throws Exception {
208    // fetching /. Should go up the filter chain (only mappings on /foo/* and /bar/*).
209    runTest("/webtest/", "", "/webtest", true, false, false);
210    // fetching /bar/. Should hit the bar servlet
211    runTest("/webtest/bar/", "", "/webtest", false, false, true);
212    // fetching /foo/xxx. Should hit the foo servlet
213    runTest("/webtest/foo/xxx", "", "/webtest", false, true, false);
214    // fetching /xxx. Should go up the chain
215    runTest("/webtest/xxx", "", "/webtest", true, false, false);
216  }
217
218  // Web app located at /webtest, using two backing servlets, mounted at /bar/*
219  // and /foo/*
220  public void testWebtestSpecific() throws Exception {
221    // fetching /. Should go up the filter chain (only mappings on /foo/* and
222    // /bar/*).
223    runTest("/webtest/", "/", "/webtest", true, false, false);
224    // fetching /bar/. Should hit the bar servlet
225    runTest("/webtest/bar/", "/bar", "/webtest", false, false, true);
226    // fetching /foo/xxx. Should hit the foo servlet
227    runTest("/webtest/foo/xxx", "/foo", "/webtest", false, true, false);
228    // fetching /xxx. Should go up the chain
229    runTest("/webtest/xxx", "/xxx", "/webtest", true, false, false);
230  }
231
232  private void runTest(final String requestURI, final String servletPath, final String contextPath,
233      final boolean filterResult, final boolean fooResult, final boolean barResult)
234      throws Exception {
235    IMocksControl testControl = createControl();
236
237    barServlet.clear();
238    fooServlet.clear();
239
240    TestFilterChain testFilterChain = new TestFilterChain();
241    HttpServletRequest req = testControl.createMock(HttpServletRequest.class);
242    HttpServletResponse res = testControl.createMock(HttpServletResponse.class);
243
244    expect(req.getMethod()).andReturn("GET").anyTimes();
245    expect(req.getRequestURI()).andReturn(requestURI).anyTimes();
246    expect(req.getServletPath()).andReturn(servletPath).anyTimes();
247    expect(req.getContextPath()).andReturn(contextPath).anyTimes();
248
249    testControl.replay();
250
251    guiceFilter.doFilter(req, res, testFilterChain);
252
253    assertEquals(filterResult, testFilterChain.isTriggered());
254    assertEquals(fooResult, fooServlet.isTriggered());
255    assertEquals(barResult, barServlet.isTriggered());
256
257    testControl.verify();
258  }
259
260  public static class TestServlet extends HttpServlet {
261    private boolean triggered = false;
262
263    @Override
264    public void doGet(HttpServletRequest req, HttpServletResponse resp) {
265      triggered = true;
266    }
267
268    public boolean isTriggered() {
269      return triggered;
270    }
271
272    public void clear() {
273      triggered = false;
274    }
275  }
276
277  public static class TestFilterChain implements FilterChain {
278    private boolean triggered = false;
279
280    public void doFilter(ServletRequest request, ServletResponse response) throws IOException,
281        ServletException {
282      triggered = true;
283    }
284
285    public boolean isTriggered() {
286      return triggered;
287    }
288
289    public void clear() {
290      triggered = false;
291    }
292  }
293}
294