lib/em-midori/websocket.rb in em-midori-0.0.6 vs lib/em-midori/websocket.rb in em-midori-0.0.7
- old
+ new
@@ -1,63 +1,75 @@
+##
+# This class provides methods for WebSocket connection instance.
class Midori::WebSocket
- attr_accessor :msg, :opcode, :events, :connection
+ attr_accessor :msg, :opcode, :events, :connection
- def initialize(connection)
- @events = Hash.new
- @connection = connection
- end
+ def initialize(connection)
+ @events = {}
+ @connection = connection
+ end
- def decode(data)
- # Fin and Opcode
- byte_tmp = data.getbyte
- fin = byte_tmp & 0b10000000
- @opcode = byte_tmp & 0b00001111
- raise Midori::Error::ContinuousFrame unless fin
- raise Midori::Error::OpCodeError unless [0x1, 0x2, 0x8, 0x9, 0xA].include?opcode
- raise Midori::Error::FrameEnd if @opcode == 0x8 # Close Frame
- return if @opcode == 0x9 || @opcode == 0xA # Ping Pong
- decode_mask(data)
- end
+ def decode(data)
+ # Fin and Opcode
+ byte_tmp = data.getbyte
+ fin = byte_tmp & 0b10000000
+ @opcode = byte_tmp & 0b00001111
+ # NOT Support Multiple Fragments
+ raise Midori::Error::ContinuousFrame unless fin
+ raise Midori::Error::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
- def decode_mask(data)
- # Mask
- byte_tmp = data.getbyte
- is_masked = byte_tmp & 0b10000000
- raise Midori::Error::NotMasked unless is_masked
- # Payload
- payload = byte_tmp & 0b01111111
- mask = 4.times.map {data.getbyte}
- # Message
- masked_msg = payload.times.map {data.getbyte}
- @msg = masked_msg.each_with_index.map {|byte, i| byte ^ mask[i % 4]}
- @msg = @msg.pack('C*').force_encoding('utf-8') if @opcode == 0x1
- end
+ def decode_mask(data)
+ # Mask
+ byte_tmp = data.getbyte
+ is_masked = byte_tmp & 0b10000000
+ raise Midori::Error::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
- def on(event, &block) # open, message, close, ping, pong
- @events[event] = block
- end
+ def on(event, &block) # open, message, close, ping, pong
+ @events[event] = block
+ end
- def send(msg)
- output = Array.new
- if msg.is_a?String
- output << 0b10000001 << msg.size << msg
- elsif msg.is_a?Array
- output << 0b10000010 << msg.size
- output.concat msg
- else
- raise Midori::Error::OpCodeError
- end
- @connection.send_data(output.pack("CCA#{msg.size}"))
+ 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::Error::OpCodeError
end
+ end
- def ping
- @connection.send_data "\t"
- end
+ def ping(str)
+ heartbeat(0b10001001, str)
+ end
- def pong
- @connection.send_data "\n"
- end
+ def pong(str)
+ heartbeat(0b10001010, str)
+ end
- def close
- Midori::Error::FrameEnd
- end
-end
\ No newline at end of file
+ def heartbeat(method, str)
+ raise Midori::Error::PingPongSizeTooLarge if str.size > 125
+ @connection.send_data [method, str.size, str].pack("CCA#{str.size}")
+ end
+
+ def close
+ raise Midori::Error::FrameEnd
+ end
+end