Class: Protocol::WebSocket::Connection
- Inherits:
-
Object
- Object
- Protocol::WebSocket::Connection
- Defined in:
- lib/protocol/websocket/connection.rb
Overview
Wraps a framer and implements for implementing connection specific interactions like reading and writing text.
Instance Attribute Summary collapse
-
#framer ⇒ Object
readonly
The framer which is used for reading and writing frames.
-
#frames ⇒ Object
Buffered frames which form part of a complete message.
-
#mask ⇒ Object
readonly
The (optional) mask which is used when generating frames.
-
#reader ⇒ Object
Returns the value of attribute reader.
-
#reserved ⇒ Object
readonly
The allowed reserved bits:.
-
#writer ⇒ Object
Returns the value of attribute writer.
Instance Method Summary collapse
-
#close ⇒ Object
Immediately transition the connection to the closed state and close the underlying connection.
-
#close! ⇒ Object
If not already closed, transition the connection to the closed state and send a close frame.
- #closed? ⇒ Boolean
- #flush ⇒ Object
-
#initialize(framer, mask: nil, **options) ⇒ Connection
constructor
A new instance of Connection.
- #open! ⇒ Object
- #pack_binary_frame(buffer, **options) ⇒ Object
- #pack_text_frame(buffer, **options) ⇒ Object
-
#read(**options) ⇒ Object
Read a message from the connection.
- #read_frame ⇒ Object
- #receive_binary(frame) ⇒ Object
- #receive_close(frame) ⇒ Object
- #receive_continuation(frame) ⇒ Object
- #receive_frame(frame) ⇒ Object
- #receive_ping(frame) ⇒ Object
- #receive_pong(frame) ⇒ Object
- #receive_text(frame) ⇒ Object
- #reserve!(bit) ⇒ Object
- #send_binary(buffer, **options) ⇒ Object
-
#send_close(code = Error::NO_ERROR, reason = "") ⇒ Object
Send a control frame with data containing a specified control sequence to begin the closing handshake.
- #send_ping(data = "") ⇒ Object
- #send_text(buffer, **options) ⇒ Object
-
#unpack_frames(frames) ⇒ Object
The default implementation for reading a message buffer.
-
#write(message, **options) ⇒ Object
Write a message to the connection.
- #write_frame(frame) ⇒ Object
Constructor Details
#initialize(framer, mask: nil, **options) ⇒ Connection
Returns a new instance of Connection.
16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/protocol/websocket/connection.rb', line 16 def initialize(framer, mask: nil, **) @framer = framer @mask = mask @state = :open @frames = [] @reserved = Frame::RESERVED @reader = self @writer = self end |
Instance Attribute Details
#framer ⇒ Object (readonly)
The framer which is used for reading and writing frames.
30 31 32 |
# File 'lib/protocol/websocket/connection.rb', line 30 def framer @framer end |
#frames ⇒ Object
Buffered frames which form part of a complete message.
39 40 41 |
# File 'lib/protocol/websocket/connection.rb', line 39 def frames @frames end |
#mask ⇒ Object (readonly)
The (optional) mask which is used when generating frames.
33 34 35 |
# File 'lib/protocol/websocket/connection.rb', line 33 def mask @mask end |
#reader ⇒ Object
Returns the value of attribute reader.
41 42 43 |
# File 'lib/protocol/websocket/connection.rb', line 41 def reader @reader end |
#reserved ⇒ Object (readonly)
The allowed reserved bits:
36 37 38 |
# File 'lib/protocol/websocket/connection.rb', line 36 def reserved @reserved end |
#writer ⇒ Object
Returns the value of attribute writer.
42 43 44 |
# File 'lib/protocol/websocket/connection.rb', line 42 def writer @writer end |
Instance Method Details
#close ⇒ Object
Immediately transition the connection to the closed state and close the underlying connection.
84 85 86 87 88 |
# File 'lib/protocol/websocket/connection.rb', line 84 def close(...) close!(...) @framer.close end |
#close! ⇒ Object
If not already closed, transition the connection to the closed state and send a close frame.
65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/protocol/websocket/connection.rb', line 65 def close!(...) unless @state == :closed @state = :closed begin send_close(...) rescue # Ignore errors. end end return self end |
#closed? ⇒ Boolean
79 80 81 |
# File 'lib/protocol/websocket/connection.rb', line 79 def closed? @state == :closed end |
#flush ⇒ Object
54 55 56 |
# File 'lib/protocol/websocket/connection.rb', line 54 def flush @framer.flush end |
#open! ⇒ Object
58 59 60 61 62 |
# File 'lib/protocol/websocket/connection.rb', line 58 def open! @state = :open return self end |
#pack_binary_frame(buffer, **options) ⇒ Object
191 192 193 194 195 196 |
# File 'lib/protocol/websocket/connection.rb', line 191 def pack_binary_frame(buffer, **) frame = BinaryFrame.new(mask: @mask) frame.pack(buffer) return frame end |
#pack_text_frame(buffer, **options) ⇒ Object
180 181 182 183 184 185 |
# File 'lib/protocol/websocket/connection.rb', line 180 def pack_text_frame(buffer, **) frame = TextFrame.new(mask: @mask) frame.pack(buffer) return frame end |
#read(**options) ⇒ Object
Read a message from the connection.
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/protocol/websocket/connection.rb', line 233 def read(**) @framer.flush while read_frame if @frames.last&.finished? frames = @frames @frames = [] buffer = @reader.unpack_frames(frames, **) return frames.first.(buffer) end end rescue ProtocolError => error close(error.code, error.) raise end |
#read_frame ⇒ Object
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/protocol/websocket/connection.rb', line 90 def read_frame return nil if closed? frame = @framer.read_frame unless (frame.flags & @reserved).zero? raise ProtocolError, "Received frame with reserved flags set!" end yield frame if block_given? frame.apply(self) return frame rescue ProtocolError => error close(error.code, error.) raise rescue => error close(Error::PROTOCOL_ERROR, error.) raise end |
#receive_binary(frame) ⇒ Object
126 127 128 129 130 131 132 |
# File 'lib/protocol/websocket/connection.rb', line 126 def receive_binary(frame) if @frames.empty? @frames << frame else raise ProtocolError, "Received binary, but expecting continuation!" end end |
#receive_close(frame) ⇒ Object
142 143 144 145 146 147 148 149 150 151 |
# File 'lib/protocol/websocket/connection.rb', line 142 def receive_close(frame) code, reason = frame.unpack # On receiving a close frame, we must enter the closed state: close!(code, reason) if code and code != Error::NO_ERROR raise ClosedError.new reason, code end end |
#receive_continuation(frame) ⇒ Object
134 135 136 137 138 139 140 |
# File 'lib/protocol/websocket/connection.rb', line 134 def receive_continuation(frame) if @frames.any? @frames << frame else raise ProtocolError, "Received unexpected continuation!" end end |
#receive_frame(frame) ⇒ Object
176 177 178 |
# File 'lib/protocol/websocket/connection.rb', line 176 def receive_frame(frame) raise ProtocolError, "Unhandled frame: #{frame}" end |
#receive_ping(frame) ⇒ Object
164 165 166 167 168 169 170 |
# File 'lib/protocol/websocket/connection.rb', line 164 def receive_ping(frame) if @state != :closed write_frame(frame.reply(mask: @mask)) else raise ProtocolError, "Cannot receive ping in state #{@state}" end end |
#receive_pong(frame) ⇒ Object
172 173 174 |
# File 'lib/protocol/websocket/connection.rb', line 172 def receive_pong(frame) # Ignore. end |
#receive_text(frame) ⇒ Object
118 119 120 121 122 123 124 |
# File 'lib/protocol/websocket/connection.rb', line 118 def receive_text(frame) if @frames.empty? @frames << frame else raise ProtocolError, "Received text, but expecting continuation!" end end |
#reserve!(bit) ⇒ Object
44 45 46 47 48 49 50 51 52 |
# File 'lib/protocol/websocket/connection.rb', line 44 def reserve!(bit) if (@reserved & bit).zero? raise ArgumentError, "Unable to use #{bit}!" end @reserved &= ~bit return true end |
#send_binary(buffer, **options) ⇒ Object
198 199 200 |
# File 'lib/protocol/websocket/connection.rb', line 198 def send_binary(buffer, **) write_frame(@writer.pack_binary_frame(buffer, **)) end |
#send_close(code = Error::NO_ERROR, reason = "") ⇒ Object
Send a control frame with data containing a specified control sequence to begin the closing handshake. Does not close the connection, until the remote end responds with a close frame.
203 204 205 206 207 208 209 |
# File 'lib/protocol/websocket/connection.rb', line 203 def send_close(code = Error::NO_ERROR, reason = "") frame = CloseFrame.new(mask: @mask) frame.pack(code, reason) self.write_frame(frame) self.flush end |
#send_ping(data = "") ⇒ Object
153 154 155 156 157 158 159 160 161 162 |
# File 'lib/protocol/websocket/connection.rb', line 153 def send_ping(data = "") if @state != :closed frame = PingFrame.new(mask: @mask) frame.pack(data) write_frame(frame) else raise ProtocolError, "Cannot send ping in state #{@state}" end end |
#send_text(buffer, **options) ⇒ Object
187 188 189 |
# File 'lib/protocol/websocket/connection.rb', line 187 def send_text(buffer, **) write_frame(@writer.pack_text_frame(buffer, **)) end |
#unpack_frames(frames) ⇒ Object
The default implementation for reading a message buffer.
227 228 229 |
# File 'lib/protocol/websocket/connection.rb', line 227 def unpack_frames(frames) frames.map(&:unpack).join("") end |
#write(message, **options) ⇒ Object
Write a message to the connection.
213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/protocol/websocket/connection.rb', line 213 def write(, **) # This is a compatibility shim for the previous implementation. We may want to eventually deprecate this use case... or maybe it's convenient enough to leave it around. if .is_a?(String) if .encoding == Encoding::UTF_8 return send_text(, **) else return send_binary(, **) end end .send(self, **) end |
#write_frame(frame) ⇒ Object
112 113 114 115 116 |
# File 'lib/protocol/websocket/connection.rb', line 112 def write_frame(frame) @framer.write_frame(frame) return frame end |