diff --git a/README.md b/README.md index b7a7ea0..3ff8e56 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,5 @@ Java socket server. Currently Connection plugin done for handling WebSocket conn WebSocket server is able to receive, read and send handshake also read/send data from/to client. some code snippets refactored from stackoverflow and mozilla.org + +this stuff should work on Java 1.2 and higher diff --git a/config/server.properties b/config/server.properties index 9badca2..65d04e1 100644 --- a/config/server.properties +++ b/config/server.properties @@ -1,9 +1,12 @@ socketPort=6050 websocketPort=6051 webPort=8080 -enabledProtocols=socket,websocket,web socketEnabled=true websocketEnabled=true webEnabled=true sslEnabled=false -maxConnections=100 \ No newline at end of file +maxConnections=100 +#not implemented yet, something like front controller pattern +serverAsDispatcher=false +#use this for dynamic port allocation +dynamicPortRange=6000-7000 \ No newline at end of file diff --git a/src/example/ExampleHttpListener.java b/src/example/ExampleHttpListener.java new file mode 100644 index 0000000..cdd42d0 --- /dev/null +++ b/src/example/ExampleHttpListener.java @@ -0,0 +1,62 @@ +package example; + +import server.core.listener.HttpMethodListener; +import server.module.WebModule; + +public class ExampleHttpListener extends WebModule { + public ExampleHttpListener() { + super(); + setRoot("/example"); + registerListener("GET /test", new HttpMethodListener() { + @Override + public void onBeforeReceive(Object message) { + System.out.println("Received request for /example: " + message); + } + + @Override + public void onAfterReceive(Object request) { + //do something with request here + ExampleHttpListener thisModule = (ExampleHttpListener) getContext(); + thisModule.test((String) request); //call it from context + //or call it directly + test("test request from /example"); + System.out.println("Finished processing request for /example: " + request); + } + + @Override + public void onBeforeBroadcast(Object message) { + //add your response here + setResponse("test response from /example"); + System.out.println("About to broadcast response for /example: " + message); + } + @Override + public void onAfterBroadcast(Object message) { + System.out.println("Finished broadcasting response for /example: " + message); + } + }); + } + + public void test(String test) { + //do something with data + setResponse("test response from /example"); + } + // public static void main(String[] args) { +// HttpMethodListener listener = new HttpMethodListener() { +// @Override +// public void onBeforeReceive(Object message) { +// System.out.println("Before receiving: " + message); +// } +// +// @Override +// public void onAfterReceive(Object message) { +// System.out.println("After receiving: " + message); +// } +// }; +// +// // Simulate receiving a message +// String simulatedMessage = "GET /index.html HTTP/1.1"; +// listener.onBeforeReceive(simulatedMessage); +// // Here would be the logic to process the message +// listener.onAfterReceive(simulatedMessage); +// } +} diff --git a/src/server/config/ConfigConstant.java b/src/server/config/ConfigConstant.java new file mode 100644 index 0000000..79c85bd --- /dev/null +++ b/src/server/config/ConfigConstant.java @@ -0,0 +1,14 @@ +package server.config; + +public class ConfigConstant { + public static final String ENABLED_PROTOCOLS = "enabledProtocols"; + public static final String WEB_ENABLED = "webEnabled"; + public static final String WEBSOCKET_ENABLED = "websocketEnabled"; + public static final String SOCKET_ENABLED = "socketEnabled"; + public static final String WEB_PORT = "webPort"; + public static final String WEBSOCKET_PORT = "websocketPort"; + public static final String SOCKET_PORT = "socketPort"; + public static final String VALUE_WEB = "web"; + public static final String VALUE_WEBSOCKET = "websocket"; + public static final String VALUE_SOCKET = "socket"; +} diff --git a/src/server/config/ServerXmlConfigValue.java b/src/server/config/ServerXmlConfigValue.java index fed4a9d..3da61ce 100644 --- a/src/server/config/ServerXmlConfigValue.java +++ b/src/server/config/ServerXmlConfigValue.java @@ -2,22 +2,23 @@ import server.core.Server; +@Deprecated public class ServerXmlConfigValue { - private static String getConfigValue(String val) { - XmlServerConfig cfg = Server.getXmlConfig(); - if (cfg == null) return ""; - String v = cfg.get(val); - return v == null ? "" : v; - } - - @SuppressWarnings("unused") - public static int getConfigValueAsInt(String val) { - String v = getConfigValue(val); - try { - return Integer.parseInt(v); - } catch (NumberFormatException e) { -// Server.LOGGER.warning("NumberFormatException: " + e.getMessage()); - return -1; - } - } +// private static String getConfigValue(String val) { +// XmlServerConfig cfg = Server.getXmlConfig(); +// if (cfg == null) return ""; +// String v = cfg.get(val); +// return v == null ? "" : v; +// } +// +// @SuppressWarnings("unused") +// public static int getConfigValueAsInt(String val) { +// String v = getConfigValue(val); +// try { +// return Integer.parseInt(v); +// } catch (NumberFormatException e) { +//// Server.LOGGER.warning("NumberFormatException: " + e.getMessage()); +// return -1; +// } +// } } diff --git a/src/server/config/XmlServerConfig.java b/src/server/config/XmlServerConfig.java index 1ffad47..db9af6d 100644 --- a/src/server/config/XmlServerConfig.java +++ b/src/server/config/XmlServerConfig.java @@ -21,7 +21,7 @@ import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; - +@Deprecated public class XmlServerConfig { private final Map parameters = new HashMap(); diff --git a/src/server/core/Connection.java b/src/server/core/Connection.java index 360a32d..6a890de 100644 --- a/src/server/core/Connection.java +++ b/src/server/core/Connection.java @@ -7,8 +7,9 @@ */ public interface Connection { void start(); - void stop(); + void stop() throws IOException; void receive() throws IOException; void broadcast() throws IOException; void broadcast(String data) throws IOException; + int getPort(); } diff --git a/src/server/core/ConnectionAbstract.java b/src/server/core/ConnectionAbstract.java index 2169ecf..0fbb957 100644 --- a/src/server/core/ConnectionAbstract.java +++ b/src/server/core/ConnectionAbstract.java @@ -10,9 +10,9 @@ * @todo add factory */ public abstract class ConnectionAbstract implements Runnable, Connection { - protected static int counter = 0; + protected static int counter; protected final Thread thread; -// protected ObjectOutputStream out; + // protected ObjectOutputStream out; // protected ObjectInputStream in; protected OutputStream outputStream; protected InputStream inputStream; @@ -24,16 +24,32 @@ public abstract class ConnectionAbstract implements Runnable, Connection { protected boolean close = false; protected int instanceNo; protected boolean stop = false; - protected java.net.Socket client; + protected Socket client; protected ServerSocket serverSocket; + protected int port; protected ConnectionAbstract(ServerSocket serverSocket) { + setCounter(0); this.serverSocket = serverSocket; - this.instanceNo = counter++; + this.port = serverSocket.getLocalPort(); + this.instanceNo = getCounter(); + incrementCounter(); this.thread = new Thread(this, MODULE_NAME + instanceNo); } + //for child classes + protected ConnectionAbstract() { + this.instanceNo = getCounter(); + incrementCounter(); + this.thread = new Thread(this, MODULE_NAME + instanceNo); + + } + + public int getPort() { + return port; + } + public static int getCounter() { return counter; } @@ -69,15 +85,16 @@ public void start() { } @Override - public void stop() { + public void stop() throws IOException { // getThread().stop(); getThread().interrupt(); stop = true; decrementCounter(); instanceNo = -1; + client.close(); } - public void processStream(Socket client) { + public void processStreamBinary(Socket client) { try { setClient(client); outputStream = new ObjectOutputStream(getClient().getOutputStream()); @@ -102,7 +119,50 @@ public void processStream() { } } + public void processStream(Socket client) { + try { + setClient(client); + outputStream = getClient().getOutputStream(); + inputStream = getClient().getInputStream(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void processStreamBinary() { + try { + processStreamBinary(serverSocket.accept()); + } catch (IOException e) { + e.printStackTrace(); +// throw new RuntimeException(e); + } + } + protected void flushOutputStream() throws IOException { outputStream.flush(); } + + protected void closeInputStream() throws IOException { + inputStream.close(); + } + + protected void closeOutputStream() throws IOException { + outputStream.close(); + } + + public String getResponseAsString() { + return response; + } + + public void setResponse(String response) { + this.response = response; + } + + public String getRequestAsString() { + return request; + } + + public void setRequest(String request) { + this.request = request; + } } diff --git a/src/server/core/HttpMethod.java b/src/server/core/HttpMethod.java new file mode 100644 index 0000000..8253085 --- /dev/null +++ b/src/server/core/HttpMethod.java @@ -0,0 +1,55 @@ +package server.core; + +public enum HttpMethod { + GET, + POST, + PUT, + DELETE, + HEAD, + OPTIONS, + PATCH; + + public static HttpMethod fromString(String method) { + for (HttpMethod httpMethod : HttpMethod.values()) { + if (httpMethod.name().equalsIgnoreCase(method)) { + return httpMethod; + } + } + throw new IllegalArgumentException("Unknown HTTP method: " + method); + } + + public static boolean isGet(String request) { + int indexOf = request.indexOf(GET.toString()); + return indexOf != -1; + } + + public static boolean isPost(String request) { + int indexOf = request.indexOf(POST.toString()); + return indexOf != -1; + } + + public static boolean isPut(String request) { + int indexOf = request.indexOf(PUT.toString()); + return indexOf != -1; + } + + public static boolean isDelete(String request) { + int indexOf = request.indexOf(DELETE.toString()); + return indexOf != -1; + } + + public static boolean isHead(String request) { + int indexOf = request.indexOf(HEAD.toString()); + return indexOf != -1; + } + + public static boolean isOptions(String request) { + int indexOf = request.indexOf(OPTIONS.toString()); + return indexOf != -1; + } + + public static boolean isPatch(String request) { + int indexOf = request.indexOf(PATCH.toString()); + return indexOf != -1; + } +} diff --git a/src/server/core/Listener.java b/src/server/core/Listener.java new file mode 100644 index 0000000..3f5f9d9 --- /dev/null +++ b/src/server/core/Listener.java @@ -0,0 +1,8 @@ +package server.core; + +public interface Listener { + void onBeforeReceive(Object message); + void onBeforeBroadcast(Object message); + void onAfterReceive(Object message); + void onAfterBroadcast(Object message); +} diff --git a/src/server/core/Responsive.java b/src/server/core/Responsive.java deleted file mode 100644 index 0135bc8..0000000 --- a/src/server/core/Responsive.java +++ /dev/null @@ -1,6 +0,0 @@ -package server.core; - -public interface Responsive { - String onBroadcast(); - String onReceive(); -} diff --git a/src/server/core/Server.java b/src/server/core/Server.java index 687a7d1..ef2f80d 100644 --- a/src/server/core/Server.java +++ b/src/server/core/Server.java @@ -1,139 +1,157 @@ package server.core; import server.config.*; +import server.module.*; import server.utils.FileUtils; -import java.util.logging.Logger; -// import java.util.logging.Level; // unused - import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.UnknownHostException; import java.util.List; import java.util.ArrayList; -import java.util.Collections; -import java.util.logging.Level; - -import server.module.*; /** * @author andrzej.salamon@gmail.com */ public final class Server extends Thread { - private static final Logger LOGGER = Logger.getLogger(Server.class.getName()); - private static final String ERR_PORT_PREFIX = "failed listening on port: "; - // private static final String FILE_CONFIG_SERVER_XML = "server.xml"; private static final String FILE_CONFIG_SERVER_PROPS = "server.properties"; - // private static final String FILE_CONFIG_SERVER_YML = "server.yml"; // wont - // implement till now - private static final String DIR_CONFIG = "config"; - private static final String CFG_ENABLED_PROTOCOLS = "enabledProtocols"; - private static final String CFG_WEB_ENABLED = "webEnabled"; - private static final String CFG_WEBSOCKET_ENABLED = "websocketEnabled"; - private static final String CFG_SOCKET_ENABLED = "socketEnabled"; + private static final String FORMAT_PARAM_LOGGER = "{0} -> {1}"; + public static final String IP = getIp(); - // removed unused commented code - private static ServerProperties serverProperties; + public enum MODULES { + SOCKET, + WEBSOCKET, + WEB + } + private static ServerProperties serverProperties; private ServerSocket serverSocket = null; private ServerSocket serverWebSocket = null; private ServerSocket serverWeb = null; - public static final String IP = getIp(); - - private static XmlServerConfig config; // thread-safe list wrapper - private final List connections = Collections.synchronizedList(new ArrayList()); + private final List connections = new ArrayList(); // flags used across threads - private volatile boolean running = false; - private volatile boolean stop = false; - private volatile boolean exitOnFail = true; - private volatile boolean startFailed = false; + private static boolean running = false; + private static boolean stop = false; + private static boolean exitOnFail = true; + private static boolean startFailed = false; public Server() { - - // Server.setConfig(new XmlServerConfig(DIR_CONFIG + FileUtils.FILE_SEPARATOR + - // FILE_CONFIG_SERVER_XML)); + // we are not using nio because we want to have it synchronous try { setServerProperties( FileUtils.loadServerProperties(DIR_CONFIG + FileUtils.FILE_SEPARATOR + FILE_CONFIG_SERVER_PROPS)); } catch (IOException e) { - LOGGER.severe(e.getMessage()); + e.printStackTrace(); +// LOGGER.log(Level.SEVERE, "No configuration found: {0}", e.getMessage()); + System.err.println("No configuration found"); startFailed = true; System.exit(-1); } if (getSocketPort() < 1 || getWebsocketPort() < 1 || getWebPort() < 1) { - LOGGER.severe("Invalid port configuration. Ports must be greater than 0."); +// LOGGER.severe("Invalid port configuration. Ports must be greater than 0."); + System.err.println("Invalid port number"); startFailed = true; System.exit(-1); } - if (ServerPropertiesValue.getConfigValueAsBoolean(CFG_SOCKET_ENABLED) - || ServerPropertiesValue.getConfigValueAsString(CFG_ENABLED_PROTOCOLS).contains("socket")) - try { - setServerSocket(new ServerSocket(getSocketPort())); - } catch (IOException e) { - LOGGER.log(Level.SEVERE, ERR_PORT_PREFIX + "{0} -> {1}", - new Object[] { getSocketPort(), e.getMessage() }); - startFailed = true; - } + configureModule(ConfigConstant.SOCKET_ENABLED, getSocketPort(), MODULES.SOCKET); + configureModule(ConfigConstant.WEBSOCKET_ENABLED, getWebsocketPort(), MODULES.WEBSOCKET); + configureModule(ConfigConstant.WEB_ENABLED, getWebPort(), MODULES.WEB); - if (ServerPropertiesValue.getConfigValueAsBoolean(CFG_WEBSOCKET_ENABLED) - || ServerPropertiesValue.getConfigValueAsString(CFG_ENABLED_PROTOCOLS).contains("websocket")) - try { - setServerWebSocket(new ServerSocket(getWebsocketPort())); - } catch (IOException e) { - LOGGER.log(Level.SEVERE, ERR_PORT_PREFIX + "{0} -> {1}", - new Object[] { getWebsocketPort(), e.getMessage() }); - startFailed = true; - } + if (startFailed && exitOnFail) { + System.exit(-1); + } - if (ServerPropertiesValue.getConfigValueAsBoolean(CFG_WEB_ENABLED)) + this.start(); + } + + public boolean isExitOnFail() { + return exitOnFail; + } + + public void setExitOnFail(boolean exitOnFail) { + Server.exitOnFail = exitOnFail; + } + + private void configureModule(String socketEnabled, int socketPort, MODULES module) { + if (isEnabled(socketEnabled)) try { - setServerWeb(new ServerSocket(getWebPort())); + setupModule(module, socketPort); } catch (IOException e) { - LOGGER.log(Level.SEVERE, ERR_PORT_PREFIX + "{0} -> {1}", new Object[] { getWebPort(), e.getMessage() }); +// LOGGER.log(Level.SEVERE, ERR_PORT_PREFIX + FORMAT_PARAM_LOGGER, +// new Object[] { socketPort, e.getMessage() }); + System.err.println("Failed to configure module: " + e.getMessage()); startFailed = true; } + } - if (startFailed && exitOnFail) { - System.exit(-1); + private static boolean isEnabled(String socketEnabled) { + return ServerPropertiesValue.getConfigValueAsBoolean(socketEnabled); + } + + private void setupModule(MODULES module, int socketPort) throws IOException { + switch (module) { + case SOCKET: + setServerSocket(createSocket(socketPort)); + addSocketModule(); + break; + case WEBSOCKET: + setServerWebSocket(createSocket(socketPort)); + addWebsocketModule(); + break; + case WEB: + setServerWeb(createSocket(socketPort)); + addWebModule(); + break; + default: + System.err.println("Unknown module: " + module); + break; } + } - addDefaultModules(); + private void addModule() { + } - this.start(); + private static ServerSocket createSocket(int port) throws IOException { + return new ServerSocket(port); } private static int getWebPort() { - return ServerPropertiesValue.getConfigValueAsInt("webPort"); + return ServerPropertiesValue.getConfigValueAsInt(ConfigConstant.WEB_PORT); } private static int getWebsocketPort() { - return ServerPropertiesValue.getConfigValueAsInt("websocketPort"); + return ServerPropertiesValue.getConfigValueAsInt(ConfigConstant.WEBSOCKET_PORT); } private static int getSocketPort() { - return ServerPropertiesValue.getConfigValueAsInt("socketPort"); + return ServerPropertiesValue.getConfigValueAsInt(ConfigConstant.SOCKET_PORT); } private void setServerWeb(ServerSocket serverWebPort) { this.serverWeb = serverWebPort; } - private void addDefaultModules() { - // add modules; connections list is thread-safe - addModule(new WebSocketModule(getServerWebSocket())); // sharing sockets intentionally - addModule(new SocketModule(getServerSocket())); + private void addWebModule() { addModule(new WebModule(getServerWeb())); } + private void addSocketModule() { + addModule(new SocketModule(getServerSocket())); + } + + private void addWebsocketModule() { + addModule(new WebSocketModule(getServerWebSocket())); + } + private ServerSocket getServerWeb() { return serverWeb; } @@ -142,8 +160,10 @@ public void addModule(SocketConnection socketConnection) { if (socketConnection == null) { return; } - if (!connections.contains(socketConnection)) { - connections.add(socketConnection); + synchronized (connections) { + if (!connections.contains(socketConnection)) { + connections.add(socketConnection); + } } } @@ -154,12 +174,15 @@ private void startModules() { } for (SocketConnection conn : connections) { try { + System.out.println("Starting module: " + conn.getClass().getSimpleName() + " at port " + conn.getPort()); conn.start(); } catch (IllegalThreadStateException e) { // already started or cannot start; log and continue - LOGGER.log(Level.SEVERE, "module start failed: {0}", e.getMessage()); +// LOGGER.log(Level.SEVERE, "module start failed: {0}", e.getMessage()); + System.err.println("module start failed: " + e.getMessage()); } catch (Exception e) { - LOGGER.log(Level.SEVERE, "unexpected module start error: {0}", e.getMessage()); +// LOGGER.log(Level.SEVERE, "unexpected module start error: {0}", e.getMessage()); + System.err.println("unexpected module start error: " + e.getMessage()); } } } @@ -174,20 +197,13 @@ private void stopModules() { try { conn.stop(); } catch (Exception e) { - LOGGER.log(Level.SEVERE, "module stop failed: {0}", e.getMessage()); +// LOGGER.log(Level.SEVERE, "module stop failed: {0}", e.getMessage()); + System.err.println("unexpected module stop error: " + e.getMessage()); } } } } - public static XmlServerConfig getXmlConfig() { - return config; - } - - public static void setXmlConfig(XmlServerConfig config) { - Server.config = config; - } - public static ServerProperties getServerProperties() { return serverProperties; } @@ -199,8 +215,8 @@ public static void setServerProperties(ServerProperties config) { @Override @SuppressWarnings("unused") public void run() { - LOGGER.info("Andrew (Web)Socket(s) Server v. 1.1"); - LOGGER.log(Level.INFO, "Started at IP: {0}", IP); + System.out.println("Andrew (Web)Socket(s) Server v. 1.1"); + System.out.println("Started at IP: " + IP); startModules(); running = true; @@ -208,7 +224,7 @@ public void run() { try { Thread.sleep(1000); // sleep server for a while } catch (InterruptedException e) { - LOGGER.log(Level.WARNING, "InterruptedException: {0}", e.getMessage()); + System.err.println("Server sleep interrupted: " + e.getMessage()); // restore interrupted status and exit loop Thread.currentThread().interrupt(); running = false; @@ -219,18 +235,17 @@ public void run() { return; } } - // ensure cleanup in case loop exits - stopModules(); - stopServer(); +// // ensure cleanup in case loop exits +// stopModules(); +// stopServer(); } - @SuppressWarnings("unused") private static String getIp() { try { InetAddress addr = InetAddress.getLocalHost(); return addr.getHostAddress(); } catch (UnknownHostException e) { - LOGGER.log(Level.WARNING, "UnknownHostException: {0}", e.getMessage()); + System.err.println("Unable to get IP address UnknownHostException: " + e.getMessage()); return ""; } } @@ -257,20 +272,17 @@ public ServerSocket getServerWebSocket() { public void stopServer() { running = false; // close server sockets quietly - closeQuietly(serverSocket); - closeQuietly(serverWebSocket); - closeQuietly(serverWeb); + closeServerSocket(serverSocket); + closeServerSocket(serverWebSocket); + closeServerSocket(serverWeb); } - @SuppressWarnings("unused") - private void closeQuietly(ServerSocket s) { - if (s == null) { - return; - } + private void closeServerSocket(ServerSocket s) { + if (s == null) return; try { s.close(); } catch (IOException e) { - LOGGER.log(Level.WARNING, "IOException closing socket: {0}", e.getMessage()); + System.err.println("Unable to close server socket: " + e.getMessage()); // swallow - best effort close } } diff --git a/src/server/core/SocketConnection.java b/src/server/core/SocketConnection.java index 03b9bfe..ef4f5e9 100644 --- a/src/server/core/SocketConnection.java +++ b/src/server/core/SocketConnection.java @@ -9,5 +9,6 @@ public interface SocketConnection extends Connection { String getId(); void processStream(); + void processStreamBinary(); void processStream(Socket client); } diff --git a/src/server/core/connection/WebConnectionAbstract.java b/src/server/core/connection/WebConnectionAbstract.java index 37050ab..ef9a292 100644 --- a/src/server/core/connection/WebConnectionAbstract.java +++ b/src/server/core/connection/WebConnectionAbstract.java @@ -11,4 +11,7 @@ protected WebConnectionAbstract(ServerSocket serverSocket) { super(serverSocket); } + public WebConnectionAbstract() { + super(); + } } diff --git a/src/server/core/connection/WebSocketConnectionAbstract.java b/src/server/core/connection/WebSocketConnectionAbstract.java index e0968c4..02442ac 100644 --- a/src/server/core/connection/WebSocketConnectionAbstract.java +++ b/src/server/core/connection/WebSocketConnectionAbstract.java @@ -2,6 +2,7 @@ //import server.core; import server.core.ConnectionAbstract; +import server.core.HttpMethod; import server.core.WebSocketConnection; import javax.xml.bind.DatatypeConverter; @@ -16,7 +17,9 @@ public abstract class WebSocketConnectionAbstract extends ConnectionAbstract implements WebSocketConnection { - protected String secWebSocketKey; + protected static final String UUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + protected String secWebSocketKey; //@todo cache it with timeout + private byte[] responseBuffer; public WebSocketConnectionAbstract(ServerSocket serverSocket) { super(serverSocket); @@ -37,7 +40,7 @@ public void sendHandshake() throws NoSuchAlgorithmException, IOException { .printBase64Binary( MessageDigest .getInstance("SHA-1") - .digest((secWebSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11") + .digest((secWebSocketKey + UUID) .getBytes(StandardCharsets.UTF_8))) + "\r\n\r\n") .getBytes(StandardCharsets.UTF_8); @@ -56,23 +59,22 @@ private Matcher setSecWebSocketKey() { } public boolean isGet() { - Matcher get = Pattern.compile("^GET").matcher(request); - return get.find(); + return HttpMethod.isGet(request); } public void receive() throws IOException { - byte[] buffer = new byte[server.core.WebSocketConnection.MAX_BUFFER]; + responseBuffer = new byte[WebSocketConnection.MAX_BUFFER]; int messageLength, mask, dataStart; - messageLength = inputStream.read(buffer); + messageLength = inputStream.read(responseBuffer); if (messageLength == -1) { return; } - requestByte = new byte[messageLength]; + responseByte = new byte[messageLength]; //b[0] is always text in my case so no need to check; - byte data = buffer[1]; //does it cause a problem ? + byte data = responseBuffer[1]; //does it cause a problem ? byte op = (byte) 127; byte length; @@ -86,17 +88,17 @@ public void receive() throws IOException { int j = 0, i=mask; for (; i < (mask + 4); i++) { //start at mask, stop at last + 4 - masks[j] = buffer[i]; //problem here + masks[j] = responseBuffer[i]; //problem here j++; } dataStart = mask + 4; for (i = dataStart, j = 0; i < messageLength; i++, j++) { - requestByte[j] = (byte) (buffer[i] ^ masks[j % 4]); + responseByte[j] = (byte) (responseBuffer[i] ^ masks[j % 4]); } - response = new String(requestByte); //why now string copy of byte ? + response = new String(responseByte); //why now string copy of byte ? } public void broadcast(String data) throws IOException { @@ -132,17 +134,17 @@ public void broadcast(String data) throws IOException { int responseLength = frameCount + rawData.length; int responseLimit = 0; - responseByte = new byte[responseLength]; + requestByte = new byte[responseLength]; for (; responseLimit < frameCount; responseLimit++) { - responseByte[responseLimit] = frame[responseLimit]; + requestByte[responseLimit] = frame[responseLimit]; } for (byte dataByte : rawData) { - responseByte[responseLimit++] = dataByte; + requestByte[responseLimit++] = dataByte; } - outputStream.write(responseByte); + outputStream.write(requestByte); flushOutputStream(); } diff --git a/src/server/core/listener/HttpMethodListener.java b/src/server/core/listener/HttpMethodListener.java new file mode 100644 index 0000000..e470264 --- /dev/null +++ b/src/server/core/listener/HttpMethodListener.java @@ -0,0 +1,37 @@ +package server.core.listener; + +import server.core.Listener; + +public class HttpMethodListener implements Listener { + private Object context; + public HttpMethodListener() { + } + + @Override + public void onBeforeReceive(Object message) { + + } + + @Override + public void onBeforeBroadcast(Object message) { + + } + + @Override + public void onAfterReceive(Object message) { + + } + + @Override + public void onAfterBroadcast(Object message) { + + } + + public Object getContext() { + return context; + } + + public void setContext(Object context) { + this.context = context; + } +} diff --git a/src/server/module/SocketModule.java b/src/server/module/SocketModule.java index b35bd68..a69e76c 100644 --- a/src/server/module/SocketModule.java +++ b/src/server/module/SocketModule.java @@ -4,6 +4,7 @@ import java.net.ServerSocket; +import server.core.Server; import server.core.connection.SocketConnectionAbstract; /** @@ -11,6 +12,7 @@ */ public class SocketModule extends SocketConnectionAbstract { public static final String MODULE_NAME = "socketModule"; + public static final Server.MODULES MODULE_TYPE = Server.MODULES.SOCKET; public SocketModule(ServerSocket serverSocket) { super(serverSocket); diff --git a/src/server/module/WebModule.java b/src/server/module/WebModule.java index a745b4e..89790c7 100644 --- a/src/server/module/WebModule.java +++ b/src/server/module/WebModule.java @@ -1,23 +1,41 @@ package server.module; +import server.core.HttpMethod; +import server.core.Server; import server.core.connection.WebConnectionAbstract; +import server.core.listener.HttpMethodListener; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; +import java.util.HashMap; +import java.util.Map; /** * @author andrzej.salamon@gmail.com */ public class WebModule extends WebConnectionAbstract { public static final String MODULE_NAME = "webModuleSocket"; + public static final Server.MODULES MODULE_TYPE = Server.MODULES.WEB; private PrintWriter printWriter; + private Map listeners = new HashMap(); + private String signature; + private String root; + private String method; + private String uri; + private String uriWithMethod; public WebModule(ServerSocket serverSocket) { super(serverSocket); } + public WebModule() { + super(); + } + @Override public String getId() { return MODULE_NAME; @@ -28,36 +46,68 @@ public void run() { try { processStream(); receive(); + setRequest("

