ipcd/src/lib_modifications.cr

128 lines
3.0 KiB
Crystal
Raw Normal View History

2020-02-04 02:06:13 +01:00
# Libraries modifications
2019-08-01 00:44:38 +02:00
require "http"
require "http/web_socket/protocol"
# The Socket library uses the IO::Buffered module.
# IO::Buffered needs a new function to check if there is still something to read
# in its buffer. Since WebSocket is compatible with all IO instances, then IO
# needs this function as well, even if it doesn't really makes sense here.
class IO
2020-01-17 13:01:07 +01:00
def empty? : Bool
true
end
end
module IO::Buffered
2020-01-17 13:01:07 +01:00
# :nodoc:
def empty? : Bool
@in_buffer_rem.size == 0
end
end
class HTTP::WebSocket
2020-01-17 13:01:07 +01:00
# Infinite loop over `run_once`.
# Stops when the socket closes or an error occurs.
def run
loop do
ret = run_once
case ret
when Error | Close
break
end
end
end
# `run_once` returns the type of the message which could be a Ping or Pong message, a closing socket, an error (when an exception occurs) or a message that is not yet entirely received.
record Ping
2020-01-17 13:01:07 +01:00
record Pong
record Close
2020-01-17 13:01:07 +01:00
record Error
record NotFinal
2019-08-01 00:44:38 +02:00
2020-01-17 13:01:07 +01:00
# :nodoc:
def run_once : Bytes | String | Close | Ping | Pong | NotFinal | Error
begin
info = @ws.receive(@buffer)
rescue IO::EOFError
close
2020-01-17 13:01:07 +01:00
return Error.new
end
2019-08-01 00:44:38 +02:00
case info.opcode
when Protocol::Opcode::PING
@current_message.write @buffer[0, info.size]
if info.final
message = @current_message.to_s
@on_ping.try &.call(message)
pong(message) unless closed?
@current_message.clear
2020-01-17 13:01:07 +01:00
return Ping.new
end
when Protocol::Opcode::PONG
@current_message.write @buffer[0, info.size]
if info.final
@on_pong.try &.call(@current_message.to_s)
@current_message.clear
2020-01-17 13:01:07 +01:00
return Pong.new
end
when Protocol::Opcode::TEXT
message = @buffer[0, info.size]
@current_message.write message
if info.final
@on_message.try &.call(@current_message.to_s)
@current_message.clear
2020-01-17 13:01:07 +01:00
return String.new message
end
when Protocol::Opcode::BINARY
message = @buffer[0, info.size]
@current_message.write message
if info.final
@on_binary.try &.call(@current_message.to_slice)
@current_message.clear
2020-01-17 13:01:07 +01:00
return message
end
when Protocol::Opcode::CLOSE
@current_message.write @buffer[0, info.size]
if info.final
message = @current_message.to_s
2020-05-27 15:01:50 +02:00
@on_close.try &.call(HTTP::WebSocket::CloseCode::NormalClosure, message)
close(HTTP::WebSocket::CloseCode::NormalClosure, message) unless closed?
@current_message.clear
2020-01-17 13:01:07 +01:00
return Close.new
end
end
2020-01-17 13:01:07 +01:00
return NotFinal.new
2019-08-01 00:44:38 +02:00
end
2020-02-04 02:06:13 +01:00
# Returns the websocket instance.
def ws
@ws
end
end
class HTTP::WebSocket::Protocol
def io
@io
end
end
class WebSocket < HTTP::WebSocket
getter? closed = false
2019-08-01 00:44:38 +02:00
def finalize
2020-02-04 02:06:13 +01:00
# puts "WrappedTCPFileDescriptor garbage collection!!"
# super
end
end
class WrappedTCPFileDescriptor < TCPSocket
# do not close the connection when garbage collected!!
def finalize
# puts "WrappedTCPFileDescriptor garbage collection!!"
# super
2019-08-01 00:44:38 +02:00
end
end