class WebSocket::Frame::Handler::Handler03
Constants
- FRAME_TYPES
Hash of frame names and it's opcodes
- FRAME_TYPES_INVERSE
Hash of frame opcodes and it's names
Public Class Methods
new(frame)
click to toggle source
Calls superclass method
WebSocket::Frame::Handler::Base::new
# File lib/websocket/frame/handler/handler03.rb, line 23 def initialize(frame) super @application_data_buffer = nil end
Public Instance Methods
decode_frame()
click to toggle source
@see WebSocket::Frame::Handler::Base#decode_frame
# File lib/websocket/frame/handler/handler03.rb, line 48 def decode_frame while @frame.data.size > 1 valid_header, more, frame_type, mask, payload_length = decode_header return unless valid_header application_data = decode_payload(payload_length, mask) if more decode_continuation_frame(application_data, frame_type) elsif frame_type == :continuation return decode_finish_continuation_frame(application_data) else raise(WebSocket::Error::Frame::InvalidPayloadEncoding) if frame_type == :text && !application_data.valid_encoding? return @frame.class.new(version: @frame.version, type: frame_type, data: application_data, decoded: true) end end nil end
encode_frame()
click to toggle source
@see WebSocket::Frame::Handler::Base#encode_frame
# File lib/websocket/frame/handler/handler03.rb, line 34 def encode_frame frame = if @frame.outgoing_masking? masking_key = SecureRandom.random_bytes(4) tmp_data = Data.new(masking_key + @frame.data) tmp_data.set_mask masking_key + tmp_data.getbytes(4, tmp_data.size) else @frame.data end encode_header + frame end
masking?()
click to toggle source
Allow turning on or off masking
# File lib/websocket/frame/handler/handler03.rb, line 68 def masking? false end
supported_frames()
click to toggle source
@see WebSocket::Frame::Base#supported_frames
# File lib/websocket/frame/handler/handler03.rb, line 29 def supported_frames %i[text binary close ping pong] end
Private Instance Methods
buffer_exists?(buffer_number)
click to toggle source
# File lib/websocket/frame/handler/handler03.rb, line 138 def buffer_exists?(buffer_number) !@frame.data.getbyte(buffer_number - 1).nil? end
decode_continuation_frame(application_data, frame_type)
click to toggle source
# File lib/websocket/frame/handler/handler03.rb, line 205 def decode_continuation_frame(application_data, frame_type) @application_data_buffer ||= String.new('') @application_data_buffer << application_data @frame_type ||= frame_type end
decode_finish_continuation_frame(application_data)
click to toggle source
# File lib/websocket/frame/handler/handler03.rb, line 211 def decode_finish_continuation_frame(application_data) raise(WebSocket::Error::Frame::UnexpectedContinuationFrame) unless @frame_type @application_data_buffer << application_data # Test valid UTF-8 encoding raise(WebSocket::Error::Frame::InvalidPayloadEncoding) if @frame_type == :text && !@application_data_buffer.valid_encoding? message = @frame.class.new(version: @frame.version, type: @frame_type, data: @application_data_buffer, decoded: true) @application_data_buffer = nil @frame_type = nil message end
decode_first_byte()
click to toggle source
# File lib/websocket/frame/handler/handler03.rb, line 142 def decode_first_byte first_byte = @frame.data.getbyte(0) raise(WebSocket::Error::Frame::ReservedBitUsed) if first_byte & 0b01110000 != 0b00000000 more = ((first_byte & 0b10000000) == 0b10000000) ^ fin frame_type = opcode_to_type first_byte & 0b00001111 raise(WebSocket::Error::Frame::FragmentedControlFrame) if more && control_frame?(frame_type) raise(WebSocket::Error::Frame::DataFrameInsteadContinuation) if data_frame?(frame_type) && !@application_data_buffer.nil? [more, frame_type] end
decode_header()
click to toggle source
# File lib/websocket/frame/handler/handler03.rb, line 118 def decode_header more, frame_type = decode_first_byte header_length, payload_length, mask = decode_second_byte(frame_type) return unless header_length # Compute the expected frame length frame_length = header_length + payload_length frame_length += 4 if mask raise(WebSocket::Error::Frame::TooLong) if frame_length > WebSocket.max_frame_size # Check buffer size return unless buffer_exists?(frame_length) # Buffer incomplete # Remove frame header @frame.data.slice!(0...header_length) [true, more, frame_type, mask, payload_length] end
decode_payload(payload_length, mask)
click to toggle source
# File lib/websocket/frame/handler/handler03.rb, line 188 def decode_payload(payload_length, mask) pointer = 0 # Read application data (unmasked if required) @frame.data.set_mask if mask pointer += 4 if mask payload = @frame.data.getbytes(pointer, payload_length) payload.force_encoding('UTF-8') pointer += payload_length @frame.data.unset_mask if mask # Throw away data up to pointer @frame.data.slice!(0...pointer) payload end
decode_payload_length(length)
click to toggle source
# File lib/websocket/frame/handler/handler03.rb, line 169 def decode_payload_length(length) case length when 127 # Length defined by 8 bytes # Check buffer size return unless buffer_exists?(10) # Buffer incomplete # Only using the last 4 bytes for now, till I work out how to # unpack 8 bytes. I'm sure 4GB frames will do for now :) [10, @frame.data.getbytes(6, 4).unpack('N').first] when 126 # Length defined by 2 bytes # Check buffer size return unless buffer_exists?(4) # Buffer incomplete [4, @frame.data.getbytes(2, 2).unpack('n').first] else [2, length] end end
decode_second_byte(frame_type)
click to toggle source
# File lib/websocket/frame/handler/handler03.rb, line 156 def decode_second_byte(frame_type) second_byte = @frame.data.getbyte(1) mask = @frame.incoming_masking? && (second_byte & 0b10000000) == 0b10000000 length = second_byte & 0b01111111 raise(WebSocket::Error::Frame::ControlFramePayloadTooLong) if length > 125 && control_frame?(frame_type) header_length, payload_length = decode_payload_length(length) [header_length, payload_length, mask] end
encode_header()
click to toggle source
# File lib/websocket/frame/handler/handler03.rb, line 95 def encode_header mask = @frame.outgoing_masking? ? 0b10000000 : 0b00000000 output = String.new('') output << (type_to_opcode(@frame.type) | (fin ? 0b10000000 : 0b00000000)) # since more, rsv1-3 are 0 and 0x80 for Draft 4 output << encode_payload_length(@frame.data.size, mask) output end
encode_payload_length(length, mask)
click to toggle source
# File lib/websocket/frame/handler/handler03.rb, line 104 def encode_payload_length(length, mask) output = String.new('') if length <= 125 output << (length | mask) # since rsv4 is 0 elsif length < 65_536 # write 2 byte length output << (126 | mask) output << [length].pack('n') else # write 8 byte length output << (127 | mask) output << [length >> 32, length & 0xFFFFFFFF].pack('NN') end output end
fin()
click to toggle source
This allows flipping the more bit to fin for draft 04
# File lib/websocket/frame/handler/handler03.rb, line 75 def fin false end
opcode_to_type(opcode)
click to toggle source
Convert frame opcode to type name @param [Integer] opcode Opcode @return [Symbol] Frame
type name or nil @raise [WebSocket::Error] if frame type name is not known
# File lib/websocket/frame/handler/handler03.rb, line 91 def opcode_to_type(opcode) FRAME_TYPES_INVERSE[opcode] || raise(WebSocket::Error::Frame::UnknownOpcode) end
type_to_opcode(frame_type)
click to toggle source
Convert frame type name to opcode @param [Symbol] frame_type Frame
type name @return [Integer] opcode or nil @raise [WebSocket::Error] if frame opcode is not known
# File lib/websocket/frame/handler/handler03.rb, line 83 def type_to_opcode(frame_type) FRAME_TYPES[frame_type] || raise(WebSocket::Error::Frame::UnknownFrameType) end