View Javadoc
1   /*
2    * Copyright (c) 2012-2013, Dienst Landelijk Gebied - Ministerie van Economische Zaken
3    * 
4    * Gepubliceerd onder de BSD 2-clause licentie, 
5    * zie https://github.com/MinELenI/CBSviewer/blob/master/LICENSE.md voor de volledige licentie.
6    */
7   package nl.mineleni.cbsviewer.servlet;
8   
9   import static javax.servlet.http.HttpServletResponse.SC_BAD_GATEWAY;
10  import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
11  import static nl.mineleni.cbsviewer.servlet.AbstractBaseServlet.USER_ID;
12  import static nl.mineleni.cbsviewer.servlet.AbstractBaseServlet.USER_PASSWORD;
13  import static nl.mineleni.cbsviewer.servlet.ReverseProxyServlet.ALLOWED_HOSTS;
14  import static nl.mineleni.cbsviewer.servlet.ReverseProxyServlet.ERR_MSG_MISSING_CONFIG;
15  import static nl.mineleni.cbsviewer.servlet.ReverseProxyServlet.FORCE_XML_MIME;
16  import static org.hamcrest.Matchers.greaterThan;
17  import static org.junit.Assert.assertEquals;
18  import static org.junit.Assert.assertFalse;
19  import static org.junit.Assert.assertSame;
20  import static org.junit.Assert.assertTrue;
21  import static org.junit.Assert.fail;
22  
23  import java.io.IOException;
24  import java.io.PrintWriter;
25  import java.lang.reflect.InvocationTargetException;
26  import java.lang.reflect.Method;
27  
28  import javax.servlet.ServletConfig;
29  import javax.servlet.ServletContext;
30  import javax.servlet.ServletException;
31  import javax.servlet.http.HttpServletRequest;
32  import javax.servlet.http.HttpServletResponse;
33  
34  import org.jmock.Expectations;
35  import org.jmock.integration.junit4.JUnitRuleMockery;
36  import org.junit.Before;
37  import org.junit.Test;
38  
39  /**
40   * Unit tests voor {@link nl.mineleni.cbsviewer.servlet.ReverseProxyServlet }
41   * 
42   * @author prinsmc
43   * 
44   */
45  public class ReverseProxyServletTest {
46  	/** servlet die we testen. */
47  	private ReverseProxyServlet pxyServlet;
48  
49  	/** junit mockery. */
50  	private final JUnitRuleMockery mockery = new JUnitRuleMockery();
51  
52  	/** ge-mockte servlet request gebruikt in de test. */
53  	private HttpServletRequest request;
54  
55  	/** ge-mockte servlet config gebruikt in de test. */
56  	private ServletConfig servletConfig;
57  
58  	/** ge-mockte servlet context gebruikt in de test. */
59  	private ServletContext servletContext;
60  
61  	/** ge-mockte servlet response gebruikt in de test. */
62  	private HttpServletResponse response;
63  
64  	/** naam server 1. test waarde. */
65  	private static final String SERVERNAME_DOESNOTEXIST = "blaat.non-existentserver1.com";
66  
67  	/** request url op server 1. test waarde. */
68  	private static final String SERVERNAME_DOESNOTEXIST_URL = "http://"
69  			+ SERVERNAME_DOESNOTEXIST + "/";
70  
71  	/** naam niet toegestane server. test waarde. */
72  	private static final String UNALLOWEDSERVERNAME = "notallowed.minlnv.nl";
73  
74  	/** request url op niet toegestane server. test waarde. */
75  	private static final String UNALLOWEDSERVERNAME_URL = "http://"
76  			+ UNALLOWEDSERVERNAME + "/test/notallowed";
77  
78  	/** naam server 2. test waarde. */
79  	public static final String SERVERNAME = "dbr0425s.dbr.agro.nl:8080";
80  
81  	/** request url op server 2. test waarde. */
82  	private static final String SERVERNAME_URL = "http://" + SERVERNAME
83  			+ "/dashboard/";
84  
85  	/** request url op server 2. test waarde. */
86  	private static final String INVALID_URL = "/lnv-common-gis/";
87  
88  	/**
89  	 * Set up test en mock objecten.
90  	 * 
91  	 * @throws Exception
92  	 *             the exception
93  	 */
94  	@SuppressWarnings("serial")
95  	@Before
96  	public void setUp() throws Exception {
97  		this.servletConfig = this.mockery.mock(ServletConfig.class);
98  		this.servletContext = this.mockery.mock(ServletContext.class);
99  		this.response = this.mockery.mock(HttpServletResponse.class);
100 		this.request = this.mockery.mock(HttpServletRequest.class);
101 
102 		// set up mock config, optional add context expectations
103 		this.mockery.checking(new Expectations() {
104 			{
105 				// zitten in de superklasse
106 				this.allowing(ReverseProxyServletTest.this.servletConfig)
107 						.getInitParameter(USER_ID);
108 				this.will(returnValue("userID"));
109 				this.allowing(ReverseProxyServletTest.this.servletConfig)
110 						.getInitParameter(USER_PASSWORD);
111 				this.will(returnValue("passID"));
112 				// te testen servlet
113 				this.allowing(ReverseProxyServletTest.this.servletConfig)
114 						.getInitParameter(FORCE_XML_MIME);
115 				this.will(returnValue("true"));
116 				this.oneOf(ReverseProxyServletTest.this.servletConfig)
117 						.getInitParameter(ALLOWED_HOSTS);
118 				this.will(returnValue(SERVERNAME_DOESNOTEXIST + ";"
119 						+ SERVERNAME));
120 				this.allowing(ReverseProxyServletTest.this.servletConfig)
121 						.getInitParameter("featureInfoType");
122 				this.will(returnValue(""));
123 			}
124 		});
125 		// Override getServletConfig/getServletContext to return the mocked
126 		// config/context
127 		this.pxyServlet = new ReverseProxyServlet() {
128 			/** return de mocked servletConfig. */
129 			@Override
130 			public ServletConfig getServletConfig() {
131 				return ReverseProxyServletTest.this.servletConfig;
132 			}
133 
134 			/** return de mocked servletContext. */
135 			@Override
136 			public ServletContext getServletContext() {
137 				return ReverseProxyServletTest.this.servletContext;
138 			}
139 		};
140 		try {
141 			this.pxyServlet.init(this.servletConfig);
142 		} catch (final ServletException e) {
143 			fail("Servlet Exception voor init() in test setup. "
144 					+ e.getLocalizedMessage());
145 		}
146 	}
147 
148 	/**
149 	 * Test methode voor
150 	 * {@link nl.mineleni.cbsviewer.servlet.ReverseProxyServlet#doGet(HttpServletRequest, javax.servlet.http.HttpServletResponse)}
151 	 * .
152 	 * 
153 	 * @throws IOException
154 	 *             when it bubbles up from the servletresponse
155 	 * 
156 	 */
157 	public void testDoGetHttpServletRequestHttpServletResponse1()
158 			throws IOException {
159 		// (#1)
160 		this.mockery.checking(new Expectations() {
161 			{
162 				this.oneOf(ReverseProxyServletTest.this.request)
163 						.getQueryString();
164 				this.will(returnValue(SERVERNAME_URL));
165 				this.allowing(ReverseProxyServletTest.this.response)
166 						.setContentType("text/xml");
167 				this.oneOf(ReverseProxyServletTest.this.response)
168 						.setContentLength((this.with(greaterThan(1))));
169 				this.oneOf(ReverseProxyServletTest.this.response).getWriter();
170 				this.will(returnValue((new PrintWriter(System.out))));
171 				this.oneOf(ReverseProxyServletTest.this.response).flushBuffer();
172 			}
173 		});
174 		try {
175 			this.pxyServlet.doGet(this.request, this.response);
176 		} catch (final ServletException e) {
177 			fail("Servlet Exception voor doGet test. (#1)" + e);
178 		}
179 	}
180 
181 	/**
182 	 * Test methode voor
183 	 * {@link nl.mineleni.cbsviewer.servlet.ReverseProxyServlet#doGet(HttpServletRequest, javax.servlet.http.HttpServletResponse)}
184 	 * .
185 	 * 
186 	 * @throws IOException
187 	 *             when it bubbles up from the servletresponse
188 	 * 
189 	 */
190 	public void testDoGetHttpServletRequestHttpServletResponse2()
191 			throws IOException {
192 		// (#2) this should fail; INVALID_URL is not a valid url
193 		this.mockery.checking(new Expectations() {
194 			{
195 				this.oneOf(ReverseProxyServletTest.this.request)
196 						.getQueryString();
197 				this.will(returnValue(INVALID_URL));
198 			}
199 		});
200 		try {
201 			this.pxyServlet.doGet(this.request, this.response);
202 			fail("Dit is een overwachte failure. (#2)");
203 		} catch (final ServletException e) {
204 			// als servlet api = 2.5
205 			assertEquals(
206 					"javax.servlet.ServletException: only HTTP(S) protocol supported",
207 					e.getMessage());
208 			// als servlet api < 2.5
209 			// assertEquals("only HTTP(S) protocol supported", e.getMessage());
210 		}
211 	}
212 
213 	/**
214 	 * Test methode voor
215 	 * {@link nl.mineleni.cbsviewer.servlet.ReverseProxyServlet#doGet(HttpServletRequest, javax.servlet.http.HttpServletResponse)}
216 	 * .
217 	 * 
218 	 * @throws IOException
219 	 *             when it bubbles up from the servletresponse
220 	 */
221 	public void testDoGetHttpServletRequestHttpServletResponse3()
222 			throws IOException {
223 		// (#3) this should fail w/ ConnectException
224 		this.mockery.checking(new Expectations() {
225 			{
226 				this.oneOf(ReverseProxyServletTest.this.request)
227 						.getQueryString();
228 				this.will(returnValue(SERVERNAME_DOESNOTEXIST_URL));
229 				// omdat we een proxy hebben geconfigurrerd krijgen we een 404
230 				// this.one(ReverseProxyServletTest.this.response).sendError(
231 				// SC_NOT_FOUND, "HTTP/1.1 404 Not Found");
232 				this.oneOf(ReverseProxyServletTest.this.response).sendError(
233 						SC_BAD_GATEWAY, "HTTP/1.1 502 Bad Gateway");
234 			}
235 		});
236 		try {
237 			this.pxyServlet.doGet(this.request, this.response);
238 		} catch (final ServletException e) {
239 			fail("(#3)" + e.getMessage());
240 		}
241 	}
242 
243 	/**
244 	 * Test methode voor
245 	 * {@link nl.mineleni.cbsviewer.servlet.ReverseProxyServlet#doGet(HttpServletRequest, javax.servlet.http.HttpServletResponse)}
246 	 * .
247 	 * 
248 	 * @throws IOException
249 	 *             when it bubbles up from the servletresponse
250 	 */
251 	public void testDoGetHttpServletRequestHttpServletResponse4()
252 			throws IOException {
253 		// (#4)
254 		this.mockery.checking(new Expectations() {
255 			{
256 				this.oneOf(ReverseProxyServletTest.this.request)
257 						.getQueryString();
258 				this.will(returnValue(UNALLOWEDSERVERNAME_URL));
259 				this.oneOf(ReverseProxyServletTest.this.response).sendError(
260 						SC_FORBIDDEN,
261 						UNALLOWEDSERVERNAME_URL
262 								+ " is not in the list of allowed servers");
263 				this.oneOf(ReverseProxyServletTest.this.response).flushBuffer();
264 			}
265 		});
266 		try {
267 			this.pxyServlet.doGet(this.request, this.response);
268 		} catch (final ServletException e) {
269 			fail("Servlet Exception voor doGet test. (#4)" + e);
270 		}
271 	}
272 
273 	/**
274 	 * Test methode voor
275 	 * {@link nl.mineleni.cbsviewer.servlet.ReverseProxyServlet#init(javax.servlet.ServletConfig)}
276 	 * , test voor missende config optie.
277 	 */
278 	@Test
279 	public final void testInitServletConfig() {
280 		// init is called in setUp()
281 		this.mockery.checking(new Expectations() {
282 			{
283 				this.oneOf(ReverseProxyServletTest.this.servletConfig)
284 						.getInitParameter(ALLOWED_HOSTS);
285 				this.will(returnValue(null));
286 			}
287 		});
288 		try {
289 			// supposed to throw e
290 			this.pxyServlet.init(this.servletConfig);
291 			fail("Een overwachte fail conditie is opgetreden. (#2)");
292 		} catch (final ServletException e) {
293 			assertSame(ERR_MSG_MISSING_CONFIG, e.getMessage());
294 		}
295 	}
296 
297 	/**
298 	 * Test methode voor
299 	 * {@link ReverseProxyServlet#checkUrlAllowed(java.lang.String)} . Deze test
300 	 * maakt een mock servletconfig en configureert de servlet. Met de
301 	 * reflection API wordt de private method van de echte ReverseProxyServlet
302 	 * aangeroepen.
303 	 */
304 	@SuppressWarnings("javadoc")
305 	// checkUrlAllowed is private
306 	@Test
307 	public void testCheckUrlAllowed() {
308 		// init is called in setUp()
309 		try {
310 			// superclass gebruiken anders krijg je de "override" class terug;
311 			// en die heeft deze method niet
312 			final Method method = this.pxyServlet.getClass().getSuperclass()
313 					.getDeclaredMethod("checkUrlAllowed", String.class);
314 			method.setAccessible(true);
315 			assertFalse((Boolean) method.invoke(this.pxyServlet,
316 					UNALLOWEDSERVERNAME));
317 			assertTrue((Boolean) method.invoke(this.pxyServlet, SERVERNAME_URL));
318 		} catch (final SecurityException e) {
319 			fail("Reflection call getDeclaredMethod failed:" + e);
320 		} catch (final NoSuchMethodException e) {
321 			fail("Reflection call getDeclaredMethod failed:" + e);
322 		} catch (final IllegalArgumentException e) {
323 			fail("Reflection call invoke failed:" + e);
324 		} catch (final IllegalAccessException e) {
325 			fail("Reflection call invoke failed:" + e);
326 		} catch (final InvocationTargetException e) {
327 			fail("Reflection call invoke failed:" + e);
328 		}
329 	}
330 }