Add web client
This commit is contained in:
parent
6ee3602128
commit
328be35ae2
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