Add web client
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
/client/web/lrpc.js
 | 
			
		||||
/s
 | 
			
		||||
/c
 | 
			
		||||
							
								
								
									
										3
									
								
								client/web/Gemfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								client/web/Gemfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
source 'https://rubygems.org'
 | 
			
		||||
 | 
			
		||||
gem 'ruby2js'
 | 
			
		||||
							
								
								
									
										19
									
								
								client/web/Gemfile.lock
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								client/web/Gemfile.lock
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
GEM
 | 
			
		||||
  remote: https://rubygems.org/
 | 
			
		||||
  specs:
 | 
			
		||||
    ast (2.4.2)
 | 
			
		||||
    parser (3.1.2.0)
 | 
			
		||||
      ast (~> 2.4.1)
 | 
			
		||||
    regexp_parser (2.1.1)
 | 
			
		||||
    ruby2js (5.0.1)
 | 
			
		||||
      parser
 | 
			
		||||
      regexp_parser (~> 2.1.1)
 | 
			
		||||
 | 
			
		||||
PLATFORMS
 | 
			
		||||
  x86_64-linux
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES
 | 
			
		||||
  ruby2js
 | 
			
		||||
 | 
			
		||||
BUNDLED WITH
 | 
			
		||||
   2.3.15
 | 
			
		||||
							
								
								
									
										3
									
								
								client/web/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								client/web/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
lrpc.js: convert.rb lrpc.rb
 | 
			
		||||
	bundle install
 | 
			
		||||
	ruby convert.rb > lrpc.js
 | 
			
		||||
							
								
								
									
										6
									
								
								client/web/convert.rb
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										6
									
								
								client/web/convert.rb
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
#!/usr/bin/ruby
 | 
			
		||||
 | 
			
		||||
require 'ruby2js'
 | 
			
		||||
require 'ruby2js/filter/functions'
 | 
			
		||||
 | 
			
		||||
puts Ruby2JS.convert(File.read('lrpc.rb'), eslevel: 2016)
 | 
			
		||||
							
								
								
									
										150
									
								
								client/web/lrpc.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								client/web/lrpc.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,150 @@
 | 
			
		||||
# LRPCResponseType represents the various types an LRPC
 | 
			
		||||
# response can have.
 | 
			
		||||
