aboutsummaryrefslogtreecommitdiff
path: root/samples
diff options
context:
space:
mode:
authorPaul Hawke <paul.hawke@gmail.com>2013-03-08 21:43:35 -0600
committerPaul Hawke <paul.hawke@gmail.com>2013-03-08 21:43:35 -0600
commit47b3e36685d8af2b8be644fd06da4297de4aa204 (patch)
treefd6e4f58c2f184ac82ca48b14f25bc83e3809004 /samples
parent48f1d0ff6e4b0114f39901048ae1f64a679a01e8 (diff)
downloadnanohttpd-47b3e36685d8af2b8be644fd06da4297de4aa204.tar.gz
Project cleanup - broke out "samples" and "core" maven modules to reduce the production JAR file footprint and remove clutter.
Diffstat (limited to 'samples')
-rw-r--r--samples/pom.xml96
-rw-r--r--samples/src/main/java/fi/iki/elonen/FileUploadTesting.java58
-rw-r--r--samples/src/main/java/fi/iki/elonen/HelloServer.java50
-rw-r--r--samples/src/main/java/fi/iki/elonen/SimpleWebServer.java337
-rw-r--r--samples/src/test/resources/file-upload-test.htm16
5 files changed, 557 insertions, 0 deletions
diff --git a/samples/pom.xml b/samples/pom.xml
new file mode 100644
index 0000000..cd955bd
--- /dev/null
+++ b/samples/pom.xml
@@ -0,0 +1,96 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>fi.iki.elonen</groupId>
+ <artifactId>nanohttpd-samples</artifactId>
+ <version>1.0.0</version>
+ <packaging>jar</packaging>
+
+ <name>NanoHTTPd-samples</name>
+ <url>http://maven.apache.org</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>fi.iki.elonen</groupId>
+ <artifactId>nanohttpd</artifactId>
+ <version>1.0.0</version>
+ </dependency>
+ </dependencies>
+
+ <distributionManagement>
+ <repository>
+ <id>fictional-io</id>
+ <name>fictional.io</name>
+ <url>ftp://fictional.io/domains/fictional.io/public_html/maven/maven2</url>
+ <uniqueVersion>false</uniqueVersion>
+ </repository>
+ <snapshotRepository>
+ <id>fictional-io</id>
+ <name>fictional.io</name>
+ <url>ftp://fictional.io/domains/fictional.io/public_html/maven/maven2</url>
+ <uniqueVersion>false</uniqueVersion>
+ </snapshotRepository>
+ </distributionManagement>
+
+ <scm>
+ <connection>scm:git:git://github.com/psh/nanohttpd.git</connection>
+ <url>scm:git:git://github.com/psh/nanohttpd.git</url>
+ <developerConnection>scm:git:git://github.com/psh/nanohttpd.git</developerConnection>
+ <tag>nanohttpd-1.0.0</tag>
+ </scm>
+
+ <build>
+ <extensions>
+ <extension>
+ <groupId>org.jvnet.wagon-svn</groupId>
+ <artifactId>wagon-svn</artifactId>
+ <version>1.8</version>
+ </extension>
+ <extension>
+ <groupId>org.apache.maven.wagon</groupId>
+ <artifactId>wagon-ftp</artifactId>
+ <version>1.0-alpha-6</version>
+ </extension>
+ </extensions>
+
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>2.2.1</version>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-release-plugin</artifactId>
+ <version>2.4</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.9</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.3.1</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/samples/src/main/java/fi/iki/elonen/FileUploadTesting.java b/samples/src/main/java/fi/iki/elonen/FileUploadTesting.java
new file mode 100644
index 0000000..f8326e1
--- /dev/null
+++ b/samples/src/main/java/fi/iki/elonen/FileUploadTesting.java
@@ -0,0 +1,58 @@
+package fi.iki.elonen;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * @author Paul S. Hawke (paul.hawke@gmail.com)
+ * On: 2/21/13 at 7:05 AM
+ */
+public class FileUploadTesting extends NanoHTTPD {
+ public FileUploadTesting() {
+ super(8080);
+ }
+
+ public static void main(String[] args) {
+ FileUploadTesting server = new FileUploadTesting();
+
+ try {
+ server.start();
+ } catch (IOException ioe) {
+ System.err.println("Couldn't start server:\n" + ioe);
+ System.exit(-1);
+ }
+
+ System.out.println("Server started, Hit Enter to stop.\n");
+
+ try {
+ System.in.read();
+ } catch (Throwable ignored) {
+ }
+
+ server.stop();
+ System.out.println("Server stopped.\n");
+ }
+
+ @Override
+ public Response serve(String uri, Method method, Map<String, String> header, Map<String, String> parms, Map<String, String> files) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("<h3>Request:</h3>");
+ sb.append("<blockquote>");
+ sb.append("<b>URI:</b>").append(uri).append("<br>");
+ sb.append("<b>Method:</b>").append(method).append("<br>");
+ sb.append("</blockquote>");
+ sb.append("<h3>Headers:</h3>");
+ sb.append("<blockquote>");
+ sb.append(String.valueOf(header));
+ sb.append("</blockquote>");
+ sb.append("<h3>Parameters:</h3>");
+ sb.append("<blockquote>");
+ sb.append(String.valueOf(parms));
+ sb.append("</blockquote>");
+ sb.append("<h3>Files:</h3>");
+ sb.append("<blockquote>");
+ sb.append(String.valueOf(files));
+ sb.append("</blockquote>");
+ return new Response("<html><body>" + sb.toString() + "</body></html>");
+ }
+}
diff --git a/samples/src/main/java/fi/iki/elonen/HelloServer.java b/samples/src/main/java/fi/iki/elonen/HelloServer.java
new file mode 100644
index 0000000..370b88b
--- /dev/null
+++ b/samples/src/main/java/fi/iki/elonen/HelloServer.java
@@ -0,0 +1,50 @@
+package fi.iki.elonen;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * An example of subclassing NanoHTTPD to make a custom HTTP server.
+ */
+public class HelloServer extends NanoHTTPD {
+ private HelloServer() {
+ super(8080);
+ }
+
+ @Override
+ public Response serve(String uri, Method method, Map<String, String> header, Map<String, String> parms, Map<String, String> files) {
+ System.out.println(method + " '" + uri + "' ");
+
+ String msg = "<html><body><h1>Hello server</h1>\n";
+ if (parms.get("username") == null)
+ msg +=
+ "<form action='?' method='get'>\n" +
+ " <p>Your name: <input type='text' name='username'></p>\n" +
+ "</form>\n";
+ else
+ msg += "<p>Hello, " + parms.get("username") + "!</p>";
+
+ msg += "</body></html>\n";
+
+ return new NanoHTTPD.Response(msg);
+ }
+
+ public static void main(String[] args) {
+ HelloServer helloServer = new HelloServer();
+
+ try {
+ helloServer.start();
+ } catch (IOException ioe) {
+ System.err.println("Couldn't start server:\n" + ioe);
+ System.exit(-1);
+ }
+
+ System.out.println("Listening on port 8080. Hit Enter to stop.\n");
+ try {
+ System.in.read();
+ } catch (Throwable ignored) {
+ }
+
+ helloServer.stop();
+ }
+}
diff --git a/samples/src/main/java/fi/iki/elonen/SimpleWebServer.java b/samples/src/main/java/fi/iki/elonen/SimpleWebServer.java
new file mode 100644
index 0000000..8f94f80
--- /dev/null
+++ b/samples/src/main/java/fi/iki/elonen/SimpleWebServer.java
@@ -0,0 +1,337 @@
+package fi.iki.elonen;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+public class SimpleWebServer extends NanoHTTPD {
+ /**
+ * Hashtable mapping (String)FILENAME_EXTENSION -> (String)MIME_TYPE
+ */
+ private static final Map<String, String> MIME_TYPES;
+ static {
+ Map<String, String> mime = new HashMap<String, String>();
+ mime.put("css", "text/css");
+ mime.put("htm", "text/html");
+ mime.put("html", "text/html");
+ mime.put("xml", "text/xml");
+ mime.put("txt", "text/plain");
+ mime.put("asc", "text/plain");
+ mime.put("gif", "image/gif");
+ mime.put("jpg", "image/jpeg");
+ mime.put("jpeg", "image/jpeg");
+ mime.put("png", "image/png");
+ mime.put("mp3", "audio/mpeg");
+ mime.put("m3u", "audio/mpeg-url");
+ mime.put("mp4", "video/mp4");
+ mime.put("ogv", "video/ogg");
+ mime.put("flv", "video/x-flv");
+ mime.put("mov", "video/quicktime");
+ mime.put("swf", "application/x-shockwave-flash");
+ mime.put("js", "application/javascript");
+ mime.put("pdf", "application/pdf");
+ mime.put("doc", "application/msword");
+ mime.put("ogg", "application/x-ogg");
+ mime.put("zip", "application/octet-stream");
+ mime.put("exe", "application/octet-stream");
+ mime.put("class", "application/octet-stream");
+ MIME_TYPES = mime;
+ }
+
+ /**
+ * The distribution licence
+ */
+ private static final String LICENCE = "Copyright (C) 2001,2005-2011 by Jarno Elonen <elonen@iki.fi>\n"
+ + "and Copyright (C) 2010 by Konstantinos Togias <info@ktogias.gr>\n" + "\n"
+ + "Redistribution and use in source and binary forms, with or without\n"
+ + "modification, are permitted provided that the following conditions\n" + "are met:\n" + "\n"
+ + "Redistributions of source code must retain the above copyright notice,\n"
+ + "this list of conditions and the following disclaimer. Redistributions in\n"
+ + "binary form must reproduce the above copyright notice, this list of\n"
+ + "conditions and the following disclaimer in the documentation and/or other\n"
+ + "materials provided with the distribution. The name of the author may not\n"
+ + "be used to endorse or promote products derived from this software without\n"
+ + "specific prior written permission. \n"
+ + " \n" + "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
+ + "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
+ + "OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"
+ + "IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"
+ + "INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n"
+ + "NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
+ + "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
+ + "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
+ + "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
+ + "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.";
+
+ private File rootDir;
+
+ public SimpleWebServer(int port, File wwwroot) {
+ super(port);
+ this.rootDir = wwwroot;
+ }
+
+ public File getRootDir() {
+ return rootDir;
+ }
+
+ /**
+ * URL-encodes everything between "/"-characters. Encodes spaces as '%20' instead of '+'.
+ */
+ private String encodeUri(String uri) {
+ String newUri = "";
+ StringTokenizer st = new StringTokenizer(uri, "/ ", true);
+ while (st.hasMoreTokens()) {
+ String tok = st.nextToken();
+ if (tok.equals("/"))
+ newUri += "/";
+ else if (tok.equals(" "))
+ newUri += "%20";
+ else {
+ try {
+ newUri += URLEncoder.encode(tok, "UTF-8");
+ } catch (UnsupportedEncodingException ignored) {
+ }
+ }
+ }
+ return newUri;
+ }
+
+ /**
+ * Serves file from homeDir and its' subdirectories (only). Uses only URI, ignores all headers and HTTP parameters.
+ */
+ public Response serveFile(String uri, Map<String, String> header, File homeDir) {
+ Response res = null;
+
+ // Make sure we won't die of an exception later
+ if (!homeDir.isDirectory())
+ res = new Response(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "INTERNAL ERRROR: serveFile(): given homeDir is not a directory.");
+
+ if (res == null) {
+ // Remove URL arguments
+ uri = uri.trim().replace(File.separatorChar, '/');
+ if (uri.indexOf('?') >= 0)
+ uri = uri.substring(0, uri.indexOf('?'));
+
+ // Prohibit getting out of current directory
+ if (uri.startsWith("src/main") || uri.endsWith("src/main") || uri.contains("../"))
+ res = new Response(Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "FORBIDDEN: Won't serve ../ for security reasons.");
+ }
+
+ File f = new File(homeDir, uri);
+ if (res == null && !f.exists())
+ res = new Response(Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Error 404, file not found.");
+
+ // List the directory, if necessary
+ if (res == null && f.isDirectory()) {
+ // Browsers get confused without '/' after the
+ // directory, send a redirect.
+ if (!uri.endsWith("/")) {
+ uri += "/";
+ res = new Response(Response.Status.REDIRECT, NanoHTTPD.MIME_HTML, "<html><body>Redirected: <a href=\"" + uri + "\">" + uri
+ + "</a></body></html>");
+ res.addHeader("Location", uri);
+ }
+
+ if (res == null) {
+ // First try index.html and index.htm
+ if (new File(f, "index.html").exists())
+ f = new File(homeDir, uri + "/index.html");
+ else if (new File(f, "index.htm").exists())
+ f = new File(homeDir, uri + "/index.htm");
+ // No index file, list the directory if it is readable
+ else if (f.canRead()) {
+ String[] files = f.list();
+ String msg = "<html><body><h1>Directory " + uri + "</h1><br/>";
+
+ if (uri.length() > 1) {
+ String u = uri.substring(0, uri.length() - 1);
+ int slash = u.lastIndexOf('/');
+ if (slash >= 0 && slash < u.length())
+ msg += "<b><a href=\"" + uri.substring(0, slash + 1) + "\">..</a></b><br/>";
+ }
+
+ if (files != null) {
+ for (int i = 0; i < files.length; ++i) {
+ File curFile = new File(f, files[i]);
+ boolean dir = curFile.isDirectory();
+ if (dir) {
+ msg += "<b>";
+ files[i] += "/";
+ }
+
+ msg += "<a href=\"" + encodeUri(uri + files[i]) + "\">" + files[i] + "</a>";
+
+ // Show file size
+ if (curFile.isFile()) {
+ long len = curFile.length();
+ msg += " &nbsp;<font size=2>(";
+ if (len < 1024)
+ msg += len + " bytes";
+ else if (len < 1024 * 1024)
+ msg += len / 1024 + "." + (len % 1024 / 10 % 100) + " KB";
+ else
+ msg += len / (1024 * 1024) + "." + len % (1024 * 1024) / 10 % 100 + " MB";
+
+ msg += ")</font>";
+ }
+ msg += "<br/>";
+ if (dir)
+ msg += "</b>";
+ }
+ }
+ msg += "</body></html>";
+ res = new Response(msg);
+ } else {
+ res = new Response(Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "FORBIDDEN: No directory listing.");
+ }
+ }
+ }
+
+ try {
+ if (res == null) {
+ // Get MIME type from file name extension, if possible
+ String mime = null;
+ int dot = f.getCanonicalPath().lastIndexOf('.');
+ if (dot >= 0)
+ mime = MIME_TYPES.get(f.getCanonicalPath().substring(dot + 1).toLowerCase());
+ if (mime == null)
+ mime = NanoHTTPD.MIME_DEFAULT_BINARY;
+
+ // Calculate etag
+ String etag = Integer.toHexString((f.getAbsolutePath() + f.lastModified() + "" + f.length()).hashCode());
+
+ // Support (simple) skipping:
+ long startFrom = 0;
+ long endAt = -1;
+ String range = header.get("range");
+ if (range != null) {
+ if (range.startsWith("bytes=")) {
+ range = range.substring("bytes=".length());
+ int minus = range.indexOf('-');
+ try {
+ if (minus > 0) {
+ startFrom = Long.parseLong(range.substring(0, minus));
+ endAt = Long.parseLong(range.substring(minus + 1));
+ }
+ } catch (NumberFormatException ignored) {
+ }
+ }
+ }
+
+ // Change return code and add Content-Range header when skipping is requested
+ long fileLen = f.length();
+ if (range != null && startFrom >= 0) {
+ if (startFrom >= fileLen) {
+ res = new Response(Response.Status.RANGE_NOT_SATISFIABLE, NanoHTTPD.MIME_PLAINTEXT, "");
+ res.addHeader("Content-Range", "bytes 0-0/" + fileLen);
+ res.addHeader("ETag", etag);
+ } else {
+ if (endAt < 0)
+ endAt = fileLen - 1;
+ long newLen = endAt - startFrom + 1;
+ if (newLen < 0)
+ newLen = 0;
+
+ final long dataLen = newLen;
+ FileInputStream fis = new FileInputStream(f) {
+ @Override
+ public int available() throws IOException {
+ return (int) dataLen;
+ }
+ };
+ fis.skip(startFrom);
+
+ res = new Response(Response.Status.PARTIAL_CONTENT, mime, fis);
+ res.addHeader("Content-Length", "" + dataLen);
+ res.addHeader("Content-Range", "bytes " + startFrom + "-" + endAt + "/" + fileLen);
+ res.addHeader("ETag", etag);
+ }
+ } else {
+ if (etag.equals(header.get("if-none-match")))
+ res = new Response(Response.Status.NOT_MODIFIED, mime, "");
+ else {
+ res = new Response(Response.Status.OK, mime, new FileInputStream(f));
+ res.addHeader("Content-Length", "" + fileLen);
+ res.addHeader("ETag", etag);
+ }
+ }
+ }
+ } catch (IOException ioe) {
+ res = new Response(Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "FORBIDDEN: Reading file failed.");
+ }
+
+ res.addHeader("Accept-Ranges", "bytes"); // Announce that the file server accepts partial content requestes
+ return res;
+ }
+
+ @Override
+ public Response serve(String uri, Method method, Map<String, String> header, Map<String, String> parms, Map<String, String> files) {
+ System.out.println(method + " '" + uri + "' ");
+
+ Iterator<String> e = header.keySet().iterator();
+ while (e.hasNext()) {
+ String value = e.next();
+ System.out.println(" HDR: '" + value + "' = '" + header.get(value) + "'");
+ }
+ e = parms.keySet().iterator();
+ while (e.hasNext()) {
+ String value = e.next();
+ System.out.println(" PRM: '" + value + "' = '" + parms.get(value) + "'");
+ }
+ e = files.keySet().iterator();
+ while (e.hasNext()) {
+ String value = e.next();
+ System.out.println(" UPLOADED: '" + value + "' = '" + files.get(value) + "'");
+ }
+
+ return serveFile(uri, header, getRootDir());
+ }
+
+ /**
+ * Starts as a standalone file server and waits for Enter.
+ */
+ public static void main(String[] args) {
+ System.out.println("NanoHTTPD 1.25 (C) 2001,2005-2011 Jarno Elonen and (C) 2010 Konstantinos Togias\n"
+ + "(Command line options: [-p port] [-d root-dir] [--licence])\n");
+
+ // Defaults
+ int port = 8080;
+ File wwwroot = new File(".").getAbsoluteFile();
+
+ // Show licence if requested
+ for (int i = 0; i < args.length; ++i)
+ if (args[i].equalsIgnoreCase("-p"))
+ port = Integer.parseInt(args[i + 1]);
+ else if (args[i].equalsIgnoreCase("-d"))
+ wwwroot = new File(args[i + 1]).getAbsoluteFile();
+ else if (args[i].toLowerCase().endsWith("licence")) {
+ System.out.println(LICENCE + "\n");
+ break;
+ }
+
+ SimpleWebServer server = new SimpleWebServer(port, wwwroot);
+
+ try {
+ server.start();
+ } catch (IOException ioe) {
+ System.err.println("Couldn't start server:\n" + ioe);
+ System.exit(-1);
+ }
+
+ System.out.println("Now serving files in port " + port + " from \"" + wwwroot + "\"");
+ System.out.println("Hit Enter to stop.\n");
+
+ try {
+ System.in.read();
+ } catch (Throwable ignored) {
+ }
+
+ server.stop();
+ }
+}
diff --git a/samples/src/test/resources/file-upload-test.htm b/samples/src/test/resources/file-upload-test.htm
new file mode 100644
index 0000000..7d553bf
--- /dev/null
+++ b/samples/src/test/resources/file-upload-test.htm
@@ -0,0 +1,16 @@
+<html>
+ <body>
+ <p>This is a file upload test for NanoHTTPD.</p>
+ <form action="http://localhost:8080/" enctype="multipart/form-data" method="post">
+ <label for="textline">Text:</label>
+ <input type="text" id="textline" name="textline" size="30"><br>
+ <label for="datafile1">First File:</label>
+ <input type="file" id="datafile1" name="datafile1" size="40"><br>
+ <label for="datafile2">Second File:</label>
+ <input type="file" id="datafile2" name="datafile2" size="40"><br>
+ <label for="datafile2">Third File:</label>
+ <input type="file" id="datafile3" name="datafile3" size="40"><br>
+ <input type="submit">
+ </form>
+ </body>
+</html>