Class: Protocol::WebSocket::Framer
- Inherits:
-
Object
- Object
- Protocol::WebSocket::Framer
- Defined in:
- lib/protocol/websocket/framer.rb
Overview
Wraps an underlying Async::IO::Stream for reading and writing binary data into structured frames.
Instance Method Summary collapse
-
#close ⇒ Object
Close the underlying stream.
-
#flush ⇒ Object
Flush the underlying stream.
-
#initialize(stream, frames = FRAMES) ⇒ Framer
constructor
Initialize a new framer wrapping the given stream.
-
#read_frame(maximum_frame_size = MAXIMUM_ALLOWED_FRAME_SIZE) ⇒ Object
Read a frame from the underlying stream.
-
#write_frame(frame) ⇒ Object
Write a frame to the underlying stream.
Constructor Details
Instance Method Details
#close ⇒ Object
Close the underlying stream.
41 42 43 |
# File 'lib/protocol/websocket/framer.rb', line 41 def close @stream.close end |
#flush ⇒ Object
Flush the underlying stream.
46 47 48 |
# File 'lib/protocol/websocket/framer.rb', line 46 def flush @stream.flush end |
#read_frame(maximum_frame_size = MAXIMUM_ALLOWED_FRAME_SIZE) ⇒ Object
Read a frame from the underlying stream.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/protocol/websocket/framer.rb', line 52 def read_frame(maximum_frame_size = MAXIMUM_ALLOWED_FRAME_SIZE) buffer = @stream.read(2) unless buffer and buffer.bytesize == 2 raise EOFError, "Could not read frame header!" end first_byte = buffer.getbyte(0) second_byte = buffer.getbyte(1) finished = (first_byte & 0b1000_0000 != 0) flags = (first_byte & 0b0111_0000) >> 4 opcode = first_byte & 0b0000_1111 if opcode >= 0x3 && opcode <= 0x7 raise ProtocolError, "Non-control opcode = #{opcode} is reserved!" elsif opcode >= 0xB raise ProtocolError, "Control opcode = #{opcode} is reserved!" end mask = (second_byte & 0b1000_0000 != 0) length = second_byte & 0b0111_1111 if opcode & 0x8 != 0 if length > 125 raise ProtocolError, "Invalid control frame payload length: #{length} > 125!" elsif !finished raise ProtocolError, "Fragmented control frame!" end end if length == 126 if mask buffer = @stream.read(6) or raise EOFError, "Could not read length and mask!" length = buffer.unpack1("n") mask = buffer.byteslice(2, 4) else buffer = @stream.read(2) or raise EOFError, "Could not read length!" length = buffer.unpack1("n") end elsif length == 127 if mask buffer = @stream.read(12) or raise EOFError, "Could not read length and mask!" length = buffer.unpack1("Q>") mask = buffer.byteslice(8, 4) else buffer = @stream.read(8) or raise EOFError, "Could not read length!" length = buffer.unpack1("Q>") end elsif mask mask = @stream.read(4) or raise EOFError, "Could not read mask!" end if length > maximum_frame_size raise ProtocolError, "Invalid payload length: #{length} > #{maximum_frame_size}!" end payload = @stream.read(length) or raise EOFError, "Could not read payload!" if payload.bytesize != length raise EOFError, "Incorrect payload length: #{length} != #{payload.bytesize}!" end klass = @frames[opcode] || Frame return klass.new(finished, payload, flags: flags, opcode: opcode, mask: mask) end |
#write_frame(frame) ⇒ Object
Write a frame to the underlying stream.
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/protocol/websocket/framer.rb', line 122 def write_frame(frame) if frame.mask and frame.mask.bytesize != 4 raise ProtocolError, "Invalid mask length!" end length = frame.length if length <= 125 short_length = length elsif length.bit_length <= 16 short_length = 126 else short_length = 127 end buffer = [ (frame.finished ? 0b1000_0000 : 0) | (frame.flags << 4) | frame.opcode, (frame.mask ? 0b1000_0000 : 0) | short_length, ].pack("CC") if short_length == 126 buffer << [length].pack("n") elsif short_length == 127 buffer << [length].pack("Q>") end buffer << frame.mask if frame.mask @stream.write(buffer) @stream.write(frame.payload) end |