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

connection[RW]
events[RW]
msg[RW]
opcode[RW]
request[RW]

Public Class Methods

new(connection) click to toggle source

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() click to toggle source

Close a websocket connection

# File lib/midori/websocket.rb, line 84
def close
  raise Midori::Exception::FrameEnd
end
decode(p1) click to toggle source
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;
}
heartbeat(method, str) click to toggle source

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
on(event, &block) click to toggle source

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
ping(str) click to toggle source

Send a Ping request @param [String] str string to send

# File lib/midori/websocket.rb, line 65
def ping(str)
  heartbeat(0b10001001, str)
end
pong(str) click to toggle source

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(msg) click to toggle source

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