LRPCResponseType = {
 | 
			
		||||
  Normal: 0,
 | 
			
		||||
  Error: 1,
 | 
			
		||||
  Channel: 2,
 | 
			
		||||
  ChannelDone: 3,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# LRPCClient represents a client for the LRPC protocol
 | 
			
		||||
# using WebSockets and the JSON codec
 | 
			
		||||
class LRPCClient
 | 
			
		||||
  def initialize(addr)
 | 
			
		||||
    # Set self variables
 | 
			
		||||
    @callMap = Map.new()
 | 
			
		||||
    @enc = TextEncoder.new()
 | 
			
		||||
    @dec = TextDecoder.new()
 | 
			
		||||
    
 | 
			
		||||
    # Create connection to lrpc server
 | 
			
		||||
    @conn = WebSocket.new(addr)
 | 
			
		||||
    @conn.binaryType = "arraybuffer"
 | 
			
		||||
    @conn.onmessage = proc do |msg|
 | 
			
		||||
      # if msg.data is string
 | 
			
		||||
      if msg.data.instance_of? String
 | 
			
		||||
        # Set json to msg.data
 | 
			
		||||
        json = msg.data
 | 
			
		||||
      else
 | 
			
		||||
        # Set json to decoded msg.data
 | 
			
		||||
        json = @dec.decode(msg.data)
 | 
			
		||||
      end
 | 
			
		||||
      # Parse JSON string
 | 
			
		||||
      val = JSON.parse(json)
 | 
			
		||||
      # Get id from callMap
 | 
			
		||||
      fns = @callMap.get(val.ID)
 | 
			
		||||
      # If fns is undefined (key does not exist), and this is
 | 
			
		||||
      # a normal response, return
 | 
			
		||||
      return if !fns && val.Type == LRPCResponseType.Normal
 | 
			
		||||
 | 
			
		||||
      case val.Type
 | 
			
		||||
      when LRPCResponseType.Normal
 | 
			
		||||
        # If fns is a channel, send the value. Otherwise,
 | 
			
		||||
        # resolve the promise with the value.
 | 
			
		||||
        if fns.isChannel
 | 
			
		||||
          fns.send(val.Return)
 | 
			
		||||
        else
 | 
			
		||||
          fns.resolve(val.Return)
 | 
			
		||||
        end
 | 
			
		||||
      when LRPCResponseType.Channel
 | 
			
		||||
        # Get channel ID from response
 | 
			
		||||
        chID = val.Return
 | 
			
		||||
        # Create new LRPCChannel
 | 
			
		||||
        ch = LRPCChannel.new(self, chID)
 | 
			
		||||
        # Set channel in map
 | 
			
		||||
        @callMap.set(chID, ch)
 | 
			
		||||
        # Resolve promise with channel
 | 
			
		||||
        fns.resolve(ch)
 | 
			
		||||
      when LRPCResponseType.ChannelDone
 | 
			
		||||
        # Close and delete channel
 | 
			
		||||
        fns.close()
 | 
			
		||||
        @callMap.delete(val.ID)
 | 
			
		||||
      when LRPCResponseType.Error
 | 
			
		||||
        # Reject promise with error
 | 
			
		||||
        fns.reject(val.Error)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # Delete item from map unless it is a channel
 | 
			
		||||
      @callMap.delete(val.ID) unless fns.isChannel
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
  # call calls a method on the server with the given
 | 
			
		||||
  # argument and returns a promise.
 | 
			
		||||
  def call(rcvr, method, arg)
 | 
			
		||||
    return Promise.new do |resolve, reject|
 | 
			
		||||
      # Get random UUID (this only works with TLS)
 | 
			
		||||
      id = crypto.randomUUID()
 | 
			
		||||
      # Add resolve/reject functions to callMap
 | 
			
		||||
      @callMap.set(id, {
 | 
			
		||||
        resolve: resolve,
 | 
			
		||||
        reject: reject,
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      # Encode data as JSON
 | 
			
		||||
      data = @enc.encode({
 | 
			
		||||
        Receiver: rcvr,
 | 
			
		||||
        Method: method,
 | 
			
		||||
        Arg: arg,
 | 
			
		||||
        ID: id,
 | 
			
		||||
      }.to_json())
 | 
			
		||||
 | 
			
		||||
      # Send data to lrpc server
 | 
			
		||||
      @conn.send(data.buffer)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
# LRPCChannel represents a channel used for lrpc.
 | 
			
		||||
class LRPCChannel
 | 
			
		||||
  def initialize(client, id)
 | 
			
		||||
    # Set self variables
 | 
			
		||||
    @client = client
 | 
			
		||||
    @id = id
 | 
			
		||||
    @arr = []
 | 
			
		||||
    # Set function variables to no-ops
 | 
			
		||||
    @onMessage = proc {|fn|}
 | 
			
		||||
    @onClose = proc {}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # isChannel is defined to allow identifying whether
 | 
			
		||||
  # an object is a channel.
 | 
			
		||||
  def isChannel() end
 | 
			
		||||
 | 
			
		||||
  # send sends a value on the channel. This should not
 | 
			
		||||
  # be called by the consumer of the channel.
 | 
			
		||||
  def send(val)
 | 
			
		||||
    @arr.push(val)
 | 
			
		||||
    fn = @onMessage
 | 
			
		||||
    fn(val)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # done cancels the context corresponding to the channel
 | 
			
		||||
  # on the server side and closes the channel.
 | 
			
		||||
  def done()
 | 
			
		||||
    @client.call("lrpc", "ChannelDone", @id)
 | 
			
		||||
    self.close()
 | 
			
		||||
    @client._callMap.delete(@id)
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
  # onMessage sets the callback to be called whenever a
 | 
			
		||||
  # message is received. The function should have one parameter
 | 
			
		||||
  # that will be set to the value received. Subsequent calls
 | 
			
		||||
  # will overwrite the callback
 | 
			
		||||
  def onMessage(fn)
 | 
			
		||||
    @onMessage = fn
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # onClose sets the callback to be called whenever the client
 | 
			
		||||
  # is closed. The function should have no parameters. 
 | 
			
		||||
  # Subsequent calls will overwrite the callback
 | 
			
		||||
  def onClose(fn)
 | 
			
		||||
    @onClose = fn
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # close closes the channel. This should not be called by the
 | 
			
		||||
  # consumer of the channel. Use done() instead.
 | 
			
		||||
  def close()
 | 
			
		||||
    fn = @onClose
 | 
			
		||||
    fn()
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
		Reference in New Issue
	
	Block a user