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.
-
#initialize(io, handler, request, ws_extentions = nil) ⇒ Websockets
constructor
initialize the websocket protocol.
-
#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.
-
#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, #read, #set_timeout, #ssl?, #timeout?, #write
Constructor Details
#initialize(io, handler, request, ws_extentions = nil) ⇒ Websockets
initialize the websocket protocol.
5 6 7 8 9 10 |
# File 'lib/iodine/http/websockets.rb', line 5 def initialize io, handler, request, ws_extentions = nil @handler = handler @ws_extentions = ws_extentions request[:io] = self super(io) end |
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.
105 106 107 108 109 110 111 112 113 |
# File 'lib/iodine/http/websockets.rb', line 105 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).
171 172 173 |
# File 'lib/iodine/http/websockets.rb', line 171 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).
175 176 177 |
# File 'lib/iodine/http/websockets.rb', line 175 def self.default_timeout= val @default_timeout = val end |
.handshake(request, response, handler) ⇒ Object
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 166 167 168 |
# File 'lib/iodine/http/websockets.rb', line 138 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]*/).each {|ex| ex = ex.split(/[\s]*;[\s]*/); ( ( 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(', ') 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, request, ws_extentions) return true end |
.message_size_limit ⇒ Object
Gets the message byte size limit for a Websocket message. Defaults to 0 (no limit)
188 189 190 |
# File 'lib/iodine/http/websockets.rb', line 188 def self. @message_size_limit ||= 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 sessage size limit is exceeded, the disconnection will be immidiate as an attack will be assumed. The protocol’s normal disconnect sequesnce will be discarded.
184 185 186 |
# File 'lib/iodine/http/websockets.rb', line 184 def self.val @message_size_limit = 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.
128 129 130 131 132 |
# File 'lib/iodine/http/websockets.rb', line 128 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
116 117 118 |
# File 'lib/iodine/http/websockets.rb', line 116 def broadcast data self.class.broadcast data, self end |
#go_away ⇒ Object
a politer disconnection.
35 36 37 38 |
# File 'lib/iodine/http/websockets.rb', line 35 def go_away write CLOSE_FRAME close end |
#on_broadcast(data) ⇒ Object
handle broadcasts.
22 23 24 |
# File 'lib/iodine/http/websockets.rb', line 22 def on_broadcast data @handler.on_broadcast(data) if @handler.respond_to? :on_broadcast end |
#on_close ⇒ Object
cleanup after closing.
26 27 28 29 30 31 32 |
# File 'lib/iodine/http/websockets.rb', line 26 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.
18 19 20 |
# File 'lib/iodine/http/websockets.rb', line 18 def data StringIO.new(data) end |
#on_open ⇒ Object
continue to initialize the websocket protocol.
12 13 14 15 16 |
# File 'lib/iodine/http/websockets.rb', line 12 def on_open @parser = {body: '', 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.
41 42 43 |
# File 'lib/iodine/http/websockets.rb', line 41 def on_shutdown go_away end |
#ping ⇒ Object
Sends a ping.
88 89 90 |
# File 'lib/iodine/http/websockets.rb', line 88 def ping write PING_FRAME end |
#pong ⇒ Object
Sends an empty pong.
92 93 94 |
# File 'lib/iodine/http/websockets.rb', line 92 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.
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 |
# File 'lib/iodine/http/websockets.rb', line 56 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.
46 47 48 49 |
# File 'lib/iodine/http/websockets.rb', line 46 def send_response response, finish = false body = response.extract_body send_data body 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.
134 135 136 |
# File 'lib/iodine/http/websockets.rb', line 134 def unicast id, data self.class.unicast id, data end |