";
}
return newFixedLengthResponse(msg + "\n");
}
}
```
Compile and run the server:
mvn compile
mvn exec:java -Dexec.mainClass="com.example.App"
If it started ok, point your browser at and enjoy a web server that asks your name and replies with a greeting.
### Nanolets
Nanolets are like servlets only that they have a extremely low profile. They offer an easy to use system for a more complex server application.
This text has to be extended with an example, so for now take a look at the unit tests for the usage.
## Status
We are currently in the process of stabilizing NanoHTTPD from the many pull requests and feature requests that were integrated over the last few months. The next release will come soon, and there will not be any more "intended" major changes before the next release. If you want to use the bleeding edge version, you can clone it from Github, or get it from sonatype.org (see "Maven dependencies / Living on the edge" below).
## Project structure
NanoHTTPD project currently consist of four parts:
* `/core` – Fully functional HTTP(s) server consisting of one (1) Java file, ready to be customized/inherited for your own project.
* `/samples` – Simple examples on how to customize NanoHTTPD. See *HelloServer.java* for a killer app that greets you enthusiastically!
* `/websocket` – Websocket implementation, also in a single Java file. Depends on core.
* `/webserver` – Standalone file server. Run & enjoy. A popular use seems to be serving files out off an Android device.
* `/nanolets` – Standalone nano app server, giving a servlet like system to the implementor.
* `/fileupload` – integration of the apache common file upload library.
## Features
### Core
* Only one Java file, providing HTTP 1.1 support.
* No fixed config files, logging, authorization etc. (Implement by yourself if you need them. Errors are passed to java.util.logging, though.)
* Support for HTTPS (SSL).
* Basic support for cookies.
* Supports parameter parsing of GET and POST methods.
* Some built-in support for HEAD, POST and DELETE requests. You can easily implement/customize any HTTP method, though.
* Supports file upload. Uses memory for small uploads, temp files for large ones.
* Never caches anything.
* Does not limit bandwidth, request time or simultaneous connections by default.
* All header names are converted to lower case so they don't vary between browsers/clients.
* Persistent connections (Connection "keep-alive") support allowing multiple requests to be served over a single socket connection.
### Websocket
* Tested on Firefox, Chrome and IE.
### Webserver
* Default code serves files and shows (prints on console) all HTTP parameters and headers.
* Supports both dynamic content and file serving.
* File server supports directory listing, `index.html` and `index.htm`.
* File server supports partial content (streaming & continue download).
* File server supports ETags.
* File server does the 301 redirection trick for directories without `/`.
* File server serves also very long files without memory overhead.
* Contains a built-in list of most common MIME types.
* Runtime extension support (extensions that serve particular MIME types) - example extension that serves Markdown formatted files. Simply including an extension JAR in the webserver classpath is enough for the extension to be loaded.
* Simple [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) support via `--cors` parameter
* by default serves `Access-Control-Allow-Headers: origin,accept,content-type`
* possibility to set `Access-Control-Allow-Headers` by setting System property: `AccessControlAllowHeader`
* _example: _ `-DAccessControlAllowHeader=origin,accept,content-type,Authorization`
* possible values:
* `--cors`: activates CORS support, `Access-Control-Allow-Origin` will be set to `*`.
* `--cors=some_value`: `Access-Control-Allow-Origin` will be set to `some_value`.
**_CORS argument examples_**
* `--cors=http://appOne.company.com`
* `--cors="http://appOne.company.com, http://appTwo.company.com"`: note the double quotes so that the two URLs are considered part of a single argument.
## Maven dependencies
NanoHTTPD is a Maven based project and deployed to central. Most development environments have means to access the central repository. The coordinates to use in Maven are:
org.nanohttpdnanohttpdCURRENT_VERSION
(Replace `CURRENT_VERSION` with whatever is reported latest at .)
The coordinates for your development environment should correspond to these. When looking for an older version take care because we switched groupId from *com.nanohttpd* to *org.nanohttpd* in mid 2015.
Next it depends what you are using NanoHTTPD for, there are three main usages.
## Gradle dependencies
In gradle you can use NanoHTTPD the same way because gradle accesses the same central repository:
dependencies {
runtime(
[group: 'org.nanohttpd', name: 'nanohttpd', version: 'CURRENT_VERSION'],
)
}
(Replace `CURRENT_VERSION` with whatever is reported latest at .)
Just replace the name with the artifact id of the module you want to use and gradle will find it for you.
### Develop your own specialized HTTP service
For a specialized HTTP (HTTPS) service you can use the module with artifactId *nanohttpd*.
org.nanohttpdnanohttpdCURRENT_VERSION
Here you write your own subclass of *org.nanohttpd.NanoHTTPD* to configure and to serve the requests.
### Develop a websocket based service
For a specialized websocket service you can use the module with artifactId *nanohttpd-websocket*.
org.nanohttpdnanohttpd-websocketCURRENT_VERSION
Here you write your own subclass of *org.nanohttpd.NanoWebSocketServer* to configure and to serve the websocket requests. A small standard echo example is included as *org.nanohttpd.samples.echo.DebugWebSocketServer*. You can use it as a starting point to implement your own services.
### Develop a custom HTTP file server
For a more classic approach, perhaps to just create a HTTP server serving mostly service files from your disk, you can use the module with artifactId *nanohttpd-webserver*.
org.nanohttpdnanohttpd-webserverCURRENT_VERSION
The included class *org.nanohttpd.SimpleWebServer* is intended to be used as a starting point for your own implementation but it also can be used as is. Starting the class as is will start a HTTP server on port 8080 and publishing the current directory.
### Living on the edge
The latest Github master version can be fetched through sonatype.org:
nanohttpdorg.nanohttpdXXXXX-SNAPSHOT
...
sonatype-snapshotshttps://oss.sonatype.org/content/repositories/snapshotstrue
### generating an self signed SSL certificate
Just a hint how to generate a certificate for localhost.
keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks -storepass password -validity 360 -keysize 2048 -ext SAN=DNS:localhost,IP:127.0.0.1 -validity 9999
This will generate a keystore file named 'keystore.jks' with a self signed certificate for a host named localhost with the IP address 127.0.0.1 . Now
you can use:
server.makeSecure(NanoHTTPD.makeSSLSocketFactory("/keystore.jks", "password".toCharArray()), null);
Before you start the server to make NanoHTTPD serve HTTPS connections, when you make sure 'keystore.jks' is in your classpath.
-----
*Thank you to everyone who has reported bugs and suggested fixes.*
================================================
FILE: build.gradle
================================================
allprojects {
apply plugin: 'java'
apply plugin: 'idea'
group = 'org.nanohttpd'
version = '2.3.2-SNAPSHOT'
}
subprojects {
apply plugin: 'java'
sourceCompatibility = 1.7
targetCompatibility = 1.7
repositories {
jcenter()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
}
}
task wrapper(type: Wrapper) {
gradleVersion = "4.4.1"
}
================================================
FILE: core/.gitignore
================================================
/.settings/
/LICENSE.txt
================================================
FILE: core/build.gradle
================================================
description = 'NanoHttpd-Core'
dependencies {
testCompile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.2.5'
testCompile group: 'org.apache.httpcomponents', name: 'httpmime', version: '4.2.5'
}
task wrapper(type: Wrapper) {
gradleVersion = "4.4.1"
}
================================================
FILE: core/pom.xml
================================================
4.0.0org.nanohttpdnanohttpd-project2.3.2-SNAPSHOTnanohttpdjarNanoHttpd-Coreorg.apache.httpcomponentshttpclient4.2.5testorg.apache.httpcomponentshttpmime4.2.5test0.82
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/ClientHandler.java
================================================
package org.nanohttpd.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.logging.Level;
import org.nanohttpd.protocols.http.tempfiles.ITempFileManager;
/**
* The runnable that will be used for every new client connection.
*/
public class ClientHandler implements Runnable {
private final NanoHTTPD httpd;
private final InputStream inputStream;
private final Socket acceptSocket;
public ClientHandler(NanoHTTPD httpd, InputStream inputStream, Socket acceptSocket) {
this.httpd = httpd;
this.inputStream = inputStream;
this.acceptSocket = acceptSocket;
}
public void close() {
NanoHTTPD.safeClose(this.inputStream);
NanoHTTPD.safeClose(this.acceptSocket);
}
@Override
public void run() {
OutputStream outputStream = null;
try {
outputStream = this.acceptSocket.getOutputStream();
ITempFileManager tempFileManager = httpd.getTempFileManagerFactory().create();
HTTPSession session = new HTTPSession(httpd, tempFileManager, this.inputStream, outputStream, this.acceptSocket.getInetAddress());
while (!this.acceptSocket.isClosed()) {
session.execute();
}
} catch (Exception e) {
// When the socket is closed by the client,
// we throw our own SocketException
// to break the "keep alive" loop above. If
// the exception was anything other
// than the expected SocketException OR a
// SocketTimeoutException, print the
// stacktrace
if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage())) && !(e instanceof SocketTimeoutException)) {
NanoHTTPD.LOG.log(Level.SEVERE, "Communication with the client broken, or an bug in the handler code", e);
}
} finally {
NanoHTTPD.safeClose(outputStream);
NanoHTTPD.safeClose(this.inputStream);
NanoHTTPD.safeClose(this.acceptSocket);
httpd.asyncRunner.closed(this);
}
}
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/HTTPSession.java
================================================
package org.nanohttpd.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.regex.Matcher;
import javax.net.ssl.SSLException;
import org.nanohttpd.protocols.http.NanoHTTPD.ResponseException;
import org.nanohttpd.protocols.http.content.ContentType;
import org.nanohttpd.protocols.http.content.CookieHandler;
import org.nanohttpd.protocols.http.request.Method;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.response.Status;
import org.nanohttpd.protocols.http.tempfiles.ITempFile;
import org.nanohttpd.protocols.http.tempfiles.ITempFileManager;
public class HTTPSession implements IHTTPSession {
public static final String POST_DATA = "postData";
private static final int REQUEST_BUFFER_LEN = 512;
private static final int MEMORY_STORE_LIMIT = 1024;
public static final int BUFSIZE = 8192;
public static final int MAX_HEADER_SIZE = 1024;
private final NanoHTTPD httpd;
private final ITempFileManager tempFileManager;
private final OutputStream outputStream;
private final BufferedInputStream inputStream;
private int splitbyte;
private int rlen;
private String uri;
private Method method;
private Map> parms;
private Map headers;
private CookieHandler cookies;
private String queryParameterString;
private String remoteIp;
private String protocolVersion;
public HTTPSession(NanoHTTPD httpd, ITempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) {
this.httpd = httpd;
this.tempFileManager = tempFileManager;
this.inputStream = new BufferedInputStream(inputStream, HTTPSession.BUFSIZE);
this.outputStream = outputStream;
}
public HTTPSession(NanoHTTPD httpd, ITempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream, InetAddress inetAddress) {
this.httpd = httpd;
this.tempFileManager = tempFileManager;
this.inputStream = new BufferedInputStream(inputStream, HTTPSession.BUFSIZE);
this.outputStream = outputStream;
this.remoteIp = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "127.0.0.1" : inetAddress.getHostAddress().toString();
this.headers = new HashMap();
}
/**
* Decodes the sent headers and loads the data into Key/value pairs
*/
private void decodeHeader(BufferedReader in, Map pre, Map> parms, Map headers) throws ResponseException {
try {
// Read the request line
String inLine = in.readLine();
if (inLine == null) {
return;
}
StringTokenizer st = new StringTokenizer(inLine);
if (!st.hasMoreTokens()) {
throw new ResponseException(Status.BAD_REQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html");
}
pre.put("method", st.nextToken());
if (!st.hasMoreTokens()) {
throw new ResponseException(Status.BAD_REQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html");
}
String uri = st.nextToken();
// Decode parameters from the URI
int qmi = uri.indexOf('?');
if (qmi >= 0) {
decodeParms(uri.substring(qmi + 1), parms);
uri = NanoHTTPD.decodePercent(uri.substring(0, qmi));
} else {
uri = NanoHTTPD.decodePercent(uri);
}
// If there's another token, its protocol version,
// followed by HTTP headers.
// NOTE: this now forces header names lower case since they are
// case insensitive and vary by client.
if (st.hasMoreTokens()) {
protocolVersion = st.nextToken();
} else {
protocolVersion = "HTTP/1.1";
NanoHTTPD.LOG.log(Level.FINE, "no protocol version specified, strange. Assuming HTTP/1.1.");
}
String line = in.readLine();
while (line != null && !line.trim().isEmpty()) {
int p = line.indexOf(':');
if (p >= 0) {
headers.put(line.substring(0, p).trim().toLowerCase(Locale.US), line.substring(p + 1).trim());
}
line = in.readLine();
}
pre.put("uri", uri);
} catch (IOException ioe) {
throw new ResponseException(Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe);
}
}
/**
* Decodes the Multipart Body data and put it into Key/Value pairs.
*/
private void decodeMultipartFormData(ContentType contentType, ByteBuffer fbuf, Map> parms, Map files) throws ResponseException {
int pcount = 0;
try {
int[] boundaryIdxs = getBoundaryPositions(fbuf, contentType.getBoundary().getBytes());
if (boundaryIdxs.length < 2) {
throw new ResponseException(Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but contains less than two boundary strings.");
}
byte[] partHeaderBuff = new byte[MAX_HEADER_SIZE];
for (int boundaryIdx = 0; boundaryIdx < boundaryIdxs.length - 1; boundaryIdx++) {
fbuf.position(boundaryIdxs[boundaryIdx]);
int len = (fbuf.remaining() < MAX_HEADER_SIZE) ? fbuf.remaining() : MAX_HEADER_SIZE;
fbuf.get(partHeaderBuff, 0, len);
BufferedReader in =
new BufferedReader(new InputStreamReader(new ByteArrayInputStream(partHeaderBuff, 0, len), Charset.forName(contentType.getEncoding())), len);
int headerLines = 0;
// First line is boundary string
String mpline = in.readLine();
headerLines++;
if (mpline == null || !mpline.contains(contentType.getBoundary())) {
throw new ResponseException(Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but chunk does not start with boundary.");
}
String partName = null, fileName = null, partContentType = null;
// Parse the reset of the header lines
mpline = in.readLine();
headerLines++;
while (mpline != null && mpline.trim().length() > 0) {
Matcher matcher = NanoHTTPD.CONTENT_DISPOSITION_PATTERN.matcher(mpline);
if (matcher.matches()) {
String attributeString = matcher.group(2);
matcher = NanoHTTPD.CONTENT_DISPOSITION_ATTRIBUTE_PATTERN.matcher(attributeString);
while (matcher.find()) {
String key = matcher.group(1);
if ("name".equalsIgnoreCase(key)) {
partName = matcher.group(2);
} else if ("filename".equalsIgnoreCase(key)) {
fileName = matcher.group(2);
// add these two line to support multiple
// files uploaded using the same field Id
if (!fileName.isEmpty()) {
if (pcount > 0)
partName = partName + String.valueOf(pcount++);
else
pcount++;
}
}
}
}
matcher = NanoHTTPD.CONTENT_TYPE_PATTERN.matcher(mpline);
if (matcher.matches()) {
partContentType = matcher.group(2).trim();
}
mpline = in.readLine();
headerLines++;
}
int partHeaderLength = 0;
while (headerLines-- > 0) {
partHeaderLength = scipOverNewLine(partHeaderBuff, partHeaderLength);
}
// Read the part data
if (partHeaderLength >= len - 4) {
throw new ResponseException(Status.INTERNAL_ERROR, "Multipart header size exceeds MAX_HEADER_SIZE.");
}
int partDataStart = boundaryIdxs[boundaryIdx] + partHeaderLength;
int partDataEnd = boundaryIdxs[boundaryIdx + 1] - 4;
fbuf.position(partDataStart);
List values = parms.get(partName);
if (values == null) {
values = new ArrayList();
parms.put(partName, values);
}
if (partContentType == null) {
// Read the part into a string
byte[] data_bytes = new byte[partDataEnd - partDataStart];
fbuf.get(data_bytes);
values.add(new String(data_bytes, contentType.getEncoding()));
} else {
// Read it into a file
String path = saveTmpFile(fbuf, partDataStart, partDataEnd - partDataStart, fileName);
if (!files.containsKey(partName)) {
files.put(partName, path);
} else {
int count = 2;
while (files.containsKey(partName + count)) {
count++;
}
files.put(partName + count, path);
}
values.add(fileName);
}
}
} catch (ResponseException re) {
throw re;
} catch (Exception e) {
throw new ResponseException(Status.INTERNAL_ERROR, e.toString());
}
}
private int scipOverNewLine(byte[] partHeaderBuff, int index) {
while (partHeaderBuff[index] != '\n') {
index++;
}
return ++index;
}
/**
* Decodes parameters in percent-encoded URI-format ( e.g.
* "name=Jack%20Daniels&pass=Single%20Malt" ) and adds them to given Map.
*/
private void decodeParms(String parms, Map> p) {
if (parms == null) {
this.queryParameterString = "";
return;
}
this.queryParameterString = parms;
StringTokenizer st = new StringTokenizer(parms, "&");
while (st.hasMoreTokens()) {
String e = st.nextToken();
int sep = e.indexOf('=');
String key = null;
String value = null;
if (sep >= 0) {
key = NanoHTTPD.decodePercent(e.substring(0, sep)).trim();
value = NanoHTTPD.decodePercent(e.substring(sep + 1));
} else {
key = NanoHTTPD.decodePercent(e).trim();
value = "";
}
List values = p.get(key);
if (values == null) {
values = new ArrayList();
p.put(key, values);
}
values.add(value);
}
}
@Override
public void execute() throws IOException {
Response r = null;
try {
// Read the first 8192 bytes.
// The full header should fit in here.
// Apache's default header limit is 8KB.
// Do NOT assume that a single read will get the entire header
// at once!
byte[] buf = new byte[HTTPSession.BUFSIZE];
this.splitbyte = 0;
this.rlen = 0;
int read = -1;
this.inputStream.mark(HTTPSession.BUFSIZE);
try {
read = this.inputStream.read(buf, 0, HTTPSession.BUFSIZE);
} catch (SSLException e) {
throw e;
} catch (IOException e) {
NanoHTTPD.safeClose(this.inputStream);
NanoHTTPD.safeClose(this.outputStream);
throw new SocketException("NanoHttpd Shutdown");
}
if (read == -1) {
// socket was been closed
NanoHTTPD.safeClose(this.inputStream);
NanoHTTPD.safeClose(this.outputStream);
throw new SocketException("NanoHttpd Shutdown");
}
while (read > 0) {
this.rlen += read;
this.splitbyte = findHeaderEnd(buf, this.rlen);
if (this.splitbyte > 0) {
break;
}
read = this.inputStream.read(buf, this.rlen, HTTPSession.BUFSIZE - this.rlen);
}
if (this.splitbyte < this.rlen) {
this.inputStream.reset();
this.inputStream.skip(this.splitbyte);
}
this.parms = new HashMap>();
if (null == this.headers) {
this.headers = new HashMap();
} else {
this.headers.clear();
}
// Create a BufferedReader for parsing the header.
BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, this.rlen)));
// Decode the header into parms and header java properties
Map pre = new HashMap();
decodeHeader(hin, pre, this.parms, this.headers);
if (null != this.remoteIp) {
this.headers.put("remote-addr", this.remoteIp);
this.headers.put("http-client-ip", this.remoteIp);
}
this.method = Method.lookup(pre.get("method"));
if (this.method == null) {
throw new ResponseException(Status.BAD_REQUEST, "BAD REQUEST: Syntax error. HTTP verb " + pre.get("method") + " unhandled.");
}
this.uri = pre.get("uri");
this.cookies = new CookieHandler(this.headers);
String connection = this.headers.get("connection");
boolean keepAlive = "HTTP/1.1".equals(protocolVersion) && (connection == null || !connection.matches("(?i).*close.*"));
// Ok, now do the serve()
// TODO: long body_size = getBodySize();
// TODO: long pos_before_serve = this.inputStream.totalRead()
// (requires implementation for totalRead())
r = httpd.handle(this);
// TODO: this.inputStream.skip(body_size -
// (this.inputStream.totalRead() - pos_before_serve))
if (r == null) {
throw new ResponseException(Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: Serve() returned a null response.");
} else {
String acceptEncoding = this.headers.get("accept-encoding");
this.cookies.unloadQueue(r);
r.setRequestMethod(this.method);
if (acceptEncoding == null || !acceptEncoding.contains("gzip")) {
r.setUseGzip(false);
}
r.setKeepAlive(keepAlive);
r.send(this.outputStream);
}
if (!keepAlive || r.isCloseConnection()) {
throw new SocketException("NanoHttpd Shutdown");
}
} catch (SocketException e) {
// throw it out to close socket object (finalAccept)
throw e;
} catch (SocketTimeoutException ste) {
// treat socket timeouts the same way we treat socket exceptions
// i.e. close the stream & finalAccept object by throwing the
// exception up the call stack.
throw ste;
} catch (SSLException ssle) {
Response resp = Response.newFixedLengthResponse(Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SSL PROTOCOL FAILURE: " + ssle.getMessage());
resp.send(this.outputStream);
NanoHTTPD.safeClose(this.outputStream);
} catch (IOException ioe) {
Response resp = Response.newFixedLengthResponse(Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
resp.send(this.outputStream);
NanoHTTPD.safeClose(this.outputStream);
} catch (ResponseException re) {
Response resp = Response.newFixedLengthResponse(re.getStatus(), NanoHTTPD.MIME_PLAINTEXT, re.getMessage());
resp.send(this.outputStream);
NanoHTTPD.safeClose(this.outputStream);
} finally {
NanoHTTPD.safeClose(r);
this.tempFileManager.clear();
}
}
/**
* Find byte index separating header from body. It must be the last byte of
* the first two sequential new lines.
*/
private int findHeaderEnd(final byte[] buf, int rlen) {
int splitbyte = 0;
while (splitbyte + 1 < rlen) {
// RFC2616
if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && splitbyte + 3 < rlen && buf[splitbyte + 2] == '\r' && buf[splitbyte + 3] == '\n') {
return splitbyte + 4;
}
// tolerance
if (buf[splitbyte] == '\n' && buf[splitbyte + 1] == '\n') {
return splitbyte + 2;
}
splitbyte++;
}
return 0;
}
/**
* Find the byte positions where multipart boundaries start. This reads a
* large block at a time and uses a temporary buffer to optimize (memory
* mapped) file access.
*/
private int[] getBoundaryPositions(ByteBuffer b, byte[] boundary) {
int[] res = new int[0];
if (b.remaining() < boundary.length) {
return res;
}
int search_window_pos = 0;
byte[] search_window = new byte[4 * 1024 + boundary.length];
int first_fill = (b.remaining() < search_window.length) ? b.remaining() : search_window.length;
b.get(search_window, 0, first_fill);
int new_bytes = first_fill - boundary.length;
do {
// Search the search_window
for (int j = 0; j < new_bytes; j++) {
for (int i = 0; i < boundary.length; i++) {
if (search_window[j + i] != boundary[i])
break;
if (i == boundary.length - 1) {
// Match found, add it to results
int[] new_res = new int[res.length + 1];
System.arraycopy(res, 0, new_res, 0, res.length);
new_res[res.length] = search_window_pos + j;
res = new_res;
}
}
}
search_window_pos += new_bytes;
// Copy the end of the buffer to the start
System.arraycopy(search_window, search_window.length - boundary.length, search_window, 0, boundary.length);
// Refill search_window
new_bytes = search_window.length - boundary.length;
new_bytes = (b.remaining() < new_bytes) ? b.remaining() : new_bytes;
b.get(search_window, boundary.length, new_bytes);
} while (new_bytes > 0);
return res;
}
@Override
public CookieHandler getCookies() {
return this.cookies;
}
@Override
public final Map getHeaders() {
return this.headers;
}
@Override
public final InputStream getInputStream() {
return this.inputStream;
}
@Override
public final Method getMethod() {
return this.method;
}
/**
* @deprecated use {@link #getParameters()} instead.
*/
@Override
@Deprecated
public final Map getParms() {
Map result = new HashMap();
for (String key : this.parms.keySet()) {
result.put(key, this.parms.get(key).get(0));
}
return result;
}
@Override
public final Map> getParameters() {
return this.parms;
}
@Override
public String getQueryParameterString() {
return this.queryParameterString;
}
private RandomAccessFile getTmpBucket() {
try {
ITempFile tempFile = this.tempFileManager.createTempFile(null);
return new RandomAccessFile(tempFile.getName(), "rw");
} catch (Exception e) {
throw new Error(e); // we won't recover, so throw an error
}
}
@Override
public final String getUri() {
return this.uri;
}
/**
* Deduce body length in bytes. Either from "content-length" header or read
* bytes.
*/
public long getBodySize() {
if (this.headers.containsKey("content-length")) {
return Long.parseLong(this.headers.get("content-length"));
} else if (this.splitbyte < this.rlen) {
return this.rlen - this.splitbyte;
}
return 0;
}
@Override
public void parseBody(Map files) throws IOException, ResponseException {
RandomAccessFile randomAccessFile = null;
try {
long size = getBodySize();
ByteArrayOutputStream baos = null;
DataOutput requestDataOutput = null;
// Store the request in memory or a file, depending on size
if (size < MEMORY_STORE_LIMIT) {
baos = new ByteArrayOutputStream();
requestDataOutput = new DataOutputStream(baos);
} else {
randomAccessFile = getTmpBucket();
requestDataOutput = randomAccessFile;
}
// Read all the body and write it to request_data_output
byte[] buf = new byte[REQUEST_BUFFER_LEN];
while (this.rlen >= 0 && size > 0) {
this.rlen = this.inputStream.read(buf, 0, (int) Math.min(size, REQUEST_BUFFER_LEN));
size -= this.rlen;
if (this.rlen > 0) {
requestDataOutput.write(buf, 0, this.rlen);
}
}
ByteBuffer fbuf = null;
if (baos != null) {
fbuf = ByteBuffer.wrap(baos.toByteArray(), 0, baos.size());
} else {
fbuf = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length());
randomAccessFile.seek(0);
}
// If the method is POST, there may be parameters
// in data section, too, read it:
if (Method.POST.equals(this.method)) {
ContentType contentType = new ContentType(this.headers.get("content-type"));
if (contentType.isMultipart()) {
String boundary = contentType.getBoundary();
if (boundary == null) {
throw new ResponseException(Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html");
}
decodeMultipartFormData(contentType, fbuf, this.parms, files);
} else {
byte[] postBytes = new byte[fbuf.remaining()];
fbuf.get(postBytes);
String postLine = new String(postBytes, contentType.getEncoding()).trim();
// Handle application/x-www-form-urlencoded
if ("application/x-www-form-urlencoded".equalsIgnoreCase(contentType.getContentType())) {
decodeParms(postLine, this.parms);
} else if (postLine.length() != 0) {
// Special case for raw POST data => create a
// special files entry "postData" with raw content
// data
files.put(POST_DATA, postLine);
}
}
} else if (Method.PUT.equals(this.method)) {
files.put("content", saveTmpFile(fbuf, 0, fbuf.limit(), null));
}
} finally {
NanoHTTPD.safeClose(randomAccessFile);
}
}
/**
* Retrieves the content of a sent file and saves it to a temporary file.
* The full path to the saved file is returned.
*/
private String saveTmpFile(ByteBuffer b, int offset, int len, String filename_hint) {
String path = "";
if (len > 0) {
FileOutputStream fileOutputStream = null;
try {
ITempFile tempFile = this.tempFileManager.createTempFile(filename_hint);
ByteBuffer src = b.duplicate();
fileOutputStream = new FileOutputStream(tempFile.getName());
FileChannel dest = fileOutputStream.getChannel();
src.position(offset).limit(offset + len);
dest.write(src.slice());
path = tempFile.getName();
} catch (Exception e) { // Catch exception if any
throw new Error(e); // we won't recover, so throw an error
} finally {
NanoHTTPD.safeClose(fileOutputStream);
}
}
return path;
}
@Override
public String getRemoteIpAddress() {
return this.remoteIp;
}
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/IHTTPSession.java
================================================
package org.nanohttpd.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import org.nanohttpd.protocols.http.NanoHTTPD.ResponseException;
import org.nanohttpd.protocols.http.content.CookieHandler;
import org.nanohttpd.protocols.http.request.Method;
/**
* Handles one session, i.e. parses the HTTP request and returns the response.
*/
public interface IHTTPSession {
void execute() throws IOException;
CookieHandler getCookies();
Map getHeaders();
InputStream getInputStream();
Method getMethod();
/**
* This method will only return the first value for a given parameter. You
* will want to use getParameters if you expect multiple values for a given
* key.
*
* @deprecated use {@link #getParameters()} instead.
*/
@Deprecated
Map getParms();
Map> getParameters();
String getQueryParameterString();
/**
* @return the path part of the URL.
*/
String getUri();
/**
* Adds the files in the request body to the files map.
*
* @param files
* map to modify
*/
void parseBody(Map files) throws IOException, ResponseException;
/**
* Get the remote ip address of the requester.
*
* @return the IP address.
*/
String getRemoteIpAddress();
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/NanoHTTPD.java
================================================
package org.nanohttpd.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.net.URLDecoder;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.response.Status;
import org.nanohttpd.protocols.http.sockets.DefaultServerSocketFactory;
import org.nanohttpd.protocols.http.sockets.SecureServerSocketFactory;
import org.nanohttpd.protocols.http.tempfiles.DefaultTempFileManagerFactory;
import org.nanohttpd.protocols.http.tempfiles.ITempFileManager;
import org.nanohttpd.protocols.http.threading.DefaultAsyncRunner;
import org.nanohttpd.protocols.http.threading.IAsyncRunner;
import org.nanohttpd.util.IFactory;
import org.nanohttpd.util.IFactoryThrowing;
import org.nanohttpd.util.IHandler;
/**
* A simple, tiny, nicely embeddable HTTP server in Java
*
*
* NanoHTTPD
*
* Copyright (c) 2012-2013 by Paul S. Hawke, 2001,2005-2013 by Jarno Elonen,
* 2010 by Konstantinos Togias
*
*
*
* Features + limitations:
*
*
*
Only one Java file
*
Java 5 compatible
*
Released as open source, Modified BSD licence
*
No fixed config files, logging, authorization etc. (Implement yourself if
* you need them.)
*
Supports parameter parsing of GET and POST methods (+ rudimentary PUT
* support in 1.25)
*
Supports both dynamic content and file serving
*
Supports file upload (since version 1.2, 2010)
*
Supports partial content (streaming)
*
Supports ETags
*
Never caches anything
*
Doesn't limit bandwidth, request time or simultaneous connections
*
Default code serves files and shows all HTTP parameters and headers
*
File server supports directory listing, index.html and index.htm
*
File server supports partial content (streaming)
*
File server supports ETags
*
File server does the 301 redirection trick for directories without '/'
*
File server supports simple skipping for files (continue download)
*
File server serves also very long files without memory overhead
*
Contains a built-in list of most common MIME types
*
All header names are converted to lower case so they don't vary between
* browsers/clients
*
*
*
*
* How to use:
*
*
*
Subclass and implement serve() and embed to your own program
*
*
*
* See the separate "LICENSE.md" file for the distribution license (Modified BSD
* licence)
*/
public abstract class NanoHTTPD {
public static final String CONTENT_DISPOSITION_REGEX = "([ |\t]*Content-Disposition[ |\t]*:)(.*)";
public static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern.compile(CONTENT_DISPOSITION_REGEX, Pattern.CASE_INSENSITIVE);
public static final String CONTENT_TYPE_REGEX = "([ |\t]*content-type[ |\t]*:)(.*)";
public static final Pattern CONTENT_TYPE_PATTERN = Pattern.compile(CONTENT_TYPE_REGEX, Pattern.CASE_INSENSITIVE);
public static final String CONTENT_DISPOSITION_ATTRIBUTE_REGEX = "[ |\t]*([a-zA-Z]*)[ |\t]*=[ |\t]*['|\"]([^\"^']*)['|\"]";
public static final Pattern CONTENT_DISPOSITION_ATTRIBUTE_PATTERN = Pattern.compile(CONTENT_DISPOSITION_ATTRIBUTE_REGEX);
public static final class ResponseException extends Exception {
private static final long serialVersionUID = 6569838532917408380L;
private final Status status;
public ResponseException(Status status, String message) {
super(message);
this.status = status;
}
public ResponseException(Status status, String message, Exception e) {
super(message, e);
this.status = status;
}
public Status getStatus() {
return this.status;
}
}
/**
* Maximum time to wait on Socket.getInputStream().read() (in milliseconds)
* This is required as the Keep-Alive HTTP connections would otherwise block
* the socket reading thread forever (or as long the browser is open).
*/
public static final int SOCKET_READ_TIMEOUT = 5000;
/**
* Common MIME type for dynamic content: plain text
*/
public static final String MIME_PLAINTEXT = "text/plain";
/**
* Common MIME type for dynamic content: html
*/
public static final String MIME_HTML = "text/html";
/**
* Pseudo-Parameter to use to store the actual query string in the
* parameters map for later re-processing.
*/
private static final String QUERY_STRING_PARAMETER = "NanoHttpd.QUERY_STRING";
/**
* logger to log to.
*/
public static final Logger LOG = Logger.getLogger(NanoHTTPD.class.getName());
/**
* Hashtable mapping (String)FILENAME_EXTENSION -> (String)MIME_TYPE
*/
protected static Map MIME_TYPES;
public static Map mimeTypes() {
if (MIME_TYPES == null) {
MIME_TYPES = new HashMap();
loadMimeTypes(MIME_TYPES, "META-INF/nanohttpd/default-mimetypes.properties");
loadMimeTypes(MIME_TYPES, "META-INF/nanohttpd/mimetypes.properties");
if (MIME_TYPES.isEmpty()) {
LOG.log(Level.WARNING, "no mime types found in the classpath! please provide mimetypes.properties");
}
}
return MIME_TYPES;
}
@SuppressWarnings({
"unchecked",
"rawtypes"
})
private static void loadMimeTypes(Map result, String resourceName) {
try {
Enumeration resources = NanoHTTPD.class.getClassLoader().getResources(resourceName);
while (resources.hasMoreElements()) {
URL url = (URL) resources.nextElement();
Properties properties = new Properties();
InputStream stream = null;
try {
stream = url.openStream();
properties.load(stream);
} catch (IOException e) {
LOG.log(Level.SEVERE, "could not load mimetypes from " + url, e);
} finally {
safeClose(stream);
}
result.putAll((Map) properties);
}
} catch (IOException e) {
LOG.log(Level.INFO, "no mime types available at " + resourceName);
}
};
/**
* Creates an SSLSocketFactory for HTTPS. Pass a loaded KeyStore and an
* array of loaded KeyManagers. These objects must properly
* loaded/initialized by the caller.
*/
public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManager[] keyManagers) throws IOException {
SSLServerSocketFactory res = null;
try {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(loadedKeyStore);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(keyManagers, trustManagerFactory.getTrustManagers(), null);
res = ctx.getServerSocketFactory();
} catch (Exception e) {
throw new IOException(e.getMessage());
}
return res;
}
/**
* Creates an SSLSocketFactory for HTTPS. Pass a loaded KeyStore and a
* loaded KeyManagerFactory. These objects must properly loaded/initialized
* by the caller.
*/
public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManagerFactory loadedKeyFactory) throws IOException {
try {
return makeSSLSocketFactory(loadedKeyStore, loadedKeyFactory.getKeyManagers());
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
/**
* Creates an SSLSocketFactory for HTTPS. Pass a KeyStore resource with your
* certificate and passphrase
*/
public static SSLServerSocketFactory makeSSLSocketFactory(String keyAndTrustStoreClasspathPath, char[] passphrase) throws IOException {
try {
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream keystoreStream = NanoHTTPD.class.getResourceAsStream(keyAndTrustStoreClasspathPath);
if (keystoreStream == null) {
throw new IOException("Unable to load keystore from classpath: " + keyAndTrustStoreClasspathPath);
}
keystore.load(keystoreStream, passphrase);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keystore, passphrase);
return makeSSLSocketFactory(keystore, keyManagerFactory);
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
/**
* Get MIME type from file name extension, if possible
*
* @param uri
* the string representing a file
* @return the connected mime/type
*/
public static String getMimeTypeForFile(String uri) {
int dot = uri.lastIndexOf('.');
String mime = null;
if (dot >= 0) {
mime = mimeTypes().get(uri.substring(dot + 1).toLowerCase());
}
return mime == null ? "application/octet-stream" : mime;
}
public static final void safeClose(Object closeable) {
try {
if (closeable != null) {
if (closeable instanceof Closeable) {
((Closeable) closeable).close();
} else if (closeable instanceof Socket) {
((Socket) closeable).close();
} else if (closeable instanceof ServerSocket) {
((ServerSocket) closeable).close();
} else {
throw new IllegalArgumentException("Unknown object to close");
}
}
} catch (IOException e) {
NanoHTTPD.LOG.log(Level.SEVERE, "Could not close", e);
}
}
public final String hostname;
public final int myPort;
private volatile ServerSocket myServerSocket;
public ServerSocket getMyServerSocket() {
return myServerSocket;
}
private IFactoryThrowing serverSocketFactory = new DefaultServerSocketFactory();
private Thread myThread;
private IHandler httpHandler;
protected List> interceptors = new ArrayList>(4);
/**
* Pluggable strategy for asynchronously executing requests.
*/
protected IAsyncRunner asyncRunner;
/**
* Pluggable strategy for creating and cleaning up temporary files.
*/
private IFactory tempFileManagerFactory;
/**
* Constructs an HTTP server on given port.
*/
public NanoHTTPD(int port) {
this(null, port);
}
// -------------------------------------------------------------------------------
// //
//
// Threading Strategy.
//
// -------------------------------------------------------------------------------
// //
/**
* Constructs an HTTP server on given hostname and port.
*/
public NanoHTTPD(String hostname, int port) {
this.hostname = hostname;
this.myPort = port;
setTempFileManagerFactory(new DefaultTempFileManagerFactory());
setAsyncRunner(new DefaultAsyncRunner());
// creates a default handler that redirects to deprecated serve();
this.httpHandler = new IHandler() {
@Override
public Response handle(IHTTPSession input) {
return NanoHTTPD.this.serve(input);
}
};
}
public void setHTTPHandler(IHandler handler) {
this.httpHandler = handler;
}
public void addHTTPInterceptor(IHandler interceptor) {
interceptors.add(interceptor);
}
/**
* Forcibly closes all connections that are open.
*/
public synchronized void closeAllConnections() {
stop();
}
/**
* create a instance of the client handler, subclasses can return a subclass
* of the ClientHandler.
*
* @param finalAccept
* the socket the cleint is connected to
* @param inputStream
* the input stream
* @return the client handler
*/
protected ClientHandler createClientHandler(final Socket finalAccept, final InputStream inputStream) {
return new ClientHandler(this, inputStream, finalAccept);
}
/**
* Instantiate the server runnable, can be overwritten by subclasses to
* provide a subclass of the ServerRunnable.
*
* @param timeout
* the socet timeout to use.
* @return the server runnable.
*/
protected ServerRunnable createServerRunnable(final int timeout) {
return new ServerRunnable(this, timeout);
}
/**
* Decode parameters from a URL, handing the case where a single parameter
* name might have been supplied several times, by return lists of values.
* In general these lists will contain a single element.
*
* @param parms
* original NanoHTTPD parameters values, as passed to the
* serve() method.
* @return a map of String (parameter name) to
* List<String> (a list of the values supplied).
*/
protected static Map> decodeParameters(Map parms) {
return decodeParameters(parms.get(NanoHTTPD.QUERY_STRING_PARAMETER));
}
// -------------------------------------------------------------------------------
// //
/**
* Decode parameters from a URL, handing the case where a single parameter
* name might have been supplied several times, by return lists of values.
* In general these lists will contain a single element.
*
* @param queryString
* a query string pulled from the URL.
* @return a map of String (parameter name) to
* List<String> (a list of the values supplied).
*/
protected static Map> decodeParameters(String queryString) {
Map> parms = new HashMap>();
if (queryString != null) {
StringTokenizer st = new StringTokenizer(queryString, "&");
while (st.hasMoreTokens()) {
String e = st.nextToken();
int sep = e.indexOf('=');
String propertyName = sep >= 0 ? decodePercent(e.substring(0, sep)).trim() : decodePercent(e).trim();
if (!parms.containsKey(propertyName)) {
parms.put(propertyName, new ArrayList());
}
String propertyValue = sep >= 0 ? decodePercent(e.substring(sep + 1)) : null;
if (propertyValue != null) {
parms.get(propertyName).add(propertyValue);
}
}
}
return parms;
}
/**
* Decode percent encoded String values.
*
* @param str
* the percent encoded String
* @return expanded form of the input, for example "foo%20bar" becomes
* "foo bar"
*/
public static String decodePercent(String str) {
String decoded = null;
try {
decoded = URLDecoder.decode(str, "UTF8");
} catch (UnsupportedEncodingException ignored) {
NanoHTTPD.LOG.log(Level.WARNING, "Encoding not supported, ignored", ignored);
}
return decoded;
}
public final int getListeningPort() {
return this.myServerSocket == null ? -1 : this.myServerSocket.getLocalPort();
}
public final boolean isAlive() {
return wasStarted() && !this.myServerSocket.isClosed() && this.myThread.isAlive();
}
public IFactoryThrowing getServerSocketFactory() {
return serverSocketFactory;
}
public void setServerSocketFactory(IFactoryThrowing serverSocketFactory) {
this.serverSocketFactory = serverSocketFactory;
}
public String getHostname() {
return hostname;
}
public IFactory getTempFileManagerFactory() {
return tempFileManagerFactory;
}
/**
* Call before start() to serve over HTTPS instead of HTTP
*/
public void makeSecure(SSLServerSocketFactory sslServerSocketFactory, String[] sslProtocols) {
this.serverSocketFactory = new SecureServerSocketFactory(sslServerSocketFactory, sslProtocols);
}
/**
* This is the "master" method that delegates requests to handlers and makes
* sure there is a response to every request. You are not supposed to call
* or override this method in any circumstances. But no one will stop you if
* you do. I'm a Javadoc, not Code Police.
*
* @param session
* the incoming session
* @return a response to the incoming session
*/
public Response handle(IHTTPSession session) {
for (IHandler interceptor : interceptors) {
Response response = interceptor.handle(session);
if (response != null)
return response;
}
return httpHandler.handle(session);
}
/**
* Override this to customize the server.
*
*
* (By default, this returns a 404 "Not Found" plain text error response.)
*
* @param session
* The HTTP session
* @return HTTP response, see class Response for details
*/
@Deprecated
protected Response serve(IHTTPSession session) {
return Response.newFixedLengthResponse(Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Not Found");
}
/**
* Pluggable strategy for asynchronously executing requests.
*
* @param asyncRunner
* new strategy for handling threads.
*/
public void setAsyncRunner(IAsyncRunner asyncRunner) {
this.asyncRunner = asyncRunner;
}
/**
* Pluggable strategy for creating and cleaning up temporary files.
*
* @param tempFileManagerFactory
* new strategy for handling temp files.
*/
public void setTempFileManagerFactory(IFactory tempFileManagerFactory) {
this.tempFileManagerFactory = tempFileManagerFactory;
}
/**
* Start the server.
*
* @throws IOException
* if the socket is in use.
*/
public void start() throws IOException {
start(NanoHTTPD.SOCKET_READ_TIMEOUT);
}
/**
* Starts the server (in setDaemon(true) mode).
*/
public void start(final int timeout) throws IOException {
start(timeout, true);
}
/**
* Start the server.
*
* @param timeout
* timeout to use for socket connections.
* @param daemon
* start the thread daemon or not.
* @throws IOException
* if the socket is in use.
*/
public void start(final int timeout, boolean daemon) throws IOException {
this.myServerSocket = this.getServerSocketFactory().create();
this.myServerSocket.setReuseAddress(true);
ServerRunnable serverRunnable = createServerRunnable(timeout);
this.myThread = new Thread(serverRunnable);
this.myThread.setDaemon(daemon);
this.myThread.setName("NanoHttpd Main Listener");
this.myThread.start();
while (!serverRunnable.hasBinded() && serverRunnable.getBindException() == null) {
try {
Thread.sleep(10L);
} catch (Throwable e) {
// on android this may not be allowed, that's why we
// catch throwable the wait should be very short because we are
// just waiting for the bind of the socket
}
}
if (serverRunnable.getBindException() != null) {
throw serverRunnable.getBindException();
}
}
/**
* Stop the server.
*/
public void stop() {
try {
safeClose(this.myServerSocket);
this.asyncRunner.closeAll();
if (this.myThread != null) {
this.myThread.join();
}
} catch (Exception e) {
NanoHTTPD.LOG.log(Level.SEVERE, "Could not stop all connections", e);
}
}
public final boolean wasStarted() {
return this.myServerSocket != null && this.myThread != null;
}
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/ServerRunnable.java
================================================
package org.nanohttpd.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.logging.Level;
/**
* The runnable that will be used for the main listening thread.
*/
public class ServerRunnable implements Runnable {
private NanoHTTPD httpd;
private final int timeout;
private IOException bindException;
private boolean hasBinded = false;
public ServerRunnable(NanoHTTPD httpd, int timeout) {
this.httpd = httpd;
this.timeout = timeout;
}
@Override
public void run() {
try {
httpd.getMyServerSocket().bind(httpd.hostname != null ? new InetSocketAddress(httpd.hostname, httpd.myPort) : new InetSocketAddress(httpd.myPort));
hasBinded = true;
} catch (IOException e) {
this.bindException = e;
return;
}
do {
try {
final Socket finalAccept = httpd.getMyServerSocket().accept();
if (this.timeout > 0) {
finalAccept.setSoTimeout(this.timeout);
}
final InputStream inputStream = finalAccept.getInputStream();
httpd.asyncRunner.exec(httpd.createClientHandler(finalAccept, inputStream));
} catch (IOException e) {
NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e);
}
} while (!httpd.getMyServerSocket().isClosed());
}
public IOException getBindException() {
return bindException;
}
public boolean hasBinded() {
return hasBinded;
}
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/content/ContentType.java
================================================
package org.nanohttpd.protocols.http.content;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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.util.regex.Matcher;
import java.util.regex.Pattern;
public class ContentType {
private static final String ASCII_ENCODING = "US-ASCII";
private static final String MULTIPART_FORM_DATA_HEADER = "multipart/form-data";
private static final String CONTENT_REGEX = "[ |\t]*([^/^ ^;^,]+/[^ ^;^,]+)";
private static final Pattern MIME_PATTERN = Pattern.compile(CONTENT_REGEX, Pattern.CASE_INSENSITIVE);
private static final String CHARSET_REGEX = "[ |\t]*(charset)[ |\t]*=[ |\t]*['|\"]?([^\"^'^;^,]*)['|\"]?";
private static final Pattern CHARSET_PATTERN = Pattern.compile(CHARSET_REGEX, Pattern.CASE_INSENSITIVE);
private static final String BOUNDARY_REGEX = "[ |\t]*(boundary)[ |\t]*=[ |\t]*['|\"]?([^\"^'^;^,]*)['|\"]?";
private static final Pattern BOUNDARY_PATTERN = Pattern.compile(BOUNDARY_REGEX, Pattern.CASE_INSENSITIVE);
private final String contentTypeHeader;
private final String contentType;
private final String encoding;
private final String boundary;
public ContentType(String contentTypeHeader) {
this.contentTypeHeader = contentTypeHeader;
if (contentTypeHeader != null) {
contentType = getDetailFromContentHeader(contentTypeHeader, MIME_PATTERN, "", 1);
encoding = getDetailFromContentHeader(contentTypeHeader, CHARSET_PATTERN, null, 2);
} else {
contentType = "";
encoding = "UTF-8";
}
if (MULTIPART_FORM_DATA_HEADER.equalsIgnoreCase(contentType)) {
boundary = getDetailFromContentHeader(contentTypeHeader, BOUNDARY_PATTERN, null, 2);
} else {
boundary = null;
}
}
private String getDetailFromContentHeader(String contentTypeHeader, Pattern pattern, String defaultValue, int group) {
Matcher matcher = pattern.matcher(contentTypeHeader);
return matcher.find() ? matcher.group(group) : defaultValue;
}
public String getContentTypeHeader() {
return contentTypeHeader;
}
public String getContentType() {
return contentType;
}
public String getEncoding() {
return encoding == null ? ASCII_ENCODING : encoding;
}
public String getBoundary() {
return boundary;
}
public boolean isMultipart() {
return MULTIPART_FORM_DATA_HEADER.equalsIgnoreCase(contentType);
}
public ContentType tryUTF8() {
if (encoding == null) {
return new ContentType(this.contentTypeHeader + "; charset=UTF-8");
}
return this;
}
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/content/Cookie.java
================================================
package org.nanohttpd.protocols.http.content;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
/**
* A simple cookie representation. This is old code and is flawed in many ways.
*
* @author LordFokas
*/
public class Cookie {
public static String getHTTPTime(int days) {
Calendar calendar = Calendar.getInstance();
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
calendar.add(Calendar.DAY_OF_MONTH, days);
return dateFormat.format(calendar.getTime());
}
private final String n, v, e;
public Cookie(String name, String value) {
this(name, value, 30);
}
public Cookie(String name, String value, int numDays) {
this.n = name;
this.v = value;
this.e = getHTTPTime(numDays);
}
public Cookie(String name, String value, String expires) {
this.n = name;
this.v = value;
this.e = expires;
}
public String getHTTPHeader() {
String fmt = "%s=%s; expires=%s";
return String.format(fmt, this.n, this.v, this.e);
}
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/content/CookieHandler.java
================================================
package org.nanohttpd.protocols.http.content;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.nanohttpd.protocols.http.response.Response;
/**
* Provides rudimentary support for cookies. Doesn't support 'path', 'secure'
* nor 'httpOnly'. Feel free to improve it and/or add unsupported features. This
* is old code and it's flawed in many ways.
*
* @author LordFokas
*/
public class CookieHandler implements Iterable {
private final HashMap cookies = new HashMap();
private final ArrayList queue = new ArrayList();
public CookieHandler(Map httpHeaders) {
String raw = httpHeaders.get("cookie");
if (raw != null) {
String[] tokens = raw.split(";");
for (String token : tokens) {
String[] data = token.trim().split("=");
if (data.length == 2) {
this.cookies.put(data[0], data[1]);
}
}
}
}
/**
* Set a cookie with an expiration date from a month ago, effectively
* deleting it on the client side.
*
* @param name
* The cookie name.
*/
public void delete(String name) {
set(name, "-delete-", -30);
}
@Override
public Iterator iterator() {
return this.cookies.keySet().iterator();
}
/**
* Read a cookie from the HTTP Headers.
*
* @param name
* The cookie's name.
* @return The cookie's value if it exists, null otherwise.
*/
public String read(String name) {
return this.cookies.get(name);
}
public void set(Cookie cookie) {
this.queue.add(cookie);
}
/**
* Sets a cookie.
*
* @param name
* The cookie's name.
* @param value
* The cookie's value.
* @param expires
* How many days until the cookie expires.
*/
public void set(String name, String value, int expires) {
this.queue.add(new Cookie(name, value, Cookie.getHTTPTime(expires)));
}
/**
* Internally used by the webserver to add all queued cookies into the
* Response's HTTP Headers.
*
* @param response
* The Response object to which headers the queued cookies will
* be added.
*/
public void unloadQueue(Response response) {
for (Cookie cookie : this.queue) {
response.addCookieHeader(cookie.getHTTPHeader());
}
}
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/request/Method.java
================================================
package org.nanohttpd.protocols.http.request;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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%
*/
/**
* HTTP Request methods, with the ability to decode a String back
* to its enum value.
*/
public enum Method {
GET,
PUT,
POST,
DELETE,
HEAD,
OPTIONS,
TRACE,
CONNECT,
PATCH,
PROPFIND,
PROPPATCH,
MKCOL,
MOVE,
COPY,
LOCK,
UNLOCK,
NOTIFY,
SUBSCRIBE;
public static Method lookup(String method) {
if (method == null)
return null;
try {
return valueOf(method);
} catch (IllegalArgumentException e) {
// TODO: Log it?
return null;
}
}
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/response/ChunkedOutputStream.java
================================================
package org.nanohttpd.protocols.http.response;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* Output stream that will automatically send every write to the wrapped
* OutputStream according to chunked transfer:
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
*/
public class ChunkedOutputStream extends FilterOutputStream {
public ChunkedOutputStream(OutputStream out) {
super(out);
}
@Override
public void write(int b) throws IOException {
byte[] data = {
(byte) b
};
write(data, 0, 1);
}
@Override
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
if (len == 0)
return;
out.write(String.format("%x\r\n", len).getBytes());
out.write(b, off, len);
out.write("\r\n".getBytes());
}
public void finish() throws IOException {
out.write("0\r\n\r\n".getBytes());
}
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/response/IStatus.java
================================================
package org.nanohttpd.protocols.http.response;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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%
*/
public interface IStatus {
String getDescription();
int getRequestStatus();
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/response/Response.java
================================================
package org.nanohttpd.protocols.http.response;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TimeZone;
import java.util.logging.Level;
import java.util.zip.GZIPOutputStream;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.content.ContentType;
import org.nanohttpd.protocols.http.request.Method;
/**
* HTTP response. Return one of these from serve().
*/
public class Response implements Closeable {
/**
* HTTP status code after processing, e.g. "200 OK", Status.OK
*/
private IStatus status;
/**
* MIME type of content, e.g. "text/html"
*/
private String mimeType;
/**
* Data of the response, may be null.
*/
private InputStream data;
private long contentLength;
/**
* Headers for the HTTP response. Use addHeader() to add lines. the
* lowercase map is automatically kept up to date.
*/
@SuppressWarnings("serial")
private final Map header = new HashMap() {
public String put(String key, String value) {
lowerCaseHeader.put(key == null ? key : key.toLowerCase(), value);
return super.put(key, value);
};
};
/**
* copy of the header map with all the keys lowercase for faster searching.
*/
private final Map lowerCaseHeader = new HashMap();
/**
* The request method that spawned this response.
*/
private Method requestMethod;
/**
* Use chunkedTransfer
*/
private boolean chunkedTransfer;
private boolean keepAlive;
private List cookieHeaders;
private GzipUsage gzipUsage = GzipUsage.DEFAULT;
private static enum GzipUsage {
DEFAULT,
ALWAYS,
NEVER;
}
/**
* Creates a fixed length response if totalBytes>=0, otherwise chunked.
*/
@SuppressWarnings({
"rawtypes",
"unchecked"
})
protected Response(IStatus status, String mimeType, InputStream data, long totalBytes) {
this.status = status;
this.mimeType = mimeType;
if (data == null) {
this.data = new ByteArrayInputStream(new byte[0]);
this.contentLength = 0L;
} else {
this.data = data;
this.contentLength = totalBytes;
}
this.chunkedTransfer = this.contentLength < 0;
this.keepAlive = true;
this.cookieHeaders = new ArrayList(10);
}
@Override
public void close() throws IOException {
if (this.data != null) {
this.data.close();
}
}
/**
* Adds a cookie header to the list. Should not be called manually, this is
* an internal utility.
*/
public void addCookieHeader(String cookie) {
cookieHeaders.add(cookie);
}
/**
* Should not be called manually. This is an internally utility for JUnit
* test purposes.
*
* @return All unloaded cookie headers.
*/
public List getCookieHeaders() {
return cookieHeaders;
}
/**
* Adds given line to the header.
*/
public void addHeader(String name, String value) {
this.header.put(name, value);
}
/**
* Indicate to close the connection after the Response has been sent.
*
* @param close
* {@code true} to hint connection closing, {@code false} to let
* connection be closed by client.
*/
public void closeConnection(boolean close) {
if (close)
this.header.put("connection", "close");
else
this.header.remove("connection");
}
/**
* @return {@code true} if connection is to be closed after this Response
* has been sent.
*/
public boolean isCloseConnection() {
return "close".equals(getHeader("connection"));
}
public InputStream getData() {
return this.data;
}
public String getHeader(String name) {
return this.lowerCaseHeader.get(name.toLowerCase());
}
public String getMimeType() {
return this.mimeType;
}
public Method getRequestMethod() {
return this.requestMethod;
}
public IStatus getStatus() {
return this.status;
}
public void setKeepAlive(boolean useKeepAlive) {
this.keepAlive = useKeepAlive;
}
/**
* Sends given response to the socket.
*/
public void send(OutputStream outputStream) {
SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));
try {
if (this.status == null) {
throw new Error("sendResponse(): Status can't be null.");
}
PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream, new ContentType(this.mimeType).getEncoding())), false);
pw.append("HTTP/1.1 ").append(this.status.getDescription()).append(" \r\n");
if (this.mimeType != null) {
printHeader(pw, "Content-Type", this.mimeType);
}
if (getHeader("date") == null) {
printHeader(pw, "Date", gmtFrmt.format(new Date()));
}
for (Entry entry : this.header.entrySet()) {
printHeader(pw, entry.getKey(), entry.getValue());
}
for (String cookieHeader : this.cookieHeaders) {
printHeader(pw, "Set-Cookie", cookieHeader);
}
if (getHeader("connection") == null) {
printHeader(pw, "Connection", (this.keepAlive ? "keep-alive" : "close"));
}
if (getHeader("content-length") != null) {
setUseGzip(false);
}
if (useGzipWhenAccepted()) {
printHeader(pw, "Content-Encoding", "gzip");
setChunkedTransfer(true);
}
long pending = this.data != null ? this.contentLength : 0;
if (this.requestMethod != Method.HEAD && this.chunkedTransfer) {
printHeader(pw, "Transfer-Encoding", "chunked");
} else if (!useGzipWhenAccepted()) {
pending = sendContentLengthHeaderIfNotAlreadyPresent(pw, pending);
}
pw.append("\r\n");
pw.flush();
sendBodyWithCorrectTransferAndEncoding(outputStream, pending);
outputStream.flush();
NanoHTTPD.safeClose(this.data);
} catch (IOException ioe) {
NanoHTTPD.LOG.log(Level.SEVERE, "Could not send response to the client", ioe);
}
}
@SuppressWarnings("static-method")
protected void printHeader(PrintWriter pw, String key, String value) {
pw.append(key).append(": ").append(value).append("\r\n");
}
protected long sendContentLengthHeaderIfNotAlreadyPresent(PrintWriter pw, long defaultSize) {
String contentLengthString = getHeader("content-length");
long size = defaultSize;
if (contentLengthString != null) {
try {
size = Long.parseLong(contentLengthString);
} catch (NumberFormatException ex) {
NanoHTTPD.LOG.severe("content-length was no number " + contentLengthString);
}
}else{
pw.print("Content-Length: " + size + "\r\n");
}
return size;
}
private void sendBodyWithCorrectTransferAndEncoding(OutputStream outputStream, long pending) throws IOException {
if (this.requestMethod != Method.HEAD && this.chunkedTransfer) {
ChunkedOutputStream chunkedOutputStream = new ChunkedOutputStream(outputStream);
sendBodyWithCorrectEncoding(chunkedOutputStream, -1);
try {
chunkedOutputStream.finish();
} catch (Exception e) {
if(this.data != null) {
this.data.close();
}
}
} else {
sendBodyWithCorrectEncoding(outputStream, pending);
}
}
private void sendBodyWithCorrectEncoding(OutputStream outputStream, long pending) throws IOException {
if (useGzipWhenAccepted()) {
GZIPOutputStream gzipOutputStream = null;
try {
gzipOutputStream = new GZIPOutputStream(outputStream);
} catch (Exception e) {
if(this.data != null) {
this.data.close();
}
}
if (gzipOutputStream != null) {
sendBody(gzipOutputStream, -1);
gzipOutputStream.finish();
}
} else {
sendBody(outputStream, pending);
}
}
/**
* Sends the body to the specified OutputStream. The pending parameter
* limits the maximum amounts of bytes sent unless it is -1, in which case
* everything is sent.
*
* @param outputStream
* the OutputStream to send data to
* @param pending
* -1 to send everything, otherwise sets a max limit to the
* number of bytes sent
* @throws IOException
* if something goes wrong while sending the data.
*/
private void sendBody(OutputStream outputStream, long pending) throws IOException {
long BUFFER_SIZE = 16 * 1024;
byte[] buff = new byte[(int) BUFFER_SIZE];
boolean sendEverything = pending == -1;
while (pending > 0 || sendEverything) {
long bytesToRead = sendEverything ? BUFFER_SIZE : Math.min(pending, BUFFER_SIZE);
int read = this.data.read(buff, 0, (int) bytesToRead);
if (read <= 0) {
break;
}
try {
outputStream.write(buff, 0, read);
} catch (Exception e) {
if(this.data != null) {
this.data.close();
}
}
if (!sendEverything) {
pending -= read;
}
}
}
public void setChunkedTransfer(boolean chunkedTransfer) {
this.chunkedTransfer = chunkedTransfer;
}
public void setData(InputStream data) {
this.data = data;
}
public void setMimeType(String mimeType) {
this.mimeType = mimeType;
}
public void setRequestMethod(Method requestMethod) {
this.requestMethod = requestMethod;
}
public void setStatus(IStatus status) {
this.status = status;
}
/**
* Create a response with unknown length (using HTTP 1.1 chunking).
*/
public static Response newChunkedResponse(IStatus status, String mimeType, InputStream data) {
return new Response(status, mimeType, data, -1);
}
public static Response newFixedLengthResponse(IStatus status, String mimeType, byte[] data) {
return newFixedLengthResponse(status, mimeType, new ByteArrayInputStream(data), data.length);
}
/**
* Create a response with known length.
*/
public static Response newFixedLengthResponse(IStatus status, String mimeType, InputStream data, long totalBytes) {
return new Response(status, mimeType, data, totalBytes);
}
/**
* Create a text response with known length.
*/
public static Response newFixedLengthResponse(IStatus status, String mimeType, String txt) {
ContentType contentType = new ContentType(mimeType);
if (txt == null) {
return newFixedLengthResponse(status, mimeType, new ByteArrayInputStream(new byte[0]), 0);
} else {
byte[] bytes;
try {
CharsetEncoder newEncoder = Charset.forName(contentType.getEncoding()).newEncoder();
if (!newEncoder.canEncode(txt)) {
contentType = contentType.tryUTF8();
}
bytes = txt.getBytes(contentType.getEncoding());
} catch (UnsupportedEncodingException e) {
NanoHTTPD.LOG.log(Level.SEVERE, "encoding problem, responding nothing", e);
bytes = new byte[0];
}
return newFixedLengthResponse(status, contentType.getContentTypeHeader(), new ByteArrayInputStream(bytes), bytes.length);
}
}
/**
* Create a text response with known length.
*/
public static Response newFixedLengthResponse(String msg) {
return newFixedLengthResponse(Status.OK, NanoHTTPD.MIME_HTML, msg);
}
public Response setUseGzip(boolean useGzip) {
gzipUsage = useGzip ? GzipUsage.ALWAYS : GzipUsage.NEVER;
return this;
}
// If a Gzip usage has been enforced, use it.
// Else decide whether or not to use Gzip.
public boolean useGzipWhenAccepted() {
if (gzipUsage == GzipUsage.DEFAULT)
return getMimeType() != null && (getMimeType().toLowerCase().contains("text/") || getMimeType().toLowerCase().contains("/json"));
else
return gzipUsage == GzipUsage.ALWAYS;
}
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/response/Status.java
================================================
package org.nanohttpd.protocols.http.response;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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%
*/
/**
* Some HTTP response status codes
*/
public enum Status implements IStatus {
SWITCH_PROTOCOL(101, "Switching Protocols"),
OK(200, "OK"),
CREATED(201, "Created"),
ACCEPTED(202, "Accepted"),
NO_CONTENT(204, "No Content"),
PARTIAL_CONTENT(206, "Partial Content"),
MULTI_STATUS(207, "Multi-Status"),
REDIRECT(301, "Moved Permanently"),
/**
* Many user agents mishandle 302 in ways that violate the RFC1945 spec
* (i.e., redirect a POST to a GET). 303 and 307 were added in RFC2616 to
* address this. You should prefer 303 and 307 unless the calling user agent
* does not support 303 and 307 functionality
*/
@Deprecated
FOUND(302, "Found"),
REDIRECT_SEE_OTHER(303, "See Other"),
NOT_MODIFIED(304, "Not Modified"),
TEMPORARY_REDIRECT(307, "Temporary Redirect"),
BAD_REQUEST(400, "Bad Request"),
UNAUTHORIZED(401, "Unauthorized"),
FORBIDDEN(403, "Forbidden"),
NOT_FOUND(404, "Not Found"),
METHOD_NOT_ALLOWED(405, "Method Not Allowed"),
NOT_ACCEPTABLE(406, "Not Acceptable"),
REQUEST_TIMEOUT(408, "Request Timeout"),
CONFLICT(409, "Conflict"),
GONE(410, "Gone"),
LENGTH_REQUIRED(411, "Length Required"),
PRECONDITION_FAILED(412, "Precondition Failed"),
PAYLOAD_TOO_LARGE(413, "Payload Too Large"),
UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"),
RANGE_NOT_SATISFIABLE(416, "Requested Range Not Satisfiable"),
EXPECTATION_FAILED(417, "Expectation Failed"),
TOO_MANY_REQUESTS(429, "Too Many Requests"),
INTERNAL_ERROR(500, "Internal Server Error"),
NOT_IMPLEMENTED(501, "Not Implemented"),
SERVICE_UNAVAILABLE(503, "Service Unavailable"),
UNSUPPORTED_HTTP_VERSION(505, "HTTP Version Not Supported");
private final int requestStatus;
private final String description;
Status(int requestStatus, String description) {
this.requestStatus = requestStatus;
this.description = description;
}
public static Status lookup(int requestStatus) {
for (Status status : Status.values()) {
if (status.getRequestStatus() == requestStatus) {
return status;
}
}
return null;
}
@Override
public String getDescription() {
return "" + this.requestStatus + " " + this.description;
}
@Override
public int getRequestStatus() {
return this.requestStatus;
}
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/sockets/DefaultServerSocketFactory.java
================================================
package org.nanohttpd.protocols.http.sockets;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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.IOException;
import java.net.ServerSocket;
import org.nanohttpd.util.IFactoryThrowing;
/**
* Creates a normal ServerSocket for TCP connections
*/
public class DefaultServerSocketFactory implements IFactoryThrowing {
@Override
public ServerSocket create() throws IOException {
return new ServerSocket();
}
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/sockets/SecureServerSocketFactory.java
================================================
package org.nanohttpd.protocols.http.sockets;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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.IOException;
import java.net.ServerSocket;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import org.nanohttpd.util.IFactoryThrowing;
/**
* Creates a new SSLServerSocket
*/
public class SecureServerSocketFactory implements IFactoryThrowing {
private SSLServerSocketFactory sslServerSocketFactory;
private String[] sslProtocols;
public SecureServerSocketFactory(SSLServerSocketFactory sslServerSocketFactory, String[] sslProtocols) {
this.sslServerSocketFactory = sslServerSocketFactory;
this.sslProtocols = sslProtocols;
}
@Override
public ServerSocket create() throws IOException {
SSLServerSocket ss = null;
ss = (SSLServerSocket) this.sslServerSocketFactory.createServerSocket();
if (this.sslProtocols != null) {
ss.setEnabledProtocols(this.sslProtocols);
} else {
ss.setEnabledProtocols(ss.getSupportedProtocols());
}
ss.setUseClientMode(false);
ss.setWantClientAuth(false);
ss.setNeedClientAuth(false);
return ss;
}
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/tempfiles/DefaultTempFile.java
================================================
package org.nanohttpd.protocols.http.tempfiles;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.nanohttpd.protocols.http.NanoHTTPD;
/**
* Default strategy for creating and cleaning up temporary files.
*
*
* By default, files are created by File.createTempFile() in the
* directory specified.
*
*/
public class DefaultTempFile implements ITempFile {
private final File file;
private final OutputStream fstream;
public DefaultTempFile(File tempdir) throws IOException {
this.file = File.createTempFile("NanoHTTPD-", "", tempdir);
this.fstream = new FileOutputStream(this.file);
}
@Override
public void delete() throws Exception {
NanoHTTPD.safeClose(this.fstream);
if (!this.file.delete()) {
throw new Exception("could not delete temporary file: " + this.file.getAbsolutePath());
}
}
@Override
public String getName() {
return this.file.getAbsolutePath();
}
@Override
public OutputStream open() throws Exception {
return this.fstream;
}
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/tempfiles/DefaultTempFileManager.java
================================================
package org.nanohttpd.protocols.http.tempfiles;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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.File;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import org.nanohttpd.protocols.http.NanoHTTPD;
/**
* Default strategy for creating and cleaning up temporary files.
*
*
* This class stores its files in the standard location (that is, wherever
* java.io.tmpdir points to). Files are added to an internal list,
* and deleted when no longer needed (that is, when clear() is
* invoked at the end of processing a request).
*
*/
public class DefaultTempFileManager implements ITempFileManager {
private final File tmpdir;
private final List tempFiles;
public DefaultTempFileManager() {
this.tmpdir = new File(System.getProperty("java.io.tmpdir"));
if (!tmpdir.exists()) {
tmpdir.mkdirs();
}
this.tempFiles = new ArrayList();
}
@Override
public void clear() {
for (ITempFile file : this.tempFiles) {
try {
file.delete();
} catch (Exception ignored) {
NanoHTTPD.LOG.log(Level.WARNING, "could not delete file ", ignored);
}
}
this.tempFiles.clear();
}
@Override
public ITempFile createTempFile(String filename_hint) throws Exception {
DefaultTempFile tempFile = new DefaultTempFile(this.tmpdir);
this.tempFiles.add(tempFile);
return tempFile;
}
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/tempfiles/DefaultTempFileManagerFactory.java
================================================
package org.nanohttpd.protocols.http.tempfiles;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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 org.nanohttpd.util.IFactory;
/**
* Default strategy for creating and cleaning up temporary files.
*/
public class DefaultTempFileManagerFactory implements IFactory {
@Override
public ITempFileManager create() {
return new DefaultTempFileManager();
}
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/tempfiles/ITempFile.java
================================================
package org.nanohttpd.protocols.http.tempfiles;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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.OutputStream;
/**
* A temp file.
*
*
* Temp files are responsible for managing the actual temporary storage and
* cleaning themselves up when no longer needed.
*
*/
public interface ITempFile {
public void delete() throws Exception;
public String getName();
public OutputStream open() throws Exception;
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/tempfiles/ITempFileManager.java
================================================
package org.nanohttpd.protocols.http.tempfiles;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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%
*/
/**
* Temp file manager.
*
*
* Temp file managers are created 1-to-1 with incoming requests, to create and
* cleanup temporary files created as a result of handling the request.
*
*/
public interface ITempFileManager {
void clear();
public ITempFile createTempFile(String filename_hint) throws Exception;
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/threading/DefaultAsyncRunner.java
================================================
package org.nanohttpd.protocols.http.threading;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.nanohttpd.protocols.http.ClientHandler;
/**
* Default threading strategy for NanoHTTPD.
*
*
* By default, the server spawns a new Thread for every incoming request. These
* are set to daemon status, and named according to the request number.
* The name is useful when profiling the application.
*
*/
public class DefaultAsyncRunner implements IAsyncRunner {
protected long requestCount;
private final List running = Collections.synchronizedList(new ArrayList());
/**
* @return a list with currently running clients.
*/
public List getRunning() {
return running;
}
@Override
public void closeAll() {
// copy of the list for concurrency
for (ClientHandler clientHandler : new ArrayList(this.running)) {
clientHandler.close();
}
}
@Override
public void closed(ClientHandler clientHandler) {
this.running.remove(clientHandler);
}
@Override
public void exec(ClientHandler clientHandler) {
++this.requestCount;
this.running.add(clientHandler);
createThread(clientHandler).start();
}
protected Thread createThread(ClientHandler clientHandler) {
Thread t = new Thread(clientHandler);
t.setDaemon(true);
t.setName("NanoHttpd Request Processor (#" + this.requestCount + ")");
return t;
}
}
================================================
FILE: core/src/main/java/org/nanohttpd/protocols/http/threading/IAsyncRunner.java
================================================
package org.nanohttpd.protocols.http.threading;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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 org.nanohttpd.protocols.http.ClientHandler;
/**
* Pluggable strategy for asynchronously executing requests.
*/
public interface IAsyncRunner {
void closeAll();
void closed(ClientHandler clientHandler);
void exec(ClientHandler code);
}
================================================
FILE: core/src/main/java/org/nanohttpd/util/IFactory.java
================================================
package org.nanohttpd.util;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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%
*/
/**
* Represents a simple factory
*
* @author LordFokas
* @param
* The Type of object to create
*/
public interface IFactory {
T create();
}
================================================
FILE: core/src/main/java/org/nanohttpd/util/IFactoryThrowing.java
================================================
package org.nanohttpd.util;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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%
*/
/**
* Represents a factory that can throw an exception instead of actually creating
* an object
*
* @author LordFokas
* @param
* The Type of object to create
* @param
* The base Type of exceptions that can be thrown
*/
public interface IFactoryThrowing {
T create() throws E;
}
================================================
FILE: core/src/main/java/org/nanohttpd/util/IHandler.java
================================================
package org.nanohttpd.util;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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%
*/
/**
* Defines a generic handler that returns an object of type O when given an
* object of type I.
*
* @author LordFokas
* @param
* The input type.
* @param
* The output type.
*/
public interface IHandler {
public O handle(I input);
}
================================================
FILE: core/src/main/java/org/nanohttpd/util/ServerRunner.java
================================================
package org.nanohttpd.util;
/*
* #%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.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.nanohttpd.protocols.http.NanoHTTPD;
public class ServerRunner {
/**
* logger to log to.
*/
private static final Logger LOG = Logger.getLogger(ServerRunner.class.getName());
public static void executeInstance(NanoHTTPD server) {
try {
server.start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
} 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");
}
public static void run(Class serverClass) {
try {
executeInstance(serverClass.newInstance());
} catch (Exception e) {
ServerRunner.LOG.log(Level.SEVERE, "Could not create server", e);
}
}
}
================================================
FILE: core/src/main/resources/META-INF/nanohttpd/default-mimetypes.properties
================================================
#default mime types for nanohttpd, use META-INF/mimetypes.properties for user defined mimetypes
css=text/css
htm=text/html
html=text/html
xml=text/xml
java=text/x-java-source, text/java
md=text/plain
txt=text/plain
asc=text/plain
gif=image/gif
jpg=image/jpeg
jpeg=image/jpeg
png=image/png
svg=image/svg+xml
mp3=audio/mpeg
m3u=audio/mpeg-url
mp4=video/mp4
ogv=video/ogg
flv=video/x-flv
mov=video/quicktime
swf=application/x-shockwave-flash
js=application/javascript
pdf=application/pdf
doc=application/msword
ogg=application/x-ogg
zip=application/octet-stream
exe=application/octet-stream
class=application/octet-stream
m3u8=application/vnd.apple.mpegurl
ts=video/mp2t
================================================
FILE: core/src/main/resources/META-INF/nanohttpd/mimetypes.properties
================================================
#mime types for nanohttpd, use a file like this for user defined mimetypes
================================================
FILE: core/src/site/site.xml
================================================
org.apache.maven.skinsmaven-fluido-skin1.3.0../images/nanohttpd_logo.png../images/nanohttpd_logo_text.pngfalsetrueNanohttpd/nanohttpdrightblack
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/BadRequestTest.java
================================================
package org.nanohttpd.junit.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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 org.junit.Test;
public class BadRequestTest extends HttpServerTest {
@Test
public void testEmptyRequest() throws IOException {
ByteArrayOutputStream outputStream = invokeServer("\n\n");
String[] expected = new String[]{
"HTTP/1.1 400 Bad Request"
};
assertResponse(outputStream, expected);
}
@Test
public void testInvalidMethod() throws IOException {
ByteArrayOutputStream outputStream = invokeServer("GETT http://example.com");
String[] expected = new String[]{
"HTTP/1.1 400 Bad Request"
};
assertResponse(outputStream, expected);
}
@Test
public void testMissingURI() throws IOException {
ByteArrayOutputStream outputStream = invokeServer("GET");
String[] expected = new String[]{
"HTTP/1.1 400 Bad Request"
};
assertResponse(outputStream, expected);
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/CookieHandlerTest.java
================================================
package org.nanohttpd.junit.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;
import org.junit.Test;
import org.nanohttpd.protocols.http.HTTPSession;
import org.nanohttpd.protocols.http.content.CookieHandler;
import org.nanohttpd.protocols.http.response.Response;
public class CookieHandlerTest extends HttpServerTest {
@Test
public void testCookieHeaderCorrectlyParsed() throws IOException {
StringBuilder requestBuilder = new StringBuilder();
requestBuilder.append("GET " + HttpServerTest.URI + " HTTP/1.1").append(System.getProperty("line.separator")).append("Cookie: theme=light; sessionToken=abc123");
ByteArrayInputStream inputStream = new ByteArrayInputStream(requestBuilder.toString().getBytes());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
HTTPSession session = this.testServer.createSession(this.tempFileManager, inputStream, outputStream);
session.execute();
Set allCookies = new HashSet();
CookieHandler cookieHandler = session.getCookies();
for (String cookie : cookieHandler) {
allCookies.add(cookie);
}
assertTrue("cookie specified in header not correctly parsed", allCookies.contains("theme"));
assertTrue("cookie specified in header not correctly parsed", allCookies.contains("sessionToken"));
assertEquals("cookie value not correctly parsed", "light", cookieHandler.read("theme"));
assertEquals("cookie value not correctly parsed", "abc123", cookieHandler.read("sessionToken"));
}
@Test
public void testCookieHeaderWithSpecialCharactersCorrectlyParsed() throws IOException {
StringBuilder requestBuilder = new StringBuilder();
// not including ; = and ,
requestBuilder.append("GET " + HttpServerTest.URI + " HTTP/1.1").append(System.getProperty("line.separator"))
.append("Cookie: theme=light; sessionToken=abc123!@#$%^&*()-_+{}[]\\|:\"'<>.?/");
ByteArrayInputStream inputStream = new ByteArrayInputStream(requestBuilder.toString().getBytes());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
HTTPSession session = this.testServer.createSession(this.tempFileManager, inputStream, outputStream);
session.execute();
Set allCookies = new HashSet();
CookieHandler cookieHandler = session.getCookies();
for (String cookie : cookieHandler) {
allCookies.add(cookie);
}
assertTrue("cookie specified in header not correctly parsed", allCookies.contains("theme"));
assertTrue("cookie specified in header not correctly parsed", allCookies.contains("sessionToken"));
assertEquals("cookie value not correctly parsed", "light", cookieHandler.read("theme"));
assertEquals("cookie value not correctly parsed", "abc123!@#$%^&*()-_+{}[]\\|:\"'<>.?/", cookieHandler.read("sessionToken"));
}
@Test
public void testUnloadQueue() throws IOException {
StringBuilder requestBuilder = new StringBuilder();
requestBuilder.append("GET " + HttpServerTest.URI + " HTTP/1.1").append(System.getProperty("line.separator")).append("Cookie: theme=light; sessionToken=abc123");
ByteArrayInputStream inputStream = new ByteArrayInputStream(requestBuilder.toString().getBytes());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
HTTPSession session = this.testServer.createSession(this.tempFileManager, inputStream, outputStream);
session.execute();
CookieHandler cookieHandler = session.getCookies();
Response response = Response.newFixedLengthResponse("");
cookieHandler.set("name", "value", 30);
cookieHandler.unloadQueue(response);
String setCookieHeader = response.getCookieHeaders().get(0);
assertTrue("unloadQueue did not set the cookies correctly", setCookieHeader.startsWith("name=value; expires="));
}
@Test
public void testDelete() throws IOException, ParseException {
StringBuilder requestBuilder = new StringBuilder();
requestBuilder.append("GET " + HttpServerTest.URI + " HTTP/1.1").append(System.getProperty("line.separator")).append("Cookie: theme=light; sessionToken=abc123");
ByteArrayInputStream inputStream = new ByteArrayInputStream(requestBuilder.toString().getBytes());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
HTTPSession session = this.testServer.createSession(this.tempFileManager, inputStream, outputStream);
session.execute();
CookieHandler cookieHandler = session.getCookies();
Response response = Response.newFixedLengthResponse("");
cookieHandler.delete("name");
cookieHandler.unloadQueue(response);
String setCookieHeader = response.getCookieHeaders().get(0);
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
String dateString = setCookieHeader.split(";")[1].split("=")[1].trim();
Date date = dateFormat.parse(dateString);
assertTrue("Deleted cookie's expiry time should be a time in the past", date.compareTo(new Date()) < 0);
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/CookieTest.java
================================================
package org.nanohttpd.junit.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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 static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.Random;
import org.junit.Test;
import org.nanohttpd.protocols.http.content.Cookie;
public class CookieTest {
@Test
public void testGetHTTPTime() {
Random random = new Random();
int randomExpirationTime = random.nextInt(100);
assertNotNull("getHTTPTime should return a non-null value for " + randomExpirationTime + " days", Cookie.getHTTPTime(randomExpirationTime));
}
@Test
public void testCookieWithNoExplicitExpirationTime() {
Cookie cookie = new Cookie("CookieKey", "CookieValue");
assertTrue("Cookie header should contain cookie key", cookie.getHTTPHeader().contains("CookieKey"));
assertTrue("Cookie header should contain cookie value", cookie.getHTTPHeader().contains("CookieValue"));
}
@Test
public void testCookieWithExplicitExpirationTime() {
Cookie cookie = new Cookie("CookieKey", "CookieValue", 40);
assertFalse("The default 30 days expires string should not be avaialbe in the cookie header" + " because the expiry has been specified as 40 days", cookie
.getHTTPHeader().contains(Cookie.getHTTPTime(30)));
assertTrue("Cookie header should contain cookie key", cookie.getHTTPHeader().contains("CookieKey"));
assertTrue("Cookie header should contain cookie value", cookie.getHTTPHeader().contains("CookieValue"));
}
@Test
public void testCookieWithExpiresString() {
Random random = new Random();
int randomExpirationTime = random.nextInt(100);
String expiresString = Cookie.getHTTPTime(randomExpirationTime);
Cookie cookie = new Cookie("CookieKey", "CookieValue", expiresString);
assertTrue("Cookie should contain the expirs string passed in the constructor", cookie.getHTTPHeader().contains(expiresString));
assertTrue("Cookie header should contain cookie key", cookie.getHTTPHeader().contains("CookieKey"));
assertTrue("Cookie header should contain cookie value", cookie.getHTTPHeader().contains("CookieValue"));
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/HttpChunkedResponseTest.java
================================================
package org.nanohttpd.junit.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* Copyright (C) 2012 - 2016 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.PipedInputStream;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.response.Status;
public class HttpChunkedResponseTest extends HttpServerTest {
private static class ChunkedInputStream extends PipedInputStream {
int chunk = 0;
String[] chunks;
private ChunkedInputStream(String[] chunks) {
this.chunks = chunks;
}
@Override
public synchronized int read(byte[] buffer, int off, int len) throws IOException {
// Too implementation-linked, but...
for (int i = 0; i < this.chunks[this.chunk].length(); ++i) {
buffer[i] = (byte) this.chunks[this.chunk].charAt(i);
}
return this.chunks[this.chunk++].length();
}
}
@org.junit.Test
public void thatChunkedContentIsChunked() throws Exception {
PipedInputStream pipedInputStream = new ChunkedInputStream(new String[]{
"some",
"thing which is longer than sixteen characters",
"whee!",
""
});
String[] expected = {
"HTTP/1.1 200 OK",
"Content-Type: what/ever",
"Date: .*",
"Connection: keep-alive",
"Transfer-Encoding: chunked",
"",
"4",
"some",
"2d",
"thing which is longer than sixteen characters",
"5",
"whee!",
"0",
""
};
this.testServer.response = Response.newChunkedResponse(Status.OK, "what/ever", pipedInputStream);
this.testServer.response.setChunkedTransfer(true);
ByteArrayOutputStream byteArrayOutputStream = invokeServer("GET / HTTP/1.1");
assertResponse(byteArrayOutputStream, expected);
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/HttpDeleteRequestTest.java
================================================
package org.nanohttpd.junit.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* 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.InputStream;
import org.junit.Test;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.response.Status;
public class HttpDeleteRequestTest extends HttpServerTest {
@Test
public void testDeleteRequestThatDoesntSendBackResponseBody_EmptyString() throws Exception {
this.testServer.response = Response.newFixedLengthResponse(Status.NO_CONTENT, NanoHTTPD.MIME_HTML, "");
ByteArrayOutputStream outputStream = invokeServer("DELETE " + HttpServerTest.URI + " HTTP/1.1");
String[] expected = {
"HTTP/1.1 204 No Content",
"Content-Type: text/html",
"Date: .*",
"Connection: keep-alive",
"Content-Length: 0",
""
};
assertResponse(outputStream, expected);
}
@Test
public void testDeleteRequestThatDoesntSendBackResponseBody_NullInputStream() throws Exception {
this.testServer.response = Response.newChunkedResponse(Status.NO_CONTENT, NanoHTTPD.MIME_HTML, (InputStream) null);
ByteArrayOutputStream outputStream = invokeServer("DELETE " + HttpServerTest.URI + " HTTP/1.1");
String[] expected = {
"HTTP/1.1 204 No Content",
"Content-Type: text/html",
"Date: .*",
"Connection: keep-alive",
"Content-Length: 0",
""
};
assertResponse(outputStream, expected);
}
@Test
public void testDeleteRequestThatDoesntSendBackResponseBody_NullString() throws Exception {
this.testServer.response = Response.newFixedLengthResponse(Status.NO_CONTENT, NanoHTTPD.MIME_HTML, (String) null);
ByteArrayOutputStream outputStream = invokeServer("DELETE " + HttpServerTest.URI + " HTTP/1.1");
String[] expected = {
"HTTP/1.1 204 No Content",
"Content-Type: text/html",
"Date: .*",
"Connection: keep-alive",
"Content-Length: 0",
""
};
assertResponse(outputStream, expected);
}
@Test
public void testDeleteRequestThatSendsBackResponseBody_Accepted() throws Exception {
this.testServer.response = Response.newFixedLengthResponse(Status.ACCEPTED, "application/xml", "");
ByteArrayOutputStream outputStream = invokeServer("DELETE " + HttpServerTest.URI + " HTTP/1.1");
String[] expected = {
"HTTP/1.1 202 Accepted",
"Content-Type: application/xml",
"Date: .*",
"Connection: keep-alive",
"Content-Length: 8",
"",
""
};
assertResponse(outputStream, expected);
}
@Test
public void testDeleteRequestThatSendsBackResponseBody_Success() throws Exception {
this.testServer.response = Response.newFixedLengthResponse(Status.OK, "application/xml", "");
ByteArrayOutputStream outputStream = invokeServer("DELETE " + HttpServerTest.URI + " HTTP/1.1");
String[] expected = {
"HTTP/1.1 200 OK",
"Content-Type: application/xml",
"Date: .*",
"Connection: keep-alive",
"Content-Length: 8",
"",
""
};
assertResponse(outputStream, expected);
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/HttpGetRequestTest.java
================================================
package org.nanohttpd.junit.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* 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 static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import java.util.List;
import org.junit.Test;
import org.nanohttpd.protocols.http.request.Method;
import org.nanohttpd.protocols.http.response.Response;
public class HttpGetRequestTest extends HttpServerTest {
@Test
public void testDecodingFieldWithEmptyValueAndFieldWithMissingValueGiveDifferentResults() {
invokeServer("GET " + HttpServerTest.URI + "?foo&bar= HTTP/1.1");
assertTrue(this.testServer.decodedParamters.get("foo") instanceof List);
assertEquals(0, this.testServer.decodedParamters.get("foo").size());
assertTrue(this.testServer.decodedParamters.get("bar") instanceof List);
assertEquals(1, this.testServer.decodedParamters.get("bar").size());
assertEquals("", this.testServer.decodedParamters.get("bar").get(0));
}
@Test
public void testDecodingMixtureOfParameters() {
invokeServer("GET " + HttpServerTest.URI + "?foo=bar&foo=baz&zot&zim= HTTP/1.1");
assertTrue(this.testServer.decodedParamters.get("foo") instanceof List);
assertEquals(2, this.testServer.decodedParamters.get("foo").size());
assertEquals("bar", this.testServer.decodedParamters.get("foo").get(0));
assertEquals("baz", this.testServer.decodedParamters.get("foo").get(1));
assertTrue(this.testServer.decodedParamters.get("zot") instanceof List);
assertEquals(0, this.testServer.decodedParamters.get("zot").size());
assertTrue(this.testServer.decodedParamters.get("zim") instanceof List);
assertEquals(1, this.testServer.decodedParamters.get("zim").size());
assertEquals("", this.testServer.decodedParamters.get("zim").get(0));
}
@Test
public void testDecodingParametersFromParameterMap() {
invokeServer("GET " + HttpServerTest.URI + "?foo=bar&foo=baz&zot&zim= HTTP/1.1");
assertEquals(this.testServer.decodedParamters, this.testServer.decodedParamtersFromParameter);
}
// --------------------------------------------------------------------------------------------------------
// //
@Test
public void testDecodingParametersWithSingleValue() {
invokeServer("GET " + HttpServerTest.URI + "?foo=bar&baz=zot HTTP/1.1");
assertEquals("foo=bar&baz=zot", this.testServer.queryParameterString);
assertTrue(this.testServer.decodedParamters.get("foo") instanceof List);
assertEquals(1, this.testServer.decodedParamters.get("foo").size());
assertEquals("bar", this.testServer.decodedParamters.get("foo").get(0));
assertTrue(this.testServer.decodedParamters.get("baz") instanceof List);
assertEquals(1, this.testServer.decodedParamters.get("baz").size());
assertEquals("zot", this.testServer.decodedParamters.get("baz").get(0));
}
@Test
public void testDecodingParametersWithSingleValueAndMissingValue() {
invokeServer("GET " + HttpServerTest.URI + "?foo&baz=zot HTTP/1.1");
assertEquals("foo&baz=zot", this.testServer.queryParameterString);
assertTrue(this.testServer.decodedParamters.get("foo") instanceof List);
assertEquals(0, this.testServer.decodedParamters.get("foo").size());
assertTrue(this.testServer.decodedParamters.get("baz") instanceof List);
assertEquals(1, this.testServer.decodedParamters.get("baz").size());
assertEquals("zot", this.testServer.decodedParamters.get("baz").get(0));
}
@Test
public void testDecodingSingleFieldRepeated() {
invokeServer("GET " + HttpServerTest.URI + "?foo=bar&foo=baz HTTP/1.1");
assertTrue(this.testServer.decodedParamters.get("foo") instanceof List);
assertEquals(2, this.testServer.decodedParamters.get("foo").size());
assertEquals("bar", this.testServer.decodedParamters.get("foo").get(0));
assertEquals("baz", this.testServer.decodedParamters.get("foo").get(1));
}
@Test
public void testEmptyHeadersSuppliedToServeMethodFromSimpleWorkingGetRequest() {
invokeServer("GET " + HttpServerTest.URI + " HTTP/1.1");
assertNotNull(this.testServer.parms);
assertNotNull(this.testServer.parameters);
assertNotNull(this.testServer.header);
assertNotNull(this.testServer.files);
assertNotNull(this.testServer.uri);
}
@Test
public void testFullyQualifiedWorkingGetRequest() throws Exception {
ByteArrayOutputStream outputStream = invokeServer("GET " + HttpServerTest.URI + " HTTP/1.1");
String[] expected = {
"HTTP/1.1 200 OK",
"Content-Type: text/html",
"Date: .*",
"Connection: keep-alive",
"Content-Length: 0",
""
};
assertResponse(outputStream, expected);
}
@Test
public void testMultipleGetParameters() {
invokeServer("GET " + HttpServerTest.URI + "?foo=bar&baz=zot HTTP/1.1");
assertEquals("bar", this.testServer.parms.get("foo"));
assertEquals("zot", this.testServer.parms.get("baz"));
assertEquals("bar", this.testServer.parameters.get("foo").get(0));
assertEquals("zot", this.testServer.parameters.get("baz").get(0));
}
@Test
public void testMultipleGetParametersWithMissingValue() {
invokeServer("GET " + HttpServerTest.URI + "?foo=&baz=zot HTTP/1.1");
assertEquals("", this.testServer.parms.get("foo"));
assertEquals("zot", this.testServer.parms.get("baz"));
assertEquals("", this.testServer.parameters.get("foo").get(0));
assertEquals("zot", this.testServer.parameters.get("baz").get(0));
}
@Test
public void testMultipleGetParametersWithMissingValueAndRequestHeaders() {
invokeServer("GET " + HttpServerTest.URI + "?foo=&baz=zot HTTP/1.1\nAccept: text/html");
assertEquals("", this.testServer.parms.get("foo"));
assertEquals("zot", this.testServer.parms.get("baz"));
assertEquals("", this.testServer.parameters.get("foo").get(0));
assertEquals("zot", this.testServer.parameters.get("baz").get(0));
assertEquals("text/html", this.testServer.header.get("accept"));
}
@Test
public void testMultipleHeaderSuppliedToServeMethodFromSimpleWorkingGetRequest() {
String userAgent = "jUnit 4.8.2 Unit Test";
String accept = "text/html";
invokeServer("GET " + HttpServerTest.URI + " HTTP/1.1\nUser-Agent: " + userAgent + "\nAccept: " + accept);
assertEquals(userAgent, this.testServer.header.get("user-agent"));
assertEquals(accept, this.testServer.header.get("accept"));
}
@Test
public void testOutputOfServeSentBackToClient() throws Exception {
String responseBody = "Success!";
this.testServer.response = Response.newFixedLengthResponse(responseBody);
ByteArrayOutputStream outputStream = invokeServer("GET " + HttpServerTest.URI + " HTTP/1.1");
String[] expected = {
"HTTP/1.1 200 OK",
"Content-Type: text/html",
"Date: .*",
"Connection: keep-alive",
"Content-Length: 8",
"",
responseBody
};
assertResponse(outputStream, expected);
}
@Test
public void testSingleGetParameter() {
invokeServer("GET " + HttpServerTest.URI + "?foo=bar HTTP/1.1");
assertEquals("bar", this.testServer.parms.get("foo"));
assertEquals("bar", this.testServer.parameters.get("foo").get(0));
}
@Test
public void testMultipleValueGetParameter() {
invokeServer("GET " + HttpServerTest.URI + "?foo=bar&foo=baz HTTP/1.1");
assertEquals("bar", this.testServer.parms.get("foo"));
assertEquals(2, this.testServer.parameters.get("foo").size());
assertEquals("bar", this.testServer.parameters.get("foo").get(0));
assertEquals("baz", this.testServer.parameters.get("foo").get(1));
}
@Test
public void testSingleGetParameterWithNoValue() {
invokeServer("GET " + HttpServerTest.URI + "?foo HTTP/1.1");
assertEquals("", this.testServer.parms.get("foo"));
assertEquals("", this.testServer.parameters.get("foo").get(0));
}
@Test
public void testSingleUserAgentHeaderSuppliedToServeMethodFromSimpleWorkingGetRequest() {
String userAgent = "jUnit 4.8.2 Unit Test";
invokeServer("GET " + HttpServerTest.URI + " HTTP/1.1\nUser-Agent: " + userAgent + "\n");
assertEquals(userAgent, this.testServer.header.get("user-agent"));
assertEquals(Method.GET, this.testServer.method);
assertEquals(HttpServerTest.URI, this.testServer.uri);
}
@Test
public void testGetQueryParameterContainsSpace() {
invokeServer("GET " + HttpServerTest.URI + "?foo=bar%20baz HTTP/1.1");
assertEquals("Parameter count in URL and decodedParameters should match.", 1, this.testServer.decodedParamters.size());
assertEquals("The query parameter value with space decoding incorrect", "bar baz", this.testServer.decodedParamters.get("foo").get(0));
}
@Test
public void testGetQueryParameterContainsQuestionMark() {
invokeServer("GET " + HttpServerTest.URI + "?foo=bar%3F HTTP/1.1");
assertEquals("Parameter count in URL and decodedParameters should match.", 1, this.testServer.decodedParamters.size());
assertEquals("The query parameter value with question mark decoding incorrect", "bar?", this.testServer.decodedParamters.get("foo").get(0));
}
@Test
public void testGetQueryParameterContainsAmpersand() {
invokeServer("GET " + HttpServerTest.URI + "?foo=bar%26 HTTP/1.1");
assertEquals("Parameter count in URL and decodedParameters should match.", 1, this.testServer.decodedParamters.size());
assertEquals("The query parameter value with ampersand decoding incorrect", "bar&", this.testServer.decodedParamters.get("foo").get(0));
}
@Test
public void testGetQueryParameterContainsSpecialCharactersSingleFieldRepeated() {
invokeServer("GET " + HttpServerTest.URI + "?foo=bar%20baz&foo=bar%3F&foo=bar%26 HTTP/1.1");
assertEquals("Parameter count in URL and decodedParameters should match.", 1, this.testServer.decodedParamters.size());
String[] parametersAsArray = this.testServer.decodedParamters.get("foo").toArray(new String[0]);
String[] expected = new String[]{
"bar baz",
"bar?",
"bar&"
};
assertArrayEquals("Repeated parameter not decoded correctly", expected, parametersAsArray);
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/HttpHeadRequestTest.java
================================================
package org.nanohttpd.junit.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* 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 static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import java.util.List;
import org.junit.Test;
import org.nanohttpd.protocols.http.request.Method;
import org.nanohttpd.protocols.http.response.Response;
public class HttpHeadRequestTest extends HttpServerTest {
@Override
public void setUp() throws Exception {
super.setUp();
String responseBody = "Success!";
this.testServer.response = Response.newFixedLengthResponse(responseBody);
}
@Test
public void testDecodingFieldWithEmptyValueAndFieldWithMissingValueGiveDifferentResults() {
invokeServer("HEAD " + HttpServerTest.URI + "?foo&bar= HTTP/1.1");
assertTrue(this.testServer.decodedParamters.get("foo") instanceof List);
assertEquals(0, this.testServer.decodedParamters.get("foo").size());
assertTrue(this.testServer.decodedParamters.get("bar") instanceof List);
assertEquals(1, this.testServer.decodedParamters.get("bar").size());
assertEquals("", this.testServer.decodedParamters.get("bar").get(0));
}
@Test
public void testDecodingMixtureOfParameters() {
invokeServer("HEAD " + HttpServerTest.URI + "?foo=bar&foo=baz&zot&zim= HTTP/1.1");
assertTrue(this.testServer.decodedParamters.get("foo") instanceof List);
assertEquals(2, this.testServer.decodedParamters.get("foo").size());
assertEquals("bar", this.testServer.decodedParamters.get("foo").get(0));
assertEquals("baz", this.testServer.decodedParamters.get("foo").get(1));
assertTrue(this.testServer.decodedParamters.get("zot") instanceof List);
assertEquals(0, this.testServer.decodedParamters.get("zot").size());
assertTrue(this.testServer.decodedParamters.get("zim") instanceof List);
assertEquals(1, this.testServer.decodedParamters.get("zim").size());
assertEquals("", this.testServer.decodedParamters.get("zim").get(0));
}
@Test
public void testDecodingParametersFromParameterMap() {
invokeServer("HEAD " + HttpServerTest.URI + "?foo=bar&foo=baz&zot&zim= HTTP/1.1");
assertEquals(this.testServer.decodedParamters, this.testServer.decodedParamtersFromParameter);
}
// --------------------------------------------------------------------------------------------------------
// //
@Test
public void testDecodingParametersWithSingleValue() {
invokeServer("HEAD " + HttpServerTest.URI + "?foo=bar&baz=zot HTTP/1.1");
assertEquals("foo=bar&baz=zot", this.testServer.queryParameterString);
assertTrue(this.testServer.decodedParamters.get("foo") instanceof List);
assertEquals(1, this.testServer.decodedParamters.get("foo").size());
assertEquals("bar", this.testServer.decodedParamters.get("foo").get(0));
assertTrue(this.testServer.decodedParamters.get("baz") instanceof List);
assertEquals(1, this.testServer.decodedParamters.get("baz").size());
assertEquals("zot", this.testServer.decodedParamters.get("baz").get(0));
}
@Test
public void testDecodingParametersWithSingleValueAndMissingValue() {
invokeServer("HEAD " + HttpServerTest.URI + "?foo&baz=zot HTTP/1.1");
assertEquals("foo&baz=zot", this.testServer.queryParameterString);
assertTrue(this.testServer.decodedParamters.get("foo") instanceof List);
assertEquals(0, this.testServer.decodedParamters.get("foo").size());
assertTrue(this.testServer.decodedParamters.get("baz") instanceof List);
assertEquals(1, this.testServer.decodedParamters.get("baz").size());
assertEquals("zot", this.testServer.decodedParamters.get("baz").get(0));
}
@Test
public void testDecodingSingleFieldRepeated() {
invokeServer("HEAD " + HttpServerTest.URI + "?foo=bar&foo=baz HTTP/1.1");
assertTrue(this.testServer.decodedParamters.get("foo") instanceof List);
assertEquals(2, this.testServer.decodedParamters.get("foo").size());
assertEquals("bar", this.testServer.decodedParamters.get("foo").get(0));
assertEquals("baz", this.testServer.decodedParamters.get("foo").get(1));
}
@Test
public void testEmptyHeadersSuppliedToServeMethodFromSimpleWorkingGetRequest() {
invokeServer("HEAD " + HttpServerTest.URI + " HTTP/1.1");
assertNotNull(this.testServer.parms);
assertNotNull(this.testServer.parameters);
assertNotNull(this.testServer.header);
assertNotNull(this.testServer.files);
assertNotNull(this.testServer.uri);
}
@Test
public void testHeadRequestDoesntSendBackResponseBody() throws Exception {
ByteArrayOutputStream outputStream = invokeServer("HEAD " + HttpServerTest.URI + " HTTP/1.1");
String[] expected = {
"HTTP/1.1 200 OK",
"Content-Type: text/html",
"Date: .*",
"Connection: keep-alive",
"Content-Length: 8",
""
};
assertResponse(outputStream, expected);
}
@Test
public void testMultipleGetParameters() {
invokeServer("HEAD " + HttpServerTest.URI + "?foo=bar&baz=zot HTTP/1.1");
assertEquals("bar", this.testServer.parms.get("foo"));
assertEquals("zot", this.testServer.parms.get("baz"));
assertEquals("bar", this.testServer.parameters.get("foo").get(0));
assertEquals("zot", this.testServer.parameters.get("baz").get(0));
}
@Test
public void testMultipleGetParametersWithMissingValue() {
invokeServer("HEAD " + HttpServerTest.URI + "?foo=&baz=zot HTTP/1.1");
assertEquals("", this.testServer.parms.get("foo"));
assertEquals("zot", this.testServer.parms.get("baz"));
assertEquals("", this.testServer.parameters.get("foo").get(0));
assertEquals("zot", this.testServer.parameters.get("baz").get(0));
}
@Test
public void testMultipleGetParametersWithMissingValueAndRequestHeaders() {
invokeServer("HEAD " + HttpServerTest.URI + "?foo=&baz=zot HTTP/1.1\nAccept: text/html");
assertEquals("", this.testServer.parms.get("foo"));
assertEquals("zot", this.testServer.parms.get("baz"));
assertEquals("", this.testServer.parameters.get("foo").get(0));
assertEquals("zot", this.testServer.parameters.get("baz").get(0));
assertEquals("text/html", this.testServer.header.get("accept"));
}
@Test
public void testMultipleHeaderSuppliedToServeMethodFromSimpleWorkingGetRequest() {
String userAgent = "jUnit 4.8.2 Unit Test";
String accept = "text/html";
invokeServer("HEAD " + HttpServerTest.URI + " HTTP/1.1\nUser-Agent: " + userAgent + "\nAccept: " + accept);
assertEquals(userAgent, this.testServer.header.get("user-agent"));
assertEquals(accept, this.testServer.header.get("accept"));
}
@Test
public void testSingleGetParameter() {
invokeServer("HEAD " + HttpServerTest.URI + "?foo=bar HTTP/1.1");
assertEquals("bar", this.testServer.parms.get("foo"));
assertEquals("bar", this.testServer.parameters.get("foo").get(0));
}
@Test
public void testMultipleValueGetParameter() {
invokeServer("HEAD " + HttpServerTest.URI + "?foo=bar&foo=baz HTTP/1.1");
assertEquals("bar", this.testServer.parms.get("foo"));
assertEquals(2, this.testServer.parameters.get("foo").size());
assertEquals("bar", this.testServer.parameters.get("foo").get(0));
assertEquals("baz", this.testServer.parameters.get("foo").get(1));
}
@Test
public void testSingleGetParameterWithNoValue() {
invokeServer("HEAD " + HttpServerTest.URI + "?foo HTTP/1.1");
assertEquals("", this.testServer.parms.get("foo"));
assertEquals("", this.testServer.parameters.get("foo").get(0));
}
@Test
public void testSingleUserAgentHeaderSuppliedToServeMethodFromSimpleWorkingGetRequest() {
String userAgent = "jUnit 4.8.2 Unit Test";
invokeServer("HEAD " + HttpServerTest.URI + " HTTP/1.1\nUser-Agent: " + userAgent + "\n");
assertEquals(userAgent, this.testServer.header.get("user-agent"));
assertEquals(Method.HEAD, this.testServer.method);
assertEquals(HttpServerTest.URI, this.testServer.uri);
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/HttpKeepAliveTest.java
================================================
package org.nanohttpd.junit.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* 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 static junit.framework.Assert.fail;
import java.io.ByteArrayOutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import org.junit.Test;
import org.nanohttpd.protocols.http.HTTPSession;
import org.nanohttpd.protocols.http.tempfiles.DefaultTempFileManager;
public class HttpKeepAliveTest extends HttpServerTest {
private Throwable error = null;
@Test
public void testManyGetRequests() throws Exception {
String request = "GET " + HttpServerTest.URI + " HTTP/1.1\r\n\r\n";
String[] expected = {
"HTTP/1.1 200 OK",
"Content-Type: text/html",
"Date: .*",
"Connection: keep-alive",
"Content-Length: 0",
""
};
testManyRequests(request, expected);
}
@Test
public void testManyPutRequests() throws Exception {
String data = "BodyData 1\nLine 2";
String request = "PUT " + HttpServerTest.URI + " HTTP/1.1\r\nContent-Length: " + data.length() + "\r\n\r\n" + data;
String[] expected = {
"HTTP/1.1 200 OK",
"Content-Type: text/html",
"Date: .*",
"Connection: keep-alive",
"Content-Length: 0",
""
};
testManyRequests(request, expected);
}
/**
* Issue the given request many times to check whether an error occurs. For
* this test, a small stack size is used, since a stack overflow is among
* the possible errors.
*
* @param request
* The request to issue
* @param expected
* The expected response
*/
public void testManyRequests(final String request, final String[] expected) throws Exception {
Runnable r = new Runnable() {
@Override
public void run() {
try {
PipedOutputStream requestStream = new PipedOutputStream();
PipedInputStream inputStream = new PipedInputStream(requestStream);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
DefaultTempFileManager tempFileManager = new DefaultTempFileManager();
try {
HTTPSession session = HttpKeepAliveTest.this.testServer.createSession(tempFileManager, inputStream, outputStream);
for (int i = 0; i < 2048; i++) {
requestStream.write(request.getBytes());
requestStream.flush();
outputStream.reset();
session.execute();
assertResponse(outputStream, expected);
}
// Finally, try "Connection: Close"
String closeReq = request.replaceAll("HTTP/1.1", "HTTP/1.1\r\nConnection: Close");
expected[3] = "Connection: close";
requestStream.write(closeReq.getBytes());
outputStream.reset();
requestStream.flush();
// Server should now close the socket by throwing a
// SocketException:
try {
session.execute();
} catch (java.net.SocketException se) {
junit.framework.Assert.assertEquals(se.getMessage(), "NanoHttpd Shutdown");
}
assertResponse(outputStream, expected);
} finally {
tempFileManager.clear();
}
} catch (Throwable t) {
HttpKeepAliveTest.this.error = t;
}
}
};
Thread t = new Thread(null, r, "Request Thread", 1 << 17);
t.start();
t.join();
if (this.error != null) {
fail("" + this.error);
this.error.printStackTrace();
}
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/HttpParsingTest.java
================================================
package org.nanohttpd.junit.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* 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 static junit.framework.Assert.assertEquals;
import org.junit.Test;
public class HttpParsingTest extends HttpServerTest {
@Test
public void testMultibyteCharacterSupport() throws Exception {
String expected = "Chinese \u738b Letters";
String input = "Chinese+%e7%8e%8b+Letters";
assertEquals(expected, this.testServer.decodePercent(input));
}
@Test
public void testNormalCharacters() throws Exception {
for (int i = 0x20; i < 0x80; i++) {
String hex = Integer.toHexString(i);
String input = "%" + hex;
char expected = (char) i;
assertEquals("" + expected, this.testServer.decodePercent(input));
}
}
@Test
public void testPlusInQueryParams() throws Exception {
assertEquals("foo bar", this.testServer.decodePercent("foo+bar"));
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/HttpPostRequestTest.java
================================================
package org.nanohttpd.junit.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* 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 static org.junit.Assert.assertEquals;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.junit.Test;
public class HttpPostRequestTest extends HttpServerTest {
public static final String CONTENT_LENGTH = "Content-Length: ";
public static final String FIELD = "caption";
public static final String VALUE = "Summer vacation";
public static final String FIELD2 = "location";
public static final String VALUE2 = "Grand Canyon";
public static final String POST_RAW_CONTENT_FILE_ENTRY = "postData";
public static final String VALUE_TEST_SIMPLE_RAW_DATA_WITH_AMPHASIS = "Test raw data & Result value";
/**
* contains common preparation steps for testing POST with Multipart Form
*
* @param fileName
* Name of file to be uploaded
* @param fileContent
* Content of file to be uploaded
* @return input String with POST request complete information including
* header, length and content
*/
private String preparePostWithMultipartForm(String fileName, String fileContent) {
String divider = UUID.randomUUID().toString();
String header = "POST " + HttpServerTest.URI + " HTTP/1.1\nContent-Type: " + "multipart/form-data, boundary=" + divider + "\r\n";
String content =
"--" + divider + "\r\n" + "Content-Disposition: form-data; name=\"" + HttpPostRequestTest.FIELD + "\"; filename=\"" + fileName + "\"\r\n"
+ "Content-Type: image/jpeg\r\n" + "\r\n" + fileContent + "\r\n" + "--" + divider + "--\r\n";
int size = content.length() + header.length();
int contentLengthHeaderValueSize = String.valueOf(size).length();
int contentLength = size + contentLengthHeaderValueSize + HttpPostRequestTest.CONTENT_LENGTH.length();
String input = header + HttpPostRequestTest.CONTENT_LENGTH + (contentLength + 5) + "\r\n\r\n" + content;
return input;
}
@Test
public void testPostWithMultipartFormUpload() throws Exception {
String filename = "GrandCanyon.txt";
String fileContent = HttpPostRequestTest.VALUE;
String input = preparePostWithMultipartForm(filename, fileContent);
invokeServer(input);
assertEquals(1, this.testServer.parms.size());
assertEquals(1, this.testServer.parameters.size());
BufferedReader reader = new BufferedReader(new FileReader(this.testServer.files.get(HttpPostRequestTest.FIELD)));
List lines = readLinesFromFile(reader);
assertLinesOfText(new String[]{
fileContent
}, lines);
}
@Test
public void testPostWithMultipartFormUploadFilenameHasSpaces() throws Exception {
String fileNameWithSpace = "Grand Canyon.txt";
String fileContent = HttpPostRequestTest.VALUE;
String input = preparePostWithMultipartForm(fileNameWithSpace, fileContent);
invokeServer(input);
String fileNameAfter = new ArrayList(this.testServer.parms.values()).get(0);
assertEquals(fileNameWithSpace, fileNameAfter);
fileNameAfter = new ArrayList(this.testServer.parameters.values().iterator().next()).get(0);
assertEquals(fileNameWithSpace, fileNameAfter);
}
@Test
public void testPostWithMultipleMultipartFormFields() throws Exception {
String divider = UUID.randomUUID().toString();
String header = "POST " + HttpServerTest.URI + " HTTP/1.1\nContent-Type: " + "multipart/form-data; boundary=" + divider + "\n";
String content =
"--" + divider + "\r\n" + "Content-Disposition: form-data; name=\"" + HttpPostRequestTest.FIELD + "\"\r\n" + "\r\n" + HttpPostRequestTest.VALUE + "\r\n"
+ "--" + divider + "\r\n" + "Content-Disposition: form-data; name=\"" + HttpPostRequestTest.FIELD2 + "\"\r\n" + "\r\n" + HttpPostRequestTest.VALUE2
+ "\r\n" + "--" + divider + "--\r\n";
int size = content.length() + header.length();
int contentLengthHeaderValueSize = String.valueOf(size).length();
int contentLength = size + contentLengthHeaderValueSize + HttpPostRequestTest.CONTENT_LENGTH.length();
String input = header + HttpPostRequestTest.CONTENT_LENGTH + (contentLength + 4) + "\r\n\r\n" + content;
invokeServer(input);
assertEquals(2, this.testServer.parms.size());
assertEquals(HttpPostRequestTest.VALUE, this.testServer.parms.get(HttpPostRequestTest.FIELD));
assertEquals(HttpPostRequestTest.VALUE2, this.testServer.parms.get(HttpPostRequestTest.FIELD2));
assertEquals(2, this.testServer.parameters.size());
assertEquals(HttpPostRequestTest.VALUE, this.testServer.parameters.get(HttpPostRequestTest.FIELD).get(0));
assertEquals(HttpPostRequestTest.VALUE2, this.testServer.parameters.get(HttpPostRequestTest.FIELD2).get(0));
}
@Test
public void testPostWithMultipleMultipartFormFieldsWhereContentTypeWasSeparatedByComma() throws Exception {
String divider = UUID.randomUUID().toString();
String header = "POST " + HttpServerTest.URI + " HTTP/1.1\nContent-Type: " + "multipart/form-data, boundary=" + divider + "\r\n";
String content =
"--" + divider + "\r\n" + "Content-Disposition: form-data; name=\"" + HttpPostRequestTest.FIELD + "\"\r\n" + "\r\n" + HttpPostRequestTest.VALUE + "\r\n"
+ "--" + divider + "\r\n" + "Content-Disposition: form-data; name=\"" + HttpPostRequestTest.FIELD2 + "\"\r\n" + "\r\n" + HttpPostRequestTest.VALUE2
+ "\r\n" + "--" + divider + "--\r\n";
int size = content.length() + header.length();
int contentLengthHeaderValueSize = String.valueOf(size).length();
int contentLength = size + contentLengthHeaderValueSize + HttpPostRequestTest.CONTENT_LENGTH.length();
String input = header + HttpPostRequestTest.CONTENT_LENGTH + (contentLength + 4) + "\r\n\r\n" + content;
invokeServer(input);
assertEquals(2, this.testServer.parms.size());
assertEquals(HttpPostRequestTest.VALUE, this.testServer.parms.get(HttpPostRequestTest.FIELD));
assertEquals(HttpPostRequestTest.VALUE2, this.testServer.parms.get(HttpPostRequestTest.FIELD2));
assertEquals(2, this.testServer.parameters.size());
assertEquals(HttpPostRequestTest.VALUE, this.testServer.parameters.get(HttpPostRequestTest.FIELD).get(0));
assertEquals(HttpPostRequestTest.VALUE2, this.testServer.parameters.get(HttpPostRequestTest.FIELD2).get(0));
}
@Test
public void testSimplePostWithSingleMultipartFormField() throws Exception {
String divider = UUID.randomUUID().toString();
String header = "POST " + HttpServerTest.URI + " HTTP/1.1\nContent-Type: " + "multipart/form-data; boundary=" + divider + "\r\n";
String content =
"--" + divider + "\r\n" + "Content-Disposition: form-data; name=\"" + HttpPostRequestTest.FIELD + "\"\r\n" + "\r\n" + HttpPostRequestTest.VALUE + "\r\n"
+ "--" + divider + "--\r\n";
int size = content.length() + header.length();
int contentLengthHeaderValueSize = String.valueOf(size).length();
int contentLength = size + contentLengthHeaderValueSize + HttpPostRequestTest.CONTENT_LENGTH.length();
String input = header + HttpPostRequestTest.CONTENT_LENGTH + (contentLength + 4) + "\r\n\r\n" + content;
invokeServer(input);
assertEquals(1, this.testServer.parms.size());
assertEquals(HttpPostRequestTest.VALUE, this.testServer.parms.get(HttpPostRequestTest.FIELD));
assertEquals(1, this.testServer.parameters.size());
assertEquals(HttpPostRequestTest.VALUE, this.testServer.parameters.get(HttpPostRequestTest.FIELD).get(0));
}
@Test
public void testSimpleRawPostData() throws Exception {
String header = "POST " + HttpServerTest.URI + " HTTP/1.1\n";
String content = HttpPostRequestTest.VALUE_TEST_SIMPLE_RAW_DATA_WITH_AMPHASIS + "\r\n";
int size = content.length() + header.length();
int contentLengthHeaderValueSize = String.valueOf(size).length();
int contentLength = size + contentLengthHeaderValueSize + HttpPostRequestTest.CONTENT_LENGTH.length();
String input = header + HttpPostRequestTest.CONTENT_LENGTH + (contentLength + 4) + "\r\n\r\n" + content;
invokeServer(input);
assertEquals(0, this.testServer.parms.size());
assertEquals(0, this.testServer.parameters.size());
assertEquals(1, this.testServer.files.size());
assertEquals(HttpPostRequestTest.VALUE_TEST_SIMPLE_RAW_DATA_WITH_AMPHASIS, this.testServer.files.get(HttpPostRequestTest.POST_RAW_CONTENT_FILE_ENTRY));
}
@Test
public void testPostWithMultipartFormFieldsAndFile() throws IOException {
String fileName = "GrandCanyon.txt";
String fileContent = HttpPostRequestTest.VALUE;
String divider = UUID.randomUUID().toString();
String header = "POST " + HttpServerTest.URI + " HTTP/1.1\nContent-Type: " + "multipart/form-data; boundary=" + divider + "\n";
String content =
"--" + divider + "\r\n" + "Content-Disposition: form-data; name=\"" + HttpPostRequestTest.FIELD + "\"; filename=\"" + fileName + "\"\r\n"
+ "Content-Type: image/jpeg\r\n" + "\r\n" + fileContent + "\r\n" + "--" + divider + "\r\n" + "Content-Disposition: form-data; name=\""
+ HttpPostRequestTest.FIELD2 + "\"\r\n" + "\r\n" + HttpPostRequestTest.VALUE2 + "\r\n" + "--" + divider + "--\r\n";
int size = content.length() + header.length();
int contentLengthHeaderValueSize = String.valueOf(size).length();
int contentLength = size + contentLengthHeaderValueSize + HttpPostRequestTest.CONTENT_LENGTH.length();
String input = header + HttpPostRequestTest.CONTENT_LENGTH + (contentLength + 4) + "\r\n\r\n" + content;
invokeServer(input);
assertEquals("Parms count did not match.", 2, this.testServer.parms.size());
assertEquals("Parameters count did not match.", 2, this.testServer.parameters.size());
assertEquals("Param value did not match", HttpPostRequestTest.VALUE2, this.testServer.parms.get(HttpPostRequestTest.FIELD2));
assertEquals("Parameter value did not match", HttpPostRequestTest.VALUE2, this.testServer.parameters.get(HttpPostRequestTest.FIELD2).get(0));
BufferedReader reader = new BufferedReader(new FileReader(this.testServer.files.get(HttpPostRequestTest.FIELD)));
List lines = readLinesFromFile(reader);
assertLinesOfText(new String[]{
fileContent
}, lines);
}
@Test
public void testPostWithMultipartFormUploadMultipleFiles() throws IOException {
String fileName = "GrandCanyon.txt";
String fileContent = HttpPostRequestTest.VALUE;
String file2Name = "AnotherPhoto.txt";
String file2Content = HttpPostRequestTest.VALUE2;
String divider = UUID.randomUUID().toString();
String header = "POST " + HttpServerTest.URI + " HTTP/1.1\nContent-Type: " + "multipart/form-data; boundary=" + divider + "\n";
String content = "--" + divider + "\r\n"//
+ "Content-Disposition: form-data; name=\"" + HttpPostRequestTest.FIELD + "\"; filename=\"" + fileName + "\"\r\n" //
+ "Content-Type: image/jpeg\r\n" + "\r\n" //
+ fileContent + "\r\n" //
+ "--" + divider + "\r\n" //
+ "Content-Disposition: form-data; name=\"" + HttpPostRequestTest.FIELD2 + "\"; filename=\"" + file2Name + "\"\r\n" //
+ "Content-Type: image/jpeg\r\n" + "\r\n" //
+ file2Content + "\r\n" //
+ "\r\n" //
+ "--" + divider + "--\r\n";
int size = content.length() + header.length();
int contentLengthHeaderValueSize = String.valueOf(size).length();
int contentLength = size + contentLengthHeaderValueSize + HttpPostRequestTest.CONTENT_LENGTH.length();
String input = header + HttpPostRequestTest.CONTENT_LENGTH + (contentLength + 4) + "\r\n\r\n" + content;
invokeServer(input);
assertEquals("Parm count did not match.", 2, this.testServer.parms.size());
assertEquals("Parameter count did not match.", 2, this.testServer.parameters.size());
BufferedReader reader = new BufferedReader(new FileReader(this.testServer.files.get(HttpPostRequestTest.FIELD)));
List lines = readLinesFromFile(reader);
assertLinesOfText(new String[]{
fileContent
}, lines);
String fileName2 = this.testServer.files.get(HttpPostRequestTest.FIELD2);
int testNumber = 0;
while (fileName2 == null && testNumber < 5) {
testNumber++;
fileName2 = this.testServer.files.get(HttpPostRequestTest.FIELD2 + testNumber);
}
reader = new BufferedReader(new FileReader(fileName2));
lines = readLinesFromFile(reader);
assertLinesOfText(new String[]{
file2Content
}, lines);
}
@Test
public void testPostWithMultipartFormUploadFileWithMultilineContent() throws Exception {
String filename = "GrandCanyon.txt";
String lineSeparator = "\n";
String fileContent = HttpPostRequestTest.VALUE + lineSeparator + HttpPostRequestTest.VALUE + lineSeparator + HttpPostRequestTest.VALUE;
String input = preparePostWithMultipartForm(filename, fileContent);
invokeServer(input);
assertEquals("Parm count did not match.", 1, this.testServer.parms.size());
assertEquals("Parameter count did not match.", 1, this.testServer.parameters.size());
BufferedReader reader = new BufferedReader(new FileReader(this.testServer.files.get(HttpPostRequestTest.FIELD)));
List lines = readLinesFromFile(reader);
assertLinesOfText(fileContent.split(lineSeparator), lines);
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/HttpPutRequestTest.java
================================================
package org.nanohttpd.junit.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* 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 static junit.framework.Assert.assertTrue;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FileReader;
import java.util.List;
import org.junit.Test;
public class HttpPutRequestTest extends HttpServerTest {
@Test
public void testPutRequestSendsContent() throws Exception {
ByteArrayOutputStream outputStream = invokeServer("PUT " + HttpServerTest.URI + " HTTP/1.1\r\n\r\nBodyData 1\nLine 2");
String[] expectedOutput = {
"HTTP/1.1 200 OK",
"Content-Type: text/html",
"Date: .*",
"Connection: keep-alive",
"Content-Length: 0",
""
};
assertResponse(outputStream, expectedOutput);
assertTrue(this.testServer.files.containsKey("content"));
BufferedReader reader = null;
try {
String[] expectedInputToServeMethodViaFile = {
"BodyData 1",
"Line 2"
};
reader = new BufferedReader(new FileReader(this.testServer.files.get("content")));
List lines = readLinesFromFile(reader);
assertLinesOfText(expectedInputToServeMethodViaFile, lines);
} finally {
if (reader != null) {
reader.close();
}
}
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/HttpSSLServerTest.java
================================================
package org.nanohttpd.junit.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* 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.File;
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpTrace;
import org.apache.http.impl.client.DefaultHttpClient;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.nanohttpd.protocols.http.NanoHTTPD;
public class HttpSSLServerTest extends HttpServerTest {
@Test
public void testSSLConnection() throws ClientProtocolException, IOException {
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpTrace httphead = new HttpTrace("https://localhost:9043/index.html");
HttpResponse response = httpclient.execute(httphead);
HttpEntity entity = response.getEntity();
Assert.assertEquals(200, response.getStatusLine().getStatusCode());
Assert.assertEquals(9043, this.testServer.getListeningPort());
Assert.assertTrue(this.testServer.isAlive());
}
/**
* using http to connect to https.
*
* @throws ClientProtocolException
* @throws IOException
*/
@Test(expected = ClientProtocolException.class)
public void testHttpOnSSLConnection() throws ClientProtocolException, IOException {
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpTrace httphead = new HttpTrace("http://localhost:9043/index.html");
httpclient.execute(httphead);
}
@Before
public void setUp() throws Exception {
System.setProperty("javax.net.ssl.trustStore", new File("src/test/resources/keystore.jks").getAbsolutePath());
this.testServer = new TestServer(9043);
this.testServer.makeSecure(NanoHTTPD.makeSSLSocketFactory("/keystore.jks", "password".toCharArray()), null);
this.tempFileManager = new TestTempFileManager();
this.testServer.start();
try {
long start = System.currentTimeMillis();
Thread.sleep(100L);
while (!this.testServer.wasStarted()) {
Thread.sleep(100L);
if (System.currentTimeMillis() - start > 2000) {
Assert.fail("could not start server");
}
}
} catch (InterruptedException e) {
}
}
@After
public void tearDown() {
this.testServer.stop();
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/HttpServerTest.java
================================================
package org.nanohttpd.junit.protocols.http;
import static org.junit.Assert.assertEquals;
/*
* #%L
* NanoHttpd-Core
* %%
* 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 static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringReader;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.nanohttpd.protocols.http.HTTPSession;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.request.Method;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.tempfiles.DefaultTempFileManager;
import org.nanohttpd.protocols.http.tempfiles.ITempFileManager;
/**
* @author Paul S. Hawke (paul.hawke@gmail.com) On: 3/10/13 at 8:32 PM
*/
public class HttpServerTest {
public static class TestServer extends NanoHTTPD {
public Response response = Response.newFixedLengthResponse("");
public String uri;
public Method method;
public Map header;
public Map parms;
public Map> parameters;
public Map files;
public Map> decodedParamters;
public Map> decodedParamtersFromParameter;
public String queryParameterString;
public TestServer() {
super(8192);
}
public TestServer(int port) {
super(port);
}
public HTTPSession createSession(ITempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) {
return new HTTPSession(this, tempFileManager, inputStream, outputStream);
}
public HTTPSession createSession(ITempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream, InetAddress inetAddress) {
return new HTTPSession(this, tempFileManager, inputStream, outputStream, inetAddress);
}
@Override
public Response serve(IHTTPSession session) {
this.uri = session.getUri();
this.method = session.getMethod();
this.header = session.getHeaders();
this.files = new HashMap();
try {
session.parseBody(this.files);
} catch (Exception e) {
e.printStackTrace();
}
this.parms = session.getParms();
this.parameters = session.getParameters();
this.queryParameterString = session.getQueryParameterString();
this.decodedParamtersFromParameter = decodeParameters(this.queryParameterString);
this.decodedParamters = decodeParameters(session.getQueryParameterString());
return this.response;
}
}
public static class TestTempFileManager extends DefaultTempFileManager {
public void _clear() {
super.clear();
}
@Override
public void clear() {
// ignore
}
}
public static final String URI = "http://www.myserver.org/pub/WWW/someFile.html";
protected TestServer testServer;
protected TestTempFileManager tempFileManager;
protected void assertLinesOfText(String[] expected, List lines) {
// assertEquals(expected.length, lines.size());
for (int i = 0; i < expected.length; i++) {
String line = lines.get(i);
assertTrue("Output line " + i + " doesn't match expectation.\n" + " Output: " + line + "\n" + "Expected: " + expected[i], line.matches(expected[i]));
}
}
protected void assertResponse(ByteArrayOutputStream outputStream, String[] expected) throws IOException {
List lines = getOutputLines(outputStream);
assertLinesOfText(expected, lines);
}
protected List getOutputLines(ByteArrayOutputStream outputStream) throws IOException {
BufferedReader reader = new BufferedReader(new StringReader(outputStream.toString()));
return readLinesFromFile(reader);
}
protected ByteArrayOutputStream invokeServer(String request) {
ByteArrayInputStream inputStream = new ByteArrayInputStream(request.getBytes());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
HTTPSession session = this.testServer.createSession(this.tempFileManager, inputStream, outputStream);
try {
session.execute();
} catch (IOException e) {
fail("" + e);
e.printStackTrace();
}
return outputStream;
}
protected List readLinesFromFile(BufferedReader reader) throws IOException {
List lines = new ArrayList();
String line = "";
while (line != null) {
line = reader.readLine();
if (line != null) {
lines.add(line.trim());
}
}
return lines;
}
@Before
public void setUp() throws Exception {
this.testServer = new TestServer();
this.tempFileManager = new TestTempFileManager();
}
@After
public void tearDown() {
this.tempFileManager._clear();
}
@Test
public void testServerExists() {
assertNotNull(this.testServer);
}
@Test
public void testMultipartFormData() throws IOException {
final int testPort = 4589;
NanoHTTPD server = null;
try {
server = new NanoHTTPD(testPort) {
final Map files = new HashMap();
@Override
public Response serve(IHTTPSession session) {
StringBuilder responseMsg = new StringBuilder();
try {
session.parseBody(this.files);
for (String key : files.keySet()) {
responseMsg.append(key);
}
} catch (Exception e) {
responseMsg.append(e.getMessage());
}
return Response.newFixedLengthResponse(responseMsg.toString());
}
};
server.start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://localhost:" + testPort);
final String fileName = "file-upload-test.htm";
FileBody bin = new FileBody(new File(getClass().getClassLoader().getResource(fileName).getFile()));
StringBody comment = new StringBody("Filename: " + fileName);
MultipartEntity reqEntity = new MultipartEntity();
reqEntity.addPart("bin", bin);
reqEntity.addPart("comment", comment);
httppost.setEntity(reqEntity);
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(instream, "UTF-8"));
String line = reader.readLine();
assertNotNull(line, "Invalid server reponse");
assertEquals("Server failed multi-part data parse" + line, "bincomment", line);
reader.close();
instream.close();
}
} finally {
if (server != null) {
server.stop();
}
}
}
@Test
public void testTempFileInterface() throws IOException {
final int testPort = 4589;
NanoHTTPD server = new NanoHTTPD(testPort) {
final Map files = new HashMap();
@Override
public Response serve(IHTTPSession session) {
String responseMsg = "pass";
try {
session.parseBody(this.files);
for (String key : files.keySet()) {
if (!(new File(files.get(key))).exists()) {
responseMsg = "fail";
}
}
} catch (Exception e) {
responseMsg = e.getMessage();
}
return Response.newFixedLengthResponse(responseMsg.toString());
}
};
server.start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://localhost:" + testPort);
final String fileName = "file-upload-test.htm";
FileBody bin = new FileBody(new File(getClass().getClassLoader().getResource(fileName).getFile()));
StringBody comment = new StringBody("Filename: " + fileName);
MultipartEntity reqEntity = new MultipartEntity();
reqEntity.addPart("bin", bin);
reqEntity.addPart("comment", comment);
httppost.setEntity(reqEntity);
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(instream, "UTF-8"));
String line = reader.readLine();
assertNotNull(line, "Invalid server reponse");
assertEquals("Server file check failed: " + line, "pass", line);
reader.close();
instream.close();
} else {
fail("No server response");
}
server.stop();
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/HttpSessionHeadersTest.java
================================================
package org.nanohttpd.junit.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.net.InetAddress;
import org.junit.Test;
import org.nanohttpd.protocols.http.HTTPSession;
public class HttpSessionHeadersTest extends HttpServerTest {
private static final String DUMMY_REQUEST_CONTENT = "dummy request content";
private static final TestTempFileManager TEST_TEMP_FILE_MANAGER = new TestTempFileManager();
@Test
public void testHeadersRemoteIp() throws Exception {
ByteArrayInputStream inputStream = new ByteArrayInputStream(HttpSessionHeadersTest.DUMMY_REQUEST_CONTENT.getBytes());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
String[] ipAddresses = {
"127.0.0.1",
"8.8.8.8",
};
for (String ipAddress : ipAddresses) {
InetAddress inetAddress = InetAddress.getByName(ipAddress);
HTTPSession session = this.testServer.createSession(HttpSessionHeadersTest.TEST_TEMP_FILE_MANAGER, inputStream, outputStream, inetAddress);
assertEquals(ipAddress, session.getRemoteIpAddress());
}
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/HttpSessionTest.java
================================================
package org.nanohttpd.junit.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* 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 static org.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.junit.Test;
import org.nanohttpd.protocols.http.HTTPSession;
public class HttpSessionTest extends HttpServerTest {
private static final String DUMMY_REQUEST_CONTENT = "dummy request content";
private static final TestTempFileManager TEST_TEMP_FILE_MANAGER = new TestTempFileManager();
@Test
public void testSessionRemoteIPAddress() throws UnknownHostException {
ByteArrayInputStream inputStream = new ByteArrayInputStream(HttpSessionTest.DUMMY_REQUEST_CONTENT.getBytes());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
HTTPSession session = this.testServer.createSession(HttpSessionTest.TEST_TEMP_FILE_MANAGER, inputStream, outputStream, inetAddress);
assertEquals("127.0.0.1", session.getRemoteIpAddress());
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/InvalidRequestTest.java
================================================
package org.nanohttpd.junit.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* 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 static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import org.junit.Test;
public class InvalidRequestTest extends HttpServerTest {
@Test
public void testGetRequestWithoutProtocol() {
invokeServer("GET " + HttpServerTest.URI + "\r\nX-Important-Header: foo");
assertNotNull(this.testServer.parms);
assertNotNull(this.testServer.parameters);
assertTrue(this.testServer.header.size() > 0);
assertNotNull(this.testServer.files);
assertNotNull(this.testServer.uri);
}
@Test
public void testGetRequestWithProtocol() {
invokeServer("GET " + HttpServerTest.URI + " HTTP/1.1\r\nX-Important-Header: foo");
assertNotNull(this.testServer.parms);
assertNotNull(this.testServer.parameters);
assertTrue(this.testServer.header.size() > 0);
assertNotNull(this.testServer.files);
assertNotNull(this.testServer.uri);
}
@Test
public void testPostRequestWithoutProtocol() {
invokeServer("POST " + HttpServerTest.URI + "\r\nContent-Length: 123");
assertNotNull(this.testServer.parms);
assertNotNull(this.testServer.parameters);
assertTrue(this.testServer.header.size() > 0);
assertNotNull(this.testServer.files);
assertNotNull(this.testServer.uri);
}
@Test
public void testPostRequestWithProtocol() {
invokeServer("POST " + HttpServerTest.URI + " HTTP/1.1\r\nContent-Length: 123");
assertNotNull(this.testServer.parms);
assertNotNull(this.testServer.parameters);
assertTrue(this.testServer.header.size() > 0);
assertNotNull(this.testServer.files);
assertNotNull(this.testServer.uri);
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/JavaIOTempDirExistTest.java
================================================
package org.nanohttpd.junit.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* 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.File;
import java.io.IOException;
import java.util.UUID;
import org.junit.Assert;
import org.junit.Test;
import org.nanohttpd.protocols.http.tempfiles.DefaultTempFile;
import org.nanohttpd.protocols.http.tempfiles.DefaultTempFileManager;
/**
* Created by Victor Nikiforov on 10/16/15.
*/
public class JavaIOTempDirExistTest {
@Test
public void testJavaIoTempDefault() throws Exception {
String tmpdir = System.getProperty("java.io.tmpdir");
DefaultTempFileManager manager = new DefaultTempFileManager();
DefaultTempFile tempFile = (DefaultTempFile) manager.createTempFile("xx");
File tempFileBackRef = new File(tempFile.getName());
Assert.assertEquals(tempFileBackRef.getParentFile(), new File(tmpdir));
// force an exception
tempFileBackRef.delete();
Exception e = null;
try {
tempFile.delete();
} catch (Exception ex) {
e = ex;
}
Assert.assertNotNull(e);
manager.clear();
}
@Test
public void testJavaIoTempSpecific() throws IOException {
final String tmpdir = System.getProperty("java.io.tmpdir");
try {
String tempFileName = UUID.randomUUID().toString();
File newDir = new File("target", tempFileName);
System.setProperty("java.io.tmpdir", newDir.getAbsolutePath());
Assert.assertEquals(false, newDir.exists());
new DefaultTempFileManager();
Assert.assertEquals(true, newDir.exists());
newDir.delete();
} finally {
System.setProperty("java.io.tmpdir", tmpdir);
}
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/LoadKeyStoreTest.java
================================================
package org.nanohttpd.junit.protocols.http;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.io.IOException;
import java.io.InputStream;
import javax.net.ssl.SSLServerSocketFactory;
/*
* #%L
* NanoHttpd-Core
* %%
* 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 org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.nanohttpd.protocols.http.NanoHTTPD;
public class LoadKeyStoreTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void loadKeyStoreFromResources() throws Exception {
String keyStorePath = "/keystore.jks";
InputStream resourceAsStream = this.getClass().getResourceAsStream(keyStorePath);
assertNotNull(resourceAsStream);
SSLServerSocketFactory sslServerSocketFactory = NanoHTTPD.makeSSLSocketFactory(keyStorePath, "password".toCharArray());
assertNotNull(sslServerSocketFactory);
}
@Test
public void loadKeyStoreFromResourcesWrongPassword() throws Exception {
String keyStorePath = "/keystore.jks";
InputStream resourceAsStream = this.getClass().getResourceAsStream(keyStorePath);
assertNotNull(resourceAsStream);
thrown.expect(IOException.class);
NanoHTTPD.makeSSLSocketFactory(keyStorePath, "wrongpassword".toCharArray());
}
@Test
public void loadNonExistentKeyStoreFromResources() throws Exception {
String nonExistentPath = "/nokeystorehere.jks";
InputStream resourceAsStream = this.getClass().getResourceAsStream(nonExistentPath);
assertNull(resourceAsStream);
thrown.expect(IOException.class);
NanoHTTPD.makeSSLSocketFactory(nonExistentPath, "".toCharArray());
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/MimeTest.java
================================================
package org.nanohttpd.junit.protocols.http;
/*
* #%L
* NanoHttpd-Core
* %%
* 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 org.junit.Assert;
import org.junit.Test;
import org.nanohttpd.protocols.http.NanoHTTPD;
public class MimeTest {
@Test
public void testExistingMimeType() throws Exception {
Assert.assertEquals("text/html", NanoHTTPD.getMimeTypeForFile("xxxx.html"));
}
@Test
public void testNotExistingMimeType() throws Exception {
Assert.assertNull(NanoHTTPD.mimeTypes().get("notExistent"));
Assert.assertEquals("application/octet-stream", NanoHTTPD.getMimeTypeForFile("xxxx.notExistent"));
}
@Test
public void testOverwritenMimeType() throws Exception {
Assert.assertEquals("video/wrongOverwrite", NanoHTTPD.getMimeTypeForFile("xxxx.ts"));
}
@Test
public void testManualMimeType() throws Exception {
NanoHTTPD.mimeTypes().put("flv", "video/manualOverwrite");
Assert.assertEquals("video/manualOverwrite", NanoHTTPD.getMimeTypeForFile("xxxx.flv"));
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/SSLServerSocketFactoryTest.java
================================================
package org.nanohttpd.junit.protocols.http;
import java.io.File;
/*
* #%L
* NanoHttpd-Core
* %%
* 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.IOException;
import java.util.Arrays;
import javax.net.ssl.SSLServerSocket;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpTrace;
import org.apache.http.impl.client.DefaultHttpClient;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.sockets.SecureServerSocketFactory;
public class SSLServerSocketFactoryTest extends HttpServerTest {
@Test
public void testSSLConnection() throws ClientProtocolException, IOException {
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpTrace httphead = new HttpTrace("https://localhost:9043/index.html");
HttpResponse response = httpclient.execute(httphead);
HttpEntity entity = response.getEntity();
Assert.assertEquals(200, response.getStatusLine().getStatusCode());
Assert.assertEquals(9043, this.testServer.getListeningPort());
Assert.assertTrue(this.testServer.isAlive());
}
@Test
public void testCreatePassesTheProtocolsToServerSocket() throws IOException {
// first find the supported protocols
SecureServerSocketFactory secureServerSocketFactory = new SecureServerSocketFactory(NanoHTTPD.makeSSLSocketFactory("/keystore.jks", "password".toCharArray()), null);
SSLServerSocket socket = (SSLServerSocket) secureServerSocketFactory.create();
String[] protocols = socket.getSupportedProtocols();
// remove one element from supported protocols
if (protocols.length > 0) {
protocols = Arrays.copyOfRange(protocols, 0, protocols.length - 1);
}
// test
secureServerSocketFactory = new SecureServerSocketFactory(NanoHTTPD.makeSSLSocketFactory("/keystore.jks", "password".toCharArray()), protocols);
socket = (SSLServerSocket) secureServerSocketFactory.create();
Assert.assertArrayEquals("Enabled protocols specified in the factory were not set to the socket.", protocols, socket.getEnabledProtocols());
}
@Before
public void setUp() throws Exception {
System.setProperty("javax.net.ssl.trustStore", new File("src/test/resources/keystore.jks").getAbsolutePath());
this.testServer = new TestServer(9043);
this.testServer.setServerSocketFactory(new SecureServerSocketFactory(NanoHTTPD.makeSSLSocketFactory("/keystore.jks", "password".toCharArray()), null));
this.tempFileManager = new TestTempFileManager();
this.testServer.start();
try {
long start = System.currentTimeMillis();
Thread.sleep(100L);
while (!this.testServer.wasStarted()) {
Thread.sleep(100L);
if (System.currentTimeMillis() - start > 2000) {
Assert.fail("could not start server");
}
}
} catch (InterruptedException e) {
}
}
@After
public void tearDown() {
this.testServer.stop();
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/ServerSocketFactoryTest.java
================================================
package org.nanohttpd.junit.protocols.http;
import java.io.File;
/*
* #%L
* NanoHttpd-Core
* %%
* 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.IOException;
import java.net.ServerSocket;
import org.junit.Assert;
import org.junit.Test;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.sockets.SecureServerSocketFactory;
import org.nanohttpd.util.IFactoryThrowing;
public class ServerSocketFactoryTest extends NanoHTTPD {
public static final int PORT = 8192;
public ServerSocketFactoryTest() {
super(PORT);
this.setServerSocketFactory(new TestFactory());
}
@Test
public void isCustomServerSocketFactory() {
System.out.println("CustomServerSocketFactory test");
Assert.assertTrue(this.getServerSocketFactory() instanceof TestFactory);
}
@Test
public void testCreateServerSocket() {
System.out.println("CreateServerSocket test");
ServerSocket ss = null;
try {
ss = this.getServerSocketFactory().create();
} catch (IOException e) {
}
Assert.assertTrue(ss != null);
}
@Test
public void testSSLServerSocketFail() {
String[] protocols = {
""
};
System.setProperty("javax.net.ssl.trustStore", new File("src/test/resources/keystore.jks").getAbsolutePath());
IFactoryThrowing ssFactory = new SecureServerSocketFactory(null, protocols);
ServerSocket ss = null;
try {
ss = ssFactory.create();
} catch (Exception e) {
}
Assert.assertTrue(ss == null);
}
private class TestFactory implements IFactoryThrowing {
@Override
public ServerSocket create() {
try {
return new ServerSocket();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/StatusTest.java
================================================
package org.nanohttpd.junit.protocols.http;
import java.util.HashMap;
import java.util.Map;
/*
* #%L
* NanoHttpd-Core
* %%
* 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 org.junit.Assert;
import org.junit.Test;
import org.nanohttpd.protocols.http.response.Status;
public class StatusTest {
@Test
public void testMessages() {
// These are values where the name of the enum does not match the status
// code description.
// By default you should not need to add any new values to this map if
// you
// make the name of the enum name match the status code description.
Map overrideValues = new HashMap();
overrideValues.put(Status.INTERNAL_ERROR, "500 Internal Server Error");
overrideValues.put(Status.SWITCH_PROTOCOL, "101 Switching Protocols");
overrideValues.put(Status.OK, "200 OK");
overrideValues.put(Status.MULTI_STATUS, "207 Multi-Status");
overrideValues.put(Status.REDIRECT, "301 Moved Permanently");
overrideValues.put(Status.REDIRECT_SEE_OTHER, "303 See Other");
overrideValues.put(Status.RANGE_NOT_SATISFIABLE, "416 Requested Range Not Satisfiable");
overrideValues.put(Status.UNSUPPORTED_HTTP_VERSION, "505 HTTP Version Not Supported");
for (Status status : Status.values()) {
if (overrideValues.containsKey(status)) {
Assert.assertEquals(overrideValues.get(status), status.getDescription());
} else {
Assert.assertEquals(getExpectedMessage(status), status.getDescription());
}
}
}
private String getExpectedMessage(Status status) {
String name = status.name().toLowerCase();
String[] words = name.split("_");
StringBuilder builder = new StringBuilder();
builder.append(status.getRequestStatus());
builder.append(' ');
for (int i = 0; i < words.length; i++) {
builder.append(Character.toUpperCase(words[i].charAt(0)));
builder.append(words[i].substring(1));
builder.append(' ');
}
return builder.toString().trim();
}
@Test
public void testLookup() throws Exception {
Assert.assertEquals(Status.SWITCH_PROTOCOL, Status.lookup(101));
Assert.assertEquals(Status.OK, Status.lookup(200));
Assert.assertEquals(Status.CREATED, Status.lookup(201));
Assert.assertEquals(Status.ACCEPTED, Status.lookup(202));
Assert.assertEquals(Status.NO_CONTENT, Status.lookup(204));
Assert.assertEquals(Status.PARTIAL_CONTENT, Status.lookup(206));
Assert.assertEquals(Status.MULTI_STATUS, Status.lookup(207));
Assert.assertEquals(Status.REDIRECT, Status.lookup(301));
Assert.assertEquals(Status.FOUND, Status.lookup(302));
Assert.assertEquals(Status.REDIRECT_SEE_OTHER, Status.lookup(303));
Assert.assertEquals(Status.NOT_MODIFIED, Status.lookup(304));
Assert.assertEquals(Status.TEMPORARY_REDIRECT, Status.lookup(307));
Assert.assertEquals(Status.BAD_REQUEST, Status.lookup(400));
Assert.assertEquals(Status.UNAUTHORIZED, Status.lookup(401));
Assert.assertEquals(Status.FORBIDDEN, Status.lookup(403));
Assert.assertEquals(Status.NOT_FOUND, Status.lookup(404));
Assert.assertEquals(Status.METHOD_NOT_ALLOWED, Status.lookup(405));
Assert.assertEquals(Status.NOT_ACCEPTABLE, Status.lookup(406));
Assert.assertEquals(Status.REQUEST_TIMEOUT, Status.lookup(408));
Assert.assertEquals(Status.CONFLICT, Status.lookup(409));
Assert.assertEquals(Status.GONE, Status.lookup(410));
Assert.assertEquals(Status.LENGTH_REQUIRED, Status.lookup(411));
Assert.assertEquals(Status.PRECONDITION_FAILED, Status.lookup(412));
Assert.assertEquals(Status.PAYLOAD_TOO_LARGE, Status.lookup(413));
Assert.assertEquals(Status.UNSUPPORTED_MEDIA_TYPE, Status.lookup(415));
Assert.assertEquals(Status.RANGE_NOT_SATISFIABLE, Status.lookup(416));
Assert.assertEquals(Status.EXPECTATION_FAILED, Status.lookup(417));
Assert.assertEquals(Status.TOO_MANY_REQUESTS, Status.lookup(429));
Assert.assertEquals(Status.INTERNAL_ERROR, Status.lookup(500));
Assert.assertEquals(Status.NOT_IMPLEMENTED, Status.lookup(501));
Assert.assertEquals(Status.SERVICE_UNAVAILABLE, Status.lookup(503));
Assert.assertEquals(Status.UNSUPPORTED_HTTP_VERSION, Status.lookup(505));
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/integration/CookieIntegrationTest.java
================================================
package org.nanohttpd.junit.protocols.http.integration;
/*
* #%L
* NanoHttpd-Core
* %%
* 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import org.apache.http.client.CookieStore;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.junit.Test;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.content.Cookie;
import org.nanohttpd.protocols.http.content.CookieHandler;
import org.nanohttpd.protocols.http.response.Response;
/**
* @author Paul S. Hawke (paul.hawke@gmail.com) On: 9/2/13 at 10:10 PM
*/
public class CookieIntegrationTest extends IntegrationTestBase {
public static class CookieTestServer extends NanoHTTPD {
List cookiesReceived = new ArrayList();
List cookiesToSend = new ArrayList();
public CookieTestServer() {
super(8192);
}
@Override
public Response serve(IHTTPSession session) {
CookieHandler cookies = session.getCookies();
for (String cookieName : cookies) {
this.cookiesReceived.add(new Cookie(cookieName, cookies.read(cookieName)));
}
for (Cookie c : this.cookiesToSend) {
cookies.set(c);
}
return Response.newFixedLengthResponse("Cookies!");
}
}
@Override
public CookieTestServer createTestServer() {
return new CookieTestServer();
}
@Test
public void testCookieSentBackToClient() throws Exception {
this.testServer.cookiesToSend.add(new Cookie("name", "value", 30));
HttpGet httpget = new HttpGet("http://localhost:8192/");
ResponseHandler responseHandler = new BasicResponseHandler();
this.httpclient.execute(httpget, responseHandler);
CookieStore cookies = this.httpclient.getCookieStore();
assertEquals(1, cookies.getCookies().size());
assertEquals("name", cookies.getCookies().get(0).getName());
assertEquals("value", cookies.getCookies().get(0).getValue());
}
@Test
public void testMultipleCookieSentBackToClient() throws Exception {
this.testServer.cookiesToSend.add(new Cookie("name0", "value0", 30));
this.testServer.cookiesToSend.add(new Cookie("name1", "value1", 30));
this.testServer.cookiesToSend.add(new Cookie("name2", "value2", 30));
this.testServer.cookiesToSend.add(new Cookie("name3", "value3", 30));
HttpGet httpget = new HttpGet("http://localhost:8192/");
ResponseHandler responseHandler = new BasicResponseHandler();
this.httpclient.execute(httpget, responseHandler);
assertEquals(4, this.httpclient.getCookieStore().getCookies().size());
}
@Test
public void testNoCookies() throws Exception {
HttpGet httpget = new HttpGet("http://localhost:8192/");
ResponseHandler responseHandler = new BasicResponseHandler();
this.httpclient.execute(httpget, responseHandler);
CookieStore cookies = this.httpclient.getCookieStore();
assertEquals(0, cookies.getCookies().size());
}
@Test
public void testServerReceivesCookiesSentFromClient() throws Exception {
BasicClientCookie clientCookie = new BasicClientCookie("name", "value");
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_YEAR, 100);
clientCookie.setExpiryDate(calendar.getTime());
clientCookie.setDomain("localhost");
this.httpclient.getCookieStore().addCookie(clientCookie);
HttpGet httpget = new HttpGet("http://localhost:8192/");
ResponseHandler responseHandler = new BasicResponseHandler();
this.httpclient.execute(httpget, responseHandler);
assertEquals(1, this.testServer.cookiesReceived.size());
assertTrue(this.testServer.cookiesReceived.get(0).getHTTPHeader().contains("name=value"));
}
@Test
public void testServerReceivesMultipleCookiesSentFromClient() throws Exception {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_YEAR, 100);
Date date = calendar.getTime();
BasicClientCookie clientCookie0 = new BasicClientCookie("name0", "value0");
BasicClientCookie clientCookie1 = new BasicClientCookie("name1", "value1");
BasicClientCookie clientCookie2 = new BasicClientCookie("name2", "value2");
BasicClientCookie clientCookie3 = new BasicClientCookie("name3", "value3");
clientCookie0.setExpiryDate(date);
clientCookie0.setDomain("localhost");
clientCookie1.setExpiryDate(date);
clientCookie1.setDomain("localhost");
clientCookie2.setExpiryDate(date);
clientCookie2.setDomain("localhost");
clientCookie3.setExpiryDate(date);
clientCookie3.setDomain("localhost");
this.httpclient.getCookieStore().addCookie(clientCookie0);
this.httpclient.getCookieStore().addCookie(clientCookie1);
this.httpclient.getCookieStore().addCookie(clientCookie2);
this.httpclient.getCookieStore().addCookie(clientCookie3);
HttpGet httpget = new HttpGet("http://localhost:8192/");
ResponseHandler responseHandler = new BasicResponseHandler();
this.httpclient.execute(httpget, responseHandler);
assertEquals(4, this.testServer.cookiesReceived.size());
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/integration/GZipIntegrationTest.java
================================================
package org.nanohttpd.junit.protocols.http.integration;
/*
* #%L
* NanoHttpd-Core
* %%
* 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 static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DecompressingHttpClient;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.response.Status;
public class GZipIntegrationTest extends IntegrationTestBase {
public static class TestServer extends NanoHTTPD {
public Response response;
public TestServer() {
super(8192);
}
@Override
public Response serve(IHTTPSession session) {
return response.setUseGzip(true);
}
}
@Override
public TestServer createTestServer() {
return new TestServer();
}
@Test
public void contentEncodingShouldBeAddedToFixedLengthResponses() throws IOException {
testServer.response = Response.newFixedLengthResponse("This is a test");
HttpGet request = new HttpGet("http://localhost:8192/");
request.addHeader("Accept-encoding", "gzip");
HttpResponse response = httpclient.execute(request);
Header contentEncoding = response.getFirstHeader("content-encoding");
assertNotNull("Content-Encoding should be set", contentEncoding);
assertEquals("gzip", contentEncoding.getValue());
}
@Test
public void contentEncodingShouldBeAddedToChunkedResponses() throws IOException {
InputStream data = new ByteArrayInputStream("This is a test".getBytes("UTF-8"));
testServer.response = Response.newChunkedResponse(Status.OK, "text/plain", data);
HttpGet request = new HttpGet("http://localhost:8192/");
request.addHeader("Accept-encoding", "gzip");
HttpResponse response = httpclient.execute(request);
Header contentEncoding = response.getFirstHeader("content-encoding");
assertNotNull("Content-Encoding should be set", contentEncoding);
assertEquals("gzip", contentEncoding.getValue());
}
@Test
public void shouldFindCorrectAcceptEncodingAmongMany() throws IOException {
testServer.response = Response.newFixedLengthResponse("This is a test");
HttpGet request = new HttpGet("http://localhost:8192/");
request.addHeader("Accept-encoding", "deflate,gzip");
HttpResponse response = httpclient.execute(request);
Header contentEncoding = response.getFirstHeader("content-encoding");
assertNotNull("Content-Encoding should be set", contentEncoding);
assertEquals("gzip", contentEncoding.getValue());
}
@Test
public void contentLengthShouldBeRemovedFromZippedResponses() throws IOException {
testServer.response = Response.newFixedLengthResponse("This is a test");
HttpGet request = new HttpGet("http://localhost:8192/");
request.addHeader("Accept-encoding", "gzip");
HttpResponse response = httpclient.execute(request);
Header contentLength = response.getFirstHeader("content-length");
assertNull("Content-Length should not be set when gzipping response", contentLength);
}
@Test
public void fixedLengthContentIsEncodedProperly() throws IOException {
testServer.response = Response.newFixedLengthResponse("This is a test");
HttpGet request = new HttpGet("http://localhost:8192/");
request.addHeader("Accept-encoding", "gzip");
HttpResponse response = new DecompressingHttpClient(httpclient).execute(request);
assertEquals("This is a test", EntityUtils.toString(response.getEntity()));
}
@Test
public void chunkedContentIsEncodedProperly() throws IOException {
InputStream data = new ByteArrayInputStream("This is a test".getBytes("UTF-8"));
testServer.response = Response.newChunkedResponse(Status.OK, "text/plain", data);
HttpGet request = new HttpGet("http://localhost:8192/");
request.addHeader("Accept-encoding", "gzip");
HttpResponse response = new DecompressingHttpClient(httpclient).execute(request);
assertEquals("This is a test", EntityUtils.toString(response.getEntity()));
}
@Test
public void noGzipWithoutAcceptEncoding() throws IOException {
testServer.response = Response.newFixedLengthResponse("This is a test");
HttpGet request = new HttpGet("http://localhost:8192/");
HttpResponse response = httpclient.execute(request);
Header contentEncoding = response.getFirstHeader("content-encoding");
assertThat(contentEncoding, is(nullValue()));
assertEquals("This is a test", EntityUtils.toString(response.getEntity()));
}
@Test
public void contentShouldNotBeGzippedIfContentLengthIsAddedManually() throws IOException {
testServer.response = Response.newFixedLengthResponse("This is a test");
testServer.response.addHeader("Content-Length", "" + ("This is a test".getBytes("UTF-8").length));
HttpGet request = new HttpGet("http://localhost:8192/");
request.addHeader("Accept-encoding", "gzip");
HttpResponse response = httpclient.execute(request);
Header contentEncoding = response.getFirstHeader("content-encoding");
assertNull("Content-Encoding should not be set when manually setting content-length", contentEncoding);
assertEquals("This is a test", EntityUtils.toString(response.getEntity()));
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/integration/GetAndPostIntegrationTest.java
================================================
package org.nanohttpd.junit.protocols.http.integration;
/*
* #%L
* NanoHttpd-Core
* %%
* 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.request.Method;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.response.Status;
/**
* @author Paul S. Hawke (paul.hawke@gmail.com) On: 5/19/13 at 5:36 PM
*/
public class GetAndPostIntegrationTest extends IntegrationTestBase {
public static class TestServer extends NanoHTTPD {
public String response;
public TestServer() {
super(8192);
}
@Override
public Response serve(IHTTPSession session) {
StringBuilder sb = new StringBuilder(String.valueOf(session.getMethod()) + ':' + this.response);
Method method = session.getMethod();
Map files = new HashMap();
if (Method.PUT.equals(method) || Method.POST.equals(method)) {
try {
session.parseBody(files);
} catch (Exception e) {
fail(e.getMessage());
}
}
String uri = session.getUri();
Map parms = session.getParms();
if (parms.size() > 1) {
parms.remove("NanoHttpd.QUERY_STRING");
sb.append("-params=").append(parms.size());
List p = new ArrayList(parms.keySet());
Collections.sort(p);
for (String k : p) {
sb.append(';').append(k).append('=').append(parms.get(k));
}
}
if ("/encodingtest".equals(uri)) {
return Response.newFixedLengthResponse(Status.OK, MIME_HTML, "Testé çaTesté ça");
} else if ("/chin".equals(uri)) {
return Response.newFixedLengthResponse(Status.OK, "application/octet-stream", sb.toString());
} else {
return Response.newFixedLengthResponse(sb.toString());
}
}
}
@Override
public TestServer createTestServer() {
return new TestServer();
}
@Test
public void testGetRequestWithParameters() throws Exception {
this.testServer.response = "testGetRequestWithParameters";
HttpGet httpget = new HttpGet("http://localhost:8192/?age=120&gender=Male");
ResponseHandler responseHandler = new BasicResponseHandler();
String responseBody = this.httpclient.execute(httpget, responseHandler);
assertEquals("GET:testGetRequestWithParameters-params=2;age=120;gender=Male", responseBody);
}
@Test
public void testPostRequestWithFormEncodedParameters() throws Exception {
this.testServer.response = "testPostRequestWithFormEncodedParameters";
HttpPost httppost = new HttpPost("http://localhost:8192/");
List postParameters = new ArrayList();
postParameters.add(new BasicNameValuePair("age", "120"));
postParameters.add(new BasicNameValuePair("gender", "Male"));
httppost.setEntity(new UrlEncodedFormEntity(postParameters));
ResponseHandler responseHandler = new BasicResponseHandler();
String responseBody = this.httpclient.execute(httppost, responseHandler);
assertEquals("POST:testPostRequestWithFormEncodedParameters-params=2;age=120;gender=Male", responseBody);
}
@Test
public void testPostRequestWithMultipartEncodedParameters() throws Exception {
this.testServer.response = "testPostRequestWithMultipartEncodedParameters";
HttpPost httppost = new HttpPost("http://localhost:8192/");
MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
reqEntity.addPart("age", new StringBody("120"));
reqEntity.addPart("gender", new StringBody("Male"));
httppost.setEntity(reqEntity);
ResponseHandler responseHandler = new BasicResponseHandler();
String responseBody = this.httpclient.execute(httppost, responseHandler);
assertEquals("POST:testPostRequestWithMultipartEncodedParameters-params=2;age=120;gender=Male", responseBody);
}
@Test
public void testPostWithNoParameters() throws Exception {
this.testServer.response = "testPostWithNoParameters";
HttpPost httppost = new HttpPost("http://localhost:8192/");
ResponseHandler responseHandler = new BasicResponseHandler();
String responseBody = this.httpclient.execute(httppost, responseHandler);
assertEquals("POST:testPostWithNoParameters", responseBody);
}
@Test
public void testSimpleGetRequest() throws Exception {
this.testServer.response = "testSimpleGetRequest";
HttpGet httpget = new HttpGet("http://localhost:8192/");
ResponseHandler responseHandler = new BasicResponseHandler();
String responseBody = this.httpclient.execute(httpget, responseHandler);
assertEquals("GET:testSimpleGetRequest", responseBody);
}
@Test
public void testPostRequestWithMultipartExtremEncodedParameters() throws Exception {
this.testServer.response = "testPostRequestWithMultipartEncodedParameters";
HttpPost httppost = new HttpPost("http://localhost:8192/chin");
MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, "sfsadfasdf", Charset.forName("UTF-8"));
reqEntity.addPart("specialString", new StringBody("拖拉图片到浏览器,可以实现预览功能", "text/plain", Charset.forName("UTF-8")));
reqEntity.addPart("gender", new StringBody("图片名称", Charset.forName("UTF-8")) {
@Override
public String getFilename() {
return "图片名称";
}
});
httppost.setEntity(reqEntity);
HttpResponse response = this.httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
String responseBody = EntityUtils.toString(entity, "UTF-8");
assertEquals("POST:testPostRequestWithMultipartEncodedParameters-params=2;gender=图片名称;specialString=拖拉图片到浏览器,可以实现预览功能", responseBody);
}
@Test
public void testPostRequestWithEncodedParameters() throws Exception {
this.testServer.response = "testPostRequestWithEncodedParameters";
HttpPost httppost = new HttpPost("http://localhost:8192/encodingtest");
HttpResponse response = this.httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
String responseBody = EntityUtils.toString(entity);
assertEquals("Testé çaTesté ça", responseBody);
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/integration/IntegrationTestBase.java
================================================
package org.nanohttpd.junit.protocols.http.integration;
/*
* #%L
* NanoHttpd-Core
* %%
* 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.IOException;
import org.apache.http.impl.client.DefaultHttpClient;
import org.junit.After;
import org.junit.Before;
import org.nanohttpd.protocols.http.NanoHTTPD;
/**
* @author Paul S. Hawke (paul.hawke@gmail.com) On: 9/2/13 at 10:02 PM
*/
public abstract class IntegrationTestBase {
protected DefaultHttpClient httpclient;
protected T testServer;
public abstract T createTestServer();
@Before
public void setUp() {
this.testServer = createTestServer();
this.httpclient = new DefaultHttpClient();
try {
this.testServer.start();
} catch (IOException e) {
e.printStackTrace();
}
}
@After
public void tearDown() {
this.httpclient.getConnectionManager().shutdown();
this.testServer.stop();
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/integration/PutStreamIntegrationTest.java
================================================
package org.nanohttpd.junit.protocols.http.integration;
/*
* #%L
* NanoHttpd-Core
* %%
* 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 static org.junit.Assert.assertEquals;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.Map;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.BasicResponseHandler;
import org.junit.Test;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.request.Method;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.response.Status;
public class PutStreamIntegrationTest extends IntegrationTestBase {
public static class TestServer extends NanoHTTPD {
public TestServer() {
super(8192);
}
@Override
public Response serve(IHTTPSession session) {
Method method = session.getMethod();
Map headers = session.getHeaders();
int contentLength = Integer.parseInt(headers.get("content-length"));
byte[] body;
try {
DataInputStream dataInputStream = new DataInputStream(session.getInputStream());
body = new byte[contentLength];
dataInputStream.readFully(body, 0, contentLength);
} catch (IOException e) {
return Response.newFixedLengthResponse(Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, e.getMessage());
}
String response = String.valueOf(method) + ':' + new String(body);
return Response.newFixedLengthResponse(response);
}
}
@Override
public TestServer createTestServer() {
return new TestServer();
}
@Test
public void testSimplePutRequest() throws Exception {
String expected = "This HttpPut request has a content-length of 48.";
HttpPut httpput = new HttpPut("http://localhost:8192/");
httpput.setEntity(new ByteArrayEntity(expected.getBytes()));
ResponseHandler responseHandler = new BasicResponseHandler();
String responseBody = this.httpclient.execute(httpput, responseHandler);
assertEquals("PUT:" + expected, responseBody);
}
}
================================================
FILE: core/src/test/java/org/nanohttpd/junit/protocols/http/integration/ShutdownTest.java
================================================
package org.nanohttpd.junit.protocols.http.integration;
/*
* #%L
* NanoHttpd-Core
* %%
* 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 static org.junit.Assert.fail;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import org.junit.Test;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.response.Response;
public class ShutdownTest {
private class TestServer extends NanoHTTPD {
public TestServer() {
super(8092);
}
@Override
public Response serve(IHTTPSession session) {
return Response.newFixedLengthResponse("Whatever");
}
}
@Test
public void connectionsAreClosedWhenServerStops() throws IOException {
TestServer server = new TestServer();
server.start();
makeRequest();
server.stop();
try {
makeRequest();
fail("Connection should be closed!");
} catch (IOException e) {
// Expected exception
}
}
private void makeRequest() throws MalformedURLException, IOException {
HttpURLConnection connection = (HttpURLConnection) new URL("http://localhost:8092/").openConnection();
// Keep-alive seems to be on by default, but just in case that changes.
connection.addRequestProperty("Connection", "keep-alive");
InputStream in = connection.getInputStream();
while (in.available() > 0) {
in.read();
}
in.close();
}
}
================================================
FILE: core/src/test/resources/META-INF/nanohttpd/mimetypes.properties
================================================
#test mime types for nanohttpd
blabla=text/blabla
ts=video/wrongOverwrite
================================================
FILE: core/src/test/resources/file-upload-test.htm
================================================
================================================
FILE: fileupload/.gitignore
================================================
/.settings/
/LICENSE.txt
================================================
FILE: fileupload/build.gradle
================================================
description = 'NanoHttpd-apache file upload integration'
dependencies {
compile group: 'commons-fileupload', name: 'commons-fileupload', version: '1.3.1'
compileOnly project(':nanohttpd')
compileOnly group: 'javax.servlet', name: 'servlet-api', version: '2.5'
testCompile project(':nanohttpd')
testCompile group: 'javax.servlet', name: 'servlet-api', version: '2.5'
testCompile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.4.1'
testCompile group: 'org.apache.httpcomponents', name: 'httpmime', version: '4.4.1'
}
task wrapper(type: Wrapper) {
gradleVersion = "4.4.1"
}
================================================
FILE: fileupload/pom.xml
================================================
nanohttpd-projectorg.nanohttpd2.3.2-SNAPSHOT4.0.0nanohttpd-apache-fileuploadNanoHttpd-apache file upload integrationnanohttpd-apache-fileupload integrates the apache file upload framework into nanohttpdorg.nanohttpdnanohttpd2.3.2-SNAPSHOTprovidedcommons-fileuploadcommons-fileupload1.3.1javax.servletservlet-api2.5providedorg.apache.httpcomponentshttpclient4.4.1testorg.apache.httpcomponentshttpmime4.4.1test0.99
================================================
FILE: fileupload/src/main/java/org/nanohttpd/fileupload/NanoFileUpload.java
================================================
package org.nanohttpd.fileupload;
/*
* #%L
* NanoHttpd-apache file upload integration
* %%
* Copyright (C) 2012 - 2016 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.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.UploadContext;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.request.Method;
/**
* @author victor & ritchieGitHub
*/
public class NanoFileUpload extends FileUpload {
public static class NanoHttpdContext implements UploadContext {
private IHTTPSession session;
public NanoHttpdContext(IHTTPSession session) {
this.session = session;
}
@Override
public long contentLength() {
long size;
try {
String cl1 = session.getHeaders().get("content-length");
size = Long.parseLong(cl1);
} catch (NumberFormatException var4) {
size = -1L;
}
return size;
}
@Override
public String getCharacterEncoding() {
return "UTF-8";
}
@Override
public String getContentType() {
return this.session.getHeaders().get("content-type");
}
@Override
public int getContentLength() {
return (int) contentLength();
}
@Override
public InputStream getInputStream() throws IOException {
return session.getInputStream();
}
}
public static final boolean isMultipartContent(IHTTPSession session) {
return session.getMethod() == Method.POST && FileUploadBase.isMultipartContent(new NanoHttpdContext(session));
}
public NanoFileUpload(FileItemFactory fileItemFactory) {
super(fileItemFactory);
}
public List parseRequest(IHTTPSession session) throws FileUploadException {
return this.parseRequest(new NanoHttpdContext(session));
}
public Map> parseParameterMap(IHTTPSession session) throws FileUploadException {
return this.parseParameterMap(new NanoHttpdContext(session));
}
public FileItemIterator getItemIterator(IHTTPSession session) throws FileUploadException, IOException {
return super.getItemIterator(new NanoHttpdContext(session));
}
}
================================================
FILE: fileupload/src/test/java/org/nanohttpd/junit/fileupload/TestNanoFileUpLoad.java
================================================
package org.nanohttpd.junit.fileupload;
/*
* #%L
* NanoHttpd-apache file upload integration
* %%
* 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.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.util.Streams;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpTrace;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.nanohttpd.fileupload.NanoFileUpload;
import org.nanohttpd.protocols.http.HTTPSession;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.request.Method;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.response.Status;
import org.nanohttpd.protocols.http.tempfiles.ITempFileManager;
/**
* very strange but if the file upload is the first request the test fails.
*
* @author ritchieGitHub
*/
@FixMethodOrder
public class TestNanoFileUpLoad {
private static final String UPLOAD_JAVA_FILE = "src/test/java/" + TestNanoFileUpLoad.class.getName().replace('.', '/') + ".java";
protected TestServer testServer;
public static class TestServer extends NanoHTTPD {
public Response response = Response.newFixedLengthResponse("");
public String uri;
public Method method;
public Map header;
public Map parms;
public Map> files;
public Map> decodedParamters;
public Map> decodedParamtersFromParameter;
public String queryParameterString;
public TestServer() {
super(8192);
uploader = new NanoFileUpload(new DiskFileItemFactory());
}
public HTTPSession createSession(ITempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) {
return new HTTPSession(this, tempFileManager, inputStream, outputStream);
}
public HTTPSession createSession(ITempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream, InetAddress inetAddress) {
return new HTTPSession(this, tempFileManager, inputStream, outputStream, inetAddress);
}
NanoFileUpload uploader;
@Override
public Response serve(IHTTPSession session) {
this.uri = session.getUri();
this.method = session.getMethod();
this.header = session.getHeaders();
this.parms = session.getParms();
if (NanoFileUpload.isMultipartContent(session)) {
try {
if ("/uploadFile1".equals(this.uri)) {
session.getHeaders().put("content-length", "AA");
files = uploader.parseParameterMap(session);
}
if ("/uploadFile2".equals(this.uri)) {
files = new HashMap>();
List parseRequest = uploader.parseRequest(session);
files.put(parseRequest.get(0).getFieldName(), parseRequest);
}
if ("/uploadFile3".equals(this.uri)) {
files = new HashMap>();
FileItemIterator iter = uploader.getItemIterator(session);
while (iter.hasNext()) {
FileItemStream item = iter.next();
final String fileName = item.getName();
FileItem fileItem = uploader.getFileItemFactory().createItem(item.getFieldName(), item.getContentType(), item.isFormField(), fileName);
files.put(fileItem.getFieldName(), Arrays.asList(new FileItem[]{
fileItem
}));
try {
Streams.copy(item.openStream(), fileItem.getOutputStream(), true);
} catch (Exception e) {
}
fileItem.setHeaders(item.getHeaders());
}
}
} catch (Exception e) {
this.response.setStatus(Status.INTERNAL_ERROR);
e.printStackTrace();
}
}
this.queryParameterString = session.getQueryParameterString();
this.decodedParamtersFromParameter = decodeParameters(this.queryParameterString);
this.decodedParamters = decodeParameters(session.getQueryParameterString());
return this.response;
}
}
@Test
public void testNormalRequest() throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpTrace httphead = new HttpTrace("http://localhost:8192/index.html");
CloseableHttpResponse response = httpclient.execute(httphead);
Assert.assertEquals(200, response.getStatusLine().getStatusCode());
response.close();
}
@Test
public void testPostWithMultipartFormUpload1() throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
String textFileName = UPLOAD_JAVA_FILE;
HttpPost post = new HttpPost("http://localhost:8192/uploadFile1");
executeUpload(httpclient, textFileName, post);
FileItem file = this.testServer.files.get("upfile").get(0);
Assert.assertEquals(file.getSize(), new File(textFileName).length());
}
@Test
public void testPostWithMultipartFormUpload2() throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
String textFileName = UPLOAD_JAVA_FILE;
HttpPost post = new HttpPost("http://localhost:8192/uploadFile2");
executeUpload(httpclient, textFileName, post);
FileItem file = this.testServer.files.get("upfile").get(0);
Assert.assertEquals(file.getSize(), new File(textFileName).length());
}
@Test
public void testPostWithMultipartFormUpload3() throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
String textFileName = UPLOAD_JAVA_FILE;
HttpPost post = new HttpPost("http://localhost:8192/uploadFile3");
executeUpload(httpclient, textFileName, post);
FileItem file = this.testServer.files.get("upfile").get(0);
Assert.assertEquals(file.getSize(), new File(textFileName).length());
}
private void executeUpload(CloseableHttpClient httpclient, String textFileName, HttpPost post) throws IOException, ClientProtocolException {
FileBody fileBody = new FileBody(new File(textFileName), ContentType.DEFAULT_BINARY);
StringBody stringBody1 = new StringBody("Message 1", ContentType.MULTIPART_FORM_DATA);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.addPart("upfile", fileBody);
builder.addPart("text1", stringBody1);
HttpEntity entity = builder.build();
//
post.setEntity(entity);
HttpResponse response = httpclient.execute(post);
Assert.assertEquals(200, response.getStatusLine().getStatusCode());
}
@Before
public void setUp() throws IOException {
this.testServer = new TestServer();
this.testServer.start();
try {
long start = System.currentTimeMillis();
Thread.sleep(100L);
while (!this.testServer.wasStarted()) {
Thread.sleep(100L);
if (System.currentTimeMillis() - start > 2000) {
Assert.fail("could not start server");
}
}
} catch (InterruptedException e) {
}
}
@After
public void tearDown() {
this.testServer.stop();
}
}
================================================
FILE: markdown-plugin/.gitignore
================================================
/.settings/
/LICENSE.txt
================================================
FILE: markdown-plugin/build.gradle
================================================
description = 'NanoHttpd-Webserver-Markdown-Plugin'
dependencies {
compile group: 'org.pegdown', name: 'pegdown', version: '1.4.1'
compileOnly project(':nanohttpd')
compileOnly project(':nanohttpd-webserver')
}
task wrapper(type: Wrapper) {
gradleVersion = "4.4.1"
}
================================================
FILE: markdown-plugin/pom.xml
================================================
4.0.0org.nanohttpdnanohttpd-project2.3.2-SNAPSHOTnanohttpd-webserver-markdown-pluginjarNanoHttpd-Webserver-Markdown-Pluginhttps://github.com/NanoHttpd/nanohttpd${project.groupId}nanohttpd${project.version}provided${project.groupId}nanohttpd-webserver${project.version}providedorg.pegdownpegdown1.4.1org.apache.maven.pluginsmaven-assembly-plugin2.2-beta-5jar-with-dependenciesfi.iki.elonen.SimpleWebServerpackagesingle0.0
================================================
FILE: markdown-plugin/src/main/java/org/nanohttpd/markdown/MarkdownWebServerPlugin.java
================================================
package org.nanohttpd.markdown;
/*
* #%L
* NanoHttpd-Webserver-Markdown-Plugin
* %%
* Copyright (C) 2012 - 2016 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.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.response.Status;
import org.nanohttpd.webserver.WebServerPlugin;
import org.pegdown.PegDownProcessor;
/**
* @author Paul S. Hawke (paul.hawke@gmail.com) On: 9/13/13 at 4:03 AM
*/
public class MarkdownWebServerPlugin implements WebServerPlugin {
/**
* logger to log to.
*/
private static final Logger LOG = Logger.getLogger(MarkdownWebServerPlugin.class.getName());
private final PegDownProcessor processor;
public MarkdownWebServerPlugin() {
this.processor = new PegDownProcessor();
}
@Override
public boolean canServeUri(String uri, File rootDir) {
File f = new File(rootDir, uri);
return f.exists();
}
@Override
public void initialize(Map commandLineOptions) {
}
private String readSource(File file) {
FileReader fileReader = null;
BufferedReader reader = null;
try {
fileReader = new FileReader(file);
reader = new BufferedReader(fileReader);
String line = null;
StringBuilder sb = new StringBuilder();
do {
line = reader.readLine();
if (line != null) {
sb.append(line).append("\n");
}
} while (line != null);
reader.close();
return sb.toString();
} catch (Exception e) {
MarkdownWebServerPlugin.LOG.log(Level.SEVERE, "could not read source", e);
return null;
} finally {
try {
if (fileReader != null) {
fileReader.close();
}
if (reader != null) {
reader.close();
}
} catch (IOException ignored) {
MarkdownWebServerPlugin.LOG.log(Level.FINEST, "close failed", ignored);
}
}
}
@Override
public Response serveFile(String uri, Map headers, IHTTPSession session, File file, String mimeType) {
String markdownSource = readSource(file);
byte[] bytes;
try {
bytes = this.processor.markdownToHtml(markdownSource).getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
MarkdownWebServerPlugin.LOG.log(Level.SEVERE, "encoding problem, responding nothing", e);
bytes = new byte[0];
}
return markdownSource == null ? null : Response.newFixedLengthResponse(Status.OK, NanoHTTPD.MIME_HTML, new ByteArrayInputStream(bytes), bytes.length);
}
}
================================================
FILE: markdown-plugin/src/main/java/org/nanohttpd/markdown/MarkdownWebServerPluginInfo.java
================================================
package org.nanohttpd.markdown;
import org.nanohttpd.webserver.WebServerPlugin;
import org.nanohttpd.webserver.WebServerPluginInfo;
/*
* #%L
* NanoHttpd-Webserver-Markdown-Plugin
* %%
* 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%
*/
/**
* @author Paul S. Hawke (paul.hawke@gmail.com) On: 9/13/13 at 4:01 AM
*/
public class MarkdownWebServerPluginInfo implements WebServerPluginInfo {
@Override
public String[] getIndexFilesForMimeType(String mime) {
return new String[]{
"index.md"
};
}
@Override
public String[] getMimeTypes() {
return new String[]{
"text/markdown"
};
}
@Override
public WebServerPlugin getWebServerPlugin(String mimeType) {
return new MarkdownWebServerPlugin();
}
}
================================================
FILE: markdown-plugin/src/main/resources/META-INF/services/org.nanohttpd.webserver.WebServerPluginInfo
================================================
org.nanohttpd.markdown.MarkdownWebServerPluginInfo
================================================
FILE: markdown-plugin/src/site/site.xml
================================================
org.apache.maven.skinsmaven-fluido-skin1.3.0../images/nanohttpd_logo.png../images/nanohttpd_logo_text.pngfalsetrueNanohttpd/nanohttpdrightblack
================================================
FILE: nanohttpd release perform.launch
================================================
================================================
FILE: nanohttpd release prepare.launch
================================================
================================================
FILE: nanolets/.gitignore
================================================
/.settings/
/LICENSE.txt
================================================
FILE: nanolets/build.gradle
================================================
description = 'NanoHttpd-nano application server'
dependencies {
compile project(':nanohttpd')
testCompile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.4.1'
}
================================================
FILE: nanolets/pom.xml
================================================
4.0.0org.nanohttpdnanohttpd-project2.3.2-SNAPSHOTnanohttpd-nanoletsjarNanoHttpd-nano application servernanohttpd-nanolets add a very easy to use version of servlets into nanohttpd.${project.groupId}nanohttpd${project.version}org.apache.httpcomponentshttpclient4.4.1test0.96
================================================
FILE: nanolets/src/main/java/org/nanohttpd/router/RouterNanoHTTPD.java
================================================
package org.nanohttpd.router;
/*
* #%L
* NanoHttpd-Samples
* %%
* 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.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.response.IStatus;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.response.Status;
/**
* @author vnnv
* @author ritchieGitHub
*/
public class RouterNanoHTTPD extends NanoHTTPD {
/**
* logger to log to.
*/
private static final Logger LOG = Logger.getLogger(RouterNanoHTTPD.class.getName());
public interface UriResponder {
public Response get(UriResource uriResource, Map urlParams, IHTTPSession session);
public Response put(UriResource uriResource, Map urlParams, IHTTPSession session);
public Response post(UriResource uriResource, Map urlParams, IHTTPSession session);
public Response delete(UriResource uriResource, Map urlParams, IHTTPSession session);
public Response other(String method, UriResource uriResource, Map urlParams, IHTTPSession session);
}
/**
* General nanolet to inherit from if you provide stream data, only chucked
* responses will be generated.
*/
public static abstract class DefaultStreamHandler implements UriResponder {
public abstract String getMimeType();
public abstract IStatus getStatus();
public abstract InputStream getData();
public Response get(UriResource uriResource, Map urlParams, IHTTPSession session) {
return Response.newChunkedResponse(getStatus(), getMimeType(), getData());
}
public Response post(UriResource uriResource, Map urlParams, IHTTPSession session) {
return get(uriResource, urlParams, session);
}
public Response put(UriResource uriResource, Map urlParams, IHTTPSession session) {
return get(uriResource, urlParams, session);
}
public Response delete(UriResource uriResource, Map urlParams, IHTTPSession session) {
return get(uriResource, urlParams, session);
}
public Response other(String method, UriResource uriResource, Map urlParams, IHTTPSession session) {
return get(uriResource, urlParams, session);
}
}
/**
* General nanolet to inherit from if you provide text or html data, only
* fixed size responses will be generated.
*/
public static abstract class DefaultHandler extends DefaultStreamHandler {
public abstract String getText();
public abstract IStatus getStatus();
public Response get(UriResource uriResource, Map urlParams, IHTTPSession session) {
return Response.newFixedLengthResponse(getStatus(), getMimeType(), getText());
}
@Override
public InputStream getData() {
throw new IllegalStateException("this method should not be called in a text based nanolet");
}
}
/**
* General nanolet to print debug info's as a html page.
*/
public static class GeneralHandler extends DefaultHandler {
@Override
public String getText() {
throw new IllegalStateException("this method should not be called");
}
@Override
public String getMimeType() {
return "text/html";
}
@Override
public IStatus getStatus() {
return Status.OK;
}
public Response get(UriResource uriResource, Map urlParams, IHTTPSession session) {
StringBuilder text = new StringBuilder("");
text.append("
*/
public void addMappings() {
router.setNotImplemented(NotImplementedHandler.class);
router.setNotFoundHandler(Error404UriHandler.class);
router.addRoute("/", Integer.MAX_VALUE / 2, IndexHandler.class);
router.addRoute("/index.html", Integer.MAX_VALUE / 2, IndexHandler.class);
}
public void addRoute(String url, Class> handler, Object... initParameter) {
router.addRoute(url, 100, handler, initParameter);
}
public void setNotImplementedHandler(Class handler) {
router.setNotImplemented(handler);
}
public void setNotFoundHandler(Class handler) {
router.setNotFoundHandler(handler);
}
public void removeRoute(String url) {
router.removeRoute(url);
}
public void setRoutePrioritizer(IRoutePrioritizer routePrioritizer) {
router.setRoutePrioritizer(routePrioritizer);
}
@Override
public Response serve(IHTTPSession session) {
// Try to find match
return router.process(session);
}
}
================================================
FILE: nanolets/src/test/java/org/nanohttpd/junit/router/AppNanolets.java
================================================
package org.nanohttpd.junit.router;
/*
* #%L
* NanoHttpd-Samples
* %%
* 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%
*/
/**
* Created by vnnv on 7/17/15.
* Simple httpd server based on NanoHTTPD
* Read the source. Everything is there.
*/
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.response.IStatus;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.response.Status;
import org.nanohttpd.router.RouterNanoHTTPD;
import org.nanohttpd.router.RouterNanoHTTPD.DefaultHandler;
import org.nanohttpd.router.RouterNanoHTTPD.DefaultStreamHandler;
import org.nanohttpd.router.RouterNanoHTTPD.GeneralHandler;
import org.nanohttpd.router.RouterNanoHTTPD.StaticPageHandler;
import org.nanohttpd.router.RouterNanoHTTPD.UriResource;
import org.nanohttpd.router.RouterNanoHTTPD.UriResponder;
import org.nanohttpd.util.ServerRunner;
public class AppNanolets extends RouterNanoHTTPD {
private static final int PORT = 9090;
public static class UserHandler extends DefaultHandler {
@Override
public String getText() {
return "not implemented";
}
public String getText(Map urlParams, IHTTPSession session) {
String text = "User handler. Method: " + session.getMethod().toString() + " ";
text += "
Uri parameters:
";
for (Map.Entry entry : urlParams.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
text += "
Param: " + key + " Value: " + value + "
";
}
text += "
Query parameters:
";
for (Map.Entry entry : session.getParms().entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
text += "
Query Param: " + key + " Value: " + value + "
";
}
text += "";
return text;
}
@Override
public String getMimeType() {
return "text/html";
}
@Override
public IStatus getStatus() {
return Status.OK;
}
public Response get(UriResource uriResource, Map urlParams, IHTTPSession session) {
String text = getText(urlParams, session);
ByteArrayInputStream inp = new ByteArrayInputStream(text.getBytes());
int size = text.getBytes().length;
return Response.newFixedLengthResponse(getStatus(), getMimeType(), inp, size);
}
}
static public class StreamUrl extends DefaultStreamHandler {
@Override
public String getMimeType() {
return "text/plain";
}
@Override
public IStatus getStatus() {
return Status.OK;
}
@Override
public InputStream getData() {
return new ByteArrayInputStream("a stream of data ;-)".getBytes());
}
}
public static class StaticPageTestHandler extends StaticPageHandler {
@Override
protected BufferedInputStream fileToInputStream(File fileOrdirectory) throws IOException {
if ("exception.html".equals(fileOrdirectory.getName())) {
throw new IOException("trigger something wrong");
}
return super.fileToInputStream(fileOrdirectory);
}
}
/**
* Create the server instance
*/
public AppNanolets() throws IOException {
super(PORT);
addMappings();
System.out.println("\nRunning! Point your browers to http://localhost:" + PORT + "/ \n");
}
/**
* Add the routes Every route is an absolute path Parameters starts with ":"
* Handler class should implement @UriResponder interface If the handler not
* implement UriResponder interface - toString() is used
*/
@Override
public void addMappings() {
super.addMappings();
addRoute("/user", UserHandler.class);
addRoute("/user", UserHandler.class); // add it twice to execute the
// priority == priority case
addRoute("/user/help", GeneralHandler.class);
addRoute("/user/:id", UserHandler.class);
addRoute("/general/:param1/:param2", GeneralHandler.class);
addRoute("/photos/:customer_id/:photo_id", null);
addRoute("/test", String.class);
addRoute("/interface", UriResponder.class); // this will cause an error
// when called
addRoute("/toBeDeleted", String.class);
removeRoute("/toBeDeleted");
addRoute("/stream", StreamUrl.class);
addRoute("/browse/(.)+", StaticPageTestHandler.class, new File("src/test/resources").getAbsoluteFile());
}
/**
* Main entry point
*
* @param args
*/
public static void main(String[] args) {
ServerRunner.run(AppNanolets.class);
}
}
================================================
FILE: nanolets/src/test/java/org/nanohttpd/junit/router/TestNanolets.java
================================================
package org.nanohttpd.junit.router;
/*
* #%L
* NanoHttpd nano application server
* %%
* 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 java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpTrace;
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;
import org.nanohttpd.protocols.http.response.Status;
import org.nanohttpd.router.RouterNanoHTTPD;
import org.nanohttpd.router.RouterNanoHTTPD.DefaultRoutePrioritizer;
import org.nanohttpd.router.RouterNanoHTTPD.Error404UriHandler;
import org.nanohttpd.router.RouterNanoHTTPD.GeneralHandler;
import org.nanohttpd.router.RouterNanoHTTPD.IndexHandler;
import org.nanohttpd.router.RouterNanoHTTPD.InsertionOrderRoutePrioritizer;
import org.nanohttpd.router.RouterNanoHTTPD.NotImplementedHandler;
import org.nanohttpd.router.RouterNanoHTTPD.ProvidedPriorityRoutePrioritizer;
import org.nanohttpd.router.RouterNanoHTTPD.StaticPageHandler;
import org.nanohttpd.router.RouterNanoHTTPD.UriResource;
import org.nanohttpd.router.RouterNanoHTTPD.UriResponder;
import org.nanohttpd.router.RouterNanoHTTPD.UriRouter;
public class TestNanolets {
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 = {};
AppNanolets.main(args);
}
});
serverStartThread.start();
// give the server some tine to start.
Thread.sleep(200);
}
public static void main(String[] args) {
{
String uri = "def";
Pattern.compile("([A-Za-z0-9\\-\\._~:/?#\\[\\]@!\\$&'\\(\\)\\*\\+,;=]+)");
Pattern URI_PATTERN = Pattern.compile("([A-Za-z0-9\\-\\._~:/?#\\[\\]@!\\$&'\\(\\)\\*\\+,;=]+)");
System.out.println(URI_PATTERN.matcher(uri).matches());
}
String uri = "photos/abc/def";
Pattern URI_PATTERN = Pattern.compile("photos/([A-Za-z0-9\\-\\._~:/?#\\[\\]@!\\$&'\\(\\)\\*\\+,;=]+)/([A-Za-z0-9\\-\\._~:/?#\\[\\]@!\\$&'\\(\\)\\*\\+,;=]+)");
Matcher matcher = URI_PATTERN.matcher(uri);
System.out.println("--------------->" + "/" + uri);
while (matcher.matches()) {
System.out.println(matcher.group());
}
// for (int index = 0; index < matcher.groupCount(); index++) {
// System.out.println(matcher.group());
// }
}
@Test
public void doSomeBasicMethodTest() throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet("http://localhost:9090/user/blabla");
CloseableHttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
String string = new String(readContents(entity), "UTF-8");
Assert.assertEquals(
"User handler. Method: GET
Uri parameters:
Param: id Value: blabla
Query parameters:
", string);
response.close();
HttpPost httppost = new HttpPost("http://localhost:9090/user/blabla");
response = httpclient.execute(httppost);
entity = response.getEntity();
string = new String(readContents(entity), "UTF-8");
Assert.assertEquals(
"User handler. Method: POST
Uri parameters:
Param: id Value: blabla
Query parameters:
", string);
response.close();
HttpPut httpgput = new HttpPut("http://localhost:9090/user/blabla");
response = httpclient.execute(httpgput);
entity = response.getEntity();
string = new String(readContents(entity), "UTF-8");
Assert.assertEquals(
"User handler. Method: PUT
The uri is mapped in the router, but no handler is specified. Status: Not implemented!", string);
response.close();
}
@Test
public void uriToString() throws Exception {
Assert.assertEquals(//
"UrlResource{uri='photos/:customer_id/:photo_id', urlParts=[customer_id, photo_id]}",//
new UriResource("/photos/:customer_id/:photo_id", 100, GeneralHandler.class).toString());
}
@Test
public void doOtherMethod() throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpTrace httphead = new HttpTrace("http://localhost:9090/index.html");
CloseableHttpResponse response = httpclient.execute(httphead);
HttpEntity entity = response.getEntity();
String string = new String(readContents(entity), "UTF-8");
Assert.assertEquals("
================================================
FILE: nanolets/src/test/resources/exception.html
================================================
this will throw an io exception
================================================
FILE: pom.xml
================================================
4.0.0org.sonatype.ossoss-parent7org.nanohttpdnanohttpd-project2.3.2-SNAPSHOTpomNanoHttpd-ProjectNanoHttpd is a light-weight HTTP server designed for embedding in other applications.http://www.nanohttpd.orgscm:git:https://github.com/NanoHttpd/nanohttpd.gitscm:git:https://github.com/NanoHttpd/nanohttpd.githttps://github.com/NanoHttpd/nanohttpdHEADnanohttpdsonatype-nexus-stagingnanohttpd sonytype Maven 2 repositoryhttps://oss.sonatype.org/service/local/staging/deploy/maven2sonatype-nexus-stagingnanohttpd sonytype Snapshot Maven 2 repositoryhttps://oss.sonatype.org/content/repositories/snapshotsprivate-deployprivate Project Sitescp://nanohttpd.org:33333/home/nanohttpd/public_html2012pshPaul Hawkehttp://www.linkedin.com/in/paulhawkeAdministratorDeveloperhttps://avatars2.githubusercontent.com/u/407647elonenJarno Elonenelonen [at] iki [dot] fiAdministratorDeveloperhttps://avatars0.githubusercontent.com/u/2125766ritchieGitHubRichard van Nieuwenhovenritchie [at] gmx [dot] atAdministratorDeveloperhttp://www.gravatar.com/avatar/9e2c2e7aa94335b72952a4b2d56bfc89.pngLordFokasDiogo Duartediogo.duarte [at] techie [dot] comAdministratorDeveloperhttps://avatars1.githubusercontent.com/u/3021352githubhttps://github.com/NanoHttpd/nanohttpd/issuescoresampleswebserverwebsocketmarkdown-pluginnanoletsfileuploadThe BSD 3-Clause Licensehttp://opensource.org/licenses/BSD-3-Clauserepoorg.apache.maven.wagonwagon-ssh2.7org.apache.maven.pluginsmaven-compiler-plugin3.31.71.7org.apache.maven.pluginsmaven-javadoc-plugin2.10.1falsefalseattach-javadocsjarorg.apache.maven.pluginsmaven-release-plugin2.5.1forked-pathorg.tinyjee.dimdoxia-include-macro1.1initialize-doxia-include-macropre-siteinitializeorg.apache.maven.pluginsmaven-site-plugin3.4falseorg.apache.maven.wagonwagon-ssh2.7org.tinyjee.dimdoxia-include-macro1.1net.ju-n.maven.doxiadoxia-module-markdown1.0.0org.codehaus.mojolicense-maven-plugin1.7falsefirstupdate-file-headerupdate-project-licenseprocess-sourcesbsd_3src/main/javasrc/test/javaorg.apache.maven.pluginsmaven-surefire-plugin2.18.11falseorg.jacocojacoco-maven-plugin0.7.4.201502262128default-prepare-agentprepare-agentdefault-reportprepare-packagereportdefault-checkcheckBUNDLELINECOVEREDRATIO${minimal.coverage}org.apache.maven.pluginsmaven-project-info-reports-plugin2.8falsefalseindexdependenciesproject-teammailing-listcimissue-trackinglicensescmorg.apache.maven.pluginsmaven-javadoc-plugin2.10.1falsefalsejavadoc-no-forkorg.apache.maven.pluginsmaven-jxr-plugin2.4true${nanohttpd.nonjavamodule}org.apache.maven.pluginsmaven-pmd-plugin3.2trueUTF-81.7false50org.codehaus.mojotaglist-maven-plugin2.4org.codehaus.mojofindbugs-maven-plugin3.0.0org.jacocojacoco-maven-plugin0.7.4.201502262128junitjunit4.12testrelease-sign-artifactsperformReleasetrueorg.apache.maven.pluginsmaven-gpg-pluginsign-artifactsverifysignjavasrc/main/javaorg.apache.maven.pluginsmaven-checkstyle-plugin2.15${project.basedir}/../src/main/checkstyle/nanohttpd-style.xml${project.basedir}/../src/main/checkstyle/checkstyle-suppressions.xmlUTF-8truefalsetruecom.googlecode.maven-java-formatter-pluginmaven-java-formatter-plugin0.4format${project.basedir}/../src/main/formatter/formatter.xmlLForg.apache.maven.pluginsmaven-checkstyle-plugin2.15checkstyleparent-buildcore/pom.xmlorg.jacocojacoco-maven-plugin0.7.4.201502262128merge-reportpackagemerge${project.basedir}/..*.execorg.eluder.coverallscoveralls-maven-plugin3.1.0use 1.7 compiler/usr/lib/jvm/java-7-openjdk-amd64/bin/javacorg.apache.maven.pluginsmaven-compiler-plugin3.31.71.7truetrue/usr/lib/jvm/java-7-openjdk-amd64/bin/javac0.77
================================================
FILE: relocation/nanohttpd/pom.xml
================================================
4.0.0com.nanohttpdnanohttpd-project2.2.0nanohttpdorg.nanohttpd
================================================
FILE: relocation/nanohttpd/src/main/resources/relocated.txt
================================================
this project was relocated to the groupid org.nanohttpd starting with version 2.2.0
================================================
FILE: relocation/nanohttpd-samples/pom.xml
================================================
4.0.0com.nanohttpdnanohttpd-project2.2.0nanohttpd-samplesorg.nanohttpd
================================================
FILE: relocation/nanohttpd-samples/src/main/resources/relocated.txt
================================================
this project was relocated to the groupid org.nanohttpd starting with version 2.2.0
================================================
FILE: relocation/nanohttpd-webserver/pom.xml
================================================
4.0.0com.nanohttpdnanohttpd-project2.2.0nanohttpd-webserverorg.nanohttpd
================================================
FILE: relocation/nanohttpd-webserver/src/main/resources/relocated.txt
================================================
this project was relocated to the groupid org.nanohttpd starting with version 2.2.0
================================================
FILE: relocation/nanohttpd-webserver-markdown-plugin/pom.xml
================================================
4.0.0com.nanohttpdnanohttpd-project2.2.0nanohttpd-webserver-markdown-pluginorg.nanohttpd
================================================
FILE: relocation/nanohttpd-webserver-markdown-plugin/src/main/resources/relocated.txt
================================================
this project was relocated to the groupid org.nanohttpd starting with version 2.2.0
================================================
FILE: relocation/nanohttpd-websocket/pom.xml
================================================
4.0.0com.nanohttpdnanohttpd-project2.2.0nanohttpd-websocketorg.nanohttpd
================================================
FILE: relocation/nanohttpd-websocket/src/main/resources/relocated.txt
================================================
this project was relocated to the groupid org.nanohttpd starting with version 2.2.0
================================================
FILE: relocation/pom.xml
================================================
4.0.0org.sonatype.ossoss-parent7com.nanohttpdnanohttpd-project2.2.0pomsonatype-nexus-stagingnanohttpd sonytype Maven 2 repositoryhttps://oss.sonatype.org/service/local/staging/deploy/maven2org.nanohttpdorg.apache.maven.pluginsmaven-gpg-pluginsign-artifactsverifysignnanohttpdnanohttpd-webservernanohttpd-webserver-markdown-pluginnanohttpd-samplesnanohttpd-websocket
================================================
FILE: samples/.gitignore
================================================
/.settings/
/LICENSE.txt
================================================
FILE: samples/build.gradle
================================================
description = 'NanoHttpd-Samples'
dependencies {
compile project(':nanohttpd')
compile project(':nanohttpd-webserver')
}
task wrapper(type: Wrapper) {
gradleVersion = "4.4.1"
}
================================================
FILE: samples/pom.xml
================================================
4.0.0org.nanohttpdnanohttpd-project2.3.2-SNAPSHOTnanohttpd-samplessamples for nanohttpdjarNanoHttpd-Samples${project.groupId}nanohttpd${project.version}${project.groupId}nanohttpd-webserver${project.version}0.0
================================================
FILE: samples/src/main/java/org/nanohttpd/samples/http/DebugServer.java
================================================
package org.nanohttpd.samples.http;
/*
* #%L
* NanoHttpd-Samples
* %%
* 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.util.HashMap;
import java.util.List;
import java.util.Map;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.util.ServerRunner;
public class DebugServer extends NanoHTTPD {
public static void main(String[] args) {
ServerRunner.run(DebugServer.class);
}
public DebugServer() {
super(8080);
}
private void listItem(StringBuilder sb, Map.Entry entry) {
sb.append("
");
return sb.toString();
}
}
================================================
FILE: samples/src/main/java/org/nanohttpd/samples/http/HelloServer.java
================================================
package org.nanohttpd.samples.http;
/*
* #%L
* NanoHttpd-Samples
* %%
* 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.util.Map;
import java.util.logging.Logger;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.request.Method;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.util.ServerRunner;
/**
* An example of subclassing NanoHTTPD to make a custom HTTP server.
*/
public class HelloServer extends NanoHTTPD {
/**
* logger to log to.
*/
private static final Logger LOG = Logger.getLogger(HelloServer.class.getName());
public static void main(String[] args) {
ServerRunner.run(HelloServer.class);
}
public HelloServer() {
super(8080);
}
@Override
public Response serve(IHTTPSession session) {
Method method = session.getMethod();
String uri = session.getUri();
HelloServer.LOG.info(method + " '" + uri + "' ");
String msg = "
";
}
msg += "\n";
return Response.newFixedLengthResponse(msg);
}
}
================================================
FILE: samples/src/main/java/org/nanohttpd/samples/tempfiles/TempFilesServer.java
================================================
package org.nanohttpd.samples.tempfiles;
/*
* #%L
* NanoHttpd-Samples
* %%
* 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.File;
import java.util.ArrayList;
import java.util.List;
import org.nanohttpd.protocols.http.tempfiles.DefaultTempFile;
import org.nanohttpd.protocols.http.tempfiles.ITempFile;
import org.nanohttpd.protocols.http.tempfiles.ITempFileManager;
import org.nanohttpd.samples.http.DebugServer;
import org.nanohttpd.util.IFactory;
import org.nanohttpd.util.ServerRunner;
/**
* @author Paul S. Hawke (paul.hawke@gmail.com) On: 3/9/13 at 12:47 AM
*/
public class TempFilesServer extends DebugServer {
private static class ExampleManager implements ITempFileManager {
private final File tmpdir;
private final List tempFiles;
private ExampleManager() {
this.tmpdir = new File(System.getProperty("java.io.tmpdir"));
this.tempFiles = new ArrayList();
}
@Override
public void clear() {
if (!this.tempFiles.isEmpty()) {
System.out.println("Cleaning up:");
}
for (ITempFile file : this.tempFiles) {
try {
System.out.println(" " + file.getName());
file.delete();
} catch (Exception ignored) {
}
}
this.tempFiles.clear();
}
@Override
public ITempFile createTempFile(String filename_hint) throws Exception {
DefaultTempFile tempFile = new DefaultTempFile(this.tmpdir);
this.tempFiles.add(tempFile);
System.out.println("Created tempFile: " + tempFile.getName());
return tempFile;
}
}
private static class ExampleManagerFactory implements IFactory {
@Override
public ITempFileManager create() {
return new ExampleManager();
}
}
public static void main(String[] args) {
TempFilesServer server = new TempFilesServer();
server.setTempFileManagerFactory(new ExampleManagerFactory());
ServerRunner.executeInstance(server);
}
}
================================================
FILE: samples/src/site/site.xml
================================================
org.apache.maven.skinsmaven-fluido-skin1.3.0../images/nanohttpd_logo.png../images/nanohttpd_logo_text.pngfalsetrueNanohttpd/nanohttpdrightblack
================================================
FILE: settings.gradle
================================================
rootProject.name = 'nanohttpd-project'
include ':nanohttpd'
include ':nanohttpd-samples'
include ':nanohttpd-webserver'
include ':nanohttpd-websocket'
include ':nanohttpd-webserver-markdown-plugin'
include ':nanohttpd-nanolets'
include ':nanohttpd-apache-fileupload'
project(':nanohttpd').projectDir = "$rootDir/core" as File
project(':nanohttpd-samples').projectDir = "$rootDir/samples" as File
project(':nanohttpd-webserver').projectDir = "$rootDir/webserver" as File
project(':nanohttpd-websocket').projectDir = "$rootDir/websocket" as File
project(':nanohttpd-webserver-markdown-plugin').projectDir = "$rootDir/markdown-plugin" as File
project(':nanohttpd-nanolets').projectDir = "$rootDir/nanolets" as File
project(':nanohttpd-apache-fileupload').projectDir = "$rootDir/fileupload" as File
================================================
FILE: settings.xml
================================================
sonatype-nexus-staging${env.CI_DEPLOY_USERNAME}${env.CI_DEPLOY_PASSWORD}
================================================
FILE: src/main/checkstyle/checkstyle-suppressions.xml
================================================
================================================
FILE: src/main/checkstyle/nanohttpd-style.xml
================================================
================================================
FILE: src/main/formatter/formatter.xml
================================================
================================================
FILE: src/site/markdown/index.md
================================================
## NanoHTTPD – a tiny web server in Java
*NanoHTTPD* is a light-weight HTTP server designed for embedding in other applications, released under a Modified BSD licence.
It is being developed at Github and uses Apache Maven for builds & unit testing:
* Build status: [](https://travis-ci.org/NanoHttpd/nanohttpd)
* Coverage Status: [](https://coveralls.io/r/NanoHttpd/nanohttpd)
* Current central released version: [](https://maven-badges.herokuapp.com/maven-central/com.nanohttpd/nanohttpd)
## Quickstart
We'll create a custom HTTP server project using Maven for build/dep system. This tutorial assumes you are using a Unix variant and a shell. First, install Maven and Java SDK if not already installed. Then run:
mvn compile
mvn exec:java -pl webserver -Dexec.mainClass="fi.iki.elonen.SimpleWebServer"
You should now have a HTTP file server running on .
### Custom web app
Let's raise the bar and build a custom web application next:
mvn archetype:generate -DgroupId=com.example -DartifactId=myHellopApp -DinteractiveMode=false
cd myHellopApp
Edit `pom.xml`, and add this between \:
org.nanohttpdnanohttpd2.2.0
Edit `src/main/java/com/example/App.java` and replace it with:
```java
package com.example;
import java.io.IOException;
import java.util.Map;
import fi.iki.elonen.NanoHTTPD;
public class App extends NanoHTTPD {
public App() throws IOException {
super(8080);
start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
System.out.println("\nRunning! Point your browers to http://localhost:8080/ \n");
}
public static void main(String[] args) {
try {
new App();
} catch (IOException ioe) {
System.err.println("Couldn't start server:\n" + ioe);
}
}
@Override
public Response serve(IHTTPSession session) {
String msg = "
";
}
return newFixedLengthResponse(msg + "\n");
}
}
```
Compile and run the server:
mvn compile
mvn exec:java -Dexec.mainClass="com.example.App"
If it started ok, point your browser at and enjoy a web server that asks your name and replies with a greeting.
### Nanolets
Nanolets are like sevlet's only that they have a extrem low profile. They offer an easy to use system for a more complex server application.
This text has to be extrended with an example, so for now take a look at the unit tests for the usage.
## Status
We are currently in the process of stabilizing NanoHttpd from the many pull requests and feature requests that were integrated over the last few months. The next release will come soon, and there will not be any more "intended" major changes before the next release. If you want to use the bleeding edge version, you can clone it from Github, or get it from sonatype.org (see "Maven dependencies / Living on the edge" below).
## Project structure
NanoHTTPD project currently consist of four parts:
* `/core` – Fully functional HTTP(s) server consisting of one (1) Java file, ready to be customized/inherited for your own project
* `/samples` – Simple examples on how to customize NanoHTTPD. See *HelloServer.java* for a killer app that greets you enthusiastically!
* `/websocket` – Websocket implementation, also in a single Java file. Depends on core.
* `/webserver` – Standalone file server. Run & enjoy. A popular use seems to be serving files out off an Android device.
* `/nanolets` – Standalone nano app server, giving a servlet like system to the implementor.
* `/fileupload` – integration of the apache common file upload library.
## Features
### Core
* Only one Java file, providing HTTP 1.1 support.
* No fixed config files, logging, authorization etc. (Implement by yourself if you need them. Errors are passed to java.util.logging, though.)
* Support for HTTPS (SSL)
* Basic support for cookies
* Supports parameter parsing of GET and POST methods.
* Some built-in support for HEAD, POST and DELETE requests. You can easily implement/customize any HTTP method, though.
* Supports file upload. Uses memory for small uploads, temp files for large ones.
* Never caches anything.
* Does not limit bandwidth, request time or simultaneous connections by default.
* All header names are converted to lower case so they don't vary between browsers/clients.
* Persistent connections (Connection "keep-alive") support allowing multiple requests to be served over a single socket connection.
### Websocket
* Tested on Firefox, Chrome and IE.
### Webserver
* Default code serves files and shows (prints on console) all HTTP parameters and headers.
* Supports both dynamic content and file serving.
* File server supports directory listing, `index.html` and `index.htm`.
* File server supports partial content (streaming & continue download).
* File server supports ETags.
* File server does the 301 redirection trick for directories without `/`.
* File server serves also very long files without memory overhead.
* Contains a built-in list of most common MIME types.
* Runtime extension support (extensions that serve particular MIME types) - example extension that serves Markdown formatted files. Simply including an extension JAR in the webserver classpath is enough for the extension to be loaded.
* Simple [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) support via `--cors` paramater
* by default serves `Access-Control-Allow-Headers: origin,accept,content-type`
* possibility to set `Access-Control-Allow-Headers` by setting System property: `AccessControlAllowHeader`
* _example: _ `-DAccessControlAllowHeader=origin,accept,content-type,Authorization`
* possible values:
* `--cors`: activates CORS support, `Access-Control-Allow-Origin` will be set to `*`
* `--cors=some_value`: `Access-Control-Allow-Origin` will be set to `some_value`.
**_CORS argument examples_**
* `--cors=http://appOne.company.com`
* `--cors="http://appOne.company.com, http://appTwo.company.com"`: note the double quotes so that the 2 URLs are considered part of a single argument.
## Maven dependencies
NanoHTTPD is a Maven based project and deployed to central. Most development environments have means to access the central repository. The coordinates to use in Maven are:
org.nanohttpdnanohttpd2.2.0
The coordinates for your development environment should correspond to these. When looking for an older version take care because we switched groupId from *com.nanohttpd* to *org.nanohttpd* in mid 2015.
Next it depends what you are useing nanohttpd for, there are tree main usages.
## Gradle dependencies
In gradle you can use nano http the same way because gradle accesses the same central repository:
dependencies {
runtime(
[group: 'org.nanohttpd', name: 'nanohttpd', version: '2.2.0'],
)
}
Just replace the name with the artifact id of the module you want to use and gradle will find it for you.
### Develop your own specialized HTTP service
For a specialized HTTP (HTTPS) service you can use the module with artifactId *nanohttpd*.
org.nanohttpdnanohttpd2.2.0VERSION
Here you write your own subclass of *fi.iki.elonen.NanoHTTPD* to configure and to serve the requests.
### Develop a websocket based service
For a specialized websocket service you can use the module with artifactId *nanohttpd-websocket*.
org.nanohttpdnanohttpd-websocket2.2.0
Here you write your own subclass of *fi.iki.elonen.NanoWebSocketServer* to configure and to serve the websocket requests. A small standard echo example is included as *fi.iki.elonen.samples.echo.DebugWebSocketServer*. You can use it as a starting point to implement your own services.
### Develop a custom HTTP file server
For a more classic aproach, perhaps to just create a HTTP server serving mostly service files from your disk, you can use the module with artifactId *nanohttpd-webserver*.
org.nanohttpdnanohttpd-webserver2.2.0
The included class *fi.iki.elonen.SimpleWebServer* is intended to be used as a starting point for your own implementation but it also can be used as is. Staring the class as is will start a http server on port 8080 and publishing the current directory.
### Living on the edge
The latest Github master version can be fetched through sonatype.org:
nanohttpdorg.nanohttpdXXXXX-SNAPSHOT
...
sonatype-snapshotshttps://oss.sonatype.org/content/repositories/snapshotstrue
### generating an self signed ssl certificate
Just a hint how to generate a certificate for localhost.
keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks -storepass password -validity 360 -keysize 2048 -ext SAN=DNS:localhost,IP:127.0.0.1 -validity 9999
This will generate a keystore file named 'keystore.jks' with a self signed certificate for a host named localhost with the ip adress 127.0.0.1 . Now
you can use:
server.makeSecure(NanoHTTPD.makeSSLSocketFactory("/keystore.jks", "password".toCharArray()), null);
Before you start the server to make Nanohttpd serve https connections, when you make sure 'keystore.jks' is in your classpath .
-----
*Thank you to everyone who has reported bugs and suggested fixes.*
================================================
FILE: src/site/site.xml
================================================
org.apache.maven.skinsmaven-fluido-skin1.3.0images/nanohttpd_logo.pngimages/nanohttpd_logo_text.pngfalsetrueNanohttpd/nanohttpdrightblack
================================================
FILE: webserver/.gitignore
================================================
/.settings/
/LICENSE.txt
================================================
FILE: webserver/build.gradle
================================================
description = 'NanoHttpd-Webserver'
dependencies {
compile project(':nanohttpd')
testCompile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.4.1'
}
task wrapper(type: Wrapper) {
gradleVersion = "4.4.1"
}
================================================
FILE: webserver/pom.xml
================================================
4.0.0org.nanohttpdnanohttpd-project2.3.2-SNAPSHOTnanohttpd-webserverjarNanoHttpd-Webservernanohttpd-webserver can serve any local directory as a webserver using nanohttpd.${project.groupId}nanohttpd${project.version}org.apache.httpcomponentshttpclient4.4.1testorg.apache.maven.pluginsmaven-assembly-plugin2.2-beta-5jar-with-dependenciesfi.iki.elonen.SimpleWebServerpackagesingle0.75
================================================
FILE: webserver/src/main/java/org/nanohttpd/webserver/InternalRewrite.java
================================================
package org.nanohttpd.webserver;
/*
* #%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.ByteArrayInputStream;
import java.util.Map;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.response.Status;
/**
* @author Paul S. Hawke (paul.hawke@gmail.com) On: 9/15/13 at 2:52 PM
*/
public class InternalRewrite extends Response {
private final String uri;
private final Map headers;
public InternalRewrite(Map headers, String uri) {
super(Status.OK, NanoHTTPD.MIME_HTML, new ByteArrayInputStream(new byte[0]), 0);
this.headers = headers;
this.uri = uri;
}
public Map getHeaders() {
return this.headers;
}
public String getUri() {
return this.uri;
}
}
================================================
FILE: webserver/src/main/java/org/nanohttpd/webserver/SimpleWebServer.java
================================================
package org.nanohttpd.webserver;
/*
* #%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.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.StringTokenizer;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.request.Method;
import org.nanohttpd.protocols.http.response.IStatus;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.response.Status;
import org.nanohttpd.util.ServerRunner;
public class SimpleWebServer extends NanoHTTPD {
/**
* Default Index file names.
*/
@SuppressWarnings("serial")
public static final List INDEX_FILE_NAMES = new ArrayList() {
{
add("index.html");
add("index.htm");
}
};
/**
* The distribution licence
*/
private static final String LICENCE;
static {
mimeTypes();
String text;
try {
InputStream stream = SimpleWebServer.class.getResourceAsStream("/LICENSE.txt");
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int count;
while ((count = stream.read(buffer)) >= 0) {
bytes.write(buffer, 0, count);
}
text = bytes.toString("UTF-8");
} catch (Exception e) {
text = "unknown";
}
LICENCE = text;
}
private static Map mimeTypeHandlers = new HashMap();
/**
* Starts as a standalone file server and waits for Enter.
*/
public static void main(String[] args) {
// Defaults
int port = 8080;
String host = null; // bind to all interfaces by default
List rootDirs = new ArrayList();
boolean quiet = false;
String cors = null;
Map options = new HashMap();
// Parse command-line, with short and long versions of the options.
for (int i = 0; i < args.length; ++i) {
if ("-h".equalsIgnoreCase(args[i]) || "--host".equalsIgnoreCase(args[i])) {
host = args[i + 1];
} else if ("-p".equalsIgnoreCase(args[i]) || "--port".equalsIgnoreCase(args[i])) {
port = Integer.parseInt(args[i + 1]);
} else if ("-q".equalsIgnoreCase(args[i]) || "--quiet".equalsIgnoreCase(args[i])) {
quiet = true;
} else if ("-d".equalsIgnoreCase(args[i]) || "--dir".equalsIgnoreCase(args[i])) {
rootDirs.add(new File(args[i + 1]).getAbsoluteFile());
} else if (args[i].startsWith("--cors")) {
cors = "*";
int equalIdx = args[i].indexOf('=');
if (equalIdx > 0) {
cors = args[i].substring(equalIdx + 1);
}
} else if ("--licence".equalsIgnoreCase(args[i])) {
System.out.println(SimpleWebServer.LICENCE + "\n");
} else if (args[i].startsWith("-X:")) {
int dot = args[i].indexOf('=');
if (dot > 0) {
String name = args[i].substring(0, dot);
String value = args[i].substring(dot + 1, args[i].length());
options.put(name, value);
}
}
}
if (rootDirs.isEmpty()) {
rootDirs.add(new File(".").getAbsoluteFile());
}
options.put("host", host);
options.put("port", "" + port);
options.put("quiet", String.valueOf(quiet));
StringBuilder sb = new StringBuilder();
for (File dir : rootDirs) {
if (sb.length() > 0) {
sb.append(":");
}
try {
sb.append(dir.getCanonicalPath());
} catch (IOException ignored) {
}
}
options.put("home", sb.toString());
ServiceLoader serviceLoader = ServiceLoader.load(WebServerPluginInfo.class);
for (WebServerPluginInfo info : serviceLoader) {
String[] mimeTypes = info.getMimeTypes();
for (String mime : mimeTypes) {
String[] indexFiles = info.getIndexFilesForMimeType(mime);
if (!quiet) {
System.out.print("# Found plugin for Mime type: \"" + mime + "\"");
if (indexFiles != null) {
System.out.print(" (serving index files: ");
for (String indexFile : indexFiles) {
System.out.print(indexFile + " ");
}
}
System.out.println(").");
}
registerPluginForMimeType(indexFiles, mime, info.getWebServerPlugin(mime), options);
}
}
ServerRunner.executeInstance(new SimpleWebServer(host, port, rootDirs, quiet, cors));
}
protected static void registerPluginForMimeType(String[] indexFiles, String mimeType, WebServerPlugin plugin, Map commandLineOptions) {
if (mimeType == null || plugin == null) {
return;
}
if (indexFiles != null) {
for (String filename : indexFiles) {
int dot = filename.lastIndexOf('.');
if (dot >= 0) {
String extension = filename.substring(dot + 1).toLowerCase();
mimeTypes().put(extension, mimeType);
}
}
SimpleWebServer.INDEX_FILE_NAMES.addAll(Arrays.asList(indexFiles));
}
SimpleWebServer.mimeTypeHandlers.put(mimeType, plugin);
plugin.initialize(commandLineOptions);
}
private final boolean quiet;
private final String cors;
protected List rootDirs;
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, null);
}
public SimpleWebServer(String host, int port, List wwwroots, boolean quiet) {
this(host, port, wwwroots, quiet, null);
}
public SimpleWebServer(String host, int port, List wwwroots, boolean quiet, String cors) {
super(host, port);
this.quiet = quiet;
this.cors = cors;
this.rootDirs = new ArrayList(wwwroots);
init();
}
private boolean canServeUri(String uri, File homeDir) {
boolean canServeUri;
File f = new File(homeDir, uri);
canServeUri = f.exists();
if (!canServeUri) {
WebServerPlugin plugin = SimpleWebServer.mimeTypeHandlers.get(getMimeTypeForFile(uri));
if (plugin != null) {
canServeUri = plugin.canServeUri(uri, homeDir);
}
}
return canServeUri;
}
/**
* 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 ("/".equals(tok)) {
newUri += "/";
} else if (" ".equals(tok)) {
newUri += "%20";
} else {
try {
newUri += URLEncoder.encode(tok, "UTF-8");
} catch (UnsupportedEncodingException ignored) {
}
}
}
return newUri;
}
private String findIndexFileInDirectory(File directory) {
for (String fileName : SimpleWebServer.INDEX_FILE_NAMES) {
File indexFile = new File(directory, fileName);
if (indexFile.isFile()) {
return fileName;
}
}
return null;
}
protected Response getForbiddenResponse(String s) {
return Response.newFixedLengthResponse(Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "FORBIDDEN: " + s);
}
protected Response getInternalErrorResponse(String s) {
return Response.newFixedLengthResponse(Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "INTERNAL ERROR: " + s);
}
protected Response getNotFoundResponse() {
return Response.newFixedLengthResponse(Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Error 404, file not found.");
}
/**
* Used to initialize and customize the server.
*/
public void init() {
}
protected String listDirectory(String uri, File f) {
String heading = "Directory " + uri;
StringBuilder msg =
new StringBuilder("" + heading + "" + "
" + heading + "
");
String up = null;
if (uri.length() > 1) {
String u = uri.substring(0, uri.length() - 1);
int slash = u.lastIndexOf('/');
if (slash >= 0 && slash < u.length()) {
up = uri.substring(0, slash + 1);
}
}
List files = Arrays.asList(f.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return new File(dir, name).isFile();
}
}));
Collections.sort(files);
List directories = Arrays.asList(f.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return new File(dir, name).isDirectory();
}
}));
Collections.sort(directories);
if (up != null || directories.size() + files.size() > 0) {
msg.append("
");
if (up != null || directories.size() > 0) {
msg.append("");
if (up != null) {
msg.append("
");
}
msg.append("");
return msg.toString();
}
public static Response newFixedLengthResponse(IStatus status, String mimeType, String message) {
Response response = Response.newFixedLengthResponse(status, mimeType, message);
response.addHeader("Accept-Ranges", "bytes");
return response;
}
private Response respond(Map headers, IHTTPSession session, String uri) {
// First let's handle CORS OPTION query
Response r;
if (cors != null && Method.OPTIONS.equals(session.getMethod())) {
r = Response.newFixedLengthResponse(Status.OK, MIME_PLAINTEXT, null, 0);
} else {
r = defaultRespond(headers, session, uri);
}
if (cors != null) {
r = addCORSHeaders(headers, r, cors);
}
return r;
}
private Response defaultRespond(Map headers, IHTTPSession session, String uri) {
// 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.contains("../")) {
return getForbiddenResponse("Won't serve ../ for security reasons.");
}
boolean canServeUri = false;
File homeDir = null;
for (int i = 0; !canServeUri && i < this.rootDirs.size(); i++) {
homeDir = this.rootDirs.get(i);
canServeUri = canServeUri(uri, homeDir);
}
if (!canServeUri) {
return getNotFoundResponse();
}
// Browsers get confused without '/' after the directory, send a
// redirect.
File f = new File(homeDir, uri);
if (f.isDirectory() && !uri.endsWith("/")) {
uri += "/";
Response res = newFixedLengthResponse(Status.REDIRECT, NanoHTTPD.MIME_HTML, "Redirected: " + uri + "");
res.addHeader("Location", uri);
return res;
}
if (f.isDirectory()) {
// First look for index files (index.html, index.htm, etc) and if
// none found, list the directory if readable.
String indexFile = findIndexFileInDirectory(f);
if (indexFile == null) {
if (f.canRead()) {
// No index file, list the directory if it is readable
return newFixedLengthResponse(Status.OK, NanoHTTPD.MIME_HTML, listDirectory(uri, f));
} else {
return getForbiddenResponse("No directory listing.");
}
} else {
return respond(headers, session, uri + indexFile);
}
}
String mimeTypeForFile = getMimeTypeForFile(uri);
WebServerPlugin plugin = SimpleWebServer.mimeTypeHandlers.get(mimeTypeForFile);
Response response = null;
if (plugin != null && plugin.canServeUri(uri, homeDir)) {
response = plugin.serveFile(uri, headers, session, f, mimeTypeForFile);
if (response != null && response instanceof InternalRewrite) {
InternalRewrite rewrite = (InternalRewrite) response;
return respond(rewrite.getHeaders(), session, rewrite.getUri());
}
} else {
response = serveFile(uri, headers, f, mimeTypeForFile);
}
return response != null ? response : getNotFoundResponse();
}
@Override
public Response serve(IHTTPSession session) {
Map header = session.getHeaders();
Map parms = session.getParms();
String uri = session.getUri();
if (!this.quiet) {
System.out.println(session.getMethod() + " '" + uri + "' ");
Iterator 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) + "'");
}
}
for (File homeDir : this.rootDirs) {
// Make sure we won't die of an exception later
if (!homeDir.isDirectory()) {
return getInternalErrorResponse("given path is not a directory (" + homeDir + ").");
}
}
return respond(Collections.unmodifiableMap(header), session, uri);
}
/**
* Serves file from homeDir and its' subdirectories (only). Uses only URI,
* ignores all headers and HTTP parameters.
*/
Response serveFile(String uri, Map header, File file, String mime) {
Response res;
try {
// Calculate etag
String etag = Integer.toHexString((file.getAbsolutePath() + file.lastModified() + "" + file.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) {
}
}
}
// get if-range header. If present, it must match etag or else we
// should ignore the range request
String ifRange = header.get("if-range");
boolean headerIfRangeMissingOrMatching = (ifRange == null || etag.equals(ifRange));
String ifNoneMatch = header.get("if-none-match");
boolean headerIfNoneMatchPresentAndMatching = ifNoneMatch != null && ("*".equals(ifNoneMatch) || ifNoneMatch.equals(etag));
// Change return code and add Content-Range header when skipping is
// requested
long fileLen = file.length();
if (headerIfRangeMissingOrMatching && range != null && startFrom >= 0 && startFrom < fileLen) {
// range request that matches current etag
// and the startFrom of the range is satisfiable
if (headerIfNoneMatchPresentAndMatching) {
// range request that matches current etag
// and the startFrom of the range is satisfiable
// would return range from file
// respond with not-modified
res = newFixedLengthResponse(Status.NOT_MODIFIED, mime, "");
res.addHeader("ETag", etag);
} else {
if (endAt < 0) {
endAt = fileLen - 1;
}
long newLen = endAt - startFrom + 1;
if (newLen < 0) {
newLen = 0;
}
FileInputStream fis = new FileInputStream(file);
fis.skip(startFrom);
res = Response.newFixedLengthResponse(Status.PARTIAL_CONTENT, mime, fis, newLen);
res.addHeader("Accept-Ranges", "bytes");
res.addHeader("Content-Length", "" + newLen);
res.addHeader("Content-Range", "bytes " + startFrom + "-" + endAt + "/" + fileLen);
res.addHeader("ETag", etag);
}
} else {
if (headerIfRangeMissingOrMatching && range != null && startFrom >= fileLen) {
// return the size of the file
// 4xx responses are not trumped by if-none-match
res = newFixedLengthResponse(Status.RANGE_NOT_SATISFIABLE, NanoHTTPD.MIME_PLAINTEXT, "");
res.addHeader("Content-Range", "bytes */" + fileLen);
res.addHeader("ETag", etag);
} else if (range == null && headerIfNoneMatchPresentAndMatching) {
// full-file-fetch request
// would return entire file
// respond with not-modified
res = newFixedLengthResponse(Status.NOT_MODIFIED, mime, "");
res.addHeader("ETag", etag);
} else if (!headerIfRangeMissingOrMatching && headerIfNoneMatchPresentAndMatching) {
// range request that doesn't match current etag
// would return entire (different) file
// respond with not-modified
res = newFixedLengthResponse(Status.NOT_MODIFIED, mime, "");
res.addHeader("ETag", etag);
} else {
// supply the file
res = newFixedFileResponse(file, mime);
res.addHeader("Content-Length", "" + fileLen);
res.addHeader("ETag", etag);
}
}
} catch (IOException ioe) {
res = getForbiddenResponse("Reading file failed.");
}
return res;
}
private Response newFixedFileResponse(File file, String mime) throws FileNotFoundException {
Response res;
res = Response.newFixedLengthResponse(Status.OK, mime, new FileInputStream(file), (int) file.length());
res.addHeader("Accept-Ranges", "bytes");
return res;
}
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);
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
public final static String DEFAULT_ALLOWED_HEADERS = "origin,accept,content-type";
public final static String ACCESS_CONTROL_ALLOW_HEADER_PROPERTY_NAME = "AccessControlAllowHeader";
}
================================================
FILE: webserver/src/main/java/org/nanohttpd/webserver/WebServerPlugin.java
================================================
package org.nanohttpd.webserver;
/*
* #%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.File;
import java.util.Map;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.response.Response;
/**
* @author Paul S. Hawke (paul.hawke@gmail.com) On: 9/14/13 at 8:09 AM
*/
public interface WebServerPlugin {
boolean canServeUri(String uri, File rootDir);
void initialize(Map commandLineOptions);
Response serveFile(String uri, Map headers, IHTTPSession session, File file, String mimeType);
}
================================================
FILE: webserver/src/main/java/org/nanohttpd/webserver/WebServerPluginInfo.java
================================================
package org.nanohttpd.webserver;
/*
* #%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%
*/
/**
* @author Paul S. Hawke (paul.hawke@gmail.com) On: 9/14/13 at 8:09 AM
*/
public interface WebServerPluginInfo {
String[] getIndexFilesForMimeType(String mime);
String[] getMimeTypes();
WebServerPlugin getWebServerPlugin(String mimeType);
}
================================================
FILE: webserver/src/site/site.xml
================================================
org.apache.maven.skinsmaven-fluido-skin1.3.0../images/nanohttpd_logo.png../images/nanohttpd_logo_text.pngfalsetrueNanohttpd/nanohttpdrightblack
================================================
FILE: webserver/src/test/java/org/nanohttpd/junit/webserver/AbstractTestHttpServer.java
================================================
package org.nanohttpd.junit.webserver;
/*
* #%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;
}
}
================================================
FILE: webserver/src/test/java/org/nanohttpd/junit/webserver/DummyPlugin.java
================================================
package org.nanohttpd.junit.webserver;
/*
* #%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.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.Map;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.response.Status;
import org.nanohttpd.webserver.InternalRewrite;
import org.nanohttpd.webserver.WebServerPlugin;
public class DummyPlugin implements WebServerPlugin {
@Override
public boolean canServeUri(String uri, File rootDir) {
return true;
}
@Override
public void initialize(Map commandLineOptions) {
}
@Override
public Response serveFile(String uri, Map headers, IHTTPSession session, File file, String mimeType) {
if (uri.contains("rewrite")) {
return new InternalRewrite(headers, "/testdir/test.html");
}
byte[] bytes = "".getBytes();
InputStream data = new ByteArrayInputStream(bytes);
return Response.newFixedLengthResponse(Status.OK, "text/xml", data, bytes.length);
}
}
================================================
FILE: webserver/src/test/java/org/nanohttpd/junit/webserver/DummyPluginInfo.java
================================================
package org.nanohttpd.junit.webserver;
import org.nanohttpd.webserver.WebServerPlugin;
import org.nanohttpd.webserver.WebServerPluginInfo;
/*
* #%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%
*/
public class DummyPluginInfo implements WebServerPluginInfo {
@Override
public String[] getIndexFilesForMimeType(String mime) {
return new String[]{
"index.xml"
};
}
@Override
public String[] getMimeTypes() {
return new String[]{
"text/xml"
};
}
@Override
public WebServerPlugin getWebServerPlugin(String mimeType) {
return new DummyPlugin();
}
}
================================================
FILE: webserver/src/test/java/org/nanohttpd/junit/webserver/TestCorsHttpServer.java
================================================
package org.nanohttpd.junit.webserver;
/*
* #%L
* NanoHttpd-Webserver
* %%
* Copyright (C) 2012 - 2016 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.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;
import org.nanohttpd.webserver.SimpleWebServer;
/**
* @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);
}
}
}
================================================
FILE: webserver/src/test/java/org/nanohttpd/junit/webserver/TestCorsHttpServerWithSingleOrigin.java
================================================
package org.nanohttpd.junit.webserver;
/*
* #%L
* NanoHttpd-Webserver
* %%
* Copyright (C) 2012 - 2016 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.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;
import org.nanohttpd.webserver.SimpleWebServer;
/**
* @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();
}
}
================================================
FILE: webserver/src/test/java/org/nanohttpd/junit/webserver/TestHttpServer.java
================================================
package org.nanohttpd.junit.webserver;
/*
* #%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 static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.UnsupportedEncodingException;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
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;
import org.nanohttpd.webserver.SimpleWebServer;
public class TestHttpServer 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"
};
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 doTest404() throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet("http://localhost:9090/xxx/yyy.html");
CloseableHttpResponse response = httpclient.execute(httpget);
Assert.assertEquals(404, response.getStatusLine().getStatusCode());
response.close();
}
@Test
public void doPlugin() throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet("http://localhost:9090/index.xml");
CloseableHttpResponse response = httpclient.execute(httpget);
String string = new String(readContents(response.getEntity()), "UTF-8");
Assert.assertEquals("", string);
response.close();
httpget = new HttpGet("http://localhost:9090/testdir/testdir/different.xml");
response = httpclient.execute(httpget);
string = new String(readContents(response.getEntity()), "UTF-8");
Assert.assertEquals("", string);
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.assertEquals("\n\ndummy\n\n\n\t
it works
\n\n", string);
response.close();
httpget = new HttpGet("http://localhost:9090/");
response = httpclient.execute(httpget);
entity = response.getEntity();
string = new String(readContents(entity), "UTF-8");
Assert.assertTrue(string.indexOf("testdir") > 0);
response.close();
httpget = new HttpGet("http://localhost:9090/testdir");
response = httpclient.execute(httpget);
entity = response.getEntity();
string = new String(readContents(entity), "UTF-8");
Assert.assertTrue(string.indexOf("test.html") > 0);
response.close();
httpget = new HttpGet("http://localhost:9090/testdir/testpdf.pdf");
response = httpclient.execute(httpget);
entity = response.getEntity();
byte[] actual = readContents(entity);
byte[] expected = readContents(new FileInputStream("src/test/resources/testdir/testpdf.pdf"));
Assert.assertArrayEquals(expected, actual);
response.close();
}
@Test
public void doArgumentTest() throws InterruptedException, UnsupportedEncodingException, IOException {
final String testPort = "9458";
Thread testServer = new Thread(new Runnable() {
@Override
public void run() {
String[] args = {
"-h",
"localhost",
"-p",
testPort,
"-d",
"src/test/resources"
};
SimpleWebServer.main(args);
}
});
testServer.start();
Thread.sleep(200);
HttpGet httpget = new HttpGet("http://localhost:" + testPort + "/");
CloseableHttpClient httpclient = HttpClients.createDefault();
CloseableHttpResponse response = null;
try {
response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
String str = new String(readContents(entity), "UTF-8");
Assert.assertTrue("The response entity didn't contain the string 'testdir'", str.indexOf("testdir") >= 0);
} finally {
if (response != null)
response.close();
}
}
@Test
public void testURLContainsParentDirectory() throws ClientProtocolException, IOException {
CloseableHttpResponse response = null;
try {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("http://localhost:9090/../test.html");
response = httpClient.execute(httpGet);
Assert.assertEquals("The response status should be 403(Forbidden), " + "since the server won't serve requests with '../' due to security reasons", 403, response
.getStatusLine().getStatusCode());
} finally {
if (response != null) {
response.close();
}
}
}
@Test
public void testIndexFileIsShownWhenURLEndsWithDirectory() throws ClientProtocolException, IOException {
CloseableHttpResponse response = null;
try {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("http://localhost:9090/testdir/testdir");
response = httpClient.execute(httpGet);
HttpEntity entity = response.getEntity();
String responseString = new String(readContents(entity), "UTF-8");
Assert.assertThat("When the URL ends with a directory, and if an index.html file is present in that directory," + " the server should respond with that file",
responseString, containsString("Simple index file"));
} finally {
if (response != null) {
response.close();
}
}
}
@Test
public void testPluginInternalRewrite() throws ClientProtocolException, IOException {
CloseableHttpResponse response = null;
try {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("http://localhost:9090/rewrite/index.xml");
response = httpClient.execute(httpGet);
HttpEntity entity = response.getEntity();
String responseString = new String(readContents(entity), "UTF-8");
Assert.assertThat("If a plugin returns an InternalRewrite from the serveFile method, the rewritten request should be served", responseString,
allOf(containsString("dummy"), containsString("it works")));
} finally {
if (response != null) {
response.close();
}
}
}
@Test
public void testRangeHeaderWithStartPositionOnly() throws ClientProtocolException, IOException {
CloseableHttpResponse response = null;
try {
HttpGet httpGet = new HttpGet("http://localhost:9090/testdir/test.html");
httpGet.addHeader("range", "bytes=10-");
CloseableHttpClient httpClient = HttpClients.createDefault();
response = httpClient.execute(httpGet);
HttpEntity entity = response.getEntity();
String responseString = new String(readContents(entity), "UTF-8");
Assert.assertThat("The data from the beginning of the file should have been skipped as specified in the 'range' header", responseString,
not(containsString("")));
Assert.assertThat("The response should contain the data from the end of the file since end position was not given in the 'range' header", responseString,
containsString(""));
Assert.assertEquals("The content length should be the length starting from the requested byte", "74", response.getHeaders("Content-Length")[0].getValue());
Assert.assertEquals("The 'Content-Range' header should contain the correct lengths and offsets based on the range served", "bytes 10-83/84",
response.getHeaders("Content-Range")[0].getValue());
Assert.assertEquals("Response status for a successful range request should be PARTIAL_CONTENT(206)", 206, response.getStatusLine().getStatusCode());
} finally {
if (response != null) {
response.close();
}
}
}
@Test
public void testRangeStartGreaterThanFileLength() throws ClientProtocolException, IOException {
CloseableHttpResponse response = null;
try {
HttpGet httpGet = new HttpGet("http://localhost:9090/testdir/test.html");
httpGet.addHeader("range", "bytes=1000-");
CloseableHttpClient httpClient = HttpClients.createDefault();
response = httpClient.execute(httpGet);
Assert.assertEquals("Response status for a request with 'range' header value which exceeds file length should be RANGE_NOT_SATISFIABLE(416)", 416, response
.getStatusLine().getStatusCode());
Assert.assertEquals("The 'Content-Range' header should contain the correct lengths and offsets based on the range served", "bytes */84",
response.getHeaders("Content-Range")[0].getValue());
} finally {
if (response != null) {
response.close();
}
}
}
@Test
public void testRangeHeaderWithStartAndEndPosition() throws ClientProtocolException, IOException {
CloseableHttpResponse response = null;
try {
HttpGet httpGet = new HttpGet("http://localhost:9090/testdir/test.html");
httpGet.addHeader("range", "bytes=10-40");
CloseableHttpClient httpClient = HttpClients.createDefault();
response = httpClient.execute(httpGet);
HttpEntity entity = response.getEntity();
String responseString = new String(readContents(entity), "UTF-8");
Assert.assertThat("The data from the beginning of the file should have been skipped as specified in the 'range' header", responseString,
not(containsString("")));
Assert.assertThat("The data from the end of the file should have been skipped as specified in the 'range' header", responseString, not(containsString("")));
Assert.assertEquals("The 'Content-Length' should be the length from the requested start position to end position", "31",
response.getHeaders("Content-Length")[0].getValue());
Assert.assertEquals("The 'Contnet-Range' header should contain the correct lengths and offsets based on the range served", "bytes 10-40/84",
response.getHeaders("Content-Range")[0].getValue());
Assert.assertEquals("Response status for a successful request with 'range' header should be PARTIAL_CONTENT(206)", 206, response.getStatusLine().getStatusCode());
} finally {
if (response != null) {
response.close();
}
}
}
@Test
public void testIfNoneMatchHeader() throws ClientProtocolException, IOException {
CloseableHttpResponse response = null;
try {
HttpGet httpGet = new HttpGet("http://localhost:9090/testdir/test.html");
httpGet.addHeader("if-none-match", "*");
CloseableHttpClient httpClient = HttpClients.createDefault();
response = httpClient.execute(httpGet);
Assert.assertEquals("The response status to a reqeuest with 'if-non-match=*' header should be NOT_MODIFIED(304), if the file exists", 304, response
.getStatusLine().getStatusCode());
} finally {
if (response != null) {
response.close();
}
}
}
@Test
public void testRangeHeaderAndIfNoneMatchHeader() throws ClientProtocolException, IOException {
CloseableHttpResponse response = null;
try {
HttpGet httpGet = new HttpGet("http://localhost:9090/testdir/test.html");
httpGet.addHeader("range", "bytes=10-20");
httpGet.addHeader("if-none-match", "*");
CloseableHttpClient httpClient = HttpClients.createDefault();
response = httpClient.execute(httpGet);
Assert.assertEquals("The response status to a reqeuest with 'if-non-match=*' header and 'range' header should be NOT_MODIFIED(304),"
+ " if the file exists, because 'if-non-match' header should be given priority", 304, response.getStatusLine().getStatusCode());
} finally {
if (response != null) {
response.close();
}
}
}
}
================================================
FILE: webserver/src/test/resources/META-INF/services/org.nanohttpd.webserver.WebServerPluginInfo
================================================
org.nanohttpd.junit.webserver.DummyPluginInfo
================================================
FILE: webserver/src/test/resources/testdir/test.html
================================================
dummy
it works
================================================
FILE: webserver/src/test/resources/testdir/testdir/different.xml
================================================
This sould not show up ;-)
================================================
FILE: webserver/src/test/resources/testdir/testdir/index.html
================================================
Simple index file
================================================
FILE: websocket/.gitignore
================================================
/.settings/
/LICENSE.txt
================================================
FILE: websocket/build.gradle
================================================
description = 'NanoHttpd-Websocket'
dependencies {
compile project(':nanohttpd')
testCompile group: 'org.mockito', name: 'mockito-all', version: '1.9.5'
testCompile group: 'org.eclipse.jetty.websocket', name: 'websocket-client', version: '9.3.0.M2'
}
task wrapper(type: Wrapper) {
gradleVersion = "4.4.1"
}
================================================
FILE: websocket/pom.xml
================================================
4.0.0org.nanohttpdnanohttpd-project2.3.2-SNAPSHOTnanohttpd-websocketjarNanoHttpd-Websocketnanohttpd-websocket is a very low profile websocket server based on nanohttpd.org.apache.maven.pluginsmaven-jar-plugindefault-jar**/samples/**echo-jarpackagejarechotruefi.iki.elonen.samples.echo.EchoSocketSample${project.groupId}nanohttpd${project.version}org.mockitomockito-all1.9.5testorg.eclipse.jetty.websocketwebsocket-client9.3.0.M2test0.67
================================================
FILE: websocket/src/main/java/org/nanohttpd/protocols/websockets/CloseCode.java
================================================
package org.nanohttpd.protocols.websockets;
/*
* #%L
* NanoHttpd-Websocket
* %%
* Copyright (C) 2012 - 2016 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%
*/
public enum CloseCode {
NormalClosure(1000),
GoingAway(1001),
ProtocolError(1002),
UnsupportedData(1003),
NoStatusRcvd(1005),
AbnormalClosure(1006),
InvalidFramePayloadData(1007),
PolicyViolation(1008),
MessageTooBig(1009),
MandatoryExt(1010),
InternalServerError(1011),
TLSHandshake(1015);
public static CloseCode find(int value) {
for (CloseCode code : values()) {
if (code.getValue() == value) {
return code;
}
}
return null;
}
private final int code;
private CloseCode(int code) {
this.code = code;
}
public int getValue() {
return this.code;
}
}
================================================
FILE: websocket/src/main/java/org/nanohttpd/protocols/websockets/CloseFrame.java
================================================
package org.nanohttpd.protocols.websockets;
/*
* #%L
* NanoHttpd-Websocket
* %%
* Copyright (C) 2012 - 2016 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.nio.charset.CharacterCodingException;
public class CloseFrame extends WebSocketFrame {
private static byte[] generatePayload(CloseCode code, String closeReason) throws CharacterCodingException {
if (code != null) {
byte[] reasonBytes = text2Binary(closeReason);
byte[] payload = new byte[reasonBytes.length + 2];
payload[0] = (byte) (code.getValue() >> 8 & 0xFF);
payload[1] = (byte) (code.getValue() & 0xFF);
System.arraycopy(reasonBytes, 0, payload, 2, reasonBytes.length);
return payload;
} else {
return new byte[0];
}
}
private CloseCode _closeCode;
private String _closeReason;
public CloseFrame(CloseCode code, String closeReason) throws CharacterCodingException {
super(OpCode.Close, true, generatePayload(code, closeReason));
}
public CloseFrame(WebSocketFrame wrap) throws CharacterCodingException {
super(wrap);
assert wrap.getOpCode() == OpCode.Close;
if (wrap.getBinaryPayload().length >= 2) {
this._closeCode = CloseCode.find((wrap.getBinaryPayload()[0] & 0xFF) << 8 | wrap.getBinaryPayload()[1] & 0xFF);
this._closeReason = binary2Text(getBinaryPayload(), 2, getBinaryPayload().length - 2);
}
}
public CloseCode getCloseCode() {
return this._closeCode;
}
public String getCloseReason() {
return this._closeReason;
}
}
================================================
FILE: websocket/src/main/java/org/nanohttpd/protocols/websockets/NanoWSD.java
================================================
package org.nanohttpd.protocols.websockets;
/*
* #%L
* NanoHttpd-Websocket
* %%
* Copyright (C) 2012 - 2016 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.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.logging.Logger;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.response.Status;
import org.nanohttpd.util.IHandler;
public abstract class NanoWSD extends NanoHTTPD {
/**
* logger to log to.
*/
public static final Logger LOG = Logger.getLogger(NanoWSD.class.getName());
public static final String HEADER_UPGRADE = "upgrade";
public static final String HEADER_UPGRADE_VALUE = "websocket";
public static final String HEADER_CONNECTION = "connection";
public static final String HEADER_CONNECTION_VALUE = "Upgrade";
public static final String HEADER_WEBSOCKET_VERSION = "sec-websocket-version";
public static final String HEADER_WEBSOCKET_VERSION_VALUE = "13";
public static final String HEADER_WEBSOCKET_KEY = "sec-websocket-key";
public static final String HEADER_WEBSOCKET_ACCEPT = "sec-websocket-accept";
public static final String HEADER_WEBSOCKET_PROTOCOL = "sec-websocket-protocol";
private final static String WEBSOCKET_KEY_MAGIC = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
private final static char[] ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
/**
* Translates the specified byte array into Base64 string.
*
* Android has android.util.Base64, sun has sun.misc.Base64Encoder, Java 8
* hast java.util.Base64, I have this from stackoverflow:
* http://stackoverflow.com/a/4265472
*
*
* @param buf
* the byte array (not null)
* @return the translated Base64 string (not null)
*/
private static String encodeBase64(byte[] buf) {
int size = buf.length;
char[] ar = new char[(size + 2) / 3 * 4];
int a = 0;
int i = 0;
while (i < size) {
byte b0 = buf[i++];
byte b1 = i < size ? buf[i++] : 0;
byte b2 = i < size ? buf[i++] : 0;
int mask = 0x3F;
ar[a++] = NanoWSD.ALPHABET[b0 >> 2 & mask];
ar[a++] = NanoWSD.ALPHABET[(b0 << 4 | (b1 & 0xFF) >> 4) & mask];
ar[a++] = NanoWSD.ALPHABET[(b1 << 2 | (b2 & 0xFF) >> 6) & mask];
ar[a++] = NanoWSD.ALPHABET[b2 & mask];
}
switch (size % 3) {
case 1:
ar[--a] = '=';
case 2:
ar[--a] = '=';
}
return new String(ar);
}
public static String makeAcceptKey(String key) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-1");
String text = key + NanoWSD.WEBSOCKET_KEY_MAGIC;
md.update(text.getBytes(), 0, text.length());
byte[] sha1hash = md.digest();
return encodeBase64(sha1hash);
}
protected final class Interceptor implements IHandler {
public Interceptor() {
}
@Override
public Response handle(IHTTPSession input) {
return handleWebSocket(input);
}
}
public NanoWSD(int port) {
super(port);
addHTTPInterceptor(new Interceptor());
}
public NanoWSD(String hostname, int port) {
super(hostname, port);
addHTTPInterceptor(new Interceptor());
}
private boolean isWebSocketConnectionHeader(Map headers) {
String connection = headers.get(NanoWSD.HEADER_CONNECTION);
return connection != null && connection.toLowerCase().contains(NanoWSD.HEADER_CONNECTION_VALUE.toLowerCase());
}
protected boolean isWebsocketRequested(IHTTPSession session) {
Map headers = session.getHeaders();
String upgrade = headers.get(NanoWSD.HEADER_UPGRADE);
boolean isCorrectConnection = isWebSocketConnectionHeader(headers);
boolean isUpgrade = NanoWSD.HEADER_UPGRADE_VALUE.equalsIgnoreCase(upgrade);
return isUpgrade && isCorrectConnection;
}
// --------------------------------Listener--------------------------------
protected abstract WebSocket openWebSocket(IHTTPSession handshake);
public Response handleWebSocket(final IHTTPSession session) {
Map headers = session.getHeaders();
if (isWebsocketRequested(session)) {
if (!NanoWSD.HEADER_WEBSOCKET_VERSION_VALUE.equalsIgnoreCase(headers.get(NanoWSD.HEADER_WEBSOCKET_VERSION))) {
return Response.newFixedLengthResponse(Status.BAD_REQUEST, NanoHTTPD.MIME_PLAINTEXT,
"Invalid Websocket-Version " + headers.get(NanoWSD.HEADER_WEBSOCKET_VERSION));
}
if (!headers.containsKey(NanoWSD.HEADER_WEBSOCKET_KEY)) {
return Response.newFixedLengthResponse(Status.BAD_REQUEST, NanoHTTPD.MIME_PLAINTEXT, "Missing Websocket-Key");
}
WebSocket webSocket = openWebSocket(session);
Response handshakeResponse = webSocket.getHandshakeResponse();
try {
handshakeResponse.addHeader(NanoWSD.HEADER_WEBSOCKET_ACCEPT, makeAcceptKey(headers.get(NanoWSD.HEADER_WEBSOCKET_KEY)));
} catch (NoSuchAlgorithmException e) {
return Response.newFixedLengthResponse(Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT,
"The SHA-1 Algorithm required for websockets is not available on the server.");
}
if (headers.containsKey(NanoWSD.HEADER_WEBSOCKET_PROTOCOL)) {
handshakeResponse.addHeader(NanoWSD.HEADER_WEBSOCKET_PROTOCOL, headers.get(NanoWSD.HEADER_WEBSOCKET_PROTOCOL).split(",")[0]);
}
return handshakeResponse;
} else {
return null;
}
}
}
================================================
FILE: websocket/src/main/java/org/nanohttpd/protocols/websockets/OpCode.java
================================================
package org.nanohttpd.protocols.websockets;
/*
* #%L
* NanoHttpd-Websocket
* %%
* Copyright (C) 2012 - 2016 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%
*/
public enum OpCode {
Continuation(0),
Text(1),
Binary(2),
Close(8),
Ping(9),
Pong(10);
public static OpCode find(byte value) {
for (OpCode opcode : values()) {
if (opcode.getValue() == value) {
return opcode;
}
}
return null;
}
private final byte code;
private OpCode(int code) {
this.code = (byte) code;
}
public byte getValue() {
return this.code;
}
public boolean isControlFrame() {
return this == Close || this == Ping || this == Pong;
}
}
================================================
FILE: websocket/src/main/java/org/nanohttpd/protocols/websockets/State.java
================================================
package org.nanohttpd.protocols.websockets;
/*
* #%L
* NanoHttpd-Websocket
* %%
* Copyright (C) 2012 - 2016 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%
*/
public enum State {
UNCONNECTED,
CONNECTING,
OPEN,
CLOSING,
CLOSED
}
================================================
FILE: websocket/src/main/java/org/nanohttpd/protocols/websockets/WebSocket.java
================================================
package org.nanohttpd.protocols.websockets;
/*
* #%L
* NanoHttpd-Websocket
* %%
* Copyright (C) 2012 - 2016 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.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.CharacterCodingException;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.response.Status;
public abstract class WebSocket {
private final InputStream in;
private OutputStream out;
private OpCode continuousOpCode = null;
private final List continuousFrames = new LinkedList();
private State state = State.UNCONNECTED;
protected boolean enforceNoGzip = true;
private final IHTTPSession handshakeRequest;
private final Response handshakeResponse = new Response(Status.SWITCH_PROTOCOL, null, (InputStream) null, 0) {
@Override
public void send(OutputStream out) {
WebSocket.this.out = out;
WebSocket.this.state = State.CONNECTING;
super.send(out);
WebSocket.this.state = State.OPEN;
WebSocket.this.onOpen();
readWebsocket();
}
};
public WebSocket(IHTTPSession handshakeRequest) {
this.handshakeRequest = handshakeRequest;
this.in = handshakeRequest.getInputStream();
if (enforceNoGzip)
handshakeResponse.setUseGzip(false);
this.handshakeResponse.addHeader(NanoWSD.HEADER_UPGRADE, NanoWSD.HEADER_UPGRADE_VALUE);
this.handshakeResponse.addHeader(NanoWSD.HEADER_CONNECTION, NanoWSD.HEADER_CONNECTION_VALUE);
}
public boolean isOpen() {
return state == State.OPEN;
}
protected abstract void onOpen();
protected abstract void onClose(CloseCode code, String reason, boolean initiatedByRemote);
protected abstract void onMessage(WebSocketFrame message);
protected abstract void onPong(WebSocketFrame pong);
protected abstract void onException(IOException exception);
/**
* Debug method. Do not Override unless for debug purposes!
*
* @param frame
* The received WebSocket Frame.
*/
protected void debugFrameReceived(WebSocketFrame frame) {
}
/**
* Debug method. Do not Override unless for debug purposes!
* This method is called before actually sending the frame.
*
* @param frame
* The sent WebSocket Frame.
*/
protected void debugFrameSent(WebSocketFrame frame) {
}
public void close(CloseCode code, String reason, boolean initiatedByRemote) throws IOException {
State oldState = this.state;
this.state = State.CLOSING;
if (oldState == State.OPEN) {
sendFrame(new CloseFrame(code, reason));
} else {
doClose(code, reason, initiatedByRemote);
}
}
private void doClose(CloseCode code, String reason, boolean initiatedByRemote) {
if (this.state == State.CLOSED) {
return;
}
if (this.in != null) {
try {
this.in.close();
} catch (IOException e) {
NanoWSD.LOG.log(Level.FINE, "close failed", e);
}
}
if (this.out != null) {
try {
this.out.close();
} catch (IOException e) {
NanoWSD.LOG.log(Level.FINE, "close failed", e);
}
}
this.state = State.CLOSED;
onClose(code, reason, initiatedByRemote);
}
// --------------------------------IO--------------------------------------
public IHTTPSession getHandshakeRequest() {
return this.handshakeRequest;
}
public Response getHandshakeResponse() {
return this.handshakeResponse;
}
private void handleCloseFrame(WebSocketFrame frame) throws IOException {
CloseCode code = CloseCode.NormalClosure;
String reason = "";
if (frame instanceof CloseFrame) {
code = ((CloseFrame) frame).getCloseCode();
reason = ((CloseFrame) frame).getCloseReason();
}
if (this.state == State.CLOSING) {
// Answer for my requested close
doClose(code, reason, false);
} else {
close(code, reason, true);
}
}
private void handleFrameFragment(WebSocketFrame frame) throws IOException {
if (frame.getOpCode() != OpCode.Continuation) {
// First
if (this.continuousOpCode != null) {
throw new WebSocketException(CloseCode.ProtocolError, "Previous continuous frame sequence not completed.");
}
this.continuousOpCode = frame.getOpCode();
this.continuousFrames.clear();
this.continuousFrames.add(frame);
} else if (frame.isFin()) {
// Last
if (this.continuousOpCode == null) {
throw new WebSocketException(CloseCode.ProtocolError, "Continuous frame sequence was not started.");
}
this.continuousFrames.add(frame);
onMessage(new WebSocketFrame(this.continuousOpCode, this.continuousFrames));
this.continuousOpCode = null;
this.continuousFrames.clear();
} else if (this.continuousOpCode == null) {
// Unexpected
throw new WebSocketException(CloseCode.ProtocolError, "Continuous frame sequence was not started.");
} else {
// Intermediate
this.continuousFrames.add(frame);
}
}
private void handleWebsocketFrame(WebSocketFrame frame) throws IOException {
debugFrameReceived(frame);
if (frame.getOpCode() == OpCode.Close) {
handleCloseFrame(frame);
} else if (frame.getOpCode() == OpCode.Ping) {
sendFrame(new WebSocketFrame(OpCode.Pong, true, frame.getBinaryPayload()));
} else if (frame.getOpCode() == OpCode.Pong) {
onPong(frame);
} else if (!frame.isFin() || frame.getOpCode() == OpCode.Continuation) {
handleFrameFragment(frame);
} else if (this.continuousOpCode != null) {
throw new WebSocketException(CloseCode.ProtocolError, "Continuous frame sequence not completed.");
} else if (frame.getOpCode() == OpCode.Text || frame.getOpCode() == OpCode.Binary) {
onMessage(frame);
} else {
throw new WebSocketException(CloseCode.ProtocolError, "Non control or continuous frame expected.");
}
}
// --------------------------------Close-----------------------------------
public void ping(byte[] payload) throws IOException {
sendFrame(new WebSocketFrame(OpCode.Ping, true, payload));
}
// --------------------------------Public
// Facade---------------------------
private void readWebsocket() {
try {
while (this.state == State.OPEN) {
handleWebsocketFrame(WebSocketFrame.read(this.in));
}
} catch (CharacterCodingException e) {
onException(e);
doClose(CloseCode.InvalidFramePayloadData, e.toString(), false);
} catch (IOException e) {
onException(e);
if (e instanceof WebSocketException) {
doClose(((WebSocketException) e).getCode(), ((WebSocketException) e).getReason(), false);
}
} finally {
doClose(CloseCode.InternalServerError, "Handler terminated without closing the connection.", false);
}
}
public void send(byte[] payload) throws IOException {
sendFrame(new WebSocketFrame(OpCode.Binary, true, payload));
}
public void send(String payload) throws IOException {
sendFrame(new WebSocketFrame(OpCode.Text, true, payload));
}
public synchronized void sendFrame(WebSocketFrame frame) throws IOException {
debugFrameSent(frame);
frame.write(this.out);
}
}
================================================
FILE: websocket/src/main/java/org/nanohttpd/protocols/websockets/WebSocketException.java
================================================
package org.nanohttpd.protocols.websockets;
/*
* #%L
* NanoHttpd-Websocket
* %%
* Copyright (C) 2012 - 2016 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.IOException;
public class WebSocketException extends IOException {
private static final long serialVersionUID = 1L;
private final CloseCode code;
private final String reason;
public WebSocketException(CloseCode code, String reason) {
this(code, reason, null);
}
public WebSocketException(CloseCode code, String reason, Exception cause) {
super(code + ": " + reason, cause);
this.code = code;
this.reason = reason;
}
public WebSocketException(Exception cause) {
this(CloseCode.InternalServerError, cause.toString(), cause);
}
public CloseCode getCode() {
return this.code;
}
public String getReason() {
return this.reason;
}
}
================================================
FILE: websocket/src/main/java/org/nanohttpd/protocols/websockets/WebSocketFrame.java
================================================
package org.nanohttpd.protocols.websockets;
/*
* #%L
* NanoHttpd-Websocket
* %%
* Copyright (C) 2012 - 2016 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.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
public class WebSocketFrame {
public static final Charset TEXT_CHARSET = Charset.forName("UTF-8");
public static String binary2Text(byte[] payload) throws CharacterCodingException {
return new String(payload, WebSocketFrame.TEXT_CHARSET);
}
public static String binary2Text(byte[] payload, int offset, int length) throws CharacterCodingException {
return new String(payload, offset, length, WebSocketFrame.TEXT_CHARSET);
}
private static int checkedRead(int read) throws IOException {
if (read < 0) {
throw new EOFException();
}
return read;
}
public static WebSocketFrame read(InputStream in) throws IOException {
byte head = (byte) checkedRead(in.read());
boolean fin = (head & 0x80) != 0;
OpCode opCode = OpCode.find((byte) (head & 0x0F));
if ((head & 0x70) != 0) {
throw new WebSocketException(CloseCode.ProtocolError, "The reserved bits (" + Integer.toBinaryString(head & 0x70) + ") must be 0.");
}
if (opCode == null) {
throw new WebSocketException(CloseCode.ProtocolError, "Received frame with reserved/unknown opcode " + (head & 0x0F) + ".");
} else if (opCode.isControlFrame() && !fin) {
throw new WebSocketException(CloseCode.ProtocolError, "Fragmented control frame.");
}
WebSocketFrame frame = new WebSocketFrame(opCode, fin);
frame.readPayloadInfo(in);
frame.readPayload(in);
if (frame.getOpCode() == OpCode.Close) {
return new CloseFrame(frame);
} else {
return frame;
}
}
public static byte[] text2Binary(String payload) throws CharacterCodingException {
return payload.getBytes(WebSocketFrame.TEXT_CHARSET);
}
private OpCode opCode;
private boolean fin;
private byte[] maskingKey;
private byte[] payload;
// --------------------------------GETTERS---------------------------------
private transient int _payloadLength;
private transient String _payloadString;
private WebSocketFrame(OpCode opCode, boolean fin) {
setOpCode(opCode);
setFin(fin);
}
public WebSocketFrame(OpCode opCode, boolean fin, byte[] payload) {
this(opCode, fin, payload, null);
}
public WebSocketFrame(OpCode opCode, boolean fin, byte[] payload, byte[] maskingKey) {
this(opCode, fin);
setMaskingKey(maskingKey);
setBinaryPayload(payload);
}
public WebSocketFrame(OpCode opCode, boolean fin, String payload) throws CharacterCodingException {
this(opCode, fin, payload, null);
}
public WebSocketFrame(OpCode opCode, boolean fin, String payload, byte[] maskingKey) throws CharacterCodingException {
this(opCode, fin);
setMaskingKey(maskingKey);
setTextPayload(payload);
}
public WebSocketFrame(OpCode opCode, List fragments) throws WebSocketException {
setOpCode(opCode);
setFin(true);
long _payloadLength = 0;
for (WebSocketFrame inter : fragments) {
_payloadLength += inter.getBinaryPayload().length;
}
if (_payloadLength < 0 || _payloadLength > Integer.MAX_VALUE) {
throw new WebSocketException(CloseCode.MessageTooBig, "Max frame length has been exceeded.");
}
this._payloadLength = (int) _payloadLength;
byte[] payload = new byte[this._payloadLength];
int offset = 0;
for (WebSocketFrame inter : fragments) {
System.arraycopy(inter.getBinaryPayload(), 0, payload, offset, inter.getBinaryPayload().length);
offset += inter.getBinaryPayload().length;
}
setBinaryPayload(payload);
}
public WebSocketFrame(WebSocketFrame clone) {
setOpCode(clone.getOpCode());
setFin(clone.isFin());
setBinaryPayload(clone.getBinaryPayload());
setMaskingKey(clone.getMaskingKey());
}
public byte[] getBinaryPayload() {
return this.payload;
}
public byte[] getMaskingKey() {
return this.maskingKey;
}
public OpCode getOpCode() {
return this.opCode;
}
// --------------------------------SERIALIZATION---------------------------
public String getTextPayload() {
if (this._payloadString == null) {
try {
this._payloadString = binary2Text(getBinaryPayload());
} catch (CharacterCodingException e) {
throw new RuntimeException("Undetected CharacterCodingException", e);
}
}
return this._payloadString;
}
public boolean isFin() {
return this.fin;
}
public boolean isMasked() {
return this.maskingKey != null && this.maskingKey.length == 4;
}
private String payloadToString() {
if (this.payload == null) {
return "null";
} else {
final StringBuilder sb = new StringBuilder();
sb.append('[').append(this.payload.length).append("b] ");
if (getOpCode() == OpCode.Text) {
String text = getTextPayload();
if (text.length() > 100) {
sb.append(text.substring(0, 100)).append("...");
} else {
sb.append(text);
}
} else {
sb.append("0x");
for (int i = 0; i < Math.min(this.payload.length, 50); ++i) {
sb.append(Integer.toHexString(this.payload[i] & 0xFF));
}
if (this.payload.length > 50) {
sb.append("...");
}
}
return sb.toString();
}
}
private void readPayload(InputStream in) throws IOException {
this.payload = new byte[this._payloadLength];
int read = 0;
while (read < this._payloadLength) {
read += checkedRead(in.read(this.payload, read, this._payloadLength - read));
}
if (isMasked()) {
for (int i = 0; i < this.payload.length; i++) {
this.payload[i] ^= this.maskingKey[i % 4];
}
}
// Test for Unicode errors
if (getOpCode() == OpCode.Text) {
this._payloadString = binary2Text(getBinaryPayload());
}
}
// --------------------------------ENCODING--------------------------------
private void readPayloadInfo(InputStream in) throws IOException {
byte b = (byte) checkedRead(in.read());
boolean masked = (b & 0x80) != 0;
this._payloadLength = (byte) (0x7F & b);
if (this._payloadLength == 126) {
// checkedRead must return int for this to work
this._payloadLength = (checkedRead(in.read()) << 8 | checkedRead(in.read())) & 0xFFFF;
if (this._payloadLength < 126) {
throw new WebSocketException(CloseCode.ProtocolError, "Invalid data frame 2byte length. (not using minimal length encoding)");
}
} else if (this._payloadLength == 127) {
long _payloadLength =
(long) checkedRead(in.read()) << 56 | (long) checkedRead(in.read()) << 48 | (long) checkedRead(in.read()) << 40 | (long) checkedRead(in.read()) << 32
| checkedRead(in.read()) << 24 | checkedRead(in.read()) << 16 | checkedRead(in.read()) << 8 | checkedRead(in.read());
if (_payloadLength < 65536) {
throw new WebSocketException(CloseCode.ProtocolError, "Invalid data frame 4byte length. (not using minimal length encoding)");
}
if (_payloadLength < 0 || _payloadLength > Integer.MAX_VALUE) {
throw new WebSocketException(CloseCode.MessageTooBig, "Max frame length has been exceeded.");
}
this._payloadLength = (int) _payloadLength;
}
if (this.opCode.isControlFrame()) {
if (this._payloadLength > 125) {
throw new WebSocketException(CloseCode.ProtocolError, "Control frame with payload length > 125 bytes.");
}
if (this.opCode == OpCode.Close && this._payloadLength == 1) {
throw new WebSocketException(CloseCode.ProtocolError, "Received close frame with payload len 1.");
}
}
if (masked) {
this.maskingKey = new byte[4];
int read = 0;
while (read < this.maskingKey.length) {
read += checkedRead(in.read(this.maskingKey, read, this.maskingKey.length - read));
}
}
}
public void setBinaryPayload(byte[] payload) {
this.payload = payload;
this._payloadLength = payload.length;
this._payloadString = null;
}
public void setFin(boolean fin) {
this.fin = fin;
}
public void setMaskingKey(byte[] maskingKey) {
if (maskingKey != null && maskingKey.length != 4) {
throw new IllegalArgumentException("MaskingKey " + Arrays.toString(maskingKey) + " hasn't length 4");
}
this.maskingKey = maskingKey;
}
public void setOpCode(OpCode opcode) {
this.opCode = opcode;
}
public void setTextPayload(String payload) throws CharacterCodingException {
this.payload = text2Binary(payload);
this._payloadLength = payload.length();
this._payloadString = payload;
}
// --------------------------------CONSTANTS-------------------------------
public void setUnmasked() {
setMaskingKey(null);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("WS[");
sb.append(getOpCode());
sb.append(", ").append(isFin() ? "fin" : "inter");
sb.append(", ").append(isMasked() ? "masked" : "unmasked");
sb.append(", ").append(payloadToString());
sb.append(']');
return sb.toString();
}
// ------------------------------------------------------------------------
public void write(OutputStream out) throws IOException {
byte header = 0;
if (this.fin) {
header |= 0x80;
}
header |= this.opCode.getValue() & 0x0F;
out.write(header);
this._payloadLength = getBinaryPayload().length;
if (this._payloadLength <= 125) {
out.write(isMasked() ? 0x80 | (byte) this._payloadLength : (byte) this._payloadLength);
} else if (this._payloadLength <= 0xFFFF) {
out.write(isMasked() ? 0xFE : 126);
out.write(this._payloadLength >>> 8);
out.write(this._payloadLength);
} else {
out.write(isMasked() ? 0xFF : 127);
out.write(this._payloadLength >>> 56 & 0); // integer only
// contains
// 31 bit
out.write(this._payloadLength >>> 48 & 0);
out.write(this._payloadLength >>> 40 & 0);
out.write(this._payloadLength >>> 32 & 0);
out.write(this._payloadLength >>> 24);
out.write(this._payloadLength >>> 16);
out.write(this._payloadLength >>> 8);
out.write(this._payloadLength);
}
if (isMasked()) {
out.write(this.maskingKey);
for (int i = 0; i < this._payloadLength; i++) {
out.write(getBinaryPayload()[i] ^ this.maskingKey[i % 4]);
}
} else {
out.write(getBinaryPayload());
}
out.flush();
}
}
================================================
FILE: websocket/src/main/java/org/nanohttpd/samples/websockets/DebugWebSocketServer.java
================================================
package org.nanohttpd.samples.websockets;
/*
* #%L
* NanoHttpd-Websocket
* %%
* 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.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.websockets.CloseCode;
import org.nanohttpd.protocols.websockets.NanoWSD;
import org.nanohttpd.protocols.websockets.WebSocket;
import org.nanohttpd.protocols.websockets.WebSocketFrame;
/**
* @author Paul S. Hawke (paul.hawke@gmail.com) On: 4/23/14 at 10:31 PM
*/
public class DebugWebSocketServer extends NanoWSD {
/**
* logger to log to.
*/
private static final Logger LOG = Logger.getLogger(DebugWebSocketServer.class.getName());
private final boolean debug;
public DebugWebSocketServer(int port, boolean debug) {
super(port);
this.debug = debug;
}
@Override
protected WebSocket openWebSocket(IHTTPSession handshake) {
return new DebugWebSocket(this, handshake);
}
private static class DebugWebSocket extends WebSocket {
private final DebugWebSocketServer server;
public DebugWebSocket(DebugWebSocketServer server, IHTTPSession handshakeRequest) {
super(handshakeRequest);
this.server = server;
}
@Override
protected void onOpen() {
}
@Override
protected void onClose(CloseCode code, String reason, boolean initiatedByRemote) {
if (server.debug) {
System.out.println("C [" + (initiatedByRemote ? "Remote" : "Self") + "] " + (code != null ? code : "UnknownCloseCode[" + code + "]")
+ (reason != null && !reason.isEmpty() ? ": " + reason : ""));
}
}
@Override
protected void onMessage(WebSocketFrame message) {
try {
message.setUnmasked();
sendFrame(message);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected void onPong(WebSocketFrame pong) {
if (server.debug) {
System.out.println("P " + pong);
}
}
@Override
protected void onException(IOException exception) {
DebugWebSocketServer.LOG.log(Level.SEVERE, "exception occured", exception);
}
@Override
protected void debugFrameReceived(WebSocketFrame frame) {
if (server.debug) {
System.out.println("R " + frame);
}
}
@Override
protected void debugFrameSent(WebSocketFrame frame) {
if (server.debug) {
System.out.println("S " + frame);
}
}
}
}
================================================
FILE: websocket/src/main/java/org/nanohttpd/samples/websockets/EchoSocketSample.java
================================================
package org.nanohttpd.samples.websockets;
/*
* #%L
* NanoHttpd-Websocket
* %%
* 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.IOException;
import org.nanohttpd.protocols.websockets.NanoWSD;
public class EchoSocketSample {
public static void main(String[] args) throws IOException {
final boolean debugMode = args.length >= 2 && "-d".equals(args[1].toLowerCase());
NanoWSD ws = new DebugWebSocketServer(args.length > 0 ? Integer.parseInt(args[0]) : 9090, debugMode);
ws.start();
System.out.println("Server started, hit Enter to stop.\n");
try {
System.in.read();
} catch (IOException ignored) {
}
ws.stop();
System.out.println("Server stopped.\n");
}
}
================================================
FILE: websocket/src/site/site.xml
================================================
org.apache.maven.skinsmaven-fluido-skin1.3.0../images/nanohttpd_logo.png../images/nanohttpd_logo_text.pngfalsetrueNanohttpd/nanohttpdrightblack
================================================
FILE: websocket/src/test/java/org/nanohttpd/junit/protocols/websockets/EchoWebSocketsTest.java
================================================
package org.nanohttpd.junit.protocols.websockets;
/*
* #%L
* NanoHttpd-Websocket
* %%
* 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 static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.URI;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.nanohttpd.protocols.websockets.NanoWSD;
import org.nanohttpd.samples.websockets.DebugWebSocketServer;
import org.nanohttpd.samples.websockets.EchoSocketSample;
public class EchoWebSocketsTest {
private static NanoWSD server;
@BeforeClass
public static void setUp() throws Exception {
EchoWebSocketsTest.server = new DebugWebSocketServer(9191, true);
EchoWebSocketsTest.server.start();
}
@AfterClass
public static void tearDown() throws Exception {
EchoWebSocketsTest.server.stop();
}
@Test
public void testDirectoryArgument() throws IOException, InterruptedException {
final String testPort = "9458";
PipedOutputStream stdIn = new PipedOutputStream();
System.setIn(new PipedInputStream(stdIn));
Thread testServer = new Thread(new Runnable() {
@Override
public void run() {
String[] args = {
testPort,
"-d"
};
try {
EchoSocketSample.main(args);
} catch (IOException e) {
fail("Exception: " + e.getMessage());
}
}
});
testServer.start();
Thread.sleep(1000);
stdIn.write(System.getProperty("line.separator").getBytes());
testServer.join(1000);
assertFalse("Test server failed to close", testServer.isAlive());
}
@Test
public void testWebsocketClient() throws Exception {
String destUri = "ws://localhost:9191";
WebSocketClient client = new WebSocketClient();
SimpleEchoSocket socket = new SimpleEchoSocket();
socket.getToSendMessages().add("Hello");
socket.getToSendMessages().add("Thanks for the conversation.");
socket.getToSendMessages().add(createString(31000));
socket.getToSendMessages().add(createString(65400));
try {
client.start();
URI echoUri = new URI(destUri);
ClientUpgradeRequest request = new ClientUpgradeRequest();
client.connect(socket, echoUri, request);
System.out.printf("Connecting to : %s%n", echoUri);
socket.awaitClose(5, TimeUnit.SECONDS);
} catch (Throwable t) {
t.printStackTrace();
} finally {
try {
client.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
Assert.assertEquals(4, socket.getReceivedMessages().size());
Assert.assertEquals("Hello", socket.getReceivedMessages().get(0));
Assert.assertEquals("Thanks for the conversation.", socket.getReceivedMessages().get(1));
}
private String createString(int i) {
StringBuilder builder = new StringBuilder();
while (builder.length() < i) {
builder.append("A very long text.");
}
return builder.toString();
}
}
================================================
FILE: websocket/src/test/java/org/nanohttpd/junit/protocols/websockets/SimpleEchoSocket.java
================================================
package org.nanohttpd.junit.protocols.websockets;
/*
* #%L
* NanoHttpd-Websocket
* %%
* 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.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
/**
* Basic Echo Client Socket
*/
@WebSocket(maxTextMessageSize = 64 * 1024)
public class SimpleEchoSocket {
private final List receivedMessages = new ArrayList();
private final List toSendMessages = new ArrayList();
private final CountDownLatch closeLatch;
public SimpleEchoSocket() {
this.closeLatch = new CountDownLatch(1);
}
public boolean awaitClose(int duration, TimeUnit unit) throws InterruptedException {
return this.closeLatch.await(duration, unit);
}
public List getReceivedMessages() {
return this.receivedMessages;
}
public List getToSendMessages() {
return this.toSendMessages;
}
@OnWebSocketClose
public void onClose(int statusCode, String reason) {
System.out.printf("Connection closed: %d - %s%n", statusCode, reason);
this.closeLatch.countDown();
}
@OnWebSocketConnect
public void onConnect(Session session) {
System.out.printf("Got connect: %s%n", session);
try {
Future fut;
for (String message : this.toSendMessages) {
fut = session.getRemote().sendStringByFuture(message);
fut.get(5, TimeUnit.SECONDS);
}
session.close(StatusCode.NORMAL, "I'm done");
} catch (Throwable t) {
t.printStackTrace();
}
}
@OnWebSocketMessage
public void onMessage(String msg) {
System.out.printf("Got msg: %s%n", msg);
this.receivedMessages.add(msg);
}
}
================================================
FILE: websocket/src/test/java/org/nanohttpd/junit/protocols/websockets/WebSocketResponseHandlerTest.java
================================================
package org.nanohttpd.junit.protocols.websockets;
/*
* #%L
* NanoHttpd-Websocket
* %%
* 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 static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static org.mockito.Mockito.when;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.response.Status;
import org.nanohttpd.protocols.websockets.CloseCode;
import org.nanohttpd.protocols.websockets.NanoWSD;
import org.nanohttpd.protocols.websockets.OpCode;
import org.nanohttpd.protocols.websockets.WebSocket;
import org.nanohttpd.protocols.websockets.WebSocketFrame;
import org.nanohttpd.util.IHandler;
@RunWith(MockitoJUnitRunner.class)
public class WebSocketResponseHandlerTest {
@Mock
private IHTTPSession session;
private MockedWSD nanoWebSocketServer;
private Map headers;
private static class MockedWSD extends NanoWSD {
public MockedWSD(int port) {
super(port);
}
public MockedWSD(String hostname, int port) {
super(hostname, port);
}
// This is to work around Mockito being a little bitch.
public void initialize() {
interceptors = new ArrayList>();
addHTTPInterceptor(new Interceptor());
setHTTPHandler(new IHandler() {
@Override
public Response handle(IHTTPSession input) {
return serve(input);
}
});
}
@Override
protected WebSocket openWebSocket(IHTTPSession handshake) {
return new WebSocket(handshake) { // Dummy websocket inner class.
@Override
protected void onPong(WebSocketFrame pong) {
}
@Override
protected void onOpen() {
}
@Override
protected void onMessage(WebSocketFrame message) {
}
@Override
protected void onException(IOException exception) {
}
@Override
protected void onClose(CloseCode code, String reason, boolean initiatedByRemote) {
}
};
}
}
@Before
public void setUp() {
// Be careful.
// This does NOT call any constructors, instead, directly creates
// the object in memory. I wasted 3 fucking hours attempting to
// debug this. ~ LordFokas
this.nanoWebSocketServer = Mockito.mock(MockedWSD.class, Mockito.CALLS_REAL_METHODS);
// this could have been avoided if Mockito had a way to call fucking
// constructors!!
this.nanoWebSocketServer.initialize();
this.headers = new HashMap();
this.headers.put("upgrade", "websocket");
this.headers.put("connection", "Upgrade");
this.headers.put("sec-websocket-key", "x3JJHMbDL1EzLkh9GBhXDw==");
this.headers.put("sec-websocket-protocol", "chat, superchat");
this.headers.put("sec-websocket-version", "13");
when(this.session.getHeaders()).thenReturn(this.headers);
}
@Test
public void testConnectionHeaderHandlesKeepAlive_FixingFirefoxConnectIssue() {
this.headers.put("connection", "keep-alive, Upgrade");
Response handshakeResponse = this.nanoWebSocketServer.handle(this.session);
assertNotNull(handshakeResponse);
}
@Test
public void testHandshakeReturnsResponseWithExpectedHeaders() {
Response handshakeResponse = this.nanoWebSocketServer.handle(this.session);
assertNotNull(handshakeResponse);
assertEquals(handshakeResponse.getHeader(NanoWSD.HEADER_WEBSOCKET_ACCEPT), "HSmrc0sMlYUkAGmm5OPpG2HaGWk=");
assertEquals(handshakeResponse.getHeader(NanoWSD.HEADER_WEBSOCKET_PROTOCOL), "chat");
}
@Test
public void testMissingKeyReturnsErrorResponse() {
this.headers.remove("sec-websocket-key");
Response handshakeResponse = this.nanoWebSocketServer.handle(this.session);
assertNotNull(handshakeResponse);
assertEquals(Status.BAD_REQUEST, handshakeResponse.getStatus());
}
@Test
public void testWrongConnectionHeaderReturnsNullResponse() {
this.headers.put("connection", "Junk");
Response handshakeResponse = this.nanoWebSocketServer.handle(this.session);
assertNull(handshakeResponse.getHeader(NanoWSD.HEADER_UPGRADE));
}
@Test
public void testWrongUpgradeHeaderReturnsNullResponse() {
this.headers.put("upgrade", "not a websocket");
Response handshakeResponse = this.nanoWebSocketServer.handle(this.session);
assertNull(handshakeResponse.getHeader(NanoWSD.HEADER_UPGRADE));
}
@Test
public void testWrongWebsocketVersionReturnsErrorResponse() {
this.headers.put("sec-websocket-version", "12");
Response handshakeResponse = this.nanoWebSocketServer.handle(this.session);
assertNotNull(handshakeResponse);
assertEquals(Status.BAD_REQUEST, handshakeResponse.getStatus());
}
@Test
public void testSetMaskingKeyThrowsExceptionMaskingKeyLengthIsNotFour() {
WebSocketFrame webSocketFrame = new WebSocketFrame(OpCode.Text, true, new byte[0]);
for (int maskingKeyLength = 0; maskingKeyLength < 10; maskingKeyLength++) {
if (maskingKeyLength == 4)
continue;
try {
webSocketFrame.setMaskingKey(new byte[maskingKeyLength]);
Assert.fail("IllegalArgumentException expected but not thrown");
} catch (IllegalArgumentException e) {
}
}
}
@Test
public void testIsMasked() {
WebSocketFrame webSocketFrame = new WebSocketFrame(OpCode.Text, true, new byte[0]);
Assert.assertFalse("isMasked should return true if masking key is not set.", webSocketFrame.isMasked());
webSocketFrame.setMaskingKey(new byte[4]);
Assert.assertTrue("isMasked should return true if correct masking key is set.", webSocketFrame.isMasked());
}
@Test
public void testWriteWhenNotMasked() throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
WebSocketFrame webSocketFrame = new WebSocketFrame(OpCode.Text, true, "payload".getBytes());
webSocketFrame.write(byteArrayOutputStream);
byte[] writtenBytes = byteArrayOutputStream.toByteArray();
Assert.assertEquals(9, writtenBytes.length);
Assert.assertEquals("Header byte incorrect.", -127, writtenBytes[0]);
Assert.assertEquals("Payload length byte incorrect.", 7, writtenBytes[1]);
Assert.assertArrayEquals(new byte[]{
-127,
7,
112,
97,
121,
108,
111,
97,
100
}, writtenBytes);
}
@Test
public void testWriteWhenMasked() throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
WebSocketFrame webSocketFrame = new WebSocketFrame(OpCode.Binary, true, "payload".getBytes());
webSocketFrame.setMaskingKey(new byte[]{
12,
45,
33,
32
});
webSocketFrame.write(byteArrayOutputStream);
byte[] writtenBytes = byteArrayOutputStream.toByteArray();
Assert.assertEquals(13, writtenBytes.length);
Assert.assertEquals("Header byte incorrect.", -126, writtenBytes[0]);
Assert.assertEquals("Payload length byte incorrect.", -121, writtenBytes[1]);
Assert.assertArrayEquals(new byte[]{
-126,
-121,
12,
45,
33,
32,
124,
76,
88,
76,
99,
76,
69
}, writtenBytes);
}
@Test
public void testWriteWhenNotMaskedPayloadLengthGreaterThan125() throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
WebSocketFrame webSocketFrame = new WebSocketFrame(OpCode.Ping, true, new byte[257]);
webSocketFrame.write(byteArrayOutputStream);
byte[] writtenBytes = byteArrayOutputStream.toByteArray();
Assert.assertEquals(261, writtenBytes.length);
Assert.assertEquals("Header byte incorrect.", -119, writtenBytes[0]);
Assert.assertArrayEquals("Payload length bytes incorrect.", new byte[]{
126,
1,
1
}, new byte[]{
writtenBytes[1],
writtenBytes[2],
writtenBytes[3]
});
}
@Test
public void testWriteWhenMaskedPayloadLengthGreaterThan125() throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
WebSocketFrame webSocketFrame = new WebSocketFrame(OpCode.Ping, false, new byte[257]);
webSocketFrame.setMaskingKey(new byte[]{
19,
25,
79,
11
});
webSocketFrame.write(byteArrayOutputStream);
byte[] writtenBytes = byteArrayOutputStream.toByteArray();
Assert.assertEquals(265, writtenBytes.length);
Assert.assertEquals("Header byte incorrect.", 9, writtenBytes[0]);
Assert.assertArrayEquals("Payload length bytes incorrect.", new byte[]{
-2,
1,
1
}, new byte[]{
writtenBytes[1],
writtenBytes[2],
writtenBytes[3]
});
}
@Test
public void testWriteWhenNotMaskedPayloadLengthGreaterThan65535() throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
WebSocketFrame webSocketFrame = new WebSocketFrame(OpCode.Ping, true, new byte[65536]);
webSocketFrame.write(byteArrayOutputStream);
byte[] writtenBytes = byteArrayOutputStream.toByteArray();
Assert.assertEquals(65546, writtenBytes.length);
Assert.assertEquals("Header byte incorrect.", -119, writtenBytes[0]);
Assert.assertArrayEquals("Payload length bytes incorrect.", new byte[]{
127,
0,
0,
0,
0,
0,
1,
0,
0
}, Arrays.copyOfRange(writtenBytes, 1, 10));
}
}
================================================
FILE: websocket/src/test/resources/echo-test.html
================================================
WebSocket Test