<?xml version='1.0' encoding='utf-8' ?>
<rss version='2.0' xmlns:dc='http://purl.org/dc/elements/1.1/'>
  <channel>
    <title>Wolfmans Howlings: A TCP socket bridge for Javascript</title>
    <link>http://blog.wolfman.com/articles/2009/9/12/a-tcp-socket-bridge-for-javascript</link>
    <description>A programmers Blog about Ruby, Rails and a few other issue</description>
    <language>en-us</language>
    <ttl>40</ttl>
    <item>
      <title>A TCP socket bridge for Javascript</title>
      <description>
        &lt;p&gt;In the process of rewriting my voice server in
        &lt;a href=&quot;http://blog.wolfman.com/articles/2009/4/12/using-erlang-with-jinterface&quot;&gt;Erlang&lt;/a&gt;,
        I decided a new web-based Javascript UI would also be welcome. One problem of
        course is my voice server requires a TCP connection and a UDP socket
        for sending voice, not to mention the whole voice capture, playback
        thing.&lt;/p&gt;
        
        &lt;p&gt;In order for this to work the voice and playback would need to be
           written in a Java Applet, so it made sense to put the whole TCP/UDP
           communications stuff in there too.&lt;/p&gt;
        
        &lt;p&gt;Firefox uses
        &lt;a href=&quot;https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/LiveConnect_Overview&quot;&gt;Liveconnect&lt;/a&gt;
        in its Javascript that allows Javascript to call methods in a Java
        Applet directly, and also for Java to call Javascript functions, as
        Javascript has no way of opening an arbitrary TCP socket that would
        have to be in the Java Applet.&lt;/p&gt;
        
        &lt;p&gt;One thing that seems to not be documented in the Livescript docs, is
        that the Java method that is called directly from Javascript is not
        run with any privileges at all, including the one that allows TCP
        connections back to the same server that served up the Applet, or any
        privileges that a signed Jar would have. So if you try to call an open
        socket from javascript you will get the dreaded error:
        &lt;code&gt;java.security.AccessControlException: access denied (java.net.SocketPermission 127.0.0.1:3000 connect,resolve)&lt;/code&gt;&lt;/p&gt;
        
        &lt;p&gt;I found by Googling that a workaround is to run a thread from the Java
        Applet start() method, and pass any commands that require privileges
        to that thread. This thread must be started from the &lt;code&gt;Applet.start()&lt;/code&gt;
        method, not from any method called from Javascript. However this will
        not work if the page is opened as a file, only if it is served over
        http.&lt;/p&gt;
        
        &lt;p&gt;I came across many clever methods of doing this in my research, one
          was to pass a Runnable into this thread, so you can have that thread
          run arbitrary Java code. However I wanted something a little simpler
          as for now I would do a TCP socket bridge, which only required three
          calls, Open, Close and Write. There was no need to pass in a
          Runnable to do that. The Open needed to be a synchronous call, the
          close and write could be asynchronous.&lt;/p&gt;
        
        &lt;p&gt;The relatively simple solution I came up with using the concurrency
        libraries in recent versions of Java is shown here, other versions can
        be found on the web.&lt;/p&gt;
        
        &lt;p&gt;After starting up the Communication Thread from the Applet start(), I
        pass in one of the commands I want it to execute, I use the
        SynchonousQueue class, to make sure only one command can be executed
        at a time, and in the case of open() I use another SynchonousQueue to
        return the results. This makes the open() call totally synchronous and
        will block (and hang the browser) until either the socket is opened,
        fails to open, or a timeout occurs.&lt;/p&gt;
        
        &lt;p&gt;I am writing JSON strings to the server as that provides a nice way to
          do a simple RPC protocol that can be easily generated in Javascript,
          and I return JSON as again Javascript can handle that easily. As the
          server is Erlang I use &lt;code&gt;{packet, 2}&lt;/code&gt; in the TCP socket meaning each
          packet is preceded by the BigEndian count of the size of the packet,
          this gets over the need to make sure the entire JSON has been
          received before decoding it.&lt;/p&gt;
        
        &lt;p&gt;If you do not want to sign your applet in a jar then you need to only
           connect to the same server the applet is served from, so the code
           allows for open to only specify the port, and it figures out the
           host name. There is also an open call that specifies an arbitrary
           host, but for this to work you need to sign the jar. (In my case I
           will need to sign the jar eventually as the capture audio calls
           require special privileges). There are plenty of howtos on the web
           about self-signing jars so I won't go into it here.&lt;/p&gt;
        
        &lt;p&gt;Onto the code...&lt;/p&gt;
        
        &lt;pre&gt;package com.e4net.socketbridge;
        
        import java.applet.Applet;
        import java.io.BufferedOutputStream;
        import java.io.InputStream;
        import java.io.OutputStream;
        import java.net.Socket;
        import java.net.URL;
        import java.nio.ByteBuffer;
        import java.nio.ByteOrder;
        import java.util.concurrent.SynchronousQueue;
        import java.util.concurrent.TimeUnit;
        
        import netscape.javascript.JSObject;
        
        /**
         * @author Jim Morris, e4 Networks LLC
         * @license public domain
         */
        public class E4SocketBridge extends Applet
        {
            private static final long serialVersionUID = 1L;
        
            private JSObject win;
        
            private SynchronousQueue&amp;lt;Work&amp;gt; workQueue= new SynchronousQueue&amp;lt;Work&amp;gt;();
            private SynchronousQueue&amp;lt;Boolean&amp;gt; resultQueue= new SynchronousQueue&amp;lt;Boolean&amp;gt;();
        
            public enum WorkType{OPEN, CLOSE, WRITE};
        
            public void init() {
                win = JSObject.getWindow(this);
            }
        
            // an item of work passed into the comm thread
            protected class Work {
                private WorkType command;
                private Object data;
                private Work(WorkType command, Object data) {
                    this.command = command;
                    this.data = data;
                }
                public WorkType getCommand() {
                    return command;
                }
                public Object getData() {
                    return data;
                }
            }
        
            // Thread that reads incomign data from server and sens it asynchronously to Javascript
            private class ReadThread implements Runnable {
                private JSObject win;
                private InputStream ins;
                byte [] cntbuf= new byte[2];
                byte [] buf= new byte[65536];
        
                public ReadThread(JSObject win, InputStream ins) {
                    this.win= win;
                    this.ins= ins;
                }
        
                // reads all the bytes specified into the given buffer
                private boolean getBytes(byte [] data, int len){
                    int cnt= 0;
                    int t;
                    while(len &amp;gt; cnt){
                        try {
                            t= ins.read(data, cnt, len-cnt);
                        } catch (Exception ex) {
                            return false;
                        }
        
                        if(t &amp;lt; 0){
                            return false;
                        }
                        cnt += t;
                    }
                    return true;
                }
        
                public void run() {
                    try {
                        // read input and notify javascript
                        // all packets are prepended with big-endian 2 byte packet length, read the whole thing before sending a string to JS 
                        boolean running= true;
        
                        while(running){
                            try {
                                if(!getBytes(cntbuf, 2)){ // read in header count
                                    running= false;                         
                                    break;
                                }
                                int count= be_short(cntbuf); // bigendian 2 byte count
                                System.out.println(&amp;quot;got count: &amp;quot; + count);    
                                if(!getBytes(buf, count)){
                                    running= false;                         
                                    break;
                                }
                                Object [] arg= new Object[1];
                                arg[0]= new String(buf, 0, count);
                                win.call(&amp;quot;got_data&amp;quot;, arg);
        
                            } catch (Exception e) {
                                running= false;
                            }
                        }
        
                        Object[] arg= new Object[1];
                        arg[0]= &amp;quot;closed&amp;quot;;
                        win.call(&amp;quot;closed&amp;quot;, arg);
                    } catch(Exception ex) { }
                }
        
                // converts two bytes into a bigendian unsigned short
                private int be_short(byte[] buf) {
                    int l1= (int)buf[0]&amp;amp;255;
                    int l2= (int)buf[1]&amp;amp;255;
                    return ((l1 &amp;lt;&amp;lt; 8) + l2);
                }
            }
        
            private Thread commThread;
            private CommRun commRun;
        
            // open the socket in a thread so signed jar applet privs work
            private class CommRun implements Runnable {
                private String host;
                private int port;
                private JSObject win;
                private Socket sock;
                private InputStream ins;
                private OutputStream outs;
                private BufferedOutputStream bos;
                private Thread readThread;
                private ByteBuffer bb;
        
                public CommRun(JSObject w){
                    this.win= w;
                    readThread= null;
                    // default is to open to the host the applet was loaded from
                    URL urlServer = getCodeBase();
                    host= urlServer.getHost();
                }
        
                void setHost(String host){
                    this.host= host;
                }
        
                void setPort(int port){
                    this.port= port;
                }
        
                private void open(){
                    System.out.println(&amp;quot;open &amp;quot; + host + &amp;quot;:&amp;quot; + port);    
        
                    boolean b;
                    try {
                        sock= new Socket(host, port);
                        ins = sock.getInputStream();
                        outs = sock.getOutputStream();
                        bos= new BufferedOutputStream(outs);
        
                        // Use a ByteBuffer to automatically convert shorts to bigendian
                        bb= ByteBuffer.allocate(65538); // worst case size
                        bb.order(ByteOrder.BIG_ENDIAN);
        
                        readThread= new Thread(new ReadThread (win, ins));
                        readThread.start();
                        b= true;
                    } catch(Exception ex) {
                        ex.printStackTrace();
                        b= false;
                    }
                    try {
                        resultQueue.offer(new Boolean(b), 10, TimeUnit.SECONDS);
                    } catch (InterruptedException e) {
                    }
                }
        
                /**
                 * Send data to socket
                 *  
                 * @param data to send, will be a single String
                 * @throws Exception 
                 * 
                 */
                private void send(String data) throws Exception {
                    bb.clear();
                    // prepend string with size of string
                    packString((String) data);
                    bb.flip();
                    bos.write(bb.array(), 0, bb.limit());   
                    bos.flush();
                }
        
                private void packString(String data) throws Exception {
                    byte [] buf= data.getBytes();
                    int len= buf.length;
                    if(len &amp;gt; 32767)
                        throw new Exception(&amp;quot;string too long&amp;quot;);
                    bb.putShort((short)len).put(buf);
                }
        
                private void close(){
                    System.out.println(&amp;quot;close&amp;quot;);
                    try { sock.close(); } catch(Exception e){}
                    if(readThread != null)
                        readThread.interrupt();
                }
        
                public void run() {
                    boolean running= true;
                    Work work= null;
        
                    while(running){
                        try {
                            work= workQueue.take();
                        } catch (InterruptedException e) {
                            running= false;
                            break;
                        }
        
                        switch (work.getCommand()) {
                        case OPEN:
                            open();
                            break;
        
                        case CLOSE:
                            close();
                            break;
        
                        case WRITE:
                            try {
                                send((String)work.getData());
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            break;
        
                        default:
                            break;
                        }
                    }
                }
        
            }
        
            // This is called when the Applet starts
            public void start() {
                System.out.println(&amp;quot;Applet started&amp;quot;);
                // start the communications thread that will run with the privilege of a Java Applet
                commRun= new CommRun(win);
                commThread= new Thread(commRun);
                commThread.start();
            }
        
            // The following methods are called from Javascript
        
            /**
             * Open a TCP socket to host:port or just port
             * Complicated because liveConnect (or the javascript to java bridge), does not run
             * any directly called methods under the signed jar privilege, so we need to signal the thread
             * which does run privileged to do the open.
             * We wait for the open to succeed or fail
             * 
             * @param host IP or host name
             * @param port port
             */
            public boolean open(String host, int port) {
                commRun.setHost(host);
                return open(port);
            }
        
            /**
             * Just open back to Host that served this Applet
             * Wait for the open to finish and return the result to Javascript caller
             * @param port
             * @return true if open succeeded else false
             */
            public boolean open(int port) {
                commRun.setPort(port);
                boolean b;
                try {
                    b= workQueue.offer(new Work(WorkType.OPEN, null), 10, TimeUnit.SECONDS);
                    if(b){
                        Boolean r= resultQueue.poll(10, TimeUnit.SECONDS);
                        b= (r == null) ? false : r.booleanValue();
                    } else
                        System.out.println(&amp;quot;Timed out waiting for open&amp;quot;);
                } catch (InterruptedException e) {
                    return false;
                }
                return b;
            }
        
            /**
             * write the String to the socket
             * @param data
             * @return true on success false on timeout or failure
             */
            public boolean write(Object data) {
                boolean b;
                try {
                    b= workQueue.offer(new Work(WorkType.WRITE, data), 10, TimeUnit.SECONDS);
                    if(!b)
                        System.out.println(&amp;quot;Timed out waiting for write&amp;quot;);
                } catch (Exception e) {
                    return false;
                }
                return b;
            }
        
            /**
             * Close the socket
             */
            public void close() {
                try {
                    workQueue.offer(new Work(WorkType.CLOSE, null), 10, TimeUnit.SECONDS);
                } catch (Exception e) {
                }
            }
        
        }
        &lt;/pre&gt;
        
        &lt;p&gt;The applet is loaded as the following html shows with a simple
        sample...&lt;/p&gt;
        
        &lt;pre&gt;&amp;lt;html&amp;gt;
        &amp;lt;head&amp;gt;
         &amp;lt;title&amp;gt;applet html page&amp;lt;/title&amp;gt;
         &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;e4socketbridge.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;/head&amp;gt;
        
        &amp;lt;body&amp;gt;
        
          Test opening a socket to localhost:3000
        
          &amp;lt;p&amp;gt;
          &amp;lt;applet name=&amp;quot;socketBridge&amp;quot; archive=&amp;quot;e4socketbridge.jar&amp;quot; code=&amp;quot;com.e4net.socketbridge.E4SocketBridge&amp;quot; width=1 height=1 mayscript=&amp;quot;true&amp;quot; scriptable=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/applet&amp;gt;
          &amp;lt;/p&amp;gt;
        
          &amp;lt;input type=&amp;quot;button&amp;quot; value=&amp;quot;Open&amp;quot; onclick=&amp;quot;openSocket()&amp;quot; /&amp;gt;
          &amp;lt;input type=&amp;quot;button&amp;quot; value=&amp;quot;Write string&amp;quot; onclick=&amp;quot;writeSocket('hello there')&amp;quot; /&amp;gt;
          &amp;lt;input type=&amp;quot;button&amp;quot; value=&amp;quot;Close&amp;quot; onclick=&amp;quot;closeSocket()&amp;quot; /&amp;gt;
        &amp;lt;p /&amp;gt;
        
        &amp;lt;/body&amp;gt;
        &lt;/pre&gt;
        
        &lt;p&gt;The javascript library that talks to the Java Applet is in e4socketbridge.js&lt;/p&gt;
        
        &lt;pre&gt;
        // open a TCP socket back to the server this was loaded from at port 3000
        function openSocket()
        {
          document.socketBridge.open(3000);
        }
        
        function writeSocket(data)
        {
          document.socketBridge.write(data);
        }
        
        function closeSocket()
        {
          document.socketBridge.close();
        }
        
        // called asynchronously from Java when the socket recieves a complete packet
        function got_data(ln)
        {
          alert(&amp;quot;Got data: &amp;quot; + ln);
        }
        
        // called asynchronously from Java when the socket closes
        function closed()
        {
          alert(&amp;quot;socket closed&amp;quot;);
        }
        
        &lt;/pre&gt;
        
        &lt;p&gt;Any incoming data from the server needs to be sent to the Javascript,
           to handle that I have a readThread that listens on the TCP socket
           for any incoming data, reads the entire packet based on the 2 byte
           header, then calls a Javascript function which would handle that
           event. I'm not exactly sure how Javascript handles asynchronous
           calls from different Java threads, but it seems to work OK, and
           there is some mention of Javascript being able to handle this kind
           of thing.&lt;/p&gt;
        
        &lt;p&gt;Note I put timeouts on any method that could hang, because the entire
           browser hangs when those calls into the Java Applet hang, and
           debugging that was ugly, having to kill off the browser everytime.&lt;/p&gt;
        
        &lt;p&gt;I use a 2 byte header so no packet can be bigger than 65k, this helps
          avoid DOS (Denial of Service) attacks where someone could tie up a
          process in the server by specifying it was going to send a huge
          packet, this could also cause the server to run out of memory. The
          downside is I have to manually break up anything that needs to send
          more than 65k. If you are not concerned about the potential DOS use
          {packet, 4} instead, and change it to send a 4 byte integer instead.&lt;/p&gt;
      </description>
      <author>Jim Morris</author>
      <pubDate>Sun, 13 Sep 2009 22:40:03 -0700</pubDate>
      <link>http://blog.wolfman.com/articles/2009/9/12/a-tcp-socket-bridge-for-javascript</link>
      <guid isPermaLink='false'>urn:uuid:a4222708-fa2c-47de-8ce4-e48a4641215d</guid>
    </item>
  </channel>
</rss>
