synchronous sockets, no more threads
This commit is contained in:
parent
20c312d9a9
commit
6566c2af5c
|
@ -5,9 +5,6 @@ require 'landline/extensions/websocket'
|
||||||
|
|
||||||
class Test < Landline::App
|
class Test < Landline::App
|
||||||
websocket "/test", version: 7 do |socket|
|
websocket "/test", version: 7 do |socket|
|
||||||
socket.on :message do |msg|
|
|
||||||
puts "Client wrote: #{msg}"
|
|
||||||
end
|
|
||||||
socket.on :error do |err|
|
socket.on :error do |err|
|
||||||
puts "Error occured: #{err.inspect}"
|
puts "Error occured: #{err.inspect}"
|
||||||
puts err.backtrace
|
puts err.backtrace
|
||||||
|
@ -15,12 +12,17 @@ class Test < Landline::App
|
||||||
socket.on :close do
|
socket.on :close do
|
||||||
puts "Client closed read connection"
|
puts "Client closed read connection"
|
||||||
end
|
end
|
||||||
socket.ready
|
|
||||||
socket.write("Hi!")
|
socket.write("Hi!")
|
||||||
response = socket.read
|
while (response = socket.read)
|
||||||
socket.write("You said: #{response}")
|
if response
|
||||||
|
puts "Client wrote: #{response.inspect}"
|
||||||
|
else
|
||||||
|
puts "Client closed read connection"
|
||||||
|
end
|
||||||
|
socket.write("You said: #{response}")
|
||||||
|
end
|
||||||
socket.write("Goodbye!")
|
socket.write("Goodbye!")
|
||||||
socket.close
|
socket.close_write
|
||||||
rescue Exception => e
|
rescue Exception => e
|
||||||
puts e.inspect
|
puts e.inspect
|
||||||
puts e.backtrace
|
puts e.backtrace
|
||||||
|
|
|
@ -95,19 +95,6 @@ module Landline
|
||||||
)
|
)
|
||||||
@readable = true
|
@readable = true
|
||||||
@writable = true
|
@writable = true
|
||||||
@data = Queue.new
|
|
||||||
on :message do |msg|
|
|
||||||
@data.enq(msg)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Start the main loop for the eventifier
|
|
||||||
# @return [void]
|
|
||||||
def ready
|
|
||||||
return if @ready
|
|
||||||
|
|
||||||
_loop
|
|
||||||
@ready = true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Send data through websocket
|
# Send data through websocket
|
||||||
|
@ -126,34 +113,30 @@ module Landline
|
||||||
)
|
)
|
||||||
@io.write(frame.to_s)
|
@io.write(frame.to_s)
|
||||||
rescue Errno::EPIPE => e
|
rescue Errno::EPIPE => e
|
||||||
@writable = false
|
|
||||||
_emit :error, e
|
_emit :error, e
|
||||||
close
|
close
|
||||||
end
|
end
|
||||||
|
|
||||||
# Read data from socket synchronously
|
# Read data from socket synchronously
|
||||||
# @return [String, nil] nil returned if socket closes
|
# @return [WebSocket::Frame::Base, nil] nil if socket received a close event
|
||||||
def read
|
def read
|
||||||
unless @readable
|
unless @readable
|
||||||
raise self.class::WebSocketError,
|
raise self.class::WebSocketError,
|
||||||
"socket closed for reading"
|
"socket closed for reading"
|
||||||
end
|
end
|
||||||
|
|
||||||
@data.deq
|
_process_events(proc { _read })
|
||||||
end
|
end
|
||||||
|
|
||||||
# Close the socket for reading
|
# Read data from socket without blocking
|
||||||
# @return [void]
|
# @return [WebSocket::Frame::Base, nil] nil if socket received a close event
|
||||||
def close_read
|
def read_nonblock
|
||||||
_emit :close
|
unless @readable
|
||||||
@readable = false
|
raise self.class::WebSocketError,
|
||||||
@io.close_read
|
"socket closed for reading"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Close the socket for writing
|
_process_events(proc { _read_nonblock })
|
||||||
def close_write
|
|
||||||
@writable = false
|
|
||||||
@io.close_write
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Establish a connection through handshake
|
# Establish a connection through handshake
|
||||||
|
@ -183,62 +166,78 @@ module Landline
|
||||||
handshake
|
handshake
|
||||||
end
|
end
|
||||||
|
|
||||||
# Close the socket
|
# Close socket for reading
|
||||||
# @return [void]
|
def close_read
|
||||||
def close
|
raise WebSocketError, 'socket closed for reading' unless @readable
|
||||||
_close
|
|
||||||
@writable = false
|
_emit :close
|
||||||
@readable = false
|
@readable = false
|
||||||
|
@io.close_read
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Close socket for reading
|
||||||
|
def close_write
|
||||||
|
raise WebSocketError, 'socket closed for writing' unless @writable
|
||||||
|
|
||||||
|
write(nil, type: :close)
|
||||||
|
@writable = false
|
||||||
|
@io.close_write
|
||||||
|
end
|
||||||
|
|
||||||
|
# Close the socket entirely
|
||||||
|
def close
|
||||||
|
raise WebSocketError, 'socket closed' unless @writable or @readable
|
||||||
|
|
||||||
|
close_read if @readable
|
||||||
|
close_write if @writable
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :io, :readable, :writable
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Event reading loop
|
# Process incoming websocket events
|
||||||
# @return [void]
|
# @param next_frame [#call] callback to get the next frame
|
||||||
def _loop
|
# @return [WebSocket::Frame::Base, nil]
|
||||||
@thread = Thread.new do
|
def _process_events(next_frame)
|
||||||
loop do
|
loop do
|
||||||
msg = _read
|
frame = next_frame.call
|
||||||
if msg and [:text, :binary].include? msg.type
|
case frame.type
|
||||||
_emit :message, msg
|
when :binary, :text, :pong then return frame
|
||||||
elsif msg and msg.type == :close
|
when :ping
|
||||||
_emit :__close, msg
|
write frame.to_s, type: :pong
|
||||||
break
|
when :close
|
||||||
end
|
close_read
|
||||||
|
return nil
|
||||||
|
else raise WebSocketError, "unknown frame type #{frame.type}"
|
||||||
end
|
end
|
||||||
rescue IOError => e
|
|
||||||
@writable = false
|
|
||||||
_emit :error, e
|
|
||||||
close
|
|
||||||
ensure
|
|
||||||
close_read
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Receive data through websocket
|
# Receive data through websocket
|
||||||
# @return [String] output from frame
|
# @return [String] output from frame
|
||||||
def _read
|
def _read
|
||||||
while (char = @io.getc)
|
while (char = @io.read(1))
|
||||||
@frame_parser << char
|
@frame_parser << char
|
||||||
frame = @frame_parser.next
|
frame = @frame_parser.next
|
||||||
return frame if frame
|
return frame if frame
|
||||||
end
|
end
|
||||||
rescue Errno::ECONNRESET => e
|
rescue Errno::ECONNRESET => e
|
||||||
@writable = false
|
|
||||||
_emit :error, e
|
_emit :error, e
|
||||||
close
|
close
|
||||||
end
|
end
|
||||||
|
|
||||||
# Close the websocket
|
# Receive data through websocket asynchronously
|
||||||
# @return [void]
|
# @return [String] output from frame
|
||||||
def _close
|
def _read_nonblock
|
||||||
frame = ::WebSocket::Frame::Outgoing::Server.new(
|
while (char = @io.read_nonblock(1))
|
||||||
version: @version,
|
@frame_parser << char
|
||||||
type: :close
|
frame = @frame_parser.next
|
||||||
)
|
return frame if frame
|
||||||
@io.write(frame.to_s) if @writable
|
end
|
||||||
sleep 0.1
|
rescue Errno::ECONNRESET => e
|
||||||
@io.close
|
_emit :error, e
|
||||||
|
close
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue