/** Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved. Contact: SYSTAP, LLC DBA Blazegraph 2501 Calvert ST NW #106 Washington, DC 20008 licenses@blazegraph.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Created on Mar 8, 2011 */ package com.bigdata.util.httpd; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.net.HttpURLConnection; import java.net.URL; import java.util.LinkedHashMap; import java.util.Map; import java.util.Random; import java.util.Vector; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import junit.framework.TestCase2; import com.bigdata.util.Bytes; import com.bigdata.util.config.NicUtil; /** * Test suite for {@link NanoHTTPD}. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ */ public class TestNanoHTTPD extends TestCase2 { /** * */ public TestNanoHTTPD() { } /** * @param name */ public TestNanoHTTPD(String name) { super(name); } public void test_startShutdownNow() throws IOException { final NanoHTTPD fixture = new NanoHTTPD(0/* port */); try { assertNotSame("port", 0, fixture.getPort()); assertTrue("open", fixture.isOpen()); } finally { fixture.shutdownNow(); } assertFalse("open", fixture.isOpen()); } public void test_startShutdown() throws IOException, InterruptedException, ExecutionException, TimeoutException { final NanoHTTPD fixture = new NanoHTTPD(0/* port */); try { final ExecutorService executor = Executors .newSingleThreadScheduledExecutor(); try { assertNotSame("port", 0, fixture.getPort()); assertTrue("open", fixture.isOpen()); final FutureTask<Void> ft = new FutureTask<Void>( new Runnable() { public void run() { fixture.shutdown(); } }, null/* Void */); executor.submit(ft); // wait up to a timeout for the service to terminate. ft.get(1000L, TimeUnit.MILLISECONDS); assertFalse("open", fixture.isOpen()); } finally { executor.shutdownNow(); } } finally { fixture.shutdownNow(); } assertFalse("open", fixture.isOpen()); } /* * GET */ public void test_get() throws IOException { final NanoHTTPD fixture = new NanoHTTPD(0/* port */) { @Override public Response serve(final Request req) { if (GET.equalsIgnoreCase(req.method)) { // One signal for GET . return new Response(HTTP_OK, MIME_TEXT_PLAIN, ""); } // Another for any other method or resource. return new Response(HTTP_INTERNALERROR, MIME_TEXT_PLAIN, req.method); } }; try { // Note: Use ZERO (0) for an infinite timeout to debug. // final int timeout = 0;// timeout (ms). final int timeout = 1000;// timeout (ms). final int port = fixture.getPort(); final String hostAddr = NicUtil.getIpAddress("default.nic", "default", true/* loopbackOk */); final URL url = new URL("http", hostAddr, port, ""/* file */); if (log.isInfoEnabled()) log.info("url: " + url); HttpURLConnection conn = null; try { conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setDoOutput(true); conn.setUseCaches(false); conn.setReadTimeout(timeout); // conn.setRequestProperty("Accept", MIME_RDF_XML); // connect. conn.connect(); final int rc = conn.getResponseCode(); if (rc < 200 || rc >= 300) { throw new IOException(rc + " : " + conn.getResponseMessage() + " : " + url); } // if (log.isInfoEnabled()) // log.info("Status Line: " + conn.getResponseMessage()); /* * Write out the response body */ { final LineNumberReader r = new LineNumberReader( new InputStreamReader( conn.getInputStream(), conn.getContentEncoding() == null ? "ISO-8859-1" : conn.getContentEncoding())); try { String s; while ((s = r.readLine()) != null) { if (log.isInfoEnabled()) log.info(s); } } finally { r.close(); } } // final long elapsed = System.nanoTime() - begin; } finally { // clean up the connection resources if (conn != null) conn.disconnect(); } } finally { fixture.shutdownNow(); } } /** * Unit test for URL query parameter decode for GET. */ public void test_getDecodeParams() throws IOException { final LinkedHashMap<String, Vector<String>> expected = new LinkedHashMap<String, Vector<String>>(); { { final Vector<String> v = new Vector<String>(); v.add("1"); v.add("2"); v.add("3"); expected.put("a", v); } { final Vector<String> v = new Vector<String>(); v.add("a b c"); v.add("d e"); v.add("f"); expected.put("blue", v); } } final NanoHTTPD fixture = new NanoHTTPD(0/* port */) { @Override public Response serve(final Request req) { if (GET.equalsIgnoreCase(req.method)) { assertEquals("size", expected.size(), req.params.size()); for (Map.Entry<String, Vector<String>> e : expected .entrySet()) { assertEquals(e.getKey(), expected.get(e.getKey()), req.params.get(e.getKey())); } // One signal for GET . return new Response(HTTP_OK, MIME_TEXT_PLAIN, ""); } // Another for any other method or resource. return new Response(HTTP_INTERNALERROR, MIME_TEXT_PLAIN, req.method); } }; try { // Note: Use ZERO (0) for an infinite timeout to debug. // final int timeout = 0;// timeout (ms). final int timeout = 1000;// timeout (ms). final int port = fixture.getPort(); final String hostAddr = NicUtil.getIpAddress("default.nic", "default", true/* loopbackOk */); final StringBuilder queryParms = NanoHTTPD.encodeParams(expected); final URL url = new URL("http", hostAddr, port, "/?" + queryParms); if (log.isInfoEnabled()) log.info("url: " + url); HttpURLConnection conn = null; try { conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setDoOutput(true); conn.setUseCaches(false); conn.setReadTimeout(timeout); // conn.setRequestProperty("Accept", MIME_RDF_XML); // connect. conn.connect(); final int rc = conn.getResponseCode(); if (rc < 200 || rc >= 300) { throw new IOException(rc + " : " + conn.getResponseMessage() + " : " + url); } // if (log.isInfoEnabled()) // log.info("Status Line: " + conn.getResponseMessage()); /* * Write out the response body */ { final LineNumberReader r = new LineNumberReader( new InputStreamReader( conn.getInputStream(), conn.getContentEncoding() == null ? "ISO-8859-1" : conn.getContentEncoding())); try { String s; while ((s = r.readLine()) != null) { if (log.isInfoEnabled()) log.info(s); } } finally { r.close(); } } // final long elapsed = System.nanoTime() - begin; } finally { // clean up the connection resources if (conn != null) conn.disconnect(); } } finally { fixture.shutdownNow(); } } public void test_getStream() throws IOException { final byte[] data = new byte[Bytes.kilobyte32 * 8]; { final Random r = new Random(); r.nextBytes(data); } final NanoHTTPD fixture = new NanoHTTPD(0/* port */) { @Override public Response serve(final Request req) { if (GET.equalsIgnoreCase(req.method)) { // One signal for GET . return new Response(HTTP_OK, MIME_DEFAULT_BINARY, new ByteArrayInputStream(data)); } // Another for any other method or resource. return new Response(HTTP_INTERNALERROR, MIME_TEXT_PLAIN, req.method); } }; try { // Note: Use ZERO (0) for an infinite timeout to debug. // final int timeout = 0;// timeout (ms). final int timeout = 1000;// timeout (ms). final int port = fixture.getPort(); final String hostAddr = NicUtil.getIpAddress("default.nic", "default", true/* loopbackOk */); final URL url = new URL("http", hostAddr, port, ""/* file */); if (log.isInfoEnabled()) log.info("url: " + url); HttpURLConnection conn = null; try { conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setDoOutput(true); conn.setUseCaches(false); conn.setReadTimeout(timeout); // conn.setRequestProperty("Accept", MIME_RDF_XML); // connect. conn.connect(); final int rc = conn.getResponseCode(); if (rc < 200 || rc >= 300) { throw new IOException(rc + " : " + conn.getResponseMessage() + " : " + url); } // if (log.isInfoEnabled()) // log.info("Status Line: " + conn.getResponseMessage()); /* * Read the response body and compare it with the expected data. */ final ByteArrayOutputStream baos = new ByteArrayOutputStream(); { final InputStream is = conn.getInputStream(); try { int b; while ((b = is.read()) != -1) { baos.write(b); } } finally { is.close(); } baos.flush(); } assertEquals(data.length, baos.size()); assertEquals(data, baos.toByteArray()); } finally { // clean up the connection resources if (conn != null) conn.disconnect(); } } finally { fixture.shutdownNow(); } } /* * POST */ /** * Post with an empty request body. */ public void test_post_emptyBody() throws IOException { final NanoHTTPD fixture = new NanoHTTPD(0/* port */) { @Override public Response serve(final Request req) { if (POST.equalsIgnoreCase(req.method)) { // One signal for POST. return new Response(HTTP_OK, MIME_TEXT_PLAIN, ""); } // Another for any other method or resource. return new Response(HTTP_INTERNALERROR, MIME_TEXT_PLAIN, req.method); } }; try { // Note: Use ZERO (0) for an infinite timeout to debug. final int timeout = 0;// timeout (ms). // final int timeout = 1000;// timeout (ms). final int port = fixture.getPort(); final String hostAddr = NicUtil.getIpAddress("default.nic", "default", true/* loopbackOk */); final URL url = new URL("http", hostAddr, port, ""/* file */); if (log.isInfoEnabled()) log.info("url: " + url); HttpURLConnection conn = null; try { conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setDoOutput(true); conn.setUseCaches(false); conn.setReadTimeout(timeout); // conn.setRequestProperty("Accept", MIME_RDF_XML); // connect. conn.connect(); // send an empty POST body. conn.getOutputStream().close(); final int rc = conn.getResponseCode(); if (rc < 200 || rc >= 300) { throw new IOException(rc + " : " + conn.getResponseMessage() + " : " + url); } // if (log.isInfoEnabled()) // log.info("Status Line: " + conn.getResponseMessage()); /* * Read the response body */ { final LineNumberReader r = new LineNumberReader( new InputStreamReader( conn.getInputStream(), conn.getContentEncoding() == null ? "ISO-8859-1" : conn.getContentEncoding())); try { String s; while ((s = r.readLine()) != null) { if (log.isInfoEnabled()) log.info(s); } } finally { r.close(); } } // final long elapsed = System.nanoTime() - begin; } finally { // clean up the connection resources if (conn != null) conn.disconnect(); } } finally { fixture.shutdownNow(); } } /** * Post with <code>application/x-www-form-urlencoded</code> parameters in a * request body. */ public void test_post_application_x_www_form_urlencoded() throws IOException { final LinkedHashMap<String, Vector<String>> expected = new LinkedHashMap<String, Vector<String>>(); { { final Vector<String> v = new Vector<String>(); v.add("1"); v.add("2"); v.add("3"); expected.put("a", v); } { final Vector<String> v = new Vector<String>(); v.add("a b c"); v.add("d e"); v.add("f"); expected.put("blue", v); } } final NanoHTTPD fixture = new NanoHTTPD(0/* port */) { @Override public Response serve(final Request req) { if (POST.equalsIgnoreCase(req.method)) { assertEquals("size", expected.size(), req.params.size()); for (Map.Entry<String, Vector<String>> e : expected .entrySet()) { assertEquals(e.getKey(), expected.get(e.getKey()), req.params.get(e.getKey())); } // One signal for POST. return new Response(HTTP_OK, MIME_TEXT_PLAIN, ""); } // Another for any other method or resource. return new Response(HTTP_INTERNALERROR, MIME_TEXT_PLAIN, req.method); } }; try { // Note: Use ZERO (0) for an infinite timeout to debug. final int timeout = 0;// timeout (ms). // final int timeout = 1000;// timeout (ms). final int port = fixture.getPort(); final String hostAddr = NicUtil.getIpAddress("default.nic", "default", true/* loopbackOk */); final StringBuilder queryParms = NanoHTTPD.encodeParams(expected); final URL url = new URL("http", hostAddr, port, "/?" + queryParms); if (log.isInfoEnabled()) log.info("url: " + url); HttpURLConnection conn = null; try { conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setDoOutput(false); conn.setUseCaches(false); conn.setReadTimeout(timeout); // conn.setRequestProperty("Accept", MIME_RDF_XML); // connect. conn.connect(); final int rc = conn.getResponseCode(); if (rc < 200 || rc >= 300) { throw new IOException(rc + " : " + conn.getResponseMessage() + " : " + url); } // if (log.isInfoEnabled()) // log.info("Status Line: " + conn.getResponseMessage()); /* * Read the response body */ { final LineNumberReader r = new LineNumberReader( new InputStreamReader( conn.getInputStream(), conn.getContentEncoding() == null ? "ISO-8859-1" : conn.getContentEncoding())); try { String s; while ((s = r.readLine()) != null) { if (log.isInfoEnabled()) log.info(s); } } finally { r.close(); } } // final long elapsed = System.nanoTime() - begin; } finally { // clean up the connection resources if (conn != null) conn.disconnect(); } } finally { fixture.shutdownNow(); } } /** * Post with a non-empty request body using * <code>application/octet-stream</code>. The request body is written using * the raw {@link OutputStream}. */ public void test_post_application_octet_stream_usingOutputStream() throws IOException { final byte[] expected = "Hello World!".getBytes(); final NanoHTTPD fixture = new NanoHTTPD(0/* port */) { @Override public Response serve(final Request req) { if (POST.equalsIgnoreCase(req.method)) { try { final byte[] actual = req.getBinaryContent(); assertEquals(expected, actual); // One signal for POST. return new Response(HTTP_OK, MIME_TEXT_PLAIN, ""); } catch (IOException e) { throw new RuntimeException(e); } } // Another for any other method or resource. return new Response(HTTP_INTERNALERROR, MIME_TEXT_PLAIN, req.method); } }; try { // Note: Use ZERO (0) for an infinite timeout to debug. // final int timeout = 0;// timeout (ms). final int timeout = 1000;// timeout (ms). final int port = fixture.getPort(); final String hostAddr = NicUtil.getIpAddress("default.nic", "default", true/* loopbackOk */); final URL url = new URL("http", hostAddr, port, ""/* file */); if (log.isInfoEnabled()) log.info("url: " + url); HttpURLConnection conn = null; try { conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setDoOutput(true); conn.setUseCaches(false); conn.setReadTimeout(timeout); // send an POST body. { conn.addRequestProperty("Content-Type", NanoHTTPD.MIME_DEFAULT_BINARY); final OutputStream os = conn.getOutputStream(); os.write(expected); os.flush(); os.close(); } // connect. conn.connect(); final int rc = conn.getResponseCode(); if (rc < 200 || rc >= 300) { throw new IOException(rc + " : " + conn.getResponseMessage() + " : " + url); } /* * Read the response body */ { final LineNumberReader r = new LineNumberReader( new InputStreamReader( conn.getInputStream(), conn.getContentEncoding() == null ? "ISO-8859-1" : conn.getContentEncoding())); try { String s; while ((s = r.readLine()) != null) { if (log.isInfoEnabled()) log.info(s); } } finally { r.close(); } } // final long elapsed = System.nanoTime() - begin; } finally { // clean up the connection resources if (conn != null) conn.disconnect(); } } finally { fixture.shutdownNow(); } } /** * Post with a non-empty request body using * <code>application/octet-stream</code>. The request body is written using * a {@link Writer}. * * @throws IOException */ public void test_post_application_octet_stream_usingWriter() throws IOException { final String expected = "Hello World!"; final NanoHTTPD fixture = new NanoHTTPD(0/* port */) { @Override public Response serve(final Request req) { if (POST.equalsIgnoreCase(req.method)) { try { final String actual = req.getStringContent(); assertEquals(expected, actual); // One signal for POST. return new Response(HTTP_OK, MIME_TEXT_PLAIN, ""); } catch (IOException e) { throw new RuntimeException(e); } } // Another for any other method or resource. return new Response(HTTP_INTERNALERROR, MIME_TEXT_PLAIN, req.method); } }; try { // Note: Use ZERO (0) for an infinite timeout to debug. final int timeout = 0;// timeout (ms). // final int timeout = 1000;// timeout (ms). final int port = fixture.getPort(); final String hostAddr = NicUtil.getIpAddress("default.nic", "default", true/* loopbackOk */); final URL url = new URL("http", hostAddr, port, ""/* file */); if (log.isInfoEnabled()) log.info("url: " + url); HttpURLConnection conn = null; try { conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setDoOutput(true); conn.setUseCaches(false); conn.setReadTimeout(timeout); // conn.setRequestProperty("Accept", MIME_RDF_XML); // send an POST body. { conn.addRequestProperty("Content-Type", NanoHTTPD.MIME_TEXT_PLAIN); final OutputStream os = conn.getOutputStream(); final Writer w = new OutputStreamWriter(os); w.write(expected); w.flush(); w.close(); os.flush(); os.close(); } // connect. conn.connect(); final int rc = conn.getResponseCode(); if (rc < 200 || rc >= 300) { throw new IOException(rc + " : " + conn.getResponseMessage() + " : " + url); } // if (log.isInfoEnabled()) // log.info("Status Line: " + conn.getResponseMessage()); /* * Read the response body */ { final LineNumberReader r = new LineNumberReader( new InputStreamReader( conn.getInputStream(), conn.getContentEncoding() == null ? "ISO-8859-1" : conn.getContentEncoding())); try { String s; while ((s = r.readLine()) != null) { if (log.isInfoEnabled()) log.info(s); } } finally { r.close(); } } // final long elapsed = System.nanoTime() - begin; } finally { // clean up the connection resources if (conn != null) conn.disconnect(); } } finally { fixture.shutdownNow(); } } }