diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/DebugServer.java b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/DebugServer.java similarity index 98% rename from repl/src/main/java/me/topchetoeu/j2s/repl/debug/DebugServer.java rename to lib/src/main/java/me/topchetoeu/j2s/lib/debug/DebugServer.java index bc7d98c..13d1384 100644 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/DebugServer.java +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/DebugServer.java @@ -1,4 +1,4 @@ -package me.topchetoeu.j2s.repl.debug; +package me.topchetoeu.j2s.lib.debug; import java.io.IOException; import java.io.UncheckedIOException; @@ -15,7 +15,7 @@ import me.topchetoeu.j2s.common.SyntaxException; import me.topchetoeu.j2s.compilation.json.JSON; import me.topchetoeu.j2s.compilation.json.JSONList; import me.topchetoeu.j2s.compilation.json.JSONMap; -import me.topchetoeu.j2s.repl.debug.WebSocketMessage.Type; +import me.topchetoeu.j2s.lib.debug.WebSocketMessage.Type; public class DebugServer { public static String browserDisplayName = Metadata.name() + "/" + Metadata.version(); diff --git a/lib/src/main/java/me/topchetoeu/j2s/lib/debug/Debugger.java b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/Debugger.java new file mode 100644 index 0000000..704683d --- /dev/null +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/Debugger.java @@ -0,0 +1,37 @@ +package me.topchetoeu.j2s.lib.debug; + +import java.io.IOException; + +import me.topchetoeu.j2s.runtime.debug.DebugHandler; + +public interface Debugger extends DebugHandler { + void close(); + + void enable(V8Message msg) throws IOException; + void disable(V8Message msg) throws IOException; + + void setBreakpointByUrl(V8Message msg) throws IOException; + void removeBreakpoint(V8Message msg) throws IOException; + void continueToLocation(V8Message msg) throws IOException; + + void getScriptSource(V8Message msg) throws IOException; + void getPossibleBreakpoints(V8Message msg) throws IOException; + + void resume(V8Message msg) throws IOException; + void pause(V8Message msg) throws IOException; + + void stepInto(V8Message msg) throws IOException; + void stepOut(V8Message msg) throws IOException; + void stepOver(V8Message msg) throws IOException; + + void setPauseOnExceptions(V8Message msg) throws IOException; + + void evaluateOnCallFrame(V8Message msg) throws IOException; + + void getProperties(V8Message msg) throws IOException; + void releaseObjectGroup(V8Message msg) throws IOException; + void releaseObject(V8Message msg) throws IOException; + void callFunctionOn(V8Message msg) throws IOException; + + void runtimeEnable(V8Message msg) throws IOException; +} diff --git a/lib/src/main/java/me/topchetoeu/j2s/lib/debug/DebuggerProvider.java b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/DebuggerProvider.java new file mode 100644 index 0000000..b92292c --- /dev/null +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/DebuggerProvider.java @@ -0,0 +1,5 @@ +package me.topchetoeu.j2s.lib.debug; + +public interface DebuggerProvider { + Debugger getDebugger(WebSocket socket, HttpRequest req); +} diff --git a/lib/src/main/java/me/topchetoeu/j2s/lib/debug/HttpRequest.java b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/HttpRequest.java new file mode 100644 index 0000000..cff68fb --- /dev/null +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/HttpRequest.java @@ -0,0 +1,100 @@ +package me.topchetoeu.j2s.lib.debug; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.Socket; +import java.util.HashMap; +import java.util.IllegalFormatException; +import java.util.Map; + +import me.topchetoeu.j2s.common.Reading; + +public class HttpRequest { + public final String method; + public final String path; + public final Map headers; + public final OutputStream out; + + public void writeCode(int code, String name) { + try { out.write(("HTTP/1.1 " + code + " " + name + "\r\n").getBytes()); } + catch (IOException e) { } + } + public void writeHeader(String name, String value) { + try { out.write((name + ": " + value + "\r\n").getBytes()); } + catch (IOException e) { } + } + public void writeLastHeader(String name, String value) { + try { out.write((name + ": " + value + "\r\n\r\n").getBytes()); } + catch (IOException e) { } + } + public void writeHeadersEnd() { + try { out.write("\n".getBytes()); } + catch (IOException e) { } + } + + public void writeResponse(int code, String name, String type, byte[] data) { + writeCode(code, name); + writeHeader("Content-Type", type); + writeLastHeader("Content-Length", data.length + ""); + try { + out.write(data); + out.close(); + } + catch (IOException e) { } + } + public void writeResponse(int code, String name, String type, InputStream data) { + writeResponse(code, name, type, Reading.streamToBytes(data)); + } + + public HttpRequest(String method, String path, Map headers, OutputStream out) { + this.method = method; + this.path = path; + this.headers = headers; + this.out = out; + } + + // We dont need no http library + public static HttpRequest read(Socket socket) { + try { + var str = socket.getInputStream(); + var lines = new BufferedReader(new InputStreamReader(str)); + var line = lines.readLine(); + var i1 = line.indexOf(" "); + var i2 = line.indexOf(" ", i1 + 1); + + if (i1 < 0 || i2 < 0) { + socket.close(); + return null; + } + + var method = line.substring(0, i1).trim().toUpperCase(); + var path = line.substring(i1 + 1, i2).trim(); + var headers = new HashMap(); + + while (!(line = lines.readLine()).isEmpty()) { + var i = line.indexOf(":"); + if (i < 0) continue; + var name = line.substring(0, i).trim().toLowerCase(); + var value = line.substring(i + 1).trim(); + + if (name.length() == 0) continue; + headers.put(name, value); + } + + if (headers.containsKey("content-length")) { + try { + var i = Integer.parseInt(headers.get("content-length")); + str.skip(i); + } + catch (IllegalFormatException e) { /* ¯\_(ツ)_/¯ */ } + } + + return new HttpRequest(method, path, headers, socket.getOutputStream()); + } + catch (IOException | NullPointerException e) { return null; } + } +} + diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/ScopeObject.java b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/ScopeObject.java similarity index 99% rename from repl/src/main/java/me/topchetoeu/j2s/repl/debug/ScopeObject.java rename to lib/src/main/java/me/topchetoeu/j2s/lib/debug/ScopeObject.java index 7b5200b..75db3ab 100644 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/ScopeObject.java +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/ScopeObject.java @@ -1,4 +1,4 @@ -package me.topchetoeu.j2s.repl.debug; +package me.topchetoeu.j2s.lib.debug; import java.util.HashMap; import java.util.HashSet; diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/SimpleDebugHandler.java b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/SimpleDebugHandler.java similarity index 92% rename from repl/src/main/java/me/topchetoeu/j2s/repl/debug/SimpleDebugHandler.java rename to lib/src/main/java/me/topchetoeu/j2s/lib/debug/SimpleDebugHandler.java index 4ffc5d3..2c47d74 100644 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/SimpleDebugHandler.java +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/SimpleDebugHandler.java @@ -1,4 +1,4 @@ -package me.topchetoeu.j2s.repl.debug; +package me.topchetoeu.j2s.lib.debug; import java.util.HashMap; import java.util.WeakHashMap; @@ -69,14 +69,8 @@ public class SimpleDebugHandler implements DebugHandler { if (debugger != null) debugger.onFunctionLoad(func, map); } - private SimpleDebugHandler(boolean enabled) { - if (enabled) { - sources = new HashMap<>(); - maps = new WeakHashMap<>(); - } - } - public SimpleDebugHandler() { - this(true); + sources = new HashMap<>(); + maps = new WeakHashMap<>(); } } diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/SimpleDebugger.java b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/SimpleDebugger.java similarity index 99% rename from repl/src/main/java/me/topchetoeu/j2s/repl/debug/SimpleDebugger.java rename to lib/src/main/java/me/topchetoeu/j2s/lib/debug/SimpleDebugger.java index 90326db..9ec0656 100644 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/SimpleDebugger.java +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/SimpleDebugger.java @@ -1,4 +1,4 @@ -package me.topchetoeu.j2s.repl.debug; +package me.topchetoeu.j2s.lib.debug; import java.io.IOException; import java.util.ArrayList; diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/StackObject.java b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/StackObject.java similarity index 95% rename from repl/src/main/java/me/topchetoeu/j2s/repl/debug/StackObject.java rename to lib/src/main/java/me/topchetoeu/j2s/lib/debug/StackObject.java index e061240..01c3f33 100644 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/StackObject.java +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/StackObject.java @@ -1,4 +1,4 @@ -package me.topchetoeu.j2s.repl.debug; +package me.topchetoeu.j2s.lib.debug; import me.topchetoeu.j2s.common.Environment; import me.topchetoeu.j2s.runtime.Frame; diff --git a/lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Error.java b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Error.java new file mode 100644 index 0000000..c28e76e --- /dev/null +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Error.java @@ -0,0 +1,19 @@ +package me.topchetoeu.j2s.lib.debug; + +import me.topchetoeu.j2s.compilation.json.JSON; +import me.topchetoeu.j2s.compilation.json.JSONMap; + +public class V8Error { + public final String message; + + public V8Error(String message) { + this.message = message; + } + + @Override + public String toString() { + return JSON.stringify(new JSONMap().set("error", new JSONMap() + .set("message", message) + )); + } +} diff --git a/lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Event.java b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Event.java new file mode 100644 index 0000000..47d8ee1 --- /dev/null +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Event.java @@ -0,0 +1,22 @@ +package me.topchetoeu.j2s.lib.debug; + +import me.topchetoeu.j2s.compilation.json.JSON; +import me.topchetoeu.j2s.compilation.json.JSONMap; + +public class V8Event { + public final String name; + public final JSONMap params; + + public V8Event(String name, JSONMap params) { + this.name = name; + this.params = params; + } + + @Override + public String toString() { + return JSON.stringify(new JSONMap() + .set("method", name) + .set("params", params) + ); + } +} diff --git a/lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Message.java b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Message.java new file mode 100644 index 0000000..6a577a7 --- /dev/null +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Message.java @@ -0,0 +1,50 @@ +package me.topchetoeu.j2s.lib.debug; + +import java.util.Map; + +import me.topchetoeu.j2s.compilation.json.JSON; +import me.topchetoeu.j2s.compilation.json.JSONElement; +import me.topchetoeu.j2s.compilation.json.JSONMap; + +public class V8Message { + public final String name; + public final int id; + public final JSONMap params; + + public V8Message(String name, int id, Map params) { + this.name = name; + this.params = new JSONMap(params); + this.id = id; + } + public V8Result respond(JSONMap result) { + return new V8Result(id, result); + } + public V8Result respond() { + return new V8Result(id, new JSONMap()); + } + + public V8Message(JSONMap raw) { + if (!raw.isNumber("id")) throw new IllegalArgumentException("Expected number property 'id'."); + if (!raw.isString("method")) throw new IllegalArgumentException("Expected string property 'method'."); + + this.name = raw.string("method"); + this.id = (int)raw.number("id"); + this.params = raw.contains("params") ? raw.map("params") : new JSONMap(); + } + public V8Message(String raw) { + this(JSON.parse(null, raw).map()); + } + + public JSONMap toMap() { + var res = new JSONMap(); + return res; + } + @Override + public String toString() { + return JSON.stringify(new JSONMap() + .set("method", name) + .set("params", params) + .set("id", id) + ); + } +} diff --git a/lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Result.java b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Result.java new file mode 100644 index 0000000..b7accca --- /dev/null +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/V8Result.java @@ -0,0 +1,22 @@ +package me.topchetoeu.j2s.lib.debug; + +import me.topchetoeu.j2s.compilation.json.JSON; +import me.topchetoeu.j2s.compilation.json.JSONMap; + +public class V8Result { + public final int id; + public final JSONMap result; + + public V8Result(int id, JSONMap result) { + this.id = id; + this.result = result; + } + + @Override + public String toString() { + return JSON.stringify(new JSONMap() + .set("id", id) + .set("result", result) + ); + } +} diff --git a/lib/src/main/java/me/topchetoeu/j2s/lib/debug/WebSocket.java b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/WebSocket.java new file mode 100644 index 0000000..1d330f3 --- /dev/null +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/WebSocket.java @@ -0,0 +1,186 @@ +package me.topchetoeu.j2s.lib.debug; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +import me.topchetoeu.j2s.lib.debug.WebSocketMessage.Type; + +public class WebSocket implements AutoCloseable { + public long maxLength = 1 << 20; + + private Socket socket; + private boolean closed = false; + + private OutputStream out() throws IOException { + return socket.getOutputStream(); + } + private InputStream in() throws IOException { + return socket.getInputStream(); + } + + private long readLen(int byteLen) throws IOException { + long res = 0; + + if (byteLen == 126) { + res |= in().read() << 8; + res |= in().read(); + return res; + } + else if (byteLen == 127) { + res |= in().read() << 56; + res |= in().read() << 48; + res |= in().read() << 40; + res |= in().read() << 32; + res |= in().read() << 24; + res |= in().read() << 16; + res |= in().read() << 8; + res |= in().read(); + return res; + } + else return byteLen; + } + private byte[] readMask(boolean has) throws IOException { + if (has) { + return new byte[] { + (byte)in().read(), + (byte)in().read(), + (byte)in().read(), + (byte)in().read() + }; + } + else return new byte[4]; + } + + private void writeLength(int len) throws IOException { + if (len < 126) { + out().write((int)len); + } + else if (len <= 0xFFFF) { + out().write(126); + out().write((int)(len >> 8) & 0xFF); + out().write((int)len & 0xFF); + } + else { + out().write(127); + out().write(0); + out().write(0); + out().write(0); + out().write(0); + out().write((len >> 24) & 0xFF); + out().write((len >> 16) & 0xFF); + out().write((len >> 8) & 0xFF); + out().write(len & 0xFF); + } + } + private synchronized void write(int type, byte[] data) throws IOException { + out().write(type | 0x80); + writeLength(data.length); + out().write(data); + } + + public void send(String data) throws IOException { + if (closed) throw new IllegalStateException("Websocket is closed."); + write(1, data.getBytes()); + } + public void send(byte[] data) throws IOException { + if (closed) throw new IllegalStateException("Websocket is closed."); + write(2, data); + } + public void send(WebSocketMessage msg) throws IOException { + if (msg.type == Type.Binary) send(msg.binaryData()); + else send(msg.textData()); + } + public void send(Object data) throws IOException { + if (closed) throw new IllegalStateException("Websocket is closed."); + write(1, data.toString().getBytes()); + } + + public void close(String reason) { + if (socket != null) { + try { + write(8, reason.getBytes()); + socket.close(); + } + catch (Throwable e) { } + } + + socket = null; + closed = true; + } + public void close() { + close(""); + } + + private WebSocketMessage fail(String reason) { + System.out.println("WebSocket Error: " + reason); + close(reason); + return null; + } + + private byte[] readData() throws IOException { + var maskLen = in().read(); + var hasMask = (maskLen & 0x80) != 0; + var len = (int)readLen(maskLen & 0x7F); + var mask = readMask(hasMask); + + if (len > maxLength) fail("WebSocket Error: client exceeded configured max message size"); + else { + var buff = new byte[len]; + + if (in().read(buff) < len) fail("WebSocket Error: payload too short"); + else { + for (int i = 0; i < len; i++) { + buff[i] ^= mask[(int)(i % 4)]; + } + return buff; + } + } + + return null; + } + + public WebSocketMessage receive() throws IOException { + var data = new ByteArrayOutputStream(); + var type = 0; + + while (socket != null && !closed) { + var finId = in().read(); + if (finId < 0) break; + var fin = (finId & 0x80) != 0; + int id = finId & 0x0F; + + if (id == 0x8) { close(); return null; } + if (id >= 0x8) { + if (!fin) return fail("WebSocket Error: client-sent control frame was fragmented"); + if (id == 0x9) write(0xA, data.toByteArray()); + continue; + } + + if (type == 0) type = id; + if (type == 0) return fail("WebSocket Error: client used opcode 0x00 for first fragment"); + + var buff = readData(); + if (buff == null) break; + + if (data.size() + buff.length > maxLength) return fail("WebSocket Error: client exceeded configured max message size"); + data.write(buff); + + if (!fin) continue; + var raw = data.toByteArray(); + + if (type == 1) { + return new WebSocketMessage(new String(raw)); + } + else return new WebSocketMessage(raw); + } + + return null; + } + + public WebSocket(Socket socket) { + this.socket = socket; + } +} diff --git a/lib/src/main/java/me/topchetoeu/j2s/lib/debug/WebSocketMessage.java b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/WebSocketMessage.java new file mode 100644 index 0000000..5f1cdea --- /dev/null +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/WebSocketMessage.java @@ -0,0 +1,29 @@ +package me.topchetoeu.j2s.lib.debug; + +public class WebSocketMessage { + public static enum Type { + Text, + Binary, + } + + public final Type type; + private final Object data; + + public final String textData() { + if (type != Type.Text) throw new IllegalStateException("Message is not text."); + return (String)data; + } + public final byte[] binaryData() { + if (type != Type.Binary) throw new IllegalStateException("Message is not binary."); + return (byte[])data; + } + + public WebSocketMessage(String data) { + this.type = Type.Text; + this.data = data; + } + public WebSocketMessage(byte[] data) { + this.type = Type.Binary; + this.data = data; + } +} diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/V8Error.java b/repl/src/main/java/me/topchetoeu/j2s/repl/V8Error.java deleted file mode 100644 index dca7053..0000000 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/V8Error.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.topchetoeu.j2s.repl; - -import me.topchetoeu.j2s.compilation.json.JSON; -import me.topchetoeu.j2s.compilation.json.JSONMap; - -public class V8Error { - public final String message; - - public V8Error(String message) { - this.message = message; - } - - @Override - public String toString() { - return JSON.stringify(new JSONMap().set("error", new JSONMap() - .set("message", message) - )); - } -} \ No newline at end of file diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/Debugger.java b/repl/src/main/java/me/topchetoeu/j2s/repl/debug/Debugger.java deleted file mode 100644 index 329cf42..0000000 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/Debugger.java +++ /dev/null @@ -1,37 +0,0 @@ -package me.topchetoeu.j2s.repl.debug; - -import java.io.IOException; - -import me.topchetoeu.j2s.runtime.debug.DebugHandler; - -public interface Debugger extends DebugHandler { - void close(); - - void enable(V8Message msg) throws IOException; - void disable(V8Message msg) throws IOException; - - void setBreakpointByUrl(V8Message msg) throws IOException; - void removeBreakpoint(V8Message msg) throws IOException; - void continueToLocation(V8Message msg) throws IOException; - - void getScriptSource(V8Message msg) throws IOException; - void getPossibleBreakpoints(V8Message msg) throws IOException; - - void resume(V8Message msg) throws IOException; - void pause(V8Message msg) throws IOException; - - void stepInto(V8Message msg) throws IOException; - void stepOut(V8Message msg) throws IOException; - void stepOver(V8Message msg) throws IOException; - - void setPauseOnExceptions(V8Message msg) throws IOException; - - void evaluateOnCallFrame(V8Message msg) throws IOException; - - void getProperties(V8Message msg) throws IOException; - void releaseObjectGroup(V8Message msg) throws IOException; - void releaseObject(V8Message msg) throws IOException; - void callFunctionOn(V8Message msg) throws IOException; - - void runtimeEnable(V8Message msg) throws IOException; -} diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/DebuggerProvider.java b/repl/src/main/java/me/topchetoeu/j2s/repl/debug/DebuggerProvider.java deleted file mode 100644 index de2fa26..0000000 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/DebuggerProvider.java +++ /dev/null @@ -1,5 +0,0 @@ -package me.topchetoeu.j2s.repl.debug; - -public interface DebuggerProvider { - Debugger getDebugger(WebSocket socket, HttpRequest req); -} diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/HttpRequest.java b/repl/src/main/java/me/topchetoeu/j2s/repl/debug/HttpRequest.java deleted file mode 100644 index 708911a..0000000 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/HttpRequest.java +++ /dev/null @@ -1,101 +0,0 @@ -package me.topchetoeu.j2s.repl.debug; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.Socket; -import java.util.HashMap; -import java.util.IllegalFormatException; -import java.util.Map; - -import me.topchetoeu.j2s.common.Reading; - -public class HttpRequest { - public final String method; - public final String path; - public final Map headers; - public final OutputStream out; - - - public void writeCode(int code, String name) { - try { out.write(("HTTP/1.1 " + code + " " + name + "\r\n").getBytes()); } - catch (IOException e) { } - } - public void writeHeader(String name, String value) { - try { out.write((name + ": " + value + "\r\n").getBytes()); } - catch (IOException e) { } - } - public void writeLastHeader(String name, String value) { - try { out.write((name + ": " + value + "\r\n\r\n").getBytes()); } - catch (IOException e) { } - } - public void writeHeadersEnd() { - try { out.write("\n".getBytes()); } - catch (IOException e) { } - } - - public void writeResponse(int code, String name, String type, byte[] data) { - writeCode(code, name); - writeHeader("Content-Type", type); - writeLastHeader("Content-Length", data.length + ""); - try { - out.write(data); - out.close(); - } - catch (IOException e) { } - } - public void writeResponse(int code, String name, String type, InputStream data) { - writeResponse(code, name, type, Reading.streamToBytes(data)); - } - - public HttpRequest(String method, String path, Map headers, OutputStream out) { - this.method = method; - this.path = path; - this.headers = headers; - this.out = out; - } - - // We dont need no http library - public static HttpRequest read(Socket socket) { - try { - var str = socket.getInputStream(); - var lines = new BufferedReader(new InputStreamReader(str)); - var line = lines.readLine(); - var i1 = line.indexOf(" "); - var i2 = line.indexOf(" ", i1 + 1); - - if (i1 < 0 || i2 < 0) { - socket.close(); - return null; - } - - var method = line.substring(0, i1).trim().toUpperCase(); - var path = line.substring(i1 + 1, i2).trim(); - var headers = new HashMap(); - - while (!(line = lines.readLine()).isEmpty()) { - var i = line.indexOf(":"); - if (i < 0) continue; - var name = line.substring(0, i).trim().toLowerCase(); - var value = line.substring(i + 1).trim(); - - if (name.length() == 0) continue; - headers.put(name, value); - } - - if (headers.containsKey("content-length")) { - try { - var i = Integer.parseInt(headers.get("content-length")); - str.skip(i); - } - catch (IllegalFormatException e) { /* ¯\_(ツ)_/¯ */ } - } - - return new HttpRequest(method, path, headers, socket.getOutputStream()); - } - catch (IOException | NullPointerException e) { return null; } - } -} - diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/V8Error.java b/repl/src/main/java/me/topchetoeu/j2s/repl/debug/V8Error.java deleted file mode 100644 index 15b708b..0000000 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/V8Error.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.topchetoeu.j2s.repl.debug; - -import me.topchetoeu.j2s.compilation.json.JSON; -import me.topchetoeu.j2s.compilation.json.JSONMap; - -public class V8Error { - public final String message; - - public V8Error(String message) { - this.message = message; - } - - @Override - public String toString() { - return JSON.stringify(new JSONMap().set("error", new JSONMap() - .set("message", message) - )); - } -} diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/V8Event.java b/repl/src/main/java/me/topchetoeu/j2s/repl/debug/V8Event.java deleted file mode 100644 index 93db46f..0000000 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/V8Event.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.topchetoeu.j2s.repl.debug; - -import me.topchetoeu.j2s.compilation.json.JSON; -import me.topchetoeu.j2s.compilation.json.JSONMap; - -public class V8Event { - public final String name; - public final JSONMap params; - - public V8Event(String name, JSONMap params) { - this.name = name; - this.params = params; - } - - @Override - public String toString() { - return JSON.stringify(new JSONMap() - .set("method", name) - .set("params", params) - ); - } -} diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/V8Message.java b/repl/src/main/java/me/topchetoeu/j2s/repl/debug/V8Message.java deleted file mode 100644 index 67a6f29..0000000 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/V8Message.java +++ /dev/null @@ -1,50 +0,0 @@ -package me.topchetoeu.j2s.repl.debug; - -import java.util.Map; - -import me.topchetoeu.j2s.compilation.json.JSON; -import me.topchetoeu.j2s.compilation.json.JSONElement; -import me.topchetoeu.j2s.compilation.json.JSONMap; - -public class V8Message { - public final String name; - public final int id; - public final JSONMap params; - - public V8Message(String name, int id, Map params) { - this.name = name; - this.params = new JSONMap(params); - this.id = id; - } - public V8Result respond(JSONMap result) { - return new V8Result(id, result); - } - public V8Result respond() { - return new V8Result(id, new JSONMap()); - } - - public V8Message(JSONMap raw) { - if (!raw.isNumber("id")) throw new IllegalArgumentException("Expected number property 'id'."); - if (!raw.isString("method")) throw new IllegalArgumentException("Expected string property 'method'."); - - this.name = raw.string("method"); - this.id = (int)raw.number("id"); - this.params = raw.contains("params") ? raw.map("params") : new JSONMap(); - } - public V8Message(String raw) { - this(JSON.parse(null, raw).map()); - } - - public JSONMap toMap() { - var res = new JSONMap(); - return res; - } - @Override - public String toString() { - return JSON.stringify(new JSONMap() - .set("method", name) - .set("params", params) - .set("id", id) - ); - } -} diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/V8Result.java b/repl/src/main/java/me/topchetoeu/j2s/repl/debug/V8Result.java deleted file mode 100644 index b7f5661..0000000 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/V8Result.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.topchetoeu.j2s.repl.debug; - -import me.topchetoeu.j2s.compilation.json.JSON; -import me.topchetoeu.j2s.compilation.json.JSONMap; - -public class V8Result { - public final int id; - public final JSONMap result; - - public V8Result(int id, JSONMap result) { - this.id = id; - this.result = result; - } - - @Override - public String toString() { - return JSON.stringify(new JSONMap() - .set("id", id) - .set("result", result) - ); - } -} diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/WebSocket.java b/repl/src/main/java/me/topchetoeu/j2s/repl/debug/WebSocket.java deleted file mode 100644 index 74c59f7..0000000 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/WebSocket.java +++ /dev/null @@ -1,186 +0,0 @@ -package me.topchetoeu.j2s.repl.debug; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; - -import me.topchetoeu.j2s.repl.debug.WebSocketMessage.Type; - -public class WebSocket implements AutoCloseable { - public long maxLength = 1 << 20; - - private Socket socket; - private boolean closed = false; - - private OutputStream out() throws IOException { - return socket.getOutputStream(); - } - private InputStream in() throws IOException { - return socket.getInputStream(); - } - - private long readLen(int byteLen) throws IOException { - long res = 0; - - if (byteLen == 126) { - res |= in().read() << 8; - res |= in().read(); - return res; - } - else if (byteLen == 127) { - res |= in().read() << 56; - res |= in().read() << 48; - res |= in().read() << 40; - res |= in().read() << 32; - res |= in().read() << 24; - res |= in().read() << 16; - res |= in().read() << 8; - res |= in().read(); - return res; - } - else return byteLen; - } - private byte[] readMask(boolean has) throws IOException { - if (has) { - return new byte[] { - (byte)in().read(), - (byte)in().read(), - (byte)in().read(), - (byte)in().read() - }; - } - else return new byte[4]; - } - - private void writeLength(int len) throws IOException { - if (len < 126) { - out().write((int)len); - } - else if (len <= 0xFFFF) { - out().write(126); - out().write((int)(len >> 8) & 0xFF); - out().write((int)len & 0xFF); - } - else { - out().write(127); - out().write(0); - out().write(0); - out().write(0); - out().write(0); - out().write((len >> 24) & 0xFF); - out().write((len >> 16) & 0xFF); - out().write((len >> 8) & 0xFF); - out().write(len & 0xFF); - } - } - private synchronized void write(int type, byte[] data) throws IOException { - out().write(type | 0x80); - writeLength(data.length); - out().write(data); - } - - public void send(String data) throws IOException { - if (closed) throw new IllegalStateException("Websocket is closed."); - write(1, data.getBytes()); - } - public void send(byte[] data) throws IOException { - if (closed) throw new IllegalStateException("Websocket is closed."); - write(2, data); - } - public void send(WebSocketMessage msg) throws IOException { - if (msg.type == Type.Binary) send(msg.binaryData()); - else send(msg.textData()); - } - public void send(Object data) throws IOException { - if (closed) throw new IllegalStateException("Websocket is closed."); - write(1, data.toString().getBytes()); - } - - public void close(String reason) { - if (socket != null) { - try { - write(8, reason.getBytes()); - socket.close(); - } - catch (Throwable e) { } - } - - socket = null; - closed = true; - } - public void close() { - close(""); - } - - private WebSocketMessage fail(String reason) { - System.out.println("WebSocket Error: " + reason); - close(reason); - return null; - } - - private byte[] readData() throws IOException { - var maskLen = in().read(); - var hasMask = (maskLen & 0x80) != 0; - var len = (int)readLen(maskLen & 0x7F); - var mask = readMask(hasMask); - - if (len > maxLength) fail("WebSocket Error: client exceeded configured max message size"); - else { - var buff = new byte[len]; - - if (in().read(buff) < len) fail("WebSocket Error: payload too short"); - else { - for (int i = 0; i < len; i++) { - buff[i] ^= mask[(int)(i % 4)]; - } - return buff; - } - } - - return null; - } - - public WebSocketMessage receive() throws IOException { - var data = new ByteArrayOutputStream(); - var type = 0; - - while (socket != null && !closed) { - var finId = in().read(); - if (finId < 0) break; - var fin = (finId & 0x80) != 0; - int id = finId & 0x0F; - - if (id == 0x8) { close(); return null; } - if (id >= 0x8) { - if (!fin) return fail("WebSocket Error: client-sent control frame was fragmented"); - if (id == 0x9) write(0xA, data.toByteArray()); - continue; - } - - if (type == 0) type = id; - if (type == 0) return fail("WebSocket Error: client used opcode 0x00 for first fragment"); - - var buff = readData(); - if (buff == null) break; - - if (data.size() + buff.length > maxLength) return fail("WebSocket Error: client exceeded configured max message size"); - data.write(buff); - - if (!fin) continue; - var raw = data.toByteArray(); - - if (type == 1) { - return new WebSocketMessage(new String(raw)); - } - else return new WebSocketMessage(raw); - } - - return null; - } - - public WebSocket(Socket socket) { - this.socket = socket; - } -} diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/WebSocketMessage.java b/repl/src/main/java/me/topchetoeu/j2s/repl/debug/WebSocketMessage.java deleted file mode 100644 index c3c5a83..0000000 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/WebSocketMessage.java +++ /dev/null @@ -1,29 +0,0 @@ -package me.topchetoeu.j2s.repl.debug; - -public class WebSocketMessage { - public static enum Type { - Text, - Binary, - } - - public final Type type; - private final Object data; - - public final String textData() { - if (type != Type.Text) throw new IllegalStateException("Message is not text."); - return (String)data; - } - public final byte[] binaryData() { - if (type != Type.Binary) throw new IllegalStateException("Message is not binary."); - return (byte[])data; - } - - public WebSocketMessage(String data) { - this.type = Type.Text; - this.data = data; - } - public WebSocketMessage(byte[] data) { - this.type = Type.Binary; - this.data = data; - } -}