diff --git a/src/debuggingws.cr b/src/debuggingws.cr index 20a622a..c014fff 100644 --- a/src/debuggingws.cr +++ b/src/debuggingws.cr @@ -35,6 +35,7 @@ class WrappedTCPFileDescriptor < TCPSocket end service_name = "websocket" +domain = "localhost" port_to_listen = 1234 OptionParser.parse! do |parser| @@ -42,6 +43,10 @@ OptionParser.parse! do |parser| port_to_listen = port.to_u16 end + parser.on "-d domain", "--domain domain", "Domain to listen on (example: localhost)." do |dom| + domain = dom + end + parser.on "-s service-name", "--service-name service-name", "Service name." do |name| service_name = name end @@ -61,6 +66,8 @@ class InstanceStorage property switchtable : Hash(Int32, Int32) property connectionlist : Hash(Int32, IPC::Connection) + property alreadyconnected : Hash(Int32, Bool) + def initialize (@service : IPC::SwitchingService) # fdlist_client = [] of TCPSocket @fdlist = Hash(Int32,String).new @@ -68,6 +75,7 @@ class InstanceStorage @wssocklist = Hash(Int32, WebSocket).new @switchtable = Hash(Int32, Int32).new @connectionlist = Hash(Int32, IPC::Connection).new + @alreadyconnected = Hash(Int32, Bool).new end def remove_fd (fdclient : Int32) @@ -102,7 +110,7 @@ class InstanceStorage end end -server = TCPServer.new("localhost", port_to_listen) +server = TCPServer.new(domain, port_to_listen) service = IPC::SwitchingService.new service_name service << server.fd context = InstanceStorage.new service @@ -154,14 +162,19 @@ def websocket_client_connection(client, context : InstanceStorage) # registering the client into storing structures to avoid being garbage collected context.socklist[client.fd] = client context.wssocklist[client.fd] = wsclient + + context.alreadyconnected[client.fd] = false end def closing_client (fdclient : Int, context : InstanceStorage) # puts "closing the client #{fdclient}" context.remove_fd fdclient + context.alreadyconnected[fdclient] = false end +puts "listening under the service name #{service_name} and url ws://#{domain}:#{port_to_listen}/" + service.loop do |event| case event when IPC::Event::Connection @@ -203,7 +216,17 @@ service.loop do |event| next end - print_hexa(String.new(message), "#{CBLUE}Received message hexa#{CRESET}") + # TODO: should send json values soon + if context.alreadyconnected[activefd] + print_hexa(String.new(message), "#{CBLUE}Received message hexa#{CRESET}") + wsclient.send String.new(message) + else + print_hexa(String.new(message), "#{CBLUE}Received message hexa#{CRESET}") + context.alreadyconnected[activefd] = true + puts "then send OK" + wsclient.send "OK" + end + when IPC::Event::Switch puts "\033[36mIPC::Event::Switch#{CRESET}: from fd #{event.connection.fd}" diff --git a/src/pongd.cr b/src/pongd.cr index 23727f2..b9cafe9 100644 --- a/src/pongd.cr +++ b/src/pongd.cr @@ -1,7 +1,22 @@ +require "option_parser" require "ipc" require "./colors" -IPC::Service.new ("pong") do |event| +service_name = "pong" + +OptionParser.parse! do |parser| + parser.on "-s service_name", "--service-name service_name", "URI" do |optsn| + service_name = optsn + end + + parser.on "-h", "--help", "Show this help" do + puts parser + exit 0 + end +end + + +IPC::Service.new (service_name) do |event| case event when IPC::Event::Connection puts "#{CBLUE}IPC::Event::Connection#{CRESET}, client: #{event.connection.fd}" diff --git a/src/websocketd.cr b/src/websocketd.cr index 4098358..bda39ea 100644 --- a/src/websocketd.cr +++ b/src/websocketd.cr @@ -4,6 +4,8 @@ require "socket" require "./colors" require "./ws" +require "json" + require "socket" require "http" require "http/server" @@ -55,25 +57,27 @@ end class InstanceStorage property service : IPC::SwitchingService - property is_client : Hash(Int32,Bool) property switchtable : Hash(Int32, Int32) - property fdtotcpsocket : Hash(Int32, TCPSocket) - property fdtows : Hash(Int32, WebSocket) - property fdtoipcconnection : Hash(Int32, IPC::Connection) + property is_client : Hash(Int32,Bool) + property is_json : Hash(Int32, Bool) + property fd_to_tcpsocket : Hash(Int32, TCPSocket) + property fd_to_websocket : Hash(Int32, WebSocket) + property fd_to_ipcconnection : Hash(Int32, IPC::Connection) def initialize (@service : IPC::SwitchingService) # fdlist_client = [] of TCPSocket - @is_client = Hash(Int32,Bool).new - @fdtotcpsocket = Hash(Int32, TCPSocket).new - @fdtows = Hash(Int32, WebSocket).new @switchtable = Hash(Int32, Int32).new - @fdtoipcconnection = Hash(Int32, IPC::Connection).new + @is_client = Hash(Int32,Bool).new + @is_json = Hash(Int32,Bool).new + @fd_to_tcpsocket = Hash(Int32, TCPSocket).new + @fd_to_websocket = Hash(Int32, WebSocket).new + @fd_to_ipcconnection = Hash(Int32, IPC::Connection).new end def remove_fd (fdclient : Int32) # 1. closing both the client and the service fdservice = @switchtable[fdclient]? - tcpfdc = @fdtotcpsocket[fdclient] + tcpfdc = @fd_to_tcpsocket[fdclient] # 2. closing the TCP connections tcpfdc.close unless tcpfdc.closed? @@ -88,12 +92,13 @@ class InstanceStorage # 6. removing the client and the service from is_client @is_client = @is_client.select do |fd,v| fd != fdclient end + @is_json = @is_json.select do |fd,v| fd != fdclient end unless fdservice.nil? - service = @fdtoipcconnection[fdservice] + service = @fd_to_ipcconnection[fdservice] service.close @service.remove_fd (fdservice) - @fdtoipcconnection = @fdtoipcconnection.select do |k, v| + @fd_to_ipcconnection = @fd_to_ipcconnection.select do |k, v| k != fdservice end @@ -144,14 +149,23 @@ def websocket_client_connection(client, context : InstanceStorage) return end + # The client may ask to transcript JSON-based messages into IPC messages. + # To that end, the client may send the name of the service it wants to reach with the prefix ".JSON". + + if req_service.ends_with? ".JSON" + context.is_json[client.fd] = true + req_service = req_service.gsub /.JSON$/, "" + else + context.is_json[client.fd] = false + end + + websocket_connection_procedure req_service, client.fd, context # puts "#{headers_header}\n#{headers.to_s}\r\n" client.send "#{headers_header}\n#{headers.to_s}\r\n" wsclient = WebSocket.new client - - # the client is still not connected to a service context.is_client[client.fd] = true # listen to the client's file descriptor @@ -159,8 +173,8 @@ def websocket_client_connection(client, context : InstanceStorage) # puts "#{CBLUE}new client: #{client.fd}#{CRESET}" # registering the client into storing structures to avoid being garbage collected - context.fdtotcpsocket[client.fd] = client - context.fdtows[client.fd] = wsclient + context.fd_to_tcpsocket[client.fd] = client + context.fd_to_websocket[client.fd] = wsclient end @@ -178,7 +192,7 @@ def websocket_connection_procedure (requested_service : String, clientfd : Int32 begin # 2. establishing a connection to the service newservice = IPC::Connection.new requested_service - context.fdtoipcconnection[newservice.fd] = newservice + context.fd_to_ipcconnection[newservice.fd] = newservice # 3. listening on the client fd and the service fd context.service << newservice.fd @@ -200,14 +214,31 @@ def websocket_connection_procedure (requested_service : String, clientfd : Int32 end end +class JSONWSMessage + JSON.mapping( + mtype: Int32, + payload: String + ) + + def initialize (@mtype : Int32, @payload : String) + end +end + +class IPC::Message + def to_json + JSONWSMessage.new(@type.to_i, String.new(@payload)).to_json + end +end + def websocket_switching_procedure (activefd : Int, context : InstanceStorage) begin if context.is_client[activefd] # The client is a WebSocket on top of a TCP connection - client = context.fdtotcpsocket[activefd] - wsclient = context.fdtows[activefd] + client = context.fd_to_tcpsocket[activefd] + wsclient = context.fd_to_websocket[activefd] begin message = wsclient.read + rescue e puts "#{CRED}Exception (receiving a message)#{CRESET} #{e}" closing_client activefd, context @@ -226,6 +257,16 @@ def websocket_switching_procedure (activefd : Int, context : InstanceStorage) return end + # TODO: verify this + if context.is_json[activefd] + jsonmessage = JSONWSMessage.from_json String.new(message) + message = to_message jsonmessage.mtype, jsonmessage.payload + + # puts "JSON TYPE !!!" + # pp! jsonmessage + # pp! message + end + # puts "switching: client to service" # print_hexa(String.new(message), "#{CBLUE}Received message hexa#{CRESET}") # client => service @@ -233,7 +274,7 @@ def websocket_switching_procedure (activefd : Int, context : InstanceStorage) # XXX: this is not a TCP fd, but since behind the scene this is compatible, I'm hacking a bit serv = WrappedTCPFileDescriptor.new(fd: fdservice, family: Socket::Family::INET) - #serv = context.fdtoipcconnection[fdservice] + #serv = context.fd_to_ipcconnection[fdservice] serv.send message else @@ -241,13 +282,24 @@ def websocket_switching_procedure (activefd : Int, context : InstanceStorage) # service => client fdclient = context.switchtable[activefd] - wsclient = context.fdtows[fdclient] + wsclient = context.fd_to_websocket[fdclient] - serv = context.fdtoipcconnection[activefd] + serv = context.fd_to_ipcconnection[activefd] message = serv.read # puts "received message from service: #{message.to_s}" - buf = message.to_buffer + + if context.is_json[fdclient] + buf = message.to_json + # puts "JSON TYPE !!!" + # pp! buf + else + buf = message.to_buffer + end + + # TODO: REMOVE THIS + # buf = message.to_buffer # print_hexa String.new(buf), "\033[31m Message to send to the client \033[00m " + wsclient.send buf end rescue e