class Tubesock
Easily interact with WebSocket connections over Rack. TODO: Example with pure Rack
Constants
- HijackNotAvailable
- VERSION
Public Class Methods
hijack(env)
click to toggle source
# File lib/tubesock.rb, line 24 def self.hijack(env) if env['rack.hijack'] env['rack.hijack'].call socket = env['rack.hijack_io'] handshake = WebSocket::Handshake::Server.new handshake.from_rack env socket.write handshake.to_s self.new socket, handshake.version else raise Tubesock::HijackNotAvailable end end
new(socket, version)
click to toggle source
# File lib/tubesock.rb, line 10 def initialize(socket, version) @socket = socket @version = version @open_handlers = [] @message_handlers = [] @close_handlers = [] @error_handlers = [] @close_on_error = true @active = true end
Public Instance Methods
close()
click to toggle source
# File lib/tubesock.rb, line 93 def close return unless @active @close_handlers.each(&:call) close! @active = false end
close!()
click to toggle source
# File lib/tubesock.rb, line 102 def close! if @socket.respond_to?(:closed?) @socket.close unless @socket.closed? else @socket.close end end
closed?()
click to toggle source
# File lib/tubesock.rb, line 110 def closed? @socket.closed? end
keepalive()
click to toggle source
# File lib/tubesock.rb, line 114 def keepalive thread = Thread.new do Thread.current.abort_on_exception = true loop do sleep 5 send_data nil, :ping end end onclose do thread.kill end end
listen()
click to toggle source
# File lib/tubesock.rb, line 71 def listen keepalive Thread.new do Thread.current.abort_on_exception = true begin @open_handlers.each(&:call) each_frame do |data| @message_handlers.each do |h| begin h.call(data) rescue => e @error_handlers.each{|eh| eh.call(e,data)} close if @close_on_error end end end ensure close end end end
onclose(&block)
click to toggle source
# File lib/tubesock.rb, line 63 def onclose(&block) @close_handlers << block end
onerror(&block)
click to toggle source
# File lib/tubesock.rb, line 67 def onerror(&block) @error_handlers << block end
onmessage(&block)
click to toggle source
# File lib/tubesock.rb, line 59 def onmessage(&block) @message_handlers << block end
onopen(&block)
click to toggle source
# File lib/tubesock.rb, line 55 def onopen(&block) @open_handlers << block end
prevent_close_on_error()
click to toggle source
# File lib/tubesock.rb, line 40 def prevent_close_on_error @close_on_error = false end
send_data(data, type = :text)
click to toggle source
# File lib/tubesock.rb, line 44 def send_data data, type = :text frame = WebSocket::Frame::Outgoing::Server.new( version: @version, data: data, type: type ) @socket.write frame.to_s rescue IOError, Errno::EPIPE, Errno::ETIMEDOUT close end
Private Instance Methods
each_frame() { |data| ... }
click to toggle source
# File lib/tubesock.rb, line 129 def each_frame framebuffer = WebSocket::Frame::Incoming::Server.new(version: @version) while IO.select([@socket]) if @socket.respond_to?(:recvfrom) data, _addrinfo = @socket.recvfrom(2000) else data, _addrinfo = @socket.readpartial(2000), @socket.peeraddr end break if data.empty? framebuffer << data while frame = framebuffer.next case frame.type when :close return when :text, :binary yield frame.data when :ping # According to https://tools.ietf.org/html/rfc6455#section-5.5.3: # A Pong frame sent in response to a Ping frame must have identical "Application data" as # found in the message body of the Ping frame being replied to.' send_data frame.data, :pong end end end rescue Errno::EHOSTUNREACH, Errno::ETIMEDOUT, Errno::ECONNRESET, IOError, Errno::EBADF nil # client disconnected or timed out end