Class: Iodine::Http::WebsocketClient
- Inherits:
-
Object
- Object
- Iodine::Http::WebsocketClient
- Defined in:
- lib/iodine/http/websocket_client.rb
Overview
Create a simple Websocket Client(!).
This should be done from within an Iodine task, or the callbacks will not be called.
Use WebsocketClient.connect to initialize a client with all the callbacks needed.
Instance Attribute Summary collapse
-
#params ⇒ Object
Returns the value of attribute params.
-
#request ⇒ Object
Returns the value of attribute request.
-
#response ⇒ Object
Returns the value of attribute response.
Instance Method Summary collapse
-
#close ⇒ Object
closes the connection, if open.
-
#closed? ⇒ Boolean
checks if the socket is open (if the websocket was terminated abnormally, this might return true for a while after it should be false).
-
#cookies ⇒ Object
return a Hash with the HTTP cookies recieved during the HTTP’s handshake.
-
#initialize(options) ⇒ WebsocketClient
constructor
A new instance of WebsocketClient.
- #on(event_name, &block) ⇒ Object
- #on_close(&block) ⇒ Object
- #on_error(error = nil, &block) ⇒ Object
- #on_message(data = nil, &block) ⇒ Object
- #on_open ⇒ Object
- #on_shutdown ⇒ Object
-
#ssl? ⇒ Boolean
checks if this is an SSL websocket connection.
-
#write(data, op_code = nil, fin = true, ext = 0) ⇒ true, false
(also: #<<)
Sends data through the websocket, after client side masking.
Constructor Details
#initialize(options) ⇒ WebsocketClient
Returns a new instance of WebsocketClient.
13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/iodine/http/websocket_client.rb', line 13 def initialize @response = nil @options = @on_message = @options[:on_message] raise "Websocket client must have an #on_message Proc or handler." unless @on_message && @on_message.respond_to?(:call) @on_open = @options[:on_open] @on_close = @options[:on_close] @on_error = @options[:on_error] @renew = @options[:renew].to_i @options[:url] = URI.parse(@options[:url]) unless @options[:url].is_a?(URI) @connection_lock = Mutex.new raise TypeError, "Websocket Client `:send` should be either a String or a Proc object." if @options[:send] && !(@options[:send].is_a?(String) || @options[:send].is_a?(Proc)) on_close && (@io || raise("Connection error, cannot create websocket client")) unless connect end |
Instance Attribute Details
#params ⇒ Object
Returns the value of attribute params.
11 12 13 |
# File 'lib/iodine/http/websocket_client.rb', line 11 def params @params end |
#request ⇒ Object
Returns the value of attribute request.
11 12 13 |
# File 'lib/iodine/http/websocket_client.rb', line 11 def request @request end |
#response ⇒ Object
Returns the value of attribute response.
11 12 13 |
# File 'lib/iodine/http/websocket_client.rb', line 11 def response @response end |
Instance Method Details
#close ⇒ Object
closes the connection, if open
121 122 123 124 |
# File 'lib/iodine/http/websocket_client.rb', line 121 def close @renew = 0 @io.close if @io end |
#closed? ⇒ Boolean
checks if the socket is open (if the websocket was terminated abnormally, this might return true for a while after it should be false).
127 128 129 |
# File 'lib/iodine/http/websocket_client.rb', line 127 def closed? (@io && @io.io) ? @io.io.closed? : true end |
#cookies ⇒ Object
return a Hash with the HTTP cookies recieved during the HTTP’s handshake.
137 138 139 |
# File 'lib/iodine/http/websocket_client.rb', line 137 def @request. end |
#on(event_name, &block) ⇒ Object
28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/iodine/http/websocket_client.rb', line 28 def on event_name, &block return false unless block case event_name when :message @on_message = block when :close @on_close = block when :open raise 'The on_open even is invalid at this point.' end end |
#on_close(&block) ⇒ Object
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 |
# File 'lib/iodine/http/websocket_client.rb', line 82 def on_close(&block) return @on_close = block if block if @renew > 0 renew_proc = Proc.new do @io = nil begin raise unless connect rescue @renew -= 1 if @renew <= 0 Iodine.fatal "WebsocketClient renewal FAILED for #{@options[:url]}" on_close else Iodine.warn "WebsocketClient renewal failed for #{@options[:url]}, #{@renew} attempts left" renew_proc.call end false end end @connection_lock.synchronize { renew_proc.call } else begin instance_exec(&@on_close) if @on_close rescue => e @on_error ? @on_error.call(e) : raise(e) end end end |
#on_error(error = nil, &block) ⇒ Object
110 111 112 113 114 |
# File 'lib/iodine/http/websocket_client.rb', line 110 def on_error(error = nil, &block) return @on_error = block if block instance_exec(error, &@on_error) if @on_error on_close unless @io # if the connection was initialized, :on_close will be called by Iodine end |
#on_message(data = nil, &block) ⇒ Object
41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/iodine/http/websocket_client.rb', line 41 def (data = nil, &block) unless data @on_message = block if block return @on_message end begin instance_exec( data, &@on_message) rescue => e @on_error ? @on_error.call(e) : raise(e) end end |
#on_open ⇒ Object
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 |
# File 'lib/iodine/http/websocket_client.rb', line 53 def on_open raise 'The on_open even is invalid at this point.' if block_given? @renew = @options[:renew].to_i @io = @request[:io] Iodine::Http::Request.parse @request begin instance_exec(&@on_open) if @on_open rescue => e @on_error ? @on_error.call(e) : raise(e) end if @options[:every] && @options[:send] Iodine.run_every @options[:every], self, @options do |ws, client_params, timer| if ws.closed? timer.stop! next end if client_params[:send].is_a?(String) ws.write client_params[:send] elsif client_params[:send].is_a?(Proc) begin ws.instance_exec(&client_params[:send]) rescue => e @on_error ? @on_error.call(e) : raise(e) end end end end end |
#on_shutdown ⇒ Object
116 117 118 |
# File 'lib/iodine/http/websocket_client.rb', line 116 def on_shutdown @renew = 0 end |
#ssl? ⇒ Boolean
checks if this is an SSL websocket connection.
132 133 134 |
# File 'lib/iodine/http/websocket_client.rb', line 132 def ssl? @request.ssl? end |
#write(data, op_code = nil, fin = true, ext = 0) ⇒ true, false Also known as: <<
Sends data through the websocket, after client side masking.
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 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/iodine/http/websocket_client.rb', line 144 def write data, op_code = nil, fin = true, ext = 0 return false if !data || data.empty? 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.edit_message data } if @ws_extentions end byte_size = data.bytesize if byte_size > (::Iodine::Http::Websockets::FRAME_SIZE_LIMIT+2) # sections = byte_size/FRAME_SIZE_LIMIT + (byte_size % ::Iodine::Http::Websockets::FRAME_SIZE_LIMIT ? 1 : 0) ret = write( data.slice!( 0...::Iodine::Http::Websockets::FRAME_SIZE_LIMIT ), op_code, data.empty?, ext) && (ext = op_code = 0) until data.empty? return ret # 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 | 128).chr elsif byte_size.bit_length <= 16 header << 254.chr header << [byte_size].pack('S>'.freeze) else header << 255.chr header << [byte_size].pack('Q>'.freeze) end @@make_mask_proc ||= Proc.new {Random.rand(251) + 1} mask = Array.new(4, &(@@make_mask_proc)) header << mask.pack('C*'.freeze) @connection_lock.synchronize do return false if @io.nil? || @io.closed? @io.write header i = -1; @io.write(data.bytes.map! {|b| (b ^ mask[i = (i + 1)%4]) } .pack('C*'.freeze)) && true end end |