// https://android.googlesource.com/platform/external/nanohttpd/+/42ff2a9/websocket/src/main/java/fi/iki/elonen/NanoWebSocketServer.java sclass 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"; WebSocketFactory webSocketFactory; public NanoWebSocketServer(int port) { super(port); webSocketFactory = null; } public NanoWebSocketServer(String hostname, int port) { super(hostname, port); webSocketFactory = null; } public NanoWebSocketServer(int port, WebSocketFactory webSocketFactory) { super(port); this.webSocketFactory = webSocketFactory; } public NanoWebSocketServer(String hostname, int port,WebSocketFactory webSocketFactory) { super(hostname, port); this.webSocketFactory = webSocketFactory; } @Override public Response serve(final IHTTPSession session) { Map 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 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 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 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 base64encode(sha1hash); } }