aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoryankee <yzeugs@cipher-code.de>2014-04-24 18:46:49 +0200
committeryankee <yzeugs@cipher-code.de>2014-04-24 18:46:49 +0200
commit6aa9777e0f8337050acb378ed5ecbb24e467d85c (patch)
treea4c42ca0fc6487c51fda62fb41926975e30a6851
parent42ff2a9f759c4e28e7040a919a3940b417cc0f4e (diff)
downloadnanohttpd-6aa9777e0f8337050acb378ed5ecbb24e467d85c.tar.gz
moved code from NanoWebSocketServer to WebSocketResponseHandler, so that requests can be handled without the presence of NanoHTTPD
-rw-r--r--websocket/src/main/java/fi/iki/elonen/NanoWebSocketServer.java124
-rw-r--r--websocket/src/main/java/fi/iki/elonen/WebSocket.java4
-rw-r--r--websocket/src/main/java/fi/iki/elonen/WebSocketResponseHandler.java117
-rw-r--r--websocket/src/test/java/fi/iki/elonen/DebugWebSocketServer.java2
4 files changed, 131 insertions, 116 deletions
diff --git a/websocket/src/main/java/fi/iki/elonen/NanoWebSocketServer.java b/websocket/src/main/java/fi/iki/elonen/NanoWebSocketServer.java
index 21bc7f0..44a5e9c 100644
--- a/websocket/src/main/java/fi/iki/elonen/NanoWebSocketServer.java
+++ b/websocket/src/main/java/fi/iki/elonen/NanoWebSocketServer.java
@@ -1,140 +1,38 @@
package fi.iki.elonen;
-import fi.iki.elonen.NanoHTTPD;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Map;
public class NanoWebSocketServer extends NanoHTTPD implements WebSocketFactory {
- 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";
-
- public final static String WEBSOCKET_KEY_MAGIC = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
- private final WebSocketFactory webSocketFactory;
-
+ private final WebSocketResponseHandler responseHandler;
+
public NanoWebSocketServer(int port) {
super(port);
- webSocketFactory = null;
+ responseHandler = new WebSocketResponseHandler(this);
}
public NanoWebSocketServer(String hostname, int port) {
super(hostname, port);
- webSocketFactory = null;
+ responseHandler = new WebSocketResponseHandler(this);
}
public NanoWebSocketServer(int port, WebSocketFactory webSocketFactory) {
super(port);
- this.webSocketFactory = webSocketFactory;
+ responseHandler = new WebSocketResponseHandler(webSocketFactory);
}
public NanoWebSocketServer(String hostname, int port,WebSocketFactory webSocketFactory) {
super(hostname, port);
- this.webSocketFactory = webSocketFactory;
+ responseHandler = new WebSocketResponseHandler(webSocketFactory);
}
-
+
@Override
- public Response serve(final IHTTPSession session) {
- Map<String, String> headers = session.getHeaders();
- if (isWebsocketRequested(session)) {
- if (!HEADER_UPGRADE_VALUE.equalsIgnoreCase(headers.get(HEADER_UPGRADE))
- || !isWebSocketConnectionHeader(session.getHeaders())) {
- return new Response(Response.Status.BAD_REQUEST, NanoHTTPD.MIME_PLAINTEXT, "Invalid Websocket handshake");
- }
- if (!HEADER_WEBSOCKET_VERSION_VALUE.equalsIgnoreCase(headers.get(HEADER_WEBSOCKET_VERSION))) {
- return new Response(Response.Status.BAD_REQUEST, NanoHTTPD.MIME_PLAINTEXT, "Invalid Websocket-Version " + headers.get(HEADER_WEBSOCKET_VERSION));
- }
- if (!headers.containsKey(HEADER_WEBSOCKET_KEY)) {
- return new Response(Response.Status.BAD_REQUEST, NanoHTTPD.MIME_PLAINTEXT, "Missing Websocket-Key");
- }
-
- WebSocket webSocket = openWebSocket(session);
- try {
- webSocket.getHandshakeResponse().addHeader(HEADER_WEBSOCKET_ACCEPT, makeAcceptKey(headers.get(HEADER_WEBSOCKET_KEY)));
- } catch (NoSuchAlgorithmException e) {
- return new Response(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "The SHA-1 Algorithm required for websockets is not available on the server.");
- }
- if (headers.containsKey(HEADER_WEBSOCKET_PROTOCOL)) {
- webSocket.getHandshakeResponse().addHeader(HEADER_WEBSOCKET_PROTOCOL, headers.get(HEADER_WEBSOCKET_PROTOCOL).split(",")[0]);
- }
- return webSocket.getHandshakeResponse();
- } else {
- return super.serve(session);
- }
+ public Response serve(IHTTPSession session) {
+ Response candidate = responseHandler.serve(session);
+ return candidate == null ? super.serve(session) : candidate;
}
public WebSocket openWebSocket(IHTTPSession handshake) {
- if (webSocketFactory == null) {
- throw new Error("You must either override this method or supply a WebSocketFactory in the cosntructor");
- }
- return webSocketFactory.openWebSocket(handshake);
- }
-
- protected boolean isWebsocketRequested(IHTTPSession session) {
- Map<String, String> headers = session.getHeaders();
- String upgrade = headers.get(HEADER_UPGRADE);
- boolean isCorrectConnection = isWebSocketConnectionHeader(headers);
- boolean isUpgrade = HEADER_UPGRADE_VALUE.equalsIgnoreCase(upgrade);
- return (isUpgrade && isCorrectConnection);
- }
-
- private boolean isWebSocketConnectionHeader(Map<String, String> headers) {
- String connection = headers.get(HEADER_CONNECTION);
- return (connection != null && connection.toLowerCase().contains(HEADER_CONNECTION_VALUE.toLowerCase()));
- }
-
- public static String makeAcceptKey(String key) throws NoSuchAlgorithmException {
- MessageDigest md = MessageDigest.getInstance("SHA-1");
- String text = key + WEBSOCKET_KEY_MAGIC;
- md.update(text.getBytes(), 0, text.length());
- byte[] sha1hash = md.digest();
- return encodeBase64(sha1hash);
- }
-
-
- private final static char[] ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
-
- /**
- * Translates the specified byte array into Base64 string.
- * <p>
- * 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
- * </p>
- *
- * @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++] = ALPHABET[(b0 >> 2) & mask];
- ar[a++] = ALPHABET[((b0 << 4) | ((b1 & 0xFF) >> 4)) & mask];
- ar[a++] = ALPHABET[((b1 << 2) | ((b2 & 0xFF) >> 6)) & mask];
- ar[a++] = ALPHABET[b2 & mask];
- }
- switch (size % 3) {
- case 1:
- ar[--a] = '=';
- case 2:
- ar[--a] = '=';
- }
- return new String(ar);
+ throw new Error("You must either override this method or supply a WebSocketFactory in the cosntructor");
}
}
diff --git a/websocket/src/main/java/fi/iki/elonen/WebSocket.java b/websocket/src/main/java/fi/iki/elonen/WebSocket.java
index 84bf259..ba1a446 100644
--- a/websocket/src/main/java/fi/iki/elonen/WebSocket.java
+++ b/websocket/src/main/java/fi/iki/elonen/WebSocket.java
@@ -40,8 +40,8 @@ public abstract class WebSocket {
this.handshakeRequest = handshakeRequest;
this.in = handshakeRequest.getInputStream();
- handshakeResponse.addHeader(NanoWebSocketServer.HEADER_UPGRADE, NanoWebSocketServer.HEADER_UPGRADE_VALUE);
- handshakeResponse.addHeader(NanoWebSocketServer.HEADER_CONNECTION, NanoWebSocketServer.HEADER_CONNECTION_VALUE);
+ handshakeResponse.addHeader(WebSocketResponseHandler.HEADER_UPGRADE, WebSocketResponseHandler.HEADER_UPGRADE_VALUE);
+ handshakeResponse.addHeader(WebSocketResponseHandler.HEADER_CONNECTION, WebSocketResponseHandler.HEADER_CONNECTION_VALUE);
}
// --------------------------------IO--------------------------------------
diff --git a/websocket/src/main/java/fi/iki/elonen/WebSocketResponseHandler.java b/websocket/src/main/java/fi/iki/elonen/WebSocketResponseHandler.java
new file mode 100644
index 0000000..2805889
--- /dev/null
+++ b/websocket/src/main/java/fi/iki/elonen/WebSocketResponseHandler.java
@@ -0,0 +1,117 @@
+package fi.iki.elonen;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+
+import fi.iki.elonen.NanoHTTPD.IHTTPSession;
+import fi.iki.elonen.NanoHTTPD.Response;
+
+public class WebSocketResponseHandler {
+ 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";
+
+ public final static String WEBSOCKET_KEY_MAGIC = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+
+ private final WebSocketFactory webSocketFactory;
+
+ public WebSocketResponseHandler(WebSocketFactory webSocketFactory) {
+ super();
+ this.webSocketFactory = webSocketFactory;
+ }
+
+ public Response serve(final IHTTPSession session) {
+ Map<String, String> headers = session.getHeaders();
+ if (isWebsocketRequested(session)) {
+ if (!HEADER_UPGRADE_VALUE.equalsIgnoreCase(headers.get(HEADER_UPGRADE))
+ || !isWebSocketConnectionHeader(session.getHeaders())) {
+ return new Response(Response.Status.BAD_REQUEST, NanoHTTPD.MIME_PLAINTEXT, "Invalid Websocket handshake");
+ }
+ if (!HEADER_WEBSOCKET_VERSION_VALUE.equalsIgnoreCase(headers.get(HEADER_WEBSOCKET_VERSION))) {
+ return new Response(Response.Status.BAD_REQUEST, NanoHTTPD.MIME_PLAINTEXT, "Invalid Websocket-Version " + headers.get(HEADER_WEBSOCKET_VERSION));
+ }
+ if (!headers.containsKey(HEADER_WEBSOCKET_KEY)) {
+ return new Response(Response.Status.BAD_REQUEST, NanoHTTPD.MIME_PLAINTEXT, "Missing Websocket-Key");
+ }
+
+ WebSocket webSocket = webSocketFactory.openWebSocket(session);
+ try {
+ webSocket.getHandshakeResponse().addHeader(HEADER_WEBSOCKET_ACCEPT, makeAcceptKey(headers.get(HEADER_WEBSOCKET_KEY)));
+ } catch (NoSuchAlgorithmException e) {
+ return new Response(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "The SHA-1 Algorithm required for websockets is not available on the server.");
+ }
+ if (headers.containsKey(HEADER_WEBSOCKET_PROTOCOL)) {
+ webSocket.getHandshakeResponse().addHeader(HEADER_WEBSOCKET_PROTOCOL, headers.get(HEADER_WEBSOCKET_PROTOCOL).split(",")[0]);
+ }
+ return webSocket.getHandshakeResponse();
+ } else {
+ return null;
+ }
+ }
+
+ protected boolean isWebsocketRequested(IHTTPSession session) {
+ Map<String, String> headers = session.getHeaders();
+ String upgrade = headers.get(HEADER_UPGRADE);
+ boolean isCorrectConnection = isWebSocketConnectionHeader(headers);
+ boolean isUpgrade = HEADER_UPGRADE_VALUE.equalsIgnoreCase(upgrade);
+ return (isUpgrade && isCorrectConnection);
+ }
+
+ private boolean isWebSocketConnectionHeader(Map<String, String> headers) {
+ String connection = headers.get(HEADER_CONNECTION);
+ return (connection != null && connection.toLowerCase().contains(HEADER_CONNECTION_VALUE.toLowerCase()));
+ }
+
+ public static String makeAcceptKey(String key) throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ String text = key + WEBSOCKET_KEY_MAGIC;
+ md.update(text.getBytes(), 0, text.length());
+ byte[] sha1hash = md.digest();
+ return encodeBase64(sha1hash);
+ }
+
+
+ private final static char[] ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
+
+ /**
+ * Translates the specified byte array into Base64 string.
+ * <p>
+ * 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
+ * </p>
+ *
+ * @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++] = ALPHABET[(b0 >> 2) & mask];
+ ar[a++] = ALPHABET[((b0 << 4) | ((b1 & 0xFF) >> 4)) & mask];
+ ar[a++] = ALPHABET[((b1 << 2) | ((b2 & 0xFF) >> 6)) & mask];
+ ar[a++] = ALPHABET[b2 & mask];
+ }
+ switch (size % 3) {
+ case 1:
+ ar[--a] = '=';
+ case 2:
+ ar[--a] = '=';
+ }
+ return new String(ar);
+ }
+}
diff --git a/websocket/src/test/java/fi/iki/elonen/DebugWebSocketServer.java b/websocket/src/test/java/fi/iki/elonen/DebugWebSocketServer.java
index 49fbe4d..1f39be1 100644
--- a/websocket/src/test/java/fi/iki/elonen/DebugWebSocketServer.java
+++ b/websocket/src/test/java/fi/iki/elonen/DebugWebSocketServer.java
@@ -13,7 +13,7 @@ class DebugWebSocketServer extends NanoWebSocketServer {
}
@Override
- protected WebSocket openWebSocket(IHTTPSession handshake) {
+ public WebSocket openWebSocket(IHTTPSession handshake) {
return new DebugWebSocket(handshake, debug);
}
}