Default Response

"); broadcast(); flushOutputStream(); -// out.close(); -// in.close(); -// getClient().close(); -// try { -//// out.close(); -//// in.close(); -// client.close(); -// } catch (IOException e) { -// // e.printStackTrace(); -// } + closeOutpInputStreams(); + getClient().close(); } catch (Exception e) { e.printStackTrace(); } } } + private void closeOutpInputStreams() throws IOException { + outputStream.close(); + inputStream.close(); + } + @Override public void receive() throws IOException { + callBeforeReceive(uriWithMethod); + StringBuilder requestBuilder = new StringBuilder(); + BufferedReader in = new BufferedReader(new InputStreamReader(inputStream)); + String line = in.readLine(); + signature = line; + requestBuilder.append(line); + while ((line = in.readLine()) != null) { + requestBuilder.append(line).append("\r\n"); + } + parseSignature(); + request = requestBuilder.toString(); + callAfterReceive(uriWithMethod); + System.out.println("Received request:\n" + request); + } + + private void callBeforeReceive(String uriWithMethod) { + checkListener(uriWithMethod); + HttpMethodListener callback = listeners.get(uri); + callback.setContext(this); + callback.onBeforeReceive(request); + callback.setContext(null); //lets clear context after use + } + private void checkListener(String uriWithMethod) { + if (!listeners.containsKey(uriWithMethod)) { + throw new RuntimeException("No listener for uriWithMethod: " + uriWithMethod); + } + } + + private void callAfterReceive(String uriWithMethod) { + checkListener(uriWithMethod); + HttpMethodListener callback = listeners.get(uri); + callback.setContext(this); //current module that extends WebModule + callback.onAfterReceive(request); + callback.setContext(null); //lets clear context after use } @Override - public void broadcast() throws IOException { + public void broadcast() { printWriter = new PrintWriter(outputStream, true); printWriter.println("HTTP/1.1 200 OK"); printWriter.println("Content-Type: text/html"); printWriter.println(); - printWriter.println("

