2020-07-04 22:31:17 +02:00
|
|
|
require "http/web_socket"
|
|
|
|
require "option_parser"
|
|
|
|
|
2020-10-01 17:43:32 +02:00
|
|
|
require "ipc"
|
2020-11-02 02:32:34 +01:00
|
|
|
require "baguette-crystal-base"
|
2020-07-04 22:31:17 +02:00
|
|
|
require "./utils"
|
2020-10-01 17:43:32 +02:00
|
|
|
require "./lib_modifications.cr"
|
2020-07-04 22:31:17 +02:00
|
|
|
|
|
|
|
|
2020-11-02 02:32:34 +01:00
|
|
|
# Websocketc
|
|
|
|
# - is the application allowing libipc communications through Web Socket
|
|
|
|
# - is designed to connect to a Websocketd server, ask for a service, then exchange data
|
|
|
|
# - is WIP
|
|
|
|
|
|
|
|
# Currently, the exchange format is JSON, encapsulated in JSON:
|
|
|
|
# type: Int32 # type of the message
|
|
|
|
# payload: JSON::Any? # encapsulated payload
|
|
|
|
#
|
|
|
|
# Websocketd deserialize the type and the payload before sending it to the service.
|
|
|
|
|
2020-10-01 17:43:32 +02:00
|
|
|
|
2020-11-02 02:32:34 +01:00
|
|
|
module Websocketc
|
|
|
|
class Exception < ::Exception
|
2020-07-04 22:31:17 +02:00
|
|
|
end
|
|
|
|
|
2020-11-02 02:32:34 +01:00
|
|
|
class Context
|
|
|
|
class_property uri = "ws://localhost:1234/pong"
|
|
|
|
class_property rounds = 1
|
2020-07-04 22:31:17 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-11-02 02:32:34 +01:00
|
|
|
class Baguette::Configuration
|
|
|
|
class Websocketc < Base
|
|
|
|
property verbosity : Int32 = 2
|
|
|
|
property print_ipc_timer : Bool = false
|
|
|
|
property ipc_timer : Int32 = 30_000
|
2020-07-04 22:31:17 +02:00
|
|
|
|
2020-11-02 02:32:34 +01:00
|
|
|
def initialize
|
|
|
|
end
|
|
|
|
end
|
2020-07-04 22:31:17 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
|
2020-11-02 02:32:34 +01:00
|
|
|
require "./network.cr"
|
2020-07-04 22:31:17 +02:00
|
|
|
|
|
|
|
|
2020-11-02 02:32:34 +01:00
|
|
|
class Websocketc::Service < IPC::Server
|
|
|
|
property connections : Hash(Int32, WebSocket) = {} of Int32 => WebSocket
|
|
|
|
getter all_fd : Array(Int32) = Array(Int32).new
|
2020-07-04 22:31:17 +02:00
|
|
|
|
2020-11-02 02:32:34 +01:00
|
|
|
property config : Baguette::Configuration::Websocketc
|
2020-07-04 22:31:17 +02:00
|
|
|
|
2020-11-02 02:32:34 +01:00
|
|
|
def initialize(@config : Baguette::Configuration::Websocketc)
|
|
|
|
super "websocketc"
|
2020-07-04 22:31:17 +02:00
|
|
|
end
|
|
|
|
|
2020-11-02 02:32:34 +01:00
|
|
|
def handle_request(event : IPC::Event::MessageReceived)
|
|
|
|
request_start = Time.utc
|
|
|
|
|
|
|
|
# TODO: The message can come from an already linked fd.
|
|
|
|
|
|
|
|
request = Websocketc.requests.parse_ipc_json event.message
|
|
|
|
|
|
|
|
if request.nil?
|
|
|
|
raise "unknown request type"
|
|
|
|
end
|
|
|
|
|
|
|
|
request_name = request.class.name.sub /^Websocketc::Request::/, ""
|
|
|
|
Baguette::Log.debug "<< #{request_name}"
|
|
|
|
|
|
|
|
response = begin
|
|
|
|
request.handle self, event
|
|
|
|
rescue e : Websocketc::Exception
|
|
|
|
Baguette::Log.error "Websocketc::Exception: #{request_name} => #{e}"
|
|
|
|
Websocketc::Response::Error.new "generic error"
|
|
|
|
rescue e
|
|
|
|
Baguette::Log.error "#{request_name} generic error #{e}"
|
|
|
|
Websocketc::Response::Error.new "unknown error"
|
|
|
|
end
|
|
|
|
|
|
|
|
# If clients sent requests with an “id” field, it is copied
|
|
|
|
# in the responses. Allows identifying responses easily.
|
|
|
|
response.id = request.id
|
|
|
|
|
|
|
|
send event.fd, response
|
|
|
|
|
|
|
|
duration = Time.utc - request_start
|
|
|
|
|
|
|
|
response_name = response.class.name.sub /^Websocketc::Response::/, ""
|
|
|
|
|
|
|
|
if response.is_a? Websocketc::Response::Error
|
|
|
|
Baguette::Log.warning ">> #{response_name} (#{response.reason})"
|
|
|
|
else
|
|
|
|
Baguette::Log.debug ">> #{response_name} (Total duration: #{duration})"
|
|
|
|
end
|
2020-10-01 17:43:32 +02:00
|
|
|
end
|
|
|
|
|
2020-11-02 02:32:34 +01:00
|
|
|
def run
|
|
|
|
Baguette::Log.title "Starting websocketc"
|
|
|
|
|
|
|
|
self.loop do |event|
|
|
|
|
begin
|
|
|
|
case event
|
|
|
|
when IPC::Event::Timer
|
|
|
|
Baguette::Log.debug "Timer" if @config.print_ipc_timer
|
|
|
|
|
|
|
|
when IPC::Event::Connection
|
|
|
|
Baguette::Log.debug "connection from #{event.fd}"
|
|
|
|
@all_fd << event.fd
|
|
|
|
|
|
|
|
when IPC::Event::Disconnection
|
|
|
|
Baguette::Log.debug "disconnection from #{event.fd}"
|
|
|
|
@all_fd.select! &.!=(event.fd)
|
|
|
|
connections.delete event.fd
|
|
|
|
|
|
|
|
when IPC::Event::MessageSent
|
|
|
|
Baguette::Log.debug "message sent to #{event.fd}"
|
|
|
|
|
|
|
|
when IPC::Event::MessageReceived
|
|
|
|
Baguette::Log.debug "message received from #{event.fd}"
|
|
|
|
handle_request event
|
|
|
|
|
|
|
|
when IPC::Exception
|
|
|
|
Baguette::Log.warning "IPC::Exception: #{event.message}"
|
|
|
|
|
|
|
|
else
|
|
|
|
Baguette::Log.warning "unhandled IPC event: #{event.class}"
|
|
|
|
end
|
|
|
|
rescue e
|
|
|
|
Baguette::Log.error "exception: #{typeof(e)} - #{e.message}"
|
|
|
|
end
|
|
|
|
end
|
2020-07-04 22:31:17 +02:00
|
|
|
end
|
|
|
|
|
2020-11-02 02:32:34 +01:00
|
|
|
def self.from_cli
|
|
|
|
|
|
|
|
# First option parsing.
|
|
|
|
simulation, no_configuration, configuration_file = Baguette::Configuration.option_parser
|
|
|
|
|
|
|
|
# Websocketc configuration.
|
|
|
|
configuration = if no_configuration
|
|
|
|
Baguette::Log.info "do not load a configuration file."
|
|
|
|
Baguette::Configuration::Websocketc.new
|
|
|
|
else
|
|
|
|
# In case there is a configuration file helping with the parameters.
|
|
|
|
Baguette::Configuration::Websocketc.get(configuration_file) ||
|
|
|
|
Baguette::Configuration::Websocketc.new
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
OptionParser.parse do |parser|
|
|
|
|
parser.on "-u uri", "--uri uri", "URI" do |opturi|
|
|
|
|
Websocketc::Context.uri = opturi
|
|
|
|
end
|
|
|
|
|
|
|
|
parser.on "-r rounds", "--rounds nb-messages", "Nb messages to send." do |opt|
|
|
|
|
Websocketc::Context.rounds = opt.to_i
|
|
|
|
end
|
|
|
|
|
|
|
|
parser.on "-h", "--help", "Show this help" do
|
|
|
|
puts parser
|
|
|
|
exit 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if simulation
|
|
|
|
pp! configuration
|
|
|
|
exit 0
|
|
|
|
end
|
|
|
|
|
|
|
|
::Websocketc::Service.new configuration
|
|
|
|
end
|
2020-07-04 22:31:17 +02:00
|
|
|
end
|
2020-11-02 02:32:34 +01:00
|
|
|
|
|
|
|
Websocketc::Service.from_cli.run
|