See More

package javaforce.service; import java.io.*; import java.net.*; import java.util.*; import java.security.*; import javax.net.ssl.*; import javaforce.*; import static javaforce.service.WebSocketHandler.*; /** * Web server. * * Created : Aug 23, 2013 */ public class WebServer { private WebHandler api; private WebSocketHandler wsapi; private ServerSocket ss; private boolean secure; private boolean active = true; private ArrayList clients = new ArrayList<>(); private Object clientsLock = new Object(); public static boolean config_enable_gzip = true; public static boolean debug = false; private static String upload_folder; /** Start web server on non-secure port. */ public boolean start(WebHandler api, int port) { return start(api, port, null); } /** Start web server on secure port using provided keys. */ public boolean start(WebHandler api, int port, KeyMgmt keys) { secure = true; this.api = api; try { if (keys != null) { ss = JF.createServerSocketSSL(port, keys); } else { ss = new ServerSocket(port); } if (ss == null) { throw new Exception("Failed to start server on port:" + port); } new Server(this).start(); } catch (Exception e) { JFLog.log(e); return false; } return true; } public void setWebSocketHandler(WebSocketHandler wsapi) { this.wsapi = wsapi; } public void setUploadFolder(String folder) { upload_folder = folder; } public void stop() { active = false; if (ss != null) { try { ss.close(); } catch (Exception e) {} ss = null; } //NOTE:this could be Connection thread callback so stop threads in another thread new Thread() {public void run() { while (clients.size() > 0) { synchronized (clientsLock) { if (clients.size() == 0) break; Connection conn = clients.get(0); conn.cancel(); } JF.sleep(100); } }}.start(); } public void setClientVerify(boolean state) { SSLServerSocket ssl = (SSLServerSocket)ss; ssl.setNeedClientAuth(state); } private static class Server extends Thread { private WebServer web; public Server(WebServer web) { this.web = web; } public void run() { setName("WebServer.Server"); Socket s; while (web.active) { try { s = web.ss.accept(); Connection conn = new Connection(web, s); synchronized (web.clientsLock) { web.clients.add(conn); } conn.start(); } catch (SocketException e) { if (debug) JFLog.log("WebServer.Server:disconnected"); } catch (Exception e) { JFLog.log(e); } } } } private static class Connection extends Thread { private Socket s; private InputStream is; private WebServer web; public Connection(WebServer web, Socket s) { this.web = web; this.s = s; } public void run() { setName("WebServer.Connection"); //read request and pass to WebHandler try { StringBuilder request = new StringBuilder(); is = s.getInputStream(); while (web.active && s.isConnected()) { int ch = is.read(); if (ch == -1) break; request.append((char)ch); if (!request.toString().endsWith("\r\n\r\n")) continue; if (debug) JFLog.log("WebServer:Request detected!"); WebRequest req = new WebRequest(); req.secure = web.secure; req.request = request.toString(); req.fields = req.request.split("\r\n"); req.is = is; req.serverIP = s.getLocalAddress().getHostAddress(); if (req.serverIP.equals("0:0:0:0:0:0:0:1")) req.serverIP = "127.0.0.1"; req.serverPort = s.getLocalPort(); req.remoteIP = s.getInetAddress().getHostAddress(); req.remotePort = s.getPort(); WebResponse res = new WebResponse(); res.os = s.getOutputStream(); req.fields0 = req.fields[0].split(" "); req.init(res); if (isWebSocketRequest(req)) { String url = req.getURL(); if (debug) JFLog.log("WebServer:WEB_SOCKET:" + url); WebSocket socket = new WebSocket(s.getLocalAddress().getHostAddress(), s.getInetAddress().getHostAddress(), is, res.os, req.getHost(), req.fields0[1], req.cookies); int action = WebSocketHandler.REJECT; if (web.wsapi != null) { action = web.wsapi.doWebSocketConnect(socket); } switch (action) { case ACCEPT: sendWebSocketAccepted(req, res); processWebSocket(web, socket); break; case REJECT: sendWebSocketDenied(req, res); break; case DETACH: sendWebSocketAccepted(req, res); s = null; //do not close break; } break; } else if (WebUpload.isMultipartContent(req)) { if (debug) JFLog.log("WebServer:Upload detected!"); if (upload_folder == null) { res.setStatus(501, "Error - Uploads disabled"); } else { WebUpload upload = new WebUpload(); req.files = upload.processRequest(req, upload_folder); } } else if (req.fields0[0].equals("GET")) { if (debug) JFLog.log("WebServer:GET:" + req.getQueryString()); req.method = "GET"; web.api.doGet(req, res); } else if (req.fields0[0].equals("POST")) { if (debug) JFLog.log("WebServer:POST:" + req.getQueryString()); req.method = "POST"; web.api.doPost(req, res); } else { if (debug) JFLog.log("WebServer:Unknown request!"); res.setStatus(501, "Error - Unsupported Method"); } res.writeAll(req); if (req.fields0[2].equals("HTTP/1.0")) break; request.setLength(0); } if (s != null) { s.close(); s = null; } } catch (SSLHandshakeException ssl) { if (debug) JFLog.log("WebServer.Connection:SSL error"); } catch (SocketException e) { if (debug) JFLog.log("WebServer.Connection:disconnected"); } catch (Exception e) { JFLog.log(e); } synchronized (web.clientsLock) { web.clients.remove(this); } } public void cancel() { if (s != null) { try {s.close();} catch (Exception e) {} s = null; } } private boolean isWebSocketRequest(WebRequest req) { boolean upgrade = false; boolean websocket = false; boolean haveKey = false; for(int a=0;a 16777216) { throw new Exception("WebSocket message > 16MB"); } //now read data byte[] data = JF.readAll(socket.is, (int)length); //unmask data for(int a=0;a 0x8) continue; //other control message web.wsapi.doWebSocketMessage(socket, data, opcode); } } catch (SocketException e) { if (debug) JFLog.log("WebServer.Websocket:disconnected"); } catch (Exception e) { JFLog.log(e); } web.wsapi.doWebSocketClosed(socket); } /** Returns a chunk header for a block of data for transmission in Transfer-Encoding: chunked * Make sure to send \r\n after actual block of data. */ public static byte[] chunkHeader(byte[] in) { return String.format("%x\r\n", in.length).getBytes(); } /** Process WebSocket within WebUIServlet context. * * This method creates a new Thread that runs in the WebUIServlet ClassLoader. */ public void attachWebSocket(WebSocket socket) { new Thread() { public void run() { processWebSocket(WebServer.this, socket); } }.start(); } }