From 709bd63d3d1d89e4bb35a2f721d3b2d07cec892d Mon Sep 17 00:00:00 2001 From: Matthieu Brouillard Date: Wed, 8 Jul 2015 14:02:12 +0200 Subject: fixes #25 add basic CORS support --- .../main/java/fi/iki/elonen/SimpleWebServer.java | 65 ++++++++- .../java/fi/iki/elonen/AbstractTestHttpServer.java | 67 +++++++++ .../java/fi/iki/elonen/TestCorsHttpServer.java | 154 +++++++++++++++++++++ .../test/java/fi/iki/elonen/TestHttpServer.java | 25 +--- 4 files changed, 280 insertions(+), 31 deletions(-) create mode 100644 webserver/src/test/java/fi/iki/elonen/AbstractTestHttpServer.java create mode 100644 webserver/src/test/java/fi/iki/elonen/TestCorsHttpServer.java (limited to 'webserver') diff --git a/webserver/src/main/java/fi/iki/elonen/SimpleWebServer.java b/webserver/src/main/java/fi/iki/elonen/SimpleWebServer.java index cd2e9bd..e966807 100644 --- a/webserver/src/main/java/fi/iki/elonen/SimpleWebServer.java +++ b/webserver/src/main/java/fi/iki/elonen/SimpleWebServer.java @@ -137,6 +137,7 @@ public class SimpleWebServer extends NanoHTTPD { String host = null; // bind to all interfaces by default List rootDirs = new ArrayList(); boolean quiet = false; + boolean cors = false; Map options = new HashMap(); // Parse command-line, with short and long versions of the options. @@ -149,6 +150,8 @@ public class SimpleWebServer extends NanoHTTPD { quiet = true; } else if (args[i].equalsIgnoreCase("-d") || args[i].equalsIgnoreCase("--dir")) { rootDirs.add(new File(args[i + 1]).getAbsoluteFile()); + } else if (args[i].equalsIgnoreCase("--cors")) { + cors = true; } else if (args[i].equalsIgnoreCase("--licence")) { System.out.println(SimpleWebServer.LICENCE + "\n"); } else if (args[i].startsWith("-X:")) { @@ -199,7 +202,7 @@ public class SimpleWebServer extends NanoHTTPD { } } - ServerRunner.executeInstance(new SimpleWebServer(host, port, rootDirs, quiet)); + ServerRunner.executeInstance(new SimpleWebServer(host, port, rootDirs, quiet, cors)); } protected static void registerPluginForMimeType(String[] indexFiles, String mimeType, WebServerPlugin plugin, Map commandLineOptions) { @@ -223,20 +226,26 @@ public class SimpleWebServer extends NanoHTTPD { private final boolean quiet; + private final boolean cors; + protected List rootDirs; - public SimpleWebServer(String host, int port, File wwwroot, boolean quiet) { - super(host, port); - this.quiet = quiet; - this.rootDirs = new ArrayList(); - this.rootDirs.add(wwwroot); + public SimpleWebServer(String host, int port, File wwwroot, boolean quiet, boolean useCORS) { + this(host, port, Collections.singletonList(wwwroot), quiet, useCORS); + } - init(); + public SimpleWebServer(String host, int port, File wwwroot, boolean quiet) { + this(host, port, Collections.singletonList(wwwroot), quiet, false); } public SimpleWebServer(String host, int port, List wwwroots, boolean quiet) { + this(host, port, wwwroots, quiet, false); + } + + public SimpleWebServer(String host, int port, List wwwroots, boolean quiet, boolean useCORS) { super(host, port); this.quiet = quiet; + this.cors = useCORS; this.rootDirs = new ArrayList(wwwroots); init(); @@ -394,6 +403,21 @@ public class SimpleWebServer extends NanoHTTPD { } private Response respond(Map headers, IHTTPSession session, String uri) { + // First let's handle CORS OPTION query + Response r; + if (cors && Method.OPTIONS.equals(session.getMethod())) { + r = new NanoHTTPD.Response(Response.Status.OK, MIME_PLAINTEXT, null, 0); + } else { + r = defaultRespond(headers, session, uri); + } + + if (cors) { + r = addCORSHeaders(headers, r); + } + return r; + } + + private Response defaultRespond(Map headers, IHTTPSession session, String uri) { // Remove URL arguments uri = uri.trim().replace(File.separatorChar, '/'); if (uri.indexOf('?') >= 0) { @@ -596,4 +620,31 @@ public class SimpleWebServer extends NanoHTTPD { res.addHeader("Accept-Ranges", "bytes"); return res; } + + private Response addCORSHeaders(Map queryHeaders, Response resp) { + resp.addHeader("Access-Control-Allow-Origin", "*"); + resp.addHeader("Access-Control-Allow-Headers", calculateAllowHeaders(queryHeaders)); + resp.addHeader("Access-Control-Allow-Credentials", "true"); + resp.addHeader("Access-Control-Allow-Methods", ALLOWED_METHODS); + resp.addHeader("Access-Control-Max-Age", "" + MAX_AGE); + + return resp; + } + + private String calculateAllowHeaders(Map queryHeaders) { + // here we should use the given asked headers + // but NanoHttpd uses a Map whereas it is possible for requester to send + // several time the same header + // let's just use default values for this version + return System.getProperty(ACCESS_CONTROL_ALLOW_HEADER_PROPERTY_NAME, DEFAULT_ALLOWED_HEADERS); + } + + private final static String ALLOWED_METHODS = "GET, POST, PUT, DELETE, OPTIONS, HEAD"; + + private final static int MAX_AGE = 42 * 60 * 60; + + // explicitly relax visibility to package for tests purposes + final static String DEFAULT_ALLOWED_HEADERS = "origin,accept,content-type"; + + public final static String ACCESS_CONTROL_ALLOW_HEADER_PROPERTY_NAME = "AccessControlAllowHeader"; } diff --git a/webserver/src/test/java/fi/iki/elonen/AbstractTestHttpServer.java b/webserver/src/test/java/fi/iki/elonen/AbstractTestHttpServer.java new file mode 100644 index 0000000..b56c2b0 --- /dev/null +++ b/webserver/src/test/java/fi/iki/elonen/AbstractTestHttpServer.java @@ -0,0 +1,67 @@ +package fi.iki.elonen; + +/* + * #%L + * NanoHttpd-Webserver + * %% + * Copyright (C) 2012 - 2015 nanohttpd + * %% + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the nanohttpd nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import org.apache.http.HttpEntity; + +/** + * @author Matthieu Brouillard [matthieu@brouillard.fr] + */ +public class AbstractTestHttpServer { + + protected byte[] readContents(HttpEntity entity) throws IOException { + InputStream instream = entity.getContent(); + return readContents(instream); + } + + protected byte[] readContents(InputStream instream) throws IOException { + byte[] bytes; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + byte[] buffer = new byte[1024]; + int count; + while ((count = instream.read(buffer)) >= 0) { + out.write(buffer, 0, count); + } + bytes = out.toByteArray(); + } finally { + instream.close(); + } + return bytes; + } + +} diff --git a/webserver/src/test/java/fi/iki/elonen/TestCorsHttpServer.java b/webserver/src/test/java/fi/iki/elonen/TestCorsHttpServer.java new file mode 100644 index 0000000..93f4699 --- /dev/null +++ b/webserver/src/test/java/fi/iki/elonen/TestCorsHttpServer.java @@ -0,0 +1,154 @@ +package fi.iki.elonen; + +/* + * #%L + * NanoHttpd-Webserver + * %% + * Copyright (C) 2012 - 2015 nanohttpd + * %% + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the nanohttpd nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; + +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpOptions; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * @author Matthieu Brouillard [matthieu@brouillard.fr] + */ +public class TestCorsHttpServer extends AbstractTestHttpServer { + + private static PipedOutputStream stdIn; + + private static Thread serverStartThread; + + @BeforeClass + public static void setUp() throws Exception { + stdIn = new PipedOutputStream(); + System.setIn(new PipedInputStream(stdIn)); + serverStartThread = new Thread(new Runnable() { + + @Override + public void run() { + String[] args = { + "--host", + "localhost", + "--port", + "9090", + "--dir", + "src/test/resources", + "--cors" + }; + SimpleWebServer.main(args); + } + }); + serverStartThread.start(); + // give the server some tine to start. + Thread.sleep(100); + } + + @AfterClass + public static void tearDown() throws Exception { + stdIn.write("\n\n".getBytes()); + serverStartThread.join(2000); + Assert.assertFalse(serverStartThread.isAlive()); + } + + @Test + public void doTestOption() throws Exception { + CloseableHttpClient httpclient = HttpClients.createDefault(); + HttpOptions httpOption = new HttpOptions("http://localhost:9090/xxx/yyy.html"); + CloseableHttpResponse response = httpclient.execute(httpOption); + Assert.assertEquals(200, response.getStatusLine().getStatusCode()); + Assert.assertNotNull("Cors should have added a header: Access-Control-Allow-Origin", response.getLastHeader("Access-Control-Allow-Origin")); + Assert.assertEquals("Cors should have added a header: Access-Control-Allow-Origin: *", "*", response.getLastHeader("Access-Control-Allow-Origin").getValue()); + response.close(); + } + + @Test + public void doSomeBasicTest() throws Exception { + CloseableHttpClient httpclient = HttpClients.createDefault(); + HttpGet httpget = new HttpGet("http://localhost:9090/testdir/test.html"); + CloseableHttpResponse response = httpclient.execute(httpget); + HttpEntity entity = response.getEntity(); + String string = new String(readContents(entity), "UTF-8"); + + Assert.assertNotNull("Cors should have added a header: Access-Control-Allow-Origin", response.getLastHeader("Access-Control-Allow-Origin")); + Assert.assertEquals("Cors should have added a header: Access-Control-Allow-Origin: *", "*", response.getLastHeader("Access-Control-Allow-Origin").getValue()); + Assert.assertEquals("\n\ndummy\n\n\n\t

