Class: Iodine::Http::Websockets
- Defined in:
- lib/iodine/http/websockets.rb
Instance Attribute Summary
Attributes inherited from Protocol
Class Method Summary collapse
-
.broadcast(data, ignore_io = nil) ⇒ Object
Broadcasts data to ALL the websocket connections EXCEPT the once specified (if specified).
-
.default_timeout ⇒ Object
Gets the new connection timeout in seconds.
-
.default_timeout=(val) ⇒ Object
Sets the new connection timeout in seconds.
- .handshake(request, response, handler) ⇒ Object
-
.message_size_limit ⇒ Object
Gets the message byte size limit for a Websocket message.
-
.message_size_limit=(val) ⇒ Object
Sets the message byte size limit for a Websocket message.
-
.unicast(id, data) ⇒ true, false
Unicast data to a specific websocket connection (ONLY the connection specified).
Instance Method Summary collapse
-
#broadcast(data) ⇒ Object
Broadcasts the data to all the listening websockets, except self.
-
#go_away ⇒ Object
a politer disconnection.
-
#on_broadcast(data) ⇒ Object
handle broadcasts.
-
#on_close ⇒ Object
cleanup after closing.
-
#on_message(data) ⇒ Object
parse and handle messages.
-
#on_open ⇒ Object
continue to initialize the websocket protocol.
-
#on_shutdown ⇒ Object
a politer disconnection during shutdown.
-
#ping ⇒ Object
Sends a ping and calles the :on_ping callback (if exists).
-
#pong ⇒ Object
Sends an empty pong.
-
#send_data(data, op_code = nil, fin = true, ext = 0) ⇒ Object
(also: #<<)
Sends the data as one (or more) Websocket frames.
-
#send_response(response, finish = false) ⇒ Object
(also: #stream_response)
allow Http responses to be used for sending Websocket data.
-
#unicast(id, data) ⇒ true, false
Unicasts the data to the requested connection.
Methods inherited from Protocol
#call, #close, #closed?, each, #id, #initialize, #read, #set_timeout, #ssl?, #timeout?, #write
Constructor Details
This class inherits a constructor from Iodine::Protocol
Class Method Details
.broadcast(data, ignore_io = nil) ⇒ Object
Broadcasts data to ALL the websocket connections EXCEPT the once specified (if specified).
Data broadcasted will be recived by the websocket handler it’s #on_broadcast(ws) method (if exists).
Accepts:
- data
-
One object of data. Usually a Hash, Array, String or a JSON formatted object.
- ignore_io (optional)
-
The IO to be ignored by the broadcast. Usually the broadcaster’s IO.
102 103 104 105 106 107 108 109 110 |
# File 'lib/iodine/http/websockets.rb', line 102 def self.broadcast data, ignore_io = nil if ignore_io ig_id = ignore_io.object_id each {|io| Iodine.run io, data, &broadcast_proc unless io.object_id == ig_id} else each {|io| Iodine.run io, data, &broadcast_proc } end true end |
.default_timeout ⇒ Object
Gets the new connection timeout in seconds. Whenever this timeout is reached, a ping will be sent. Defaults to 40 (seconds).
168 169 170 |
# File 'lib/iodine/http/websockets.rb', line 168 def self.default_timeout @default_timeout end |
.default_timeout=(val) ⇒ Object
Sets the new connection timeout in seconds. Whenever this timeout is reached, a ping will be sent. Defaults to 40 (seconds).
172 173 174 |
# File 'lib/iodine/http/websockets.rb', line 172 def self.default_timeout= val @default_timeout = val end |
.handshake(request, response, handler) ⇒ Object
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/iodine/http/websockets.rb', line 135 def self.handshake request, response, handler # review handshake (version, extentions) # should consider adopting the websocket gem for handshake and framing: # https://github.com/imanel/websocket-ruby # http://www.rubydoc.info/github/imanel/websocket-ruby return refuse response unless handler || handler == true io = request[:io] response.keep_alive = true response.status = 101 response['upgrade'.freeze] = 'websocket'.freeze response['content-length'.freeze] = '0'.freeze response['connection'.freeze] = 'Upgrade'.freeze response['sec-websocket-version'.freeze] = '13'.freeze # Note that the client is only offering to use any advertised extensions # and MUST NOT use them unless the server indicates that it wishes to use the extension. ws_extentions = [] ext = [] request['sec-websocket-extensions'.freeze].to_s.split(/[\s]*[,][\s]*/.freeze).each {|ex| ex = ex.split(/[\s]*;[\s]*/.freeze); ( ( tmp = SUPPORTED_EXTENTIONS[ ex[0] ].call(ex[1..-1]) ) && (ws_extentions << tmp) && (ext << tmp.name) ) if SUPPORTED_EXTENTIONS[ ex[0] ] } ext.compact! if ext.any? response['sec-websocket-extensions'.freeze] = ext.join(', '.freeze) else ws_extentions = nil end response['sec-websocket-accept'.freeze] = Digest::SHA1.base64digest(request['sec-websocket-key'.freeze] + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'.freeze) response.session # Iodine.log "#{@request[:client_ip]} [#{Time.now.utc}] - #{@connection.object_id} Upgraded HTTP to WebSockets.\n" response.finish self.new(io.io, handler: handler, request: request, ext: ws_extentions) return true end |
.message_size_limit ⇒ Object
Gets the message byte size limit for a Websocket message. Defaults to 0 (no limit)
185 186 187 |
# File 'lib/iodine/http/websockets.rb', line 185 def self. ||= 0 end |
.message_size_limit=(val) ⇒ Object
Sets the message byte size limit for a Websocket message. Defaults to 0 (no limit)
Although memory will be allocated for the latest TCP/IP frame, this allows the websocket to disconnect if the incoming expected message size exceeds the allowed maximum size.
If the message size limit is exceeded, the disconnection will be immidiate as an attack will be assumed. The protocol’s normal disconnect sequesnce will be discarded.
181 182 183 |
# File 'lib/iodine/http/websockets.rb', line 181 def self.=val = val end |
.unicast(id, data) ⇒ true, false
Unicast data to a specific websocket connection (ONLY the connection specified).
Data broadcasted will be recived by the websocket handler it’s #on_broadcast(ws) method (if exists). Accepts:
- uuid
-
the UUID of the websocket connection recipient.
- data
-
the data to be sent.
125 126 127 128 129 |
# File 'lib/iodine/http/websockets.rb', line 125 def self.unicast id, data return false unless id && data each {|io| next unless io.id == id; Iodine.run io, data, &broadcast_proc; return true} false end |
Instance Method Details
#broadcast(data) ⇒ Object
Broadcasts the data to all the listening websockets, except self. See broadcast
113 114 115 |
# File 'lib/iodine/http/websockets.rb', line 113 def broadcast data self.class.broadcast data, self end |
#go_away ⇒ Object
a politer disconnection.
31 32 33 34 |
# File 'lib/iodine/http/websockets.rb', line 31 def go_away write CLOSE_FRAME close end |
#on_broadcast(data) ⇒ Object
handle broadcasts.
18 19 20 |
# File 'lib/iodine/http/websockets.rb', line 18 def on_broadcast data @locker.synchronize { @handler.on_broadcast(data) } if @handler.respond_to? :on_broadcast end |
#on_close ⇒ Object
cleanup after closing.
22 23 24 25 26 27 28 |
# File 'lib/iodine/http/websockets.rb', line 22 def on_close @handler.on_close if @handler.respond_to? :on_close if @ws_extentions @ws_extentions.each { |ex| ex.close } @ws_extentions.clear end end |
#on_message(data) ⇒ Object
parse and handle messages.
14 15 16 |
# File 'lib/iodine/http/websockets.rb', line 14 def data StringIO.new(data) end |
#on_open ⇒ Object
continue to initialize the websocket protocol.
5 6 7 8 9 10 11 12 |
# File 'lib/iodine/http/websockets.rb', line 5 def on_open @handler = [:handler] @ws_extentions = [:ext] [:request][:io] = self @parser = {body: String.new, stage: 0, step: 0, mask_key: [], len_bytes: []} set_timeout self.class.default_timeout @handler.on_open if @handler.respond_to? :on_open end |
#on_shutdown ⇒ Object
a politer disconnection during shutdown.
37 38 39 40 |
# File 'lib/iodine/http/websockets.rb', line 37 def on_shutdown @handler.on_shutdown if @handler.respond_to?(:on_shutdown) go_away end |
#ping ⇒ Object
Sends a ping and calles the :on_ping callback (if exists).
85 86 87 |
# File 'lib/iodine/http/websockets.rb', line 85 def ping write(PING_FRAME) && ( (@handler.respond_to?(:on_ping) && @handler.on_ping) || true) end |
#pong ⇒ Object
Sends an empty pong.
89 90 91 |
# File 'lib/iodine/http/websockets.rb', line 89 def pong write PONG_FRAME end |
#send_data(data, op_code = nil, fin = true, ext = 0) ⇒ Object Also known as: <<
Sends the data as one (or more) Websocket frames.
Use THIS method to send data using the Websocket protocol. Using Protocol#write will bypass the Websocket data framing and send the raw data, breaking the connection.
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 |
# File 'lib/iodine/http/websockets.rb', line 53 def send_data data, op_code = nil, fin = true, ext = 0 return false if !data || data.empty? return false if @io.closed? data = data.dup # needed? unless op_code # apply extenetions to the message as a whole op_code = (data.encoding == ::Encoding::UTF_8 ? 1 : 2) @ws_extentions.each { |ex| ext |= ex. data } if @ws_extentions end byte_size = data.bytesize if byte_size > (FRAME_SIZE_LIMIT+2) # sections = byte_size/FRAME_SIZE_LIMIT + (byte_size%FRAME_SIZE_LIMIT ? 1 : 0) send_data( data.slice!( 0...FRAME_SIZE_LIMIT ), op_code, data.empty?, ext) && (ext = op_code = 0) until data.empty? return true # avoid sending an empty frame. end @ws_extentions.each { |ex| ext |= ex.edit_frame data } if @ws_extentions header = ( (fin ? 0b10000000 : 0) | (op_code & 0b00001111) | ext).chr.force_encoding(::Encoding::ASCII_8BIT) if byte_size < 125 header << byte_size.chr elsif byte_size.bit_length <= 16 header << 126.chr header << [byte_size].pack('S>'.freeze) else header << 127.chr header << [byte_size].pack('Q>'.freeze) end write header write(data) && true end |
#send_response(response, finish = false) ⇒ Object Also known as: stream_response
allow Http responses to be used for sending Websocket data.
43 44 45 46 |
# File 'lib/iodine/http/websockets.rb', line 43 def send_response response, finish = false body = response.extract_body send_data body.read end |
#unicast(id, data) ⇒ true, false
Returns Unicasts the data to the requested connection. returns ‘true` if the requested connection id was found on this server. See unicast.
131 132 133 |
# File 'lib/iodine/http/websockets.rb', line 131 def unicast id, data self.class.unicast id, data end |