/* * The MIT License * * Original work sponsored and donated by National Board of e-Health (NSI), Denmark (http://www.nsi.dk) * * Copyright (C) 2011 National Board of e-Health (NSI), Denmark (http://www.nsi.dk) * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * $HeadURL$ * $Id$ */ package com.trifork.sosigw.sts; import com.trifork.sosigw.config.StaticConfigImpl; import com.trifork.sosigw.util.Util; import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.mortbay.jetty.Request; import org.mortbay.jetty.Server; import org.mortbay.jetty.handler.AbstractHandler; import org.w3c.dom.Document; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.parsers.DocumentBuilderFactory; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.Executors; public class SingleSTSProxyIntegrationTest { private static Server server; private static final String CANNED_STS_RESPONSE = "testAnswerÆblegrød"; private Document stsRequest; @Before public void setup() throws Exception { InputStream stsRequestStream = this.getClass().getClassLoader().getResourceAsStream("com/trifork/sosigw/proxy/idcard-level1-testdata.xml"); stsRequest = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(stsRequestStream); StaticConfigImpl.initSettings(); } public void setupThreadLocalRequest() { HttpServletRequest request = new RequestStub("9.8.7.6"); Util.setActiveHttpRequest(request); } @BeforeClass public static void configureServer() throws Exception { server = new Server(8587); AbstractHandler cannedResponseHandler = new AbstractHandler() { public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException, ServletException { response.setContentType("text/xml; charset=utf-8"); PrintWriter pw = response.getWriter(); if (target.endsWith("/sts")) { response.setStatus(200); pw.print(CANNED_STS_RESPONSE); } else if (target.endsWith("/replicateclientipheader")) { response.setStatus(200); pw.print(request.getHeader("X-NSI-Original-Client-IP")); } else if (target.endsWith("/sleep")) { sleep(); response.setStatus(200); pw.print(CANNED_STS_RESPONSE); } else { response.setStatus(500); pw.print("This was never intended to work"); } ((Request)request).setHandled(true); pw.flush(); } private void sleep() { try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } }; server.setHandler(cannedResponseHandler); server.start(); } @AfterClass public static void stopServer() throws Exception { if (server != null && server.isRunning()) { server.stop(); } server = null; } @Test(expected = STSUnreachableException.class) public void shouldThrowSTSUnreachableExceptionWhenServerDoesNotRespond() { new SingleSTSProxy("http://localhost:8584/").callSTS(stsRequest); // deliberately bad port } @Test(expected = STSUnreachableException.class) public void shouldThrowSTSUnreachableExceptionWhenServerUrlIsMalformed() { new SingleSTSProxy("ttp://mal.formed.url").callSTS(stsRequest); } @Test public void shouldReturnTheAnswerWhenServerDoesRespondWithSuccesCode() { setupThreadLocalRequest(); final String response = new SingleSTSProxy("http://localhost:8587/sts").callSTS(stsRequest); Assert.assertEquals(CANNED_STS_RESPONSE, response); } @Test public void shouldSetNsiClientIpHeaderInRequest() { setupThreadLocalRequest(); final String response = new SingleSTSProxy("http://localhost:8587/replicateclientipheader").callSTS(stsRequest); Assert.assertEquals("9.8.7.6", response); } @Test(expected = STSUnreachableException.class) public void shouldThrowSTSUnreachableExceptionWhenServerDoesRespondWithErrorCode() { new SingleSTSProxy("http://localhost:8587/doReturnErrorCode").callSTS(stsRequest); } @Test(timeout = 5000) public void shouldThrowSTSUnreachableExceptionWhenServerDoesNotRespondFastEnough() { setupThreadLocalRequest(); final SingleSTSProxy stsProxy = createStsProxyWithCustomTimeout("http://localhost:8587/sleep", 500); assertThrowSTSUncreachableExceptionWithSpecificCauseMessage(stsProxy, "read timed out"); } @Test(timeout = 5000) public void shouldThrowSTSUnreachableExceptionWhenServerAcceptsConnectionButDoesNotReadRequest() throws IOException { setupThreadLocalRequest(); Executors.newSingleThreadExecutor().execute(new Runnable() { public void run() { ServerSocket socket = null; try { socket = new ServerSocket(8588); socket.accept(); } catch (IOException e) { throw new RuntimeException(e); } finally { if (socket != null) { try { socket.close(); } catch (IOException ignore) { } } } } }); SingleSTSProxy stsProxy = createStsProxyWithCustomTimeout("http://localhost:8588/sleep", 500); assertThrowSTSUncreachableExceptionWithSpecificCauseMessage(stsProxy, "read timed out"); } @Test(timeout = 5000) public void shouldThrowSTSUnreachableExceptionWhenServerAcceptsConnectionAndDiscardsRequest() throws IOException { setupThreadLocalRequest(); Executors.newSingleThreadExecutor().execute(new Runnable() { public void run() { ServerSocket socket = null; try { socket = new ServerSocket(8589); final Socket clientSocket = socket.accept(); final InputStream inputStream = clientSocket.getInputStream(); int read; while((read = inputStream.read()) != -1) { //do nothing System.err.println(read); } } catch (IOException e) { throw new RuntimeException(e); } finally { if (socket != null) { try { socket.close(); } catch (IOException ignore) { } } } } }); SingleSTSProxy stsProxy = createStsProxyWithCustomTimeout("http://localhost:8589/sleep", 500); assertThrowSTSUncreachableExceptionWithSpecificCauseMessage(stsProxy, "read timed out"); } @Test public void shouldThrowSTSUnreachableExceptionWhenServerIsNotListening() throws Throwable { setupThreadLocalRequest(); SingleSTSProxy stsProxy = createStsProxyWithCustomTimeout("http://localhost:8632/sleep", 500); assertThrowSTSUncreachableExceptionWithSpecificCauseMessage(stsProxy, "connection refused"); } private void assertThrowSTSUncreachableExceptionWithSpecificCauseMessage(SingleSTSProxy stsProxy, String expectedMsg) { try { stsProxy.callSTS(stsRequest); Assert.fail("Expected STSUnreachableException"); } catch (STSUnreachableException e) { assertContains(expectedMsg, e.getMessage()); } } private void assertContains(String expectedSubstring, String entireString) { if (!entireString.toLowerCase().contains(expectedSubstring.toLowerCase())) { Assert.fail("Expected '" + entireString + "' to contain '" + expectedSubstring + "'"); } } private SingleSTSProxy createStsProxyWithCustomTimeout(String url, int timeout) { final SingleSTSProxy stsProxy = new SingleSTSProxy(url); stsProxy.config = new STSProxyStaticConfig(5, 60000, timeout); return stsProxy; } }