it works

\n\n", string); + response.close(); + } + + @Test + public void testAccessControlAllowHeaderUsesDefaultsWithoutSystemProperty() throws Exception { + Assert.assertNull("no System " + SimpleWebServer.ACCESS_CONTROL_ALLOW_HEADER_PROPERTY_NAME + " shoudl be set", + System.getProperty(SimpleWebServer.ACCESS_CONTROL_ALLOW_HEADER_PROPERTY_NAME)); + + CloseableHttpClient httpclient = HttpClients.createDefault(); + HttpOptions httpOption = new HttpOptions("http://localhost:9090/xxx/yyy.html"); + CloseableHttpResponse response = httpclient.execute(httpOption); + Assert.assertEquals(200, response.getStatusLine().getStatusCode()); + Assert.assertEquals("Cors should have added a header: Access-Control-Allow-Headers: " + SimpleWebServer.DEFAULT_ALLOWED_HEADERS, + SimpleWebServer.DEFAULT_ALLOWED_HEADERS, response.getLastHeader("Access-Control-Allow-Headers").getValue()); + response.close(); + } + + @Test + public void testAccessControlAllowHeaderUsesSystemPropertyWhenSet() throws Exception { + Assert.assertNull("no System " + SimpleWebServer.ACCESS_CONTROL_ALLOW_HEADER_PROPERTY_NAME + " shoudl be set", + System.getProperty(SimpleWebServer.ACCESS_CONTROL_ALLOW_HEADER_PROPERTY_NAME)); + + final String expectedValue = "origin"; + System.setProperty(SimpleWebServer.ACCESS_CONTROL_ALLOW_HEADER_PROPERTY_NAME, expectedValue); + + try { + CloseableHttpClient httpclient = HttpClients.createDefault(); + HttpOptions httpOption = new HttpOptions("http://localhost:9090/xxx/yyy.html"); + CloseableHttpResponse response = httpclient.execute(httpOption); + Assert.assertEquals(200, response.getStatusLine().getStatusCode()); + Assert.assertEquals("Cors should have added a header: Access-Control-Allow-Headers: " + expectedValue, expectedValue, + response.getLastHeader("Access-Control-Allow-Headers").getValue()); + response.close(); + } finally { + System.clearProperty(SimpleWebServer.ACCESS_CONTROL_ALLOW_HEADER_PROPERTY_NAME); + } + } +} diff --git a/webserver/src/test/java/fi/iki/elonen/TestHttpServer.java b/webserver/src/test/java/fi/iki/elonen/TestHttpServer.java index aa08f49..bb16741 100644 --- a/webserver/src/test/java/fi/iki/elonen/TestHttpServer.java +++ b/webserver/src/test/java/fi/iki/elonen/TestHttpServer.java @@ -50,7 +50,7 @@ import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -public class TestHttpServer { +public class TestHttpServer extends AbstractTestHttpServer { private static PipedOutputStream stdIn; @@ -146,27 +146,4 @@ public class TestHttpServer { response.close(); } - - private byte[] readContents(HttpEntity entity) throws IOException { - InputStream instream = entity.getContent(); - return readContents(instream); - } - - private byte[] readContents(InputStream instream) throws IOException { - byte[] bytes; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - - try { - byte[] buffer = new byte[1024]; - int count; - while ((count = instream.read(buffer)) >= 0) { - out.write(buffer, 0, count); - } - bytes = out.toByteArray(); - } finally { - instream.close(); - } - return bytes; - } - } -- cgit v1.2.3 From c5c77ab76ec0ca68687fb1ab500239085a4a0c6f Mon Sep 17 00:00:00 2001 From: Matthieu Brouillard Date: Mon, 20 Jul 2015 08:29:48 +0200 Subject: open visibility of SimpleWebServer#addCORSHeaders() method as requested in #207 comments --- webserver/src/main/java/fi/iki/elonen/SimpleWebServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'webserver') diff --git a/webserver/src/main/java/fi/iki/elonen/SimpleWebServer.java b/webserver/src/main/java/fi/iki/elonen/SimpleWebServer.java index e966807..2da13a1 100644 --- a/webserver/src/main/java/fi/iki/elonen/SimpleWebServer.java +++ b/webserver/src/main/java/fi/iki/elonen/SimpleWebServer.java @@ -621,7 +621,7 @@ public class SimpleWebServer extends NanoHTTPD { return res; } - private Response addCORSHeaders(Map queryHeaders, Response resp) { + protected Response addCORSHeaders(Map queryHeaders, Response resp) { resp.addHeader("Access-Control-Allow-Origin", "*"); resp.addHeader("Access-Control-Allow-Headers", calculateAllowHeaders(queryHeaders)); resp.addHeader("Access-Control-Allow-Credentials", "true"); -- cgit v1.2.3 From cbdd9d819348d2169d190f305b32d5240d1f60fc Mon Sep 17 00:00:00 2001 From: Matthieu Brouillard Date: Sun, 9 Aug 2015 12:27:05 +0200 Subject: enhance CORS support to allow to define origin --- .../main/java/fi/iki/elonen/SimpleWebServer.java | 38 +++---- .../elonen/TestCorsHttpServerWithSingleOrigin.java | 121 +++++++++++++++++++++ 2 files changed, 140 insertions(+), 19 deletions(-) create mode 100644 webserver/src/test/java/fi/iki/elonen/TestCorsHttpServerWithSingleOrigin.java (limited to 'webserver') diff --git a/webserver/src/main/java/fi/iki/elonen/SimpleWebServer.java b/webserver/src/main/java/fi/iki/elonen/SimpleWebServer.java index 2da13a1..2855b7b 100644 --- a/webserver/src/main/java/fi/iki/elonen/SimpleWebServer.java +++ b/webserver/src/main/java/fi/iki/elonen/SimpleWebServer.java @@ -32,7 +32,6 @@ package fi.iki.elonen; * OF THE POSSIBILITY OF SUCH DAMAGE. * #L% */ - import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -137,7 +136,7 @@ public class SimpleWebServer extends NanoHTTPD { String host = null; // bind to all interfaces by default List rootDirs = new ArrayList(); boolean quiet = false; - boolean cors = false; + String cors = null; Map options = new HashMap(); // Parse command-line, with short and long versions of the options. @@ -150,8 +149,12 @@ public class SimpleWebServer extends NanoHTTPD { quiet = true; } else if (args[i].equalsIgnoreCase("-d") || args[i].equalsIgnoreCase("--dir")) { rootDirs.add(new File(args[i + 1]).getAbsoluteFile()); - } else if (args[i].equalsIgnoreCase("--cors")) { - cors = true; + } else if (args[i].startsWith("--cors")) { + cors = "*"; + int equalIdx = args[i].indexOf('='); + if (equalIdx > 0) { + cors = args[i].substring(equalIdx + 1); + } } else if (args[i].equalsIgnoreCase("--licence")) { System.out.println(SimpleWebServer.LICENCE + "\n"); } else if (args[i].startsWith("-X:")) { @@ -167,7 +170,6 @@ public class SimpleWebServer extends NanoHTTPD { if (rootDirs.isEmpty()) { rootDirs.add(new File(".").getAbsoluteFile()); } - options.put("host", host); options.put("port", "" + port); options.put("quiet", String.valueOf(quiet)); @@ -182,7 +184,6 @@ public class SimpleWebServer extends NanoHTTPD { } } options.put("home", sb.toString()); - ServiceLoader serviceLoader = ServiceLoader.load(WebServerPluginInfo.class); for (WebServerPluginInfo info : serviceLoader) { String[] mimeTypes = info.getMimeTypes(); @@ -201,7 +202,6 @@ public class SimpleWebServer extends NanoHTTPD { registerPluginForMimeType(indexFiles, mime, info.getWebServerPlugin(mime), options); } } - ServerRunner.executeInstance(new SimpleWebServer(host, port, rootDirs, quiet, cors)); } @@ -226,26 +226,26 @@ public class SimpleWebServer extends NanoHTTPD { private final boolean quiet; - private final boolean cors; + private final String cors; protected List rootDirs; - public SimpleWebServer(String host, int port, File wwwroot, boolean quiet, boolean useCORS) { - this(host, port, Collections.singletonList(wwwroot), quiet, useCORS); + public SimpleWebServer(String host, int port, File wwwroot, boolean quiet, String cors) { + this(host, port, Collections.singletonList(wwwroot), quiet, cors); } public SimpleWebServer(String host, int port, File wwwroot, boolean quiet) { - this(host, port, Collections.singletonList(wwwroot), quiet, false); + this(host, port, Collections.singletonList(wwwroot), quiet, null); } public SimpleWebServer(String host, int port, List wwwroots, boolean quiet) { - this(host, port, wwwroots, quiet, false); + this(host, port, wwwroots, quiet, null); } - public SimpleWebServer(String host, int port, List wwwroots, boolean quiet, boolean useCORS) { + public SimpleWebServer(String host, int port, List wwwroots, boolean quiet, String cors) { super(host, port); this.quiet = quiet; - this.cors = useCORS; + this.cors = cors; this.rootDirs = new ArrayList(wwwroots); init(); @@ -405,14 +405,14 @@ public class SimpleWebServer extends NanoHTTPD { private Response respond(Map headers, IHTTPSession session, String uri) { // First let's handle CORS OPTION query Response r; - if (cors && Method.OPTIONS.equals(session.getMethod())) { + if (cors != null && Method.OPTIONS.equals(session.getMethod())) { r = new NanoHTTPD.Response(Response.Status.OK, MIME_PLAINTEXT, null, 0); } else { r = defaultRespond(headers, session, uri); } - if (cors) { - r = addCORSHeaders(headers, r); + if (cors != null) { + r = addCORSHeaders(headers, r, cors); } return r; } @@ -621,8 +621,8 @@ public class SimpleWebServer extends NanoHTTPD { return res; } - protected Response addCORSHeaders(Map queryHeaders, Response resp) { - resp.addHeader("Access-Control-Allow-Origin", "*"); + protected Response addCORSHeaders(Map queryHeaders, Response resp, String cors) { + resp.addHeader("Access-Control-Allow-Origin", cors); resp.addHeader("Access-Control-Allow-Headers", calculateAllowHeaders(queryHeaders)); resp.addHeader("Access-Control-Allow-Credentials", "true"); resp.addHeader("Access-Control-Allow-Methods", ALLOWED_METHODS); diff --git a/webserver/src/test/java/fi/iki/elonen/TestCorsHttpServerWithSingleOrigin.java b/webserver/src/test/java/fi/iki/elonen/TestCorsHttpServerWithSingleOrigin.java new file mode 100644 index 0000000..dbd2c4e --- /dev/null +++ b/webserver/src/test/java/fi/iki/elonen/TestCorsHttpServerWithSingleOrigin.java @@ -0,0 +1,121 @@ +package fi.iki.elonen; + +/* + * #%L + * NanoHttpd-Webserver + * %% + * Copyright (C) 2012 - 2015 nanohttpd + * %% + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the nanohttpd nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; + +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpOptions; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * @author Matthieu Brouillard [matthieu@brouillard.fr] + */ +public class TestCorsHttpServerWithSingleOrigin extends AbstractTestHttpServer { + + private static PipedOutputStream stdIn; + + private static Thread serverStartThread; + + @BeforeClass + public static void setUp() throws Exception { + stdIn = new PipedOutputStream(); + System.setIn(new PipedInputStream(stdIn)); + serverStartThread = new Thread(new Runnable() { + + @Override + public void run() { + String[] args = { + "--host", + "localhost", + "--port", + "9090", + "--dir", + "src/test/resources", + "--cors=http://localhost:9090" + }; + SimpleWebServer.main(args); + } + }); + serverStartThread.start(); + // give the server some tine to start. + Thread.sleep(100); + } + + @AfterClass + public static void tearDown() throws Exception { + stdIn.write("\n\n".getBytes()); + serverStartThread.join(2000); + Assert.assertFalse(serverStartThread.isAlive()); + } + + @Test + public void doTestOption() throws Exception { + CloseableHttpClient httpclient = HttpClients.createDefault(); + HttpOptions httpOption = new HttpOptions("http://localhost:9090/xxx/yyy.html"); + CloseableHttpResponse response = httpclient.execute(httpOption); + Assert.assertEquals(200, response.getStatusLine().getStatusCode()); + Assert.assertNotNull("Cors should have added a header: Access-Control-Allow-Origin", response.getLastHeader("Access-Control-Allow-Origin")); + Assert.assertEquals("Cors should have added a header: Access-Control-Allow-Origin: http://localhost:9090", "http://localhost:9090", + response.getLastHeader("Access-Control-Allow-Origin").getValue()); + response.close(); + } + + @Test + public void doSomeBasicTest() throws Exception { + CloseableHttpClient httpclient = HttpClients.createDefault(); + HttpGet httpget = new HttpGet("http://localhost:9090/testdir/test.html"); + CloseableHttpResponse response = httpclient.execute(httpget); + HttpEntity entity = response.getEntity(); + String string = new String(readContents(entity), "UTF-8"); + + Assert.assertNotNull("Cors should have added a header: Access-Control-Allow-Origin", response.getLastHeader("Access-Control-Allow-Origin")); + Assert.assertEquals("Cors should have added a header: Access-Control-Allow-Origin: http://localhost:9090", "http://localhost:9090", + response.getLastHeader("Access-Control-Allow-Origin").getValue()); + Assert.assertEquals("\n\ndummy\n\n\n\t

it works

\n\n", string); + response.close(); + } +} -- cgit v1.2.3