From 608a467f89f9b3f9c799f9c315f1add29d2598de Mon Sep 17 00:00:00 2001
From: Philippe PITTOLI
Date: Mon, 5 Aug 2019 01:53:35 +0200
Subject: [PATCH] playing with websockets
---
src/debuggingws.cr | 220 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 220 insertions(+)
create mode 100644 src/debuggingws.cr
diff --git a/src/debuggingws.cr b/src/debuggingws.cr
new file mode 100644
index 0000000..20a622a
--- /dev/null
+++ b/src/debuggingws.cr
@@ -0,0 +1,220 @@
+require "option_parser"
+require "ipc"
+require "socket"
+require "./colors"
+require "./ws"
+
+require "socket"
+require "http"
+require "http/server"
+require "base64"
+require "digest"
+require "./utils"
+
+class IPC::Connection
+ def initialize(fd : Int32)
+ external_connection = LibIPC::Connection.new
+ external_connection.fd = fd
+ initialize(external_connection)
+ end
+end
+
+class IPC::Message
+ def to_buffer
+ to_message @type, String.new(@payload)
+ end
+end
+
+
+class WrappedTCPFileDescriptor < TCPSocket
+ # do not close the connection when garbage collected!!
+ def finalize
+ # puts "WrappedTCPFileDescriptor garbage collection!!"
+ # super
+ end
+end
+
+service_name = "websocket"
+port_to_listen = 1234
+
+OptionParser.parse! do |parser|
+ parser.on "-p port", "--port port", "Port to listen on." do |port|
+ port_to_listen = port.to_u16
+ end
+
+ parser.on "-s service-name", "--service-name service-name", "Service name." do |name|
+ service_name = name
+ end
+
+ parser.on "-h", "--help", "Show this help" do
+ puts parser
+ exit 0
+ end
+end
+
+
+class InstanceStorage
+ property service : IPC::SwitchingService
+ property fdlist : Hash(Int32,String)
+ property socklist : Hash(Int32, TCPSocket)
+ property wssocklist : Hash(Int32, WebSocket)
+ property switchtable : Hash(Int32, Int32)
+ property connectionlist : Hash(Int32, IPC::Connection)
+
+ def initialize (@service : IPC::SwitchingService)
+ # fdlist_client = [] of TCPSocket
+ @fdlist = Hash(Int32,String).new
+ @socklist = Hash(Int32, TCPSocket).new
+ @wssocklist = Hash(Int32, WebSocket).new
+ @switchtable = Hash(Int32, Int32).new
+ @connectionlist = Hash(Int32, IPC::Connection).new
+ end
+
+ def remove_fd (fdclient : Int32)
+ # 1. closing both the client and the service
+ fdservice = @switchtable[fdclient]?
+ tcpfdc = @socklist[fdclient]
+
+ # 2. closing the TCP connections
+ tcpfdc.close unless tcpfdc.closed?
+
+ # 3. removing the client and the service fds from the loop check
+ @service.remove_fd (fdclient)
+
+ # 5. removing both the client and the service from the switchtable
+ @switchtable = @switchtable.select do |fdc, fds|
+ fdc != fdclient && fds != fdclient
+ end
+
+ # 6. removing the client and the service from fdlist
+ @fdlist = @fdlist.select do |fd,v| fd != fdclient end
+
+ unless fdservice.nil?
+ service = @connectionlist[fdservice]
+ service.close
+ @service.remove_fd (fdservice)
+ @connectionlist = @connectionlist.select do |k, v|
+ k != fdservice
+ end
+
+ @fdlist = @fdlist.select do |fd,v| fd != fdservice end
+ end
+ end
+end
+
+server = TCPServer.new("localhost", port_to_listen)
+service = IPC::SwitchingService.new service_name
+service << server.fd
+context = InstanceStorage.new service
+
+
+
+def websocket_client_connection(client, context : InstanceStorage)
+ request = HTTP::Request.from_io client
+ # pp! request
+
+ if request.nil?
+ raise "#REQUEST IS NIL"
+ end
+
+ if request.is_a? HTTP::Request::BadRequest
+ raise "BAD REQUEST DAZE~"
+ end
+
+ # FIXME: check they actually wanted to upgrade to websocket
+
+ key = request.headers["Sec-WebSocket-Key"]
+
+ response_key = Digest::SHA1.base64digest key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+ # puts response_key
+
+ # HTTP inside bru
+ headers_header = "HTTP/1.1 101 Switching Protocols"
+ headers = HTTP::Headers {
+ "Upgrade" => "websocket",
+ "Connection" => "Upgrade",
+ "Sec-WebSocket-Accept" => response_key
+ }
+
+ headers = headers.map { |key, value| "#{key}: #{value[0]}\r\n" }.join
+
+ # puts "#{headers_header}\n#{headers.to_s}\r\n"
+ client.send "#{headers_header}\n#{headers.to_s}\r\n"
+
+ wsclient = WebSocket.new client
+ wsclient.send "are we websocket yet?"
+
+ # the client is still not connected to a service
+ context.fdlist[client.fd] = "not connected"
+
+ # listen to the client's file descriptor
+ context.service << client.fd
+ # puts "#{CBLUE}new client: #{client.fd}#{CRESET}"
+
+ # registering the client into storing structures to avoid being garbage collected
+ context.socklist[client.fd] = client
+ context.wssocklist[client.fd] = wsclient
+end
+
+
+def closing_client (fdclient : Int, context : InstanceStorage)
+ # puts "closing the client #{fdclient}"
+ context.remove_fd fdclient
+end
+
+service.loop do |event|
+ case event
+ when IPC::Event::Connection
+ puts "#{CBLUE}IPC::Event::Connection: #{event.connection.fd}#{CRESET}"
+ when IPC::Event::Disconnection
+ puts "#{CBLUE}IPC::Event::Disconnection: #{event.connection.fd}#{CRESET}"
+ when IPC::Event::ExtraSocket
+ puts "#{CBLUE}IPC::Event::ExtraSocket#{CRESET}"
+
+ # 1. accept new websocket clients
+ if server.fd == event.connection.fd
+ client = server.accept
+ begin
+ websocket_client_connection client, context
+ puts "#{CBLUE}new client:#{CRESET} #{client.fd}"
+ rescue e
+ puts "Exception: #{CRED}#{e}#{CRESET}"
+ client.close
+ end
+ next
+ end
+
+ # 2. active fd != server fd
+ # is this a service or a client?
+ activefd = event.connection.fd
+
+ # since it's an external communication
+ # we have to read the message here, it's not handled by default in libipc
+ wsclient = context.wssocklist[activefd]
+ begin
+ message = wsclient.read
+ rescue e
+ puts "#{CRED}Exception (receiving a message)#{CRESET} #{e}"
+ end
+
+ if message.nil?
+ puts "#{CBLUE}disconnection of client#{CRESET} #{event.connection.fd}"
+ closing_client activefd, context
+ next
+ end
+
+ print_hexa(String.new(message), "#{CBLUE}Received message hexa#{CRESET}")
+
+ when IPC::Event::Switch
+ puts "\033[36mIPC::Event::Switch#{CRESET}: from fd #{event.connection.fd}"
+
+ raise "Not implemented."
+
+ # IPC::Event::Message has to be the last entry
+ # because ExtraSocket and Switch inherit from Message class
+ when IPC::Event::Message
+ puts "#{CBLUE}IPC::Event::Message#{CRESET}: #{event.connection.fd}"
+
+ raise "Not implemented."
+ end
+end