Hello, World!

"); + printWriter.println(getResponseAsString()); flushPrintWriter(); } @@ -67,6 +117,57 @@ private void flushPrintWriter() { @Override public void broadcast(String data) throws IOException { + callBeforeBrodcast(uriWithMethod, data); + printWriter = new PrintWriter(outputStream, true); + printWriter.println("HTTP/1.1 200 OK"); + printWriter.println("Content-Type: text/html"); + printWriter.println(); + printWriter.println(data); + flushPrintWriter(); + callAfterBrodcast(uriWithMethod, data); + } + + private void callAfterBrodcast(String uriWithMethod, String data) { + checkListener(uriWithMethod); + HttpMethodListener callback = listeners.get(uri); + callback.setContext(this); //current module that extends WebModule + callback.onAfterBroadcast(data); + callback.setContext(null); //lets clear context after use + } + + private void callBeforeBrodcast(String uriWithMethod, String data) { + checkListener(uriWithMethod); + HttpMethodListener callback = listeners.get(uri); + callback.setContext(this); //current module that extends WebModule + callback.onBeforeBroadcast(data); + callback.setContext(null); //lets clear context after use + } + + public void parseSignature() { + String[] parts = signature.split(" "); + if (parts.length >= 2) { + this.method = parts[0]; + this.uri = parts[1]; + this.uriWithMethod = method + " " + getRoot() + uri; + } + } + + public void registerListener(String uri, HttpMethodListener callback) { + if (listeners.containsKey(uri)) { + throw new IllegalArgumentException("Listener for URI already registered: " + uri); + } + listeners.put(uri, callback); + } + + public void registerListener(HttpMethod method, String uri, HttpMethodListener callback) { + registerListener(method.toString() + " " + uri, callback); + } + + public String getRoot() { + return root; + } + public void setRoot(String root) { + this.root = root; } } diff --git a/src/server/module/WebSocketModule.java b/src/server/module/WebSocketModule.java index 4dcec30..65fa19e 100644 --- a/src/server/module/WebSocketModule.java +++ b/src/server/module/WebSocketModule.java @@ -1,5 +1,6 @@ package server.module; +import server.core.Server; import server.core.connection.WebSocketConnectionAbstract; import java.io.IOException; @@ -11,6 +12,7 @@ */ public class WebSocketModule extends WebSocketConnectionAbstract { //double inheritance, not ellegant, ref,mv public static final String MODULE_NAME = "websocketModule"; + public static final Server.MODULES MODULE_TYPE = Server.MODULES.WEBSOCKET; public WebSocketModule(ServerSocket serverSocket) { @@ -24,7 +26,7 @@ public String getId() { public void run() { while (!stop) { - processStream(); + processStreamBinary(); request = getRequestAsString(); @@ -59,13 +61,13 @@ public void run() { e.printStackTrace(); } try { - outputStream.close(); + closeOutputStream(); } catch (IOException e) { System.err.println("cant close output stream"); e.printStackTrace(); } try { - inputStream.close(); + closeInputStream(); } catch (IOException e) { System.err.println("cant inputSTream close"); e.printStackTrace();