Class: Midori::WebSocket

Inherits:
Object
  • Object
show all
Defined in:
lib/midori/websocket.rb

Overview

This class provides methods for WebSocket connection instance.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(connection) ⇒ WebSocket

Init a WebSocket instance with a connection

Parameters:

  • connection (EM::Connection)

    raw EventMachine connection



13
14
15
16
# File 'lib/midori/websocket.rb', line 13

def initialize(connection)
  @events = {}
  @connection = connection
end

Instance Attribute Details

#connectionEM::Connection

raw EventMachine connection

Returns:

  • (EM::Connection)

    the current value of connection



8
9
10
# File 'lib/midori/websocket.rb', line 8

def connection
  @connection
end

#eventsHash

response for different event

Returns:

  • (Hash)

    the current value of events



8
9
10
# File 'lib/midori/websocket.rb', line 8

def events
  @events
end

#msgArray<Integer>, String

message send from client

Returns:

  • (Array<Integer>, String)

    the current value of msg



8
9
10
# File 'lib/midori/websocket.rb', line 8

def msg
  @msg
end

#opcodeInteger

operation code of WebSocket

Returns:

  • (Integer)

    the current value of opcode



8
9
10
# File 'lib/midori/websocket.rb', line 8

def opcode
  @opcode
end

#requestMidori::Request

raw request

Returns:



8
9
10
# File 'lib/midori/websocket.rb', line 8

def request
  @request
end

Instance Method Details

#closeObject

Close a websocket connection



102
103
104
# File 'lib/midori/websocket.rb', line 102

def close
  raise Midori::Exception::FrameEnd
end

#decode(data) ⇒ Object

Decode raw data send from client

Parameters:

  • data (StringIO)

    raw data

Raises:



20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/midori/websocket.rb', line 20

def decode(data)
  # Fin and Opcode
  byte_tmp = data.getbyte
  fin = byte_tmp & 0b10000000
  @opcode = byte_tmp & 0b00001111
  # NOT Support Multiple Fragments
  raise Midori::Exception::ContinuousFrame unless fin
  raise Midori::Exception::OpCodeError unless [0x1, 0x2, 0x8, 0x9, 0xA].include? opcode
  close if @opcode == 0x8 # Close Frame
  # return if @opcode == 0x9 || @opcode == 0xA # Ping Pong
  decode_mask(data)
end

#decode_mask(data) ⇒ Object

Decode masked message send from client

Parameters:

  • data (StringIO)

    raw data

Raises:



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/midori/websocket.rb', line 35

def decode_mask(data)
  # Mask
  byte_tmp = data.getbyte
  is_masked = byte_tmp & 0b10000000
  raise Midori::Exception::NotMasked unless is_masked == 128
  # Payload
  payload = byte_tmp & 0b01111111
  mask = Array.new(4) { data.getbyte }
  # Message
  masked_msg = Array.new(payload) { data.getbyte }
  @msg = masked_msg.each_with_index.map { |byte, i| byte ^ mask[i % 4] }
  @msg = @msg.pack('C*').force_encoding('utf-8') if [0x1, 0x9, 0xA].include?opcode
  # For debug
  #  data.rewind
  #  data.bytes {|byte| puts byte.to_s(16)}
end

#heartbeat(method, str) ⇒ Object

Ancestor of ping pong

Parameters:

  • method (Integer)

    opcode

  • str (String)

    string to send

Raises:



96
97
98
99
# File 'lib/midori/websocket.rb', line 96

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) { ... } ⇒ Object

API definition for events

Examples:

websocket '/websocket' do |ws|
  ws.on :message do |msg|
    puts msg
  end
end

Parameters:

  • event (Symbol)

    event name(open, message, close, ping, pong)

Yields:

  • what to do after event matched



61
62
63
# File 'lib/midori/websocket.rb', line 61

def on(event, &block) # open, message, close, ping, pong
  @events[event] = block
end

#ping(str) ⇒ Object

Send a Ping request

Parameters:

  • str (String)

    string to send



83
84
85
# File 'lib/midori/websocket.rb', line 83

def ping(str)
  heartbeat(0b10001001, str)
end

#pong(str) ⇒ Object

Send a Pong request

Parameters:

  • str (String)

    string to send



89
90
91
# File 'lib/midori/websocket.rb', line 89

def pong(str)
  heartbeat(0b10001010, str)
end

#send(msg) ⇒ Object

Send data

Parameters:

  • msg (Array<Integer>, String)

    data to send



67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/midori/websocket.rb', line 67

def send(msg)
  output = []
  if msg.is_a?String
    output << 0b10000001 << msg.size << msg
    @connection.send_data(output.pack("CCA#{msg.size}"))
  elsif msg.is_a?Array
    output << 0b10000010 << msg.size
    output.concat msg
    @connection.send_data(output.pack('C*'))
  else
    raise Midori::Exception::OpCodeError
  end
end