class Midori::WebSocket
This class provides methods for WebSocket
connection instance. @attr [Array<Integer>, String] msg message send from client @attr [Integer] opcode operation code of WebSocket
@attr [Hash] events response for different event @attr [Midori::Connection] connection raw EventMachine connection @attr [Midori::Request] request raw request
Attributes
Public Class Methods
Init a WebSocket
instance with a connection @param [Midori::Connection] connection raw EventMachine connection
# File lib/midori/websocket.rb, line 13 def initialize(connection) @events = {} @connection = connection end
Public Instance Methods
Close a websocket connection
# File lib/midori/websocket.rb, line 84 def close raise Midori::Exception::FrameEnd end
VALUE method_midori_websocket_decode(VALUE self, VALUE data) { int byte, opcode, i, n, fin; char *result; int *mask_array; ID getbyte = rb_intern("getbyte"); ID close = rb_intern("close"); byte = NUM2INT(rb_funcall(data, getbyte, 0)); fin = byte & 0x80; opcode = byte & 0x0f; if (fin != 0x80) rb_raise(ContinousFrameException, "Continous Frame hasn't been implemented yet"); rb_iv_set(self, "@opcode", INT2NUM(opcode)); if (opcode != 0x1 && opcode != 0x2 && opcode != 0x8 && opcode != 0x9 && opcode != 0xA) rb_raise(OpCodeException, "OpCode %d not supported", opcode); if (opcode == 0x8) { rb_funcall(self, close, 0); } byte = NUM2INT(rb_funcall(data, getbyte, 0)); if ((byte & 0x80) != 0x80) { rb_raise(NotMaskedException, "Messages from client MUST be masked"); } n = byte & 0x7f; result = (char *)xmalloc(n); mask_array = (int *)xmalloc(4); for (i = 0; i < 4; i++) { mask_array[i] = NUM2INT(rb_funcall(data, getbyte, 0)); } for (i = 0; i < n; i++) { result[i] = NUM2INT(rb_funcall(data, getbyte, 0)) ^ mask_array[i % 4]; } if (opcode == 0x1 || opcode == 0x9 || opcode == 0xA) { rb_iv_set(self, "@msg", rb_enc_str_new(result, n, rb_utf8_encoding())); } else { VALUE result_arr = rb_ary_new2(n); for (i = 0; i < n; i++) { rb_ary_store(result_arr, i, INT2NUM(result[i])); } rb_iv_set(self, "@msg", result_arr); } xfree(mask_array); xfree(result); return Qnil; }
Ancestor of ping pong @param [Integer] method opcode @param [String] str string to send
# File lib/midori/websocket.rb, line 78 def heartbeat(method, str) raise Midori::Exception::PingPongSizeTooLarge if str.size > 125 @connection.send_data [method, str.size, str].pack("CCA#{str.size}") end
API definition for events @param [Symbol] event event name(open, message, close, ping, pong) @yield what to do after event matched @example
websocket '/websocket' do |ws| ws.on :message do |msg| puts msg end end
# File lib/midori/websocket.rb, line 27 def on(event, &block) # open, message, close, ping, pong @events[event] = block end
Send a Ping request @param [String] str string to send
# File lib/midori/websocket.rb, line 65 def ping(str) heartbeat(0b10001001, str) end
Send a Pong request @param [String] str string to send
# File lib/midori/websocket.rb, line 71 def pong(str) heartbeat(0b10001010, str) end
Send data @param [Array<Integer>, String] msg data to send
# File lib/midori/websocket.rb, line 33 def send(msg) output = [] payload_length = [] if msg.size < 126 payload_length << msg.size elsif msg.size < 65_536 payload_length << 126 payload_length.concat([msg.size].pack('n').unpack('C*')) elsif msg.size < 2**63 payload_length << 127 payload_length.concat([msg.size >> 32, msg.size & 0xFFFFFFFF].pack('NN').unpack('C*')) else raise Midori::Exception::ContinuousFrame end if msg.is_a?String output << 0b10000001 output.concat payload_length output.concat msg.unpack('C*') @connection.send_data(output.pack('C*')) elsif msg.is_a? Array output << 0b10000010 output.concat payload_length output.concat msg @connection.send_data(output.pack('C*')) else raise Midori::Exception::OpCodeError end end