From fac0c965b11a103a1973153d40347557d9bf68d9 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Sun, 1 Dec 2024 11:42:13 -0800 Subject: Start on a graph viz animation of peeps --- .../com/seaofnodes/simple/GraphVisualizer.java | 2 +- .../main/java/com/seaofnodes/simple/JSViewer.java | 50 ++++++++++ .../main/java/com/seaofnodes/simple/Parser.java | 2 +- .../com/seaofnodes/simple/SimpleWebSocket.java | 104 +++++++++++++++++++++ 4 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 chapter18/src/main/java/com/seaofnodes/simple/JSViewer.java create mode 100644 chapter18/src/main/java/com/seaofnodes/simple/SimpleWebSocket.java (limited to 'chapter18/src/main') diff --git a/chapter18/src/main/java/com/seaofnodes/simple/GraphVisualizer.java b/chapter18/src/main/java/com/seaofnodes/simple/GraphVisualizer.java index de6d65a3..ec326a6f 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/GraphVisualizer.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/GraphVisualizer.java @@ -29,7 +29,7 @@ public class GraphVisualizer { // nodes in the graph. Collection all = findAll(stop, scope); StringBuilder sb = new StringBuilder(); - sb.append("digraph chapter11 {\n"); + sb.append("digraph chapter18 {\n"); sb.append("/*\n"); sb.append(stop._src); sb.append("\n*/\n"); diff --git a/chapter18/src/main/java/com/seaofnodes/simple/JSViewer.java b/chapter18/src/main/java/com/seaofnodes/simple/JSViewer.java new file mode 100644 index 00000000..da361afa --- /dev/null +++ b/chapter18/src/main/java/com/seaofnodes/simple/JSViewer.java @@ -0,0 +1,50 @@ +package com.seaofnodes.simple; + +import java.nio.file.Paths; + +public class JSViewer { + public static void main( String[] args ) throws Exception { + // Launch server + try( SimpleWebSocket server = new SimpleWebSocket(Paths.get("docs/index.html").toUri(),12345) ) { + int N=0; + while( true ) { + String src = server.get(); + if( src==null || src.equals("null") ) + break; + System.out.println(src); + // Client requests another frame + if( src.charAt(0)=='+' ) { + if( N<2 ) { + StringBuilder sb = new StringBuilder(); + sb.append("digraph jsviewer_1 {\n"); + sb.append("\tnode [shape=circle, style=filled];\n"); + sb.append("\tSRC [label=\"").append("next frame").append("\", shape=oval];\n"); + sb.append("}"); + server.put(sb.toString()); + System.out.println("Sent graph "+N); + N++; + } else { + // no more frames, send nothing + } + } else { + + StringBuilder sb = new StringBuilder(); + sb.append("digraph jsviewer_0 {\n"); + sb.append("\tnode [shape=circle, style=filled];\n"); + sb.append("\tSRC [label=\"").append(src).append("\", shape=oval];\n"); + sb.append("}"); + server.put(sb.toString()); + System.out.println("Sent graph "+N); + N++; + } + + //// Client reset, send new program + //server.put("!"); + } + System.out.println("Client requested shutdown"); + server.close(); + } + + } + +} diff --git a/chapter18/src/main/java/com/seaofnodes/simple/Parser.java b/chapter18/src/main/java/com/seaofnodes/simple/Parser.java index 1cdf27fb..94575559 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/Parser.java @@ -246,7 +246,7 @@ public class Parser { */ private Node parseFor() { // { var x=init,y=init,...; - // while( pred ) { + // while( test ) { // body; // next; // } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/SimpleWebSocket.java b/chapter18/src/main/java/com/seaofnodes/simple/SimpleWebSocket.java new file mode 100644 index 00000000..86ae7c9b --- /dev/null +++ b/chapter18/src/main/java/com/seaofnodes/simple/SimpleWebSocket.java @@ -0,0 +1,104 @@ +package com.seaofnodes.simple; + +import java.io.*; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; + +// Why-Oh-Why is this needed? + +// Pass in a URI and a port. +// Opens the client; client has to open a websocket at the given port. +// Connects, handshakes. +// Thereafter you can call get/put to swap frames with client. + +class SimpleWebSocket extends ServerSocket { + final InputStream _in; + final OutputStream _out; + + SimpleWebSocket(URI uri, int port) throws IOException, NoSuchAlgorithmException { + super(port); + // Launch client + java.awt.Desktop.getDesktop().browse(uri); + // Look for client; get in/out streams + Socket sock = accept(); + _in = sock. getInputStream(); + _out = sock.getOutputStream(); + + + // Establish web socket with handshake; easier with a buffered reader + BufferedReader br = new BufferedReader(new InputStreamReader(_in)); + String key = null; + while( br.ready() ) { + String line = br.readLine(); + if( line.startsWith("Sec-WebSocket-Key: ") ) + key = line.substring(19); + } + assert key!=null; + // Magic handshake + byte[] response = ("HTTP/1.1 101 Switching Protocols\r\n" + + "Connection: Upgrade\r\n" + + "Upgrade: websocket\r\n" + + "Sec-WebSocket-Accept: " + + Base64.getEncoder().encodeToString(MessageDigest.getInstance("SHA-1").digest((key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes( StandardCharsets.UTF_8 ))) + + "\r\n\r\n").getBytes( StandardCharsets.UTF_8 ); + _out.write(response,0,response.length); + } + + final byte[] _ins = new byte[1024]; + final byte[] _str = new byte[1024]; + final byte[] _key = new byte[4]; + int _x, _len; + private int rawget() { assert _x<_len; return _ins[_x++]&0xFF; } + + // Classic read from browser client. Encoded, so decoded here + public String get() throws IOException { + _len = _in.read(_ins); _x=0; + int op = rawget(); + if( (op&0x80)!=0x80 ) // FIN bit not set? + throw Utils.TODO("Multi-part from client: "+op); // Multi-part message + op &= 0x7F; // Strip FIN bit + switch( op&15) { + case 1: { + // Message length + int len = (rawget()&0xFF)-128; + if( len > 125 ) + throw Utils.TODO(); // Multi-byte length + // Encoding key + _key[0] = (byte)rawget(); + _key[1] = (byte)rawget(); + _key[2] = (byte)rawget(); + _key[3] = (byte)rawget(); + // Decode bytes + for( int i=0; i