diff options
Diffstat (limited to 'chapter18/src/main/java/com')
4 files changed, 156 insertions, 2 deletions
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<Node> 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<len; i++ ) + _str[i] = (byte)(rawget() ^ _key[i & 3]); + + return new String(_str,0,len); + } + // Client closes + case 8: return null; + // Not (yet) implemented: continuation frames, ping/pong + default: + throw Utils.TODO("Unexpected op from client: "+op); // Multi-part message + } + } + + // Classic put to browser client. No encoding. + public void put(String msg) throws IOException { + assert msg.length() < 125; + _out.write(129); // FIN + _out.write(msg.length()); + for( int i=0; i<msg.length(); i++ ) + _out.write(msg.charAt(i)); + } + + @Override public void close() throws IOException { + _out.write(0b10001000); + _out.flush(); + super.close(); + } +} |
