From e6566b58bd1b4919b72ebe15d3219c6fe4a1a1b0 Mon Sep 17 00:00:00 2001 From: Florian Date: Thu, 4 May 2023 00:52:00 +0200 Subject: [PATCH] revert missing tests + small fixes - revert the 4 missing tests, e.g. proxy test from commit 20468f5 - add virtual host name `test.localhost` to embedded jetty server (JUnit Tests) since localhost and IP-Addresses are no longer supported by the proxy and use this address inside proxy `src` - add `test-localhost` support for the docker tests. To support this the docker hostname need to be set to test.localhost by: `--hostname=test.localhost` (only for the docker tests) - proxy: add file format support for PDF - proxy: add error messages on "bad request" response - proxy: remove dead code - old proxy: add error messages on "bad request" response - fix incorrect README link to docs - add `HTTP_PROXY_READ_TIMEOUT` option -- close #240 --- .github/workflows/tests.yml | 8 +- README.md | 7 +- .../plantuml/servlet/AsciiCoderServlet.java | 4 +- .../plantuml/servlet/OldProxyServlet.java | 4 +- .../plantuml/servlet/ProxyServlet.java | 118 +++-------- src/main/webapp/resource/test2diagrams.txt | 3 +- .../plantuml/servlet/AllTests.java | 15 +- .../sourceforge/plantuml/servlet/TestEPS.java | 33 +++ .../plantuml/servlet/TestMultipageUml.java | 196 ++++++++++++++++++ .../plantuml/servlet/TestOldProxy.java | 71 +++++++ .../plantuml/servlet/TestProxy.java | 115 ++++++++++ .../servlet/server/EmbeddedJettyServer.java | 3 + .../plantuml/servlet/utils/TestUtils.java | 14 ++ .../servlet/utils/WebappTestCase.java | 6 + 14 files changed, 496 insertions(+), 101 deletions(-) create mode 100644 src/test/java/net/sourceforge/plantuml/servlet/TestEPS.java create mode 100644 src/test/java/net/sourceforge/plantuml/servlet/TestMultipageUml.java create mode 100644 src/test/java/net/sourceforge/plantuml/servlet/TestOldProxy.java create mode 100644 src/test/java/net/sourceforge/plantuml/servlet/TestProxy.java diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3e3d481..f391c20 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -93,7 +93,7 @@ jobs: - name: Lifecycle - Step 2/8 - mvn validate run: mvn --batch-mode validate - + - name: Lifecycle - Step 3/8 - mvn compile run: mvn --batch-mode compile @@ -111,7 +111,7 @@ jobs: - name: Lifecycle - Step 8/8 - mvn site run: mvn --batch-mode site - + test-embedded: runs-on: ubuntu-latest needs: test-mvn-livecycle @@ -217,7 +217,7 @@ jobs: - name: Build the jetty docker stack run: | docker image build -f Dockerfile.jetty -t plantuml-server:local . - docker run -d -p 8080:8080 -e BASE_URL=plantuml plantuml-server:local + docker run -d --hostname=test.localhost -p 8080:8080 -e BASE_URL=plantuml plantuml-server:local - name: Check running containers run: docker ps @@ -249,7 +249,7 @@ jobs: - name: Build the tomcat docker stack run: | docker image build -f Dockerfile.tomcat -t plantuml-server:local . - docker run -d -p 8080:8080 -e BASE_URL=plantuml plantuml-server:local + docker run -d --hostname=test.localhost -p 8080:8080 -e BASE_URL=plantuml plantuml-server:local - name: Check running containers run: docker ps diff --git a/README.md b/README.md index a357a03..edc6f61 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ PlantUML Server is a web application to generate UML diagrams on-the-fly. ![PlantUML Server](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/screenshot.png) -More examples and features about the Web UI can be found in [docs/WebUI](https://github.com/HeinrichAD/plantuml-server/tree/master/docs/WebUI). +More examples and features about the Web UI can be found in [docs/WebUI](https://github.com/plantuml/plantuml-server/tree/master/docs/WebUI). To know more about PlantUML, please visit https://plantuml.com. @@ -129,6 +129,9 @@ You can set all the following variables: * `HTTP_AUTHORIZATION` * when calling the `proxy` endpoint, the value of `HTTP_AUTHORIZATION` will be used to set the HTTP Authorization header * Default value: `null` +* `HTTP_PROXY_READ_TIMEOUT` + * when calling the `proxy` endpoint, the value of `HTTP_PROXY_READ_TIMEOUT` will be the connection read timeout in milliseconds + * Default value: `10000` (10 seconds) * `ALLOW_PLANTUML_INCLUDE` * Enables `!include` processing which can read files from the server into diagrams. Files are read relative to the current working directory. * Default value: `false` @@ -171,4 +174,4 @@ mvn package -f pom.jdk8.xml [-Dapache-jsp.scope=compile] It is possible to use PlantUML with a reverse proxy. -You can find this and other examples [here](./examples). +You can find this and other examples [here](https://github.com/plantuml/plantuml-server/tree/master/examples). diff --git a/src/main/java/net/sourceforge/plantuml/servlet/AsciiCoderServlet.java b/src/main/java/net/sourceforge/plantuml/servlet/AsciiCoderServlet.java index bcff40a..175a0e5 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/AsciiCoderServlet.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/AsciiCoderServlet.java @@ -67,7 +67,7 @@ public class AsciiCoderServlet extends HttpServlet { try { text = getTranscoder().decode(encodedText); } catch (Exception e) { - response.setStatus(500); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); e.printStackTrace(); } @@ -97,7 +97,7 @@ public class AsciiCoderServlet extends HttpServlet { try { encoded = getTranscoder().encode(uml.toString()); } catch (Exception e) { - response.setStatus(500); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); e.printStackTrace(); } diff --git a/src/main/java/net/sourceforge/plantuml/servlet/OldProxyServlet.java b/src/main/java/net/sourceforge/plantuml/servlet/OldProxyServlet.java index 4bdee14..1d37816 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/OldProxyServlet.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/OldProxyServlet.java @@ -61,7 +61,7 @@ public class OldProxyServlet extends HttpServlet { Matcher proxyMatcher = PROXY_PATTERN.matcher(uri); if (!proxyMatcher.matches()) { // Bad URI format. - response.setStatus(400); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "URL malformed."); return; } @@ -69,7 +69,7 @@ public class OldProxyServlet extends HttpServlet { String format = proxyMatcher.group(4); // Expected format of the generated diagram String sourceURL = proxyMatcher.group(5); if (ProxyServlet.forbiddenURL(sourceURL)) { - response.setStatus(400); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Forbidden URL format."); return; } diff --git a/src/main/java/net/sourceforge/plantuml/servlet/ProxyServlet.java b/src/main/java/net/sourceforge/plantuml/servlet/ProxyServlet.java index 0a7e026..e08796b 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/ProxyServlet.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/ProxyServlet.java @@ -29,12 +29,10 @@ import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; -import java.security.cert.Certificate; import java.util.List; +import java.util.stream.Collectors; import javax.imageio.IIOException; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLPeerUnverifiedException; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; @@ -82,25 +80,25 @@ public class ProxyServlet extends HttpServlet { return false; } - @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { - final String fmt = request.getParameter("fmt"); final String source = request.getParameter("src"); final String index = request.getParameter("idx"); - if (forbiddenURL(source)) { - response.setStatus(400); - return; - } - final URL srcUrl; // Check if the src URL is valid + final URL srcUrl; try { srcUrl = new URL(source); } catch (MalformedURLException mue) { mue.printStackTrace(); - response.setStatus(400); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "URL malformed."); + return; + } + + // Check if URL is in a forbidden format (e.g. IP-Address) + if (forbiddenURL(source)) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Forbidden URL format."); return; } @@ -112,8 +110,7 @@ public class ProxyServlet extends HttpServlet { BlockUml block = blocks.get(n); Diagram diagram = block.getDiagram(); UmlSource umlSrc = diagram.getSource(); - String uml = umlSrc.getPlainString(); - //System.out.println("uml=" + uml); + String uml = umlSrc.getPlainString("\n"); // generate the response DiagramResponse dr = new DiagramResponse(response, getOutputFormat(fmt), request); @@ -128,7 +125,6 @@ public class ProxyServlet extends HttpServlet { // Browser has closed the connection, so the HTTP OutputStream is closed // Silently catch the exception to avoid annoying log } - dr = null; } /** @@ -141,25 +137,10 @@ public class ProxyServlet extends HttpServlet { * @throws IOException if an input or output exception occurred */ private String getSource(final URL url) throws IOException { - String line; - BufferedReader rd; - StringBuilder sb; - try { - HttpURLConnection con = getConnection(url); - rd = new BufferedReader(new InputStreamReader(con.getInputStream())); - sb = new StringBuilder(); - - while ((line = rd.readLine()) != null) { - sb.append(line + '\n'); - } - rd.close(); - return sb.toString(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - rd = null; + HttpURLConnection conn = getConnection(url); + try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { + return br.lines().collect(Collectors.joining("\n")); } - return ""; } /** @@ -174,23 +155,16 @@ public class ProxyServlet extends HttpServlet { if (format == null) { return FileFormat.PNG; } - if (format.equals("svg")) { - return FileFormat.SVG; + switch (format.toLowerCase()) { + case "png": return FileFormat.PNG; + case "svg": return FileFormat.SVG; + case "eps": return FileFormat.EPS; + case "epstext": return FileFormat.EPS_TEXT; + case "txt": return FileFormat.UTXT; + case "map": return FileFormat.UTXT; + case "pdf": return FileFormat.PDF; + default: return FileFormat.PNG; } - if (format.equals("eps")) { - return FileFormat.EPS; - } - if (format.equals("epstext")) { - return FileFormat.EPS_TEXT; - } - if (format.equals("txt")) { - return FileFormat.UTXT; - } - if (format.equals("map")) { - return FileFormat.UTXT; - } - - return FileFormat.PNG; } /** @@ -203,47 +177,19 @@ public class ProxyServlet extends HttpServlet { * @throws IOException if an input or output exception occurred */ private HttpURLConnection getConnection(final URL url) throws IOException { - final HttpURLConnection con = (HttpURLConnection) url.openConnection(); - //if (con instanceof HttpsURLConnection) { - // printHttpsCert((HttpsURLConnection) con); - //} - con.setRequestMethod("GET"); + final HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); String token = System.getenv("HTTP_AUTHORIZATION"); if (token != null) { - con.setRequestProperty("Authorization", token); + conn.setRequestProperty("Authorization", token); } - con.setReadTimeout(10000); // 10 seconds - con.connect(); - return con; - } - - /** - * Debug method used to dump the certificate info. - * - * @param con the https connection - */ - @SuppressWarnings("unused") - private void printHttpsCert(final HttpsURLConnection con) { - if (con != null) { - try { - System.out.println("Response Code : " + con.getResponseCode()); - System.out.println("Cipher Suite : " + con.getCipherSuite()); - System.out.println("\n"); - - Certificate[] certs = con.getServerCertificates(); - for (Certificate cert : certs) { - System.out.println("Cert Type : " + cert.getType()); - System.out.println("Cert Hash Code : " + cert.hashCode()); - System.out.println("Cert Public Key Algorithm : " + cert.getPublicKey().getAlgorithm()); - System.out.println("Cert Public Key Format : " + cert.getPublicKey().getFormat()); - System.out.println("\n"); - } - - } catch (SSLPeerUnverifiedException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } + final String timeoutString = System.getenv("HTTP_PROXY_READ_TIMEOUT"); + int timeout = 10000; // 10 seconds as default + if (timeoutString != null && timeoutString.matches("^\\d+$")) { + timeout = Integer.parseInt(timeoutString); } + conn.setReadTimeout(timeout); + conn.connect(); + return conn; } } diff --git a/src/main/webapp/resource/test2diagrams.txt b/src/main/webapp/resource/test2diagrams.txt index ddba7d5..230112f 100644 --- a/src/main/webapp/resource/test2diagrams.txt +++ b/src/main/webapp/resource/test2diagrams.txt @@ -1,4 +1,5 @@ -This file is used by the TestProxy unit test. It contains 2 diagrams description. +' This file is used by the TestProxy unit test. +' It contains 2 diagrams description. @startuml Bob -> Alice : hello diff --git a/src/test/java/net/sourceforge/plantuml/servlet/AllTests.java b/src/test/java/net/sourceforge/plantuml/servlet/AllTests.java index d34c343..c0c2d8f 100644 --- a/src/test/java/net/sourceforge/plantuml/servlet/AllTests.java +++ b/src/test/java/net/sourceforge/plantuml/servlet/AllTests.java @@ -8,12 +8,19 @@ public class AllTests extends TestSuite { public static Test suite() { TestSuite suite = new TestSuite(AllTests.class.getName()); // $JUnit-BEGIN$ - suite.addTestSuite(TestWebUI.class); - suite.addTestSuite(TestImage.class); suite.addTestSuite(TestAsciiArt.class); - suite.addTestSuite(TestSVG.class); - suite.addTestSuite(TestMap.class); + suite.addTestSuite(TestAsciiCoder.class); suite.addTestSuite(TestCharset.class); + suite.addTestSuite(TestCheck.class); + suite.addTestSuite(TestEPS.class); + suite.addTestSuite(TestImage.class); + suite.addTestSuite(TestLanguage.class); + suite.addTestSuite(TestMap.class); + suite.addTestSuite(TestMultipageUml.class); + suite.addTestSuite(TestOldProxy.class); + suite.addTestSuite(TestProxy.class); + suite.addTestSuite(TestSVG.class); + suite.addTestSuite(TestWebUI.class); // $JUnit-END$ return suite; } diff --git a/src/test/java/net/sourceforge/plantuml/servlet/TestEPS.java b/src/test/java/net/sourceforge/plantuml/servlet/TestEPS.java new file mode 100644 index 0000000..08c3ba6 --- /dev/null +++ b/src/test/java/net/sourceforge/plantuml/servlet/TestEPS.java @@ -0,0 +1,33 @@ +package net.sourceforge.plantuml.servlet; + +import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; + +import net.sourceforge.plantuml.servlet.utils.TestUtils; +import net.sourceforge.plantuml.servlet.utils.WebappTestCase; + + +public class TestEPS extends WebappTestCase { + + /** + * Verifies the generation of the EPS for the Bob -> Alice sample + */ + public void testSimpleSequenceDiagram() throws IOException { + final URL url = new URL(getServerUrl() + "/eps/" + TestUtils.SEQBOB); + final URLConnection conn = url.openConnection(); + // Analyze response + // Verifies the Content-Type header + assertEquals( + "Response content type is not EPS", + "application/postscript", + conn.getContentType().toLowerCase() + ); + // Get the content and verify its size + String diagram = getContentText(conn); + int diagramLen = diagram.length(); + assertTrue(diagramLen > 7000); + assertTrue(diagramLen < 10000); + } + +} diff --git a/src/test/java/net/sourceforge/plantuml/servlet/TestMultipageUml.java b/src/test/java/net/sourceforge/plantuml/servlet/TestMultipageUml.java new file mode 100644 index 0000000..5494462 --- /dev/null +++ b/src/test/java/net/sourceforge/plantuml/servlet/TestMultipageUml.java @@ -0,0 +1,196 @@ +package net.sourceforge.plantuml.servlet; + +import static org.junit.Assert.assertNotEquals; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; + +import net.sourceforge.plantuml.servlet.utils.TestUtils; +import net.sourceforge.plantuml.servlet.utils.WebappTestCase; + + +public class TestMultipageUml extends WebappTestCase { + + /** + * Verifies that an multipage diagram renders correct given index (PNG). + */ + public void testPngIndexPage() throws IOException { + final URL url = new URL(getServerUrl() + "/png/1/" + TestUtils.SEQMULTIPAGE); + final HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + // Analyze response + // Verifies HTTP status code and the Content-Type + assertEquals("Bad HTTP status received", 200, conn.getResponseCode()); + assertEquals( + "Response content type is not PNG", + "image/png", + conn.getContentType().toLowerCase() + ); + // Get the image and verify its size + byte[] inMemoryImage = getContentAsBytes(conn); + int diagramLen = inMemoryImage.length; // 7525 + assertTrue(diagramLen > 6000); + assertTrue(diagramLen < 9000); + } + + /** + * Verifies that an multipage diagram renders correct even if no index is specified (PNG). + */ + public void testPngIndexPageNoIndex() throws IOException { + final URL url = new URL(getServerUrl() + "/png/" + TestUtils.SEQMULTIPAGE); + final HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + // Analyze response + // Verifies HTTP status code and the Content-Type + assertEquals("Bad HTTP status received", 200, conn.getResponseCode()); + assertEquals( + "Response content type is not PNG", + "image/png", + conn.getContentType().toLowerCase() + ); + // Get the image and verify its size + byte[] inMemoryImage = getContentAsBytes(conn); + int diagramLen = inMemoryImage.length; // 4196 + assertTrue(diagramLen > 3000); + assertTrue(diagramLen < 5000); + } + + /** + * Verifies that an multipage diagram renders correct given index (SVG). + */ + public void testSvgIndexPage() throws IOException { + final URL url = new URL(getServerUrl() + "/svg/1/" + TestUtils.SEQMULTIPAGE); + final HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + // Analyze response + // Verifies HTTP status code and the Content-Type + assertEquals("Bad HTTP status received", 200, conn.getResponseCode()); + assertEquals( + "Response content type is not SVG", + "image/svg+xml", + conn.getContentType().toLowerCase() + ); + // Get the content and verify its size + String diagram = getContentText(conn); + int diagramLen = diagram.length(); + assertTrue(diagramLen > 4500); + assertTrue(diagramLen < 6000); + } + + /** + * Verifies that an multipage diagram renders correct even if no index is specified (SVG). + */ + public void testSvgIndexPageNoIndex() throws IOException { + final URL url = new URL(getServerUrl() + "/svg/" + TestUtils.SEQMULTIPAGE); + final HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + // Analyze response + // Verifies HTTP status code and the Content-Type + assertEquals("Bad HTTP status received", 200, conn.getResponseCode()); + assertEquals( + "Response content type is not SVG", + "image/svg+xml", + conn.getContentType().toLowerCase() + ); + // Get the content and verify its size + String diagram = getContentText(conn); + int diagramLen = diagram.length(); + assertTrue(diagramLen > 1500); + assertTrue(diagramLen < 4000); + } + + /** + * Verifies that an multipage diagram renders correct given index (AsciiArt). + */ + public void testAsciiArtIndexPage() throws IOException { + final URL url = new URL(getServerUrl() + "/txt/1/" + TestUtils.SEQMULTIPAGE); + final HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + // Analyze response + // Verifies HTTP status code and the Content-Type + assertEquals("Bad HTTP status received", 200, conn.getResponseCode()); + assertEquals( + "Response content type is not TEXT PLAIN or UTF-8", + "text/plain;charset=utf-8", + conn.getContentType().toLowerCase() + ); + // Get the content and verify its size + String diagram = getContentText(conn); + int diagramLen = diagram.length(); + assertNotEquals(0, diagramLen); + // BUG/Missing Feature: plantuml renders always whole AsciiArt diagram + //assertTrue(diagramLen > ??); + //assertTrue(diagramLen < ??); + } + + /** + * Verifies that an multipage diagram renders correct even if no index is specified (AsciiArt). + */ + public void testAsciiArtIndexPageNoIndex() throws IOException { + final URL url = new URL(getServerUrl() + "/txt/" + TestUtils.SEQMULTIPAGE); + final HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + // Analyze response + // Verifies HTTP status code and the Content-Type + assertEquals("Bad HTTP status received", 200, conn.getResponseCode()); + assertEquals( + "Response content type is not TEXT PLAIN or UTF-8", + "text/plain;charset=utf-8", + conn.getContentType().toLowerCase() + ); + // Get the content and verify its size + String diagram = getContentText(conn); + int diagramLen = diagram.length(); + assertNotEquals(0, diagramLen); + // BUG/Missing Feature: plantuml renders always whole AsciiArt diagram + //assertTrue(diagramLen > ??); + //assertTrue(diagramLen < ??); + } + + /** + * Verifies that an multipage diagram renders correct given index (Map). + */ + public void testMapIndexPage() throws IOException { + final URL url = new URL(getServerUrl() + "/map/1/" + TestUtils.SEQMULTIPAGE); + final HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + // Analyze response + // Verifies HTTP status code and the Content-Type + assertEquals("Bad HTTP status received", 200, conn.getResponseCode()); + assertEquals( + "Response content type is not TEXT PLAIN or UTF-8", + "text/plain;charset=utf-8", + conn.getContentType().toLowerCase() + ); + // Get the data contained in the XML + String map = getContentText(url); + // map contains "tel:0123456789" + assertTrue( + "Response does not contain 'tel:0123456789'", + map.contains("tel:0123456789") + ); + // Verify shape: + // + // + // + assertTrue( + "Response doesn't match shape", + map.matches("^\n(\n)\n*$") + ); + } + + /** + * Verifies that an multipage diagram renders correct even if no index is specified (Map). + */ + public void testMapIndexPageNoIndex() throws IOException { + final URL url = new URL(getServerUrl() + "/map/" + TestUtils.SEQMULTIPAGE); + final HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + // Analyze response + // Verifies HTTP status code and the Content-Type + assertEquals("Bad HTTP status received", 200, conn.getResponseCode()); + assertEquals( + "Response content type is not TEXT PLAIN or UTF-8", + "text/plain;charset=utf-8", + conn.getContentType().toLowerCase() + ); + // Get the data contained in the XML + String diagram = getContentText(conn); + int diagramLen = diagram.length(); + assertEquals(0, diagramLen); + } + +} diff --git a/src/test/java/net/sourceforge/plantuml/servlet/TestOldProxy.java b/src/test/java/net/sourceforge/plantuml/servlet/TestOldProxy.java new file mode 100644 index 0000000..0fb300d --- /dev/null +++ b/src/test/java/net/sourceforge/plantuml/servlet/TestOldProxy.java @@ -0,0 +1,71 @@ +package net.sourceforge.plantuml.servlet; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; + +import net.sourceforge.plantuml.servlet.utils.WebappTestCase; + + +public class TestOldProxy extends WebappTestCase { + + /** + * Verifies the proxified reception of the default Bob and Alice diagram + */ + public void testDefaultProxy() throws IOException { + final URL url = new URL(getServerUrl() + "/proxy/" + getTestDiagramUrl()); + final HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + // Analyze response + // Verifies HTTP status code and the Content-Type + assertEquals("Bad HTTP status received", 200, conn.getResponseCode()); + assertEquals( + "Response content type is not PNG", + "image/png", + conn.getContentType().toLowerCase() + ); + // Get the image and verify its size (~2000 bytes) + byte[] inMemoryImage = getContentAsBytes(conn); + int diagramLen = inMemoryImage.length; + assertTrue(diagramLen > 2000); + assertTrue(diagramLen < 3000); + } + + public void testProxyWithFormat() throws IOException { + final URL url = new URL(getServerUrl() + "/proxy/svg/" + getTestDiagramUrl()); + final HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + // Analyze response + // Verifies HTTP status code and the Content-Type + assertEquals("Bad HTTP status received", 200, conn.getResponseCode()); + assertEquals( + "Response content type is not SVG", + "image/svg+xml", + conn.getContentType().toLowerCase() + ); + // Get the content and verify its size + String diagram = getContentText(conn); + int diagramLen = diagram.length(); + assertTrue(diagramLen > 1000); + assertTrue(diagramLen < 3000); + } + + /** + * Verifies that the HTTP header of a diagram incites the browser to cache it. + */ + public void testInvalidUrl() throws IOException { + final URL url = new URL(getServerUrl() + "/proxy/invalidURL"); + final HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + // Analyze response + // Check if status code is 400 + assertEquals( + "Bad HTTP status received", + 400, + conn.getResponseCode() + ); + // Check error message + assertTrue( + "Response is not malformed URL", + getContentText(conn.getErrorStream()).contains("URL malformed.") + ); + } + +} diff --git a/src/test/java/net/sourceforge/plantuml/servlet/TestProxy.java b/src/test/java/net/sourceforge/plantuml/servlet/TestProxy.java new file mode 100644 index 0000000..b95ff89 --- /dev/null +++ b/src/test/java/net/sourceforge/plantuml/servlet/TestProxy.java @@ -0,0 +1,115 @@ +package net.sourceforge.plantuml.servlet; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; + +import net.sourceforge.plantuml.servlet.utils.WebappTestCase; + + +public class TestProxy extends WebappTestCase { + + /** + * Verifies the proxified reception of the default Bob and Alice diagram + */ + public void testDefaultProxy() throws IOException { + final URL url = new URL(getServerUrl() + "/proxy?src=" + getTestDiagramUrl()); + final HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + // Analyze response + // Verifies HTTP status code and the Content-Type + assertEquals("Bad HTTP status received", 200, conn.getResponseCode()); + assertEquals( + "Response content type is not PNG", + "image/png", + conn.getContentType().toLowerCase() + ); + // Get the image and verify its size (~2000 bytes) + byte[] inMemoryImage = getContentAsBytes(conn); + int diagramLen = inMemoryImage.length; + assertTrue(diagramLen > 2000); + assertTrue(diagramLen < 3000); + } + + /** + * Verifies the proxified reception of the default Bob and Alice diagram with defined format. + */ + public void testProxyWithFormat() throws IOException { + final URL url = new URL(getServerUrl() + "/proxy?fmt=svg&src=" + getTestDiagramUrl()); + final HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + // Analyze response + // Verifies HTTP status code and the Content-Type + assertEquals("Bad HTTP status received", 200, conn.getResponseCode()); + assertEquals( + "Response content type is not SVG", + "image/svg+xml", + conn.getContentType().toLowerCase() + ); + // Get the content and verify its size + String diagram = getContentText(conn); + int diagramLen = diagram.length(); + assertTrue(diagramLen > 1500); + assertTrue(diagramLen < 3000); + } + + /** + * Verifies the proxified reception of the default Bob and Alice diagram with defined format and format (idx=0). + */ + public void testProxyWithFormatIdx0() throws IOException { + final URL url = new URL(getServerUrl() + "/proxy?fmt=svg&idx=0&src=" + getTestDiagramUrl()); + final HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + // Analyze response + // Verifies HTTP status code and the Content-Type + assertEquals("Bad HTTP status received", 200, conn.getResponseCode()); + assertEquals( + "Response content type is not SVG", + "image/svg+xml", + conn.getContentType().toLowerCase() + ); + // Get the content and verify its size + String diagram = getContentText(conn); + int diagramLen = diagram.length(); + assertTrue(diagramLen > 1500); + assertTrue(diagramLen < 3000); + } + + /** + * Verifies the proxified reception of the default Bob and Alice diagram with defined format and format (idx=1). + */ + public void testProxyWithFormatIdx1() throws IOException { + final URL url = new URL(getServerUrl() + "/proxy?fmt=svg&idx=1&src=" + getTestDiagramUrl()); + final HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + // Analyze response + // Verifies HTTP status code and the Content-Type + assertEquals("Bad HTTP status received", 200, conn.getResponseCode()); + assertEquals( + "Response content type is not SVG", + "image/svg+xml", + conn.getContentType().toLowerCase() + ); + // Get the content and verify its size + String diagram = getContentText(conn); + int diagramLen = diagram.length(); + assertTrue(diagramLen > 5000); + assertTrue(diagramLen < 6000); + } + + /** + * Verifies that the HTTP header of a diagram incites the browser to cache it. + */ + public void testInvalidUrl() throws IOException { + final URL url = new URL(getServerUrl() + "/proxy?src=invalidURL"); + final HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + // Analyze response, it must be HTTP error 400 + assertEquals( + "Bad HTTP status received", + 400, + conn.getResponseCode() + ); + // Check error message + assertTrue( + "Response is not malformed URL", + getContentText(conn.getErrorStream()).contains("URL malformed.") + ); + } + +} diff --git a/src/test/java/net/sourceforge/plantuml/servlet/server/EmbeddedJettyServer.java b/src/test/java/net/sourceforge/plantuml/servlet/server/EmbeddedJettyServer.java index 19f8f8e..5ffb760 100644 --- a/src/test/java/net/sourceforge/plantuml/servlet/server/EmbeddedJettyServer.java +++ b/src/test/java/net/sourceforge/plantuml/servlet/server/EmbeddedJettyServer.java @@ -17,6 +17,7 @@ public class EmbeddedJettyServer implements ServerUtils { private Server server; public EmbeddedJettyServer() { + String[] virtualHosts = new String[]{"localhost", "test.localhost"}; server = new Server(); ServerConnector connector = new ServerConnector(server); @@ -30,6 +31,7 @@ public class EmbeddedJettyServer implements ServerUtils { // PlantUML server web application WebAppContext context = new WebAppContext(server, "src/main/webapp", EmbeddedJettyServer.contextPath); + context.addVirtualHosts(virtualHosts); // Add static webjars resource files // The maven-dependency-plugin in the pom.xml provides these files. @@ -38,6 +40,7 @@ public class EmbeddedJettyServer implements ServerUtils { "target/classes/META-INF/resources/webjars", EmbeddedJettyServer.contextPath + "/webjars" ); + res.addVirtualHosts(virtualHosts); // Create server handler HandlerList handlers = new HandlerList(); diff --git a/src/test/java/net/sourceforge/plantuml/servlet/utils/TestUtils.java b/src/test/java/net/sourceforge/plantuml/servlet/utils/TestUtils.java index 0fa9bb1..0aa9893 100644 --- a/src/test/java/net/sourceforge/plantuml/servlet/utils/TestUtils.java +++ b/src/test/java/net/sourceforge/plantuml/servlet/utils/TestUtils.java @@ -30,4 +30,18 @@ public abstract class TestUtils { */ public static final String SEQBOBCODE = "@startuml\nBob -> Alice : hello\n@enduml"; + /** + * Encoded/compressed diagram source to text multipage uml diagrams. + * + * Bob -> Alice : hello + * newpage + * Bob <- Alice : hello + * Bob -> Alice : let's talk [[tel:0123456789]] + * Bob <- Alice : better not + * Bob -> Alice : <&rain> bye + * newpage + * Bob <- Alice : bye + */ + public static final String SEQMULTIPAGE = "SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vyfBBIz8J4y5IzheeagYwyX9BL4lLYX9pCbMY8ukISsnCZ0qCZOnDJEti8oDHJSXARMa9BL88I-_1DqO6xMYnCmyEuMaobGSreEb75BpKe3E1W00"; + } diff --git a/src/test/java/net/sourceforge/plantuml/servlet/utils/WebappTestCase.java b/src/test/java/net/sourceforge/plantuml/servlet/utils/WebappTestCase.java index 84f1c2b..b11f706 100644 --- a/src/test/java/net/sourceforge/plantuml/servlet/utils/WebappTestCase.java +++ b/src/test/java/net/sourceforge/plantuml/servlet/utils/WebappTestCase.java @@ -48,6 +48,12 @@ public abstract class WebappTestCase extends TestCase { return serverUtils.getServerUrl(); } + public String getTestDiagramUrl() { + // NOTE: [Old]ProxyServlet.forbiddenURL do not allow URL with IP-Addresses or localhost. + String serverUrl = getServerUrl().replace("/localhost", "/test.localhost"); + return serverUrl + "/resource/test2diagrams.txt"; + } + public String getContentText(final URL url) throws IOException { try (final InputStream responseStream = url.openStream()) { return getContentText(responseStream);