iodine 0.1.21 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -2
  3. data/.travis.yml +23 -2
  4. data/CHANGELOG.md +9 -2
  5. data/README.md +232 -179
  6. data/Rakefile +13 -1
  7. data/bin/config.ru +63 -0
  8. data/bin/console +6 -0
  9. data/bin/echo +42 -32
  10. data/bin/http-hello +62 -0
  11. data/bin/http-playground +124 -0
  12. data/bin/playground +62 -0
  13. data/bin/poc/Gemfile.lock +23 -0
  14. data/bin/poc/README.md +37 -0
  15. data/bin/poc/config.ru +66 -0
  16. data/bin/poc/gemfile +1 -0
  17. data/bin/poc/www/index.html +57 -0
  18. data/bin/raw-rbhttp +35 -0
  19. data/bin/raw_broadcast +66 -0
  20. data/bin/test_with_faye +40 -0
  21. data/bin/ws-broadcast +108 -0
  22. data/bin/ws-echo +108 -0
  23. data/exe/iodine +59 -0
  24. data/ext/iodine/base64.c +264 -0
  25. data/ext/iodine/base64.h +72 -0
  26. data/ext/iodine/bscrypt-common.h +109 -0
  27. data/ext/iodine/bscrypt.h +49 -0
  28. data/ext/iodine/extconf.rb +41 -0
  29. data/ext/iodine/hex.c +123 -0
  30. data/ext/iodine/hex.h +70 -0
  31. data/ext/iodine/http.c +200 -0
  32. data/ext/iodine/http.h +128 -0
  33. data/ext/iodine/http1.c +402 -0
  34. data/ext/iodine/http1.h +56 -0
  35. data/ext/iodine/http1_simple_parser.c +473 -0
  36. data/ext/iodine/http1_simple_parser.h +59 -0
  37. data/ext/iodine/http_request.h +128 -0
  38. data/ext/iodine/http_response.c +1606 -0
  39. data/ext/iodine/http_response.h +393 -0
  40. data/ext/iodine/http_response_http1.h +374 -0
  41. data/ext/iodine/iodine_core.c +641 -0
  42. data/ext/iodine/iodine_core.h +70 -0
  43. data/ext/iodine/iodine_http.c +615 -0
  44. data/ext/iodine/iodine_http.h +19 -0
  45. data/ext/iodine/iodine_websocket.c +430 -0
  46. data/ext/iodine/iodine_websocket.h +21 -0
  47. data/ext/iodine/libasync.c +552 -0
  48. data/ext/iodine/libasync.h +117 -0
  49. data/ext/iodine/libreact.c +347 -0
  50. data/ext/iodine/libreact.h +244 -0
  51. data/ext/iodine/libserver.c +912 -0
  52. data/ext/iodine/libserver.h +435 -0
  53. data/ext/iodine/libsock.c +950 -0
  54. data/ext/iodine/libsock.h +478 -0
  55. data/ext/iodine/misc.c +181 -0
  56. data/ext/iodine/misc.h +76 -0
  57. data/ext/iodine/random.c +193 -0
  58. data/ext/iodine/random.h +48 -0
  59. data/ext/iodine/rb-call.c +127 -0
  60. data/ext/iodine/rb-call.h +60 -0
  61. data/ext/iodine/rb-libasync.h +79 -0
  62. data/ext/iodine/rb-rack-io.c +389 -0
  63. data/ext/iodine/rb-rack-io.h +17 -0
  64. data/ext/iodine/rb-registry.c +213 -0
  65. data/ext/iodine/rb-registry.h +33 -0
  66. data/ext/iodine/sha1.c +359 -0
  67. data/ext/iodine/sha1.h +85 -0
  68. data/ext/iodine/sha2.c +825 -0
  69. data/ext/iodine/sha2.h +138 -0
  70. data/ext/iodine/siphash.c +136 -0
  71. data/ext/iodine/siphash.h +15 -0
  72. data/ext/iodine/spnlock.h +235 -0
  73. data/ext/iodine/websockets.c +696 -0
  74. data/ext/iodine/websockets.h +120 -0
  75. data/ext/iodine/xor-crypt.c +189 -0
  76. data/ext/iodine/xor-crypt.h +107 -0
  77. data/iodine.gemspec +25 -18
  78. data/lib/iodine.rb +57 -58
  79. data/lib/iodine/http.rb +0 -189
  80. data/lib/iodine/protocol.rb +36 -245
  81. data/lib/iodine/version.rb +1 -1
  82. data/lib/rack/handler/iodine.rb +145 -2
  83. metadata +115 -37
  84. data/bin/core_http_test +0 -51
  85. data/bin/em playground +0 -56
  86. data/bin/hello_world +0 -75
  87. data/bin/setup +0 -7
  88. data/lib/iodine/client.rb +0 -5
  89. data/lib/iodine/core.rb +0 -102
  90. data/lib/iodine/core_init.rb +0 -143
  91. data/lib/iodine/http/hpack.rb +0 -553
  92. data/lib/iodine/http/http1.rb +0 -251
  93. data/lib/iodine/http/http2.rb +0 -507
  94. data/lib/iodine/http/rack_support.rb +0 -108
  95. data/lib/iodine/http/request.rb +0 -462
  96. data/lib/iodine/http/response.rb +0 -474
  97. data/lib/iodine/http/session.rb +0 -143
  98. data/lib/iodine/http/websocket_client.rb +0 -335
  99. data/lib/iodine/http/websocket_handler.rb +0 -101
  100. data/lib/iodine/http/websockets.rb +0 -336
  101. data/lib/iodine/io.rb +0 -56
  102. data/lib/iodine/logging.rb +0 -46
  103. data/lib/iodine/settings.rb +0 -158
  104. data/lib/iodine/ssl_connector.rb +0 -48
  105. data/lib/iodine/timers.rb +0 -95
@@ -1,143 +0,0 @@
1
- module Iodine
2
- module Http
3
- # session management for Iodine's Http Server
4
- module SessionManager
5
-
6
- module MemSessionStorage
7
- class SessionObject < Hash
8
- def initialize id
9
- super()
10
- self[:__session_id] = id
11
- end
12
- # returns the session id (the session cookie value).
13
- def id
14
- self[:__session_id]
15
- end
16
- end
17
- @mem_storage = {}
18
- def self.fetch key
19
- @mem_storage[key] ||= SessionObject.new(key)
20
- end
21
- end
22
- module FileSessionStorage
23
- class SessionObject
24
- # called by the Plezi framework to initiate a session with the id requested
25
- def initialize id
26
- @filename = File.join Dir.tmpdir, "iodine_#{Iodine::Http.session_token}_#{id}"
27
- @data ||= {}
28
- @id = id
29
- end
30
- # returns the session id (the session cookie value).
31
- def id
32
- @id
33
- end
34
- # Get a key from the session data store.
35
- #
36
- # Due to different considirations, all keys will be converted to strings, so that `"name" == :name` and `1234 == "1234"`.
37
- # If you store two keys that evaluate as the same string, they WILL override each other.
38
- def [] key
39
- key = key.to_s
40
- load
41
- @data[key]
42
- end
43
- alias :fetch :[]
44
-
45
- # Stores a key in the session's data store.
46
- #
47
- # Due to different considirations, all keys will be converted to strings, so that `"name" == :name` and `1234 == "1234"`.
48
- # If you store two keys that evaluate as the same string, they WILL override each other.
49
- def []= key, value
50
- return delete key if value.nil?
51
- key = key.to_s
52
- load
53
- @data[key] = value
54
- save
55
- value
56
- end
57
- alias :store :[]=
58
-
59
- # @return [Hash] returns a shallow copy of the current session data as a Hash.
60
- def to_h
61
- load
62
- @data.dup
63
- end
64
-
65
- # @return [String] returns the Session data in YAML format.
66
- def to_s
67
- load
68
- @data.to_yaml
69
- end
70
-
71
- # Removes a key from the session's data store.
72
- def delete key
73
- load
74
- ret = @data.delete key.to_s
75
- save
76
- ret
77
- end
78
-
79
- # Clears the session's data.
80
- def clear
81
- @data.clear
82
- nil
83
- end
84
-
85
- # Forced the session's data to be reloaded
86
- def refresh
87
- load
88
- end
89
- protected
90
- # def destroy
91
- # # save data to tmp-file
92
- # File.delete @filename if ::File.file?(@filename) # && !::File.directory?(@filename)
93
- # end
94
- def save
95
- # save data to tmp-file
96
- IO.write @filename, @data.to_yaml
97
- end
98
- def load
99
- @data = YAML.load IO.read(@filename) if ::File.file?(@filename)
100
- end
101
- end
102
- def self.fetch key
103
- SessionObject.new key
104
- end
105
- end
106
-
107
- module_function
108
- # returns a session object
109
- def get id
110
- storage.fetch(id)
111
- end
112
- # Sets the session storage system, to allow for different storage systems.
113
- #
114
- # A Session Storage system must answer only one methods:
115
- # fetch(id):: returns a Hash like session object with all the session's data or a fresh session object if the session object did not exist before
116
- #
117
- # The Session Object should update the storage whenever data is saved to the session Object.
118
- # This is important also because a websocket 'session' could exist simultaneously with other HTTP requests (multiple browser windows) and the data should be kept updated at all times.
119
- def storage= session_storage = nil
120
- case session_storage
121
- when :file, nil
122
- @storage = Iodine::Http::SessionManager::FileSessionStorage
123
- when :mem
124
- @storage = Iodine::Http::SessionManager::MemSessionStorage
125
- else
126
- @storage = session_storage
127
- end
128
- end
129
-
130
- # returns the current session storage system.
131
- def storage
132
- @storage ||= Iodine::Http::SessionManager::FileSessionStorage
133
- end
134
- end
135
- end
136
- end
137
- # A hash like interface for storing request session data.
138
- # The store must implement:
139
- # store(key, value) (aliased as []=);
140
- # fetch(key, default = nil) (aliased as []);
141
- # delete(key);
142
- # clear;
143
- # id(); (returns the session id)
@@ -1,335 +0,0 @@
1
-
2
- module Iodine
3
- module Http
4
- # Create a simple Websocket Client(!).
5
- #
6
- # This should be done from within an Iodine task, or the callbacks will not be called.
7
- #
8
- # Use {Iodine::Http::WebsocketClient.connect} to initialize a client with all the callbacks needed.
9
- class WebsocketClient
10
-
11
- attr_accessor :response, :request, :params
12
-
13
- def initialize options
14
- @response = nil
15
- @options = options
16
- @on_message = @options[:on_message]
17
- raise "Websocket client must have an #on_message Proc or handler." unless @on_message && @on_message.respond_to?(:call)
18
- @on_open = @options[:on_open]
19
- @on_close = @options[:on_close]
20
- @on_error = @options[:on_error]
21
- @renew = @options[:renew].to_i
22
- @options[:url] = URI.parse(@options[:url]) unless @options[:url].is_a?(URI)
23
- @connection_lock = Mutex.new
24
- 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))
25
- on_close && (@io || raise("Connection error, cannot create websocket client")) unless connect
26
- end
27
-
28
- def on event_name, &block
29
- return false unless block
30
- case event_name
31
- when :message
32
- @on_message = block
33
- when :close
34
- @on_close = block
35
- when :open
36
- raise 'The on_open even is invalid at this point.'
37
- end
38
-
39
- end
40
-
41
- def on_message(data = nil, &block)
42
- unless data
43
- @on_message = block if block
44
- return @on_message
45
- end
46
- begin
47
- instance_exec( data, &@on_message)
48
- rescue => e
49
- @on_error ? @on_error.call(e) : raise(e)
50
- end
51
- end
52
-
53
- def on_open
54
- raise 'The on_open even is invalid at this point.' if block_given?
55
- @renew = @options[:renew].to_i
56
- @io = @request[:io]
57
- Iodine::Http::Request.parse @request
58
- begin
59
- instance_exec(&@on_open) if @on_open
60
- rescue => e
61
- @on_error ? @on_error.call(e) : raise(e)
62
- end
63
- if @options[:every] && @options[:send]
64
- Iodine.run_every @options[:every], self, @options do |ws, client_params, timer|
65
- if ws.closed?
66
- timer.stop!
67
- next
68
- end
69
- if client_params[:send].is_a?(String)
70
- ws.write client_params[:send]
71
- elsif client_params[:send].is_a?(Proc)
72
- begin
73
- ws.instance_exec(&client_params[:send])
74
- rescue => e
75
- @on_error ? @on_error.call(e) : raise(e)
76
- end
77
- end
78
- end
79
- end
80
- end
81
-
82
- def on_close(&block)
83
- return @on_close = block if block
84
- if @renew > 0
85
- renew_proc = Proc.new do
86
- @io = nil
87
- begin
88
- raise unless connect
89
- rescue
90
- @renew -= 1
91
- if @renew <= 0
92
- Iodine.fatal "WebsocketClient renewal FAILED for #{@options[:url]}"
93
- on_close
94
- else
95
- Iodine.warn "WebsocketClient renewal failed for #{@options[:url]}, #{@renew} attempts left"
96
- renew_proc.call
97
- end
98
- false
99
- end
100
- end
101
- @connection_lock.synchronize { renew_proc.call }
102
- else
103
- begin
104
- instance_exec(&@on_close) if @on_close
105
- rescue => e
106
- @on_error ? @on_error.call(e) : raise(e)
107
- end
108
- end
109
- end
110
- def on_error(error = nil, &block)
111
- return @on_error = block if block
112
- instance_exec(error, &@on_error) if @on_error
113
- on_close unless @io # if the connection was initialized, :on_close will be called by Iodine
114
- end
115
-
116
- def on_shutdown
117
- @renew = 0
118
- end
119
-
120
- # closes the connection, if open
121
- def close
122
- @renew = 0
123
- @io.close if @io
124
- end
125
-
126
- # 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
- def closed?
128
- (@io && @io.io) ? @io.io.closed? : true
129
- end
130
-
131
- # checks if this is an SSL websocket connection.
132
- def ssl?
133
- @request.ssl?
134
- end
135
-
136
- # return a Hash with the HTTP cookies recieved during the HTTP's handshake.
137
- def cookies
138
- @request.cookies
139
- end
140
-
141
- # Sends data through the websocket, after client side masking.
142
- #
143
- # @return [true, false] Returns the true if the data was actually sent or nil if no data was sent.
144
- def write data, op_code = nil, fin = true, ext = 0
145
- return false if !data || data.empty?
146
- data = data.dup # needed?
147
- unless op_code # apply extenetions to the message as a whole
148
- op_code = (data.encoding == ::Encoding::UTF_8 ? 1 : 2)
149
- # @ws_extentions.each { |ex| ext |= ex.edit_message data } if @ws_extentions
150
- end
151
- byte_size = data.bytesize
152
- if byte_size > (::Iodine::Http::Websockets::FRAME_SIZE_LIMIT+2)
153
- # sections = byte_size/FRAME_SIZE_LIMIT + (byte_size % ::Iodine::Http::Websockets::FRAME_SIZE_LIMIT ? 1 : 0)
154
- ret = write( data.slice!( 0...::Iodine::Http::Websockets::FRAME_SIZE_LIMIT ), op_code, data.empty?, ext) && (ext = op_code = 0) until data.empty?
155
- return ret # avoid sending an empty frame.
156
- end
157
- # @ws_extentions.each { |ex| ext |= ex.edit_frame data } if @ws_extentions
158
- header = ( (fin ? 0b10000000 : 0) | (op_code & 0b00001111) | ext).chr.force_encoding(::Encoding::ASCII_8BIT)
159
-
160
- if byte_size < 125
161
- header << (byte_size | 128).chr
162
- elsif byte_size.bit_length <= 16
163
- header << 254.chr
164
- header << [byte_size].pack('S>'.freeze)
165
- else
166
- header << 255.chr
167
- header << [byte_size].pack('Q>'.freeze)
168
- end
169
- @@make_mask_proc ||= Proc.new {Random.rand(251) + 1}
170
- mask = Array.new(4, &(@@make_mask_proc))
171
- header << mask.pack('C*'.freeze)
172
- @connection_lock.synchronize do
173
- return false if @io.nil? || @io.closed?
174
- @io.write header
175
- i = -1;
176
- @io.write(data.bytes.map! {|b| (b ^ mask[i = (i + 1)%4]) } .pack('C*'.freeze)) && true
177
- end
178
- end
179
- alias :<< :write
180
-
181
- protected
182
-
183
- def connect
184
- return false if @io && !@io.closed?
185
- socket = nil
186
- url = @options[:url]
187
- @options[:renew] ||= 5 if @options[:every] && @options[:send]
188
-
189
- ssl = url.scheme == "https" || url.scheme == "wss"
190
-
191
- url.port ||= ssl ? 443 : 80
192
- url.path = '/' if url.path.to_s.empty?
193
- socket = TCPSocket.new(url.host, url.port)
194
- if ssl
195
- context = OpenSSL::SSL::SSLContext.new
196
- context.cert_store = OpenSSL::X509::Store.new
197
- context.cert_store.set_default_paths
198
- context.set_params verify_mode: (@options[:verify_mode] || OpenSSL::SSL::VERIFY_NONE) # OpenSSL::SSL::VERIFY_PEER #OpenSSL::SSL::VERIFY_NONE
199
- ssl = OpenSSL::SSL::SSLSocket.new(socket, context)
200
- ssl.sync_close = true
201
- ssl.connect
202
- end
203
- # prep custom headers
204
- custom_headers = String.new
205
- custom_headers = @options[:headers] if @options[:headers].is_a?(String)
206
- @options[:headers].each {|k, v| custom_headers << "#{k.to_s}: #{v.to_s}\r\n"} if @options[:headers].is_a?(Hash)
207
- @options[:cookies].each {|k, v| raise 'Illegal cookie name' if k.to_s.match(/[\x00-\x20\(\)<>@,;:\\\"\/\[\]\?\=\{\}\s]/.freeze); custom_headers << "Cookie: #{ k }=#{ Iodine::Http::Request.encode_url v }\r\n"} if @options[:cookies].is_a?(Hash)
208
-
209
- # send protocol upgrade request
210
- websocket_key = [(Array.new(16) {rand 255} .pack 'c*'.freeze )].pack('m0*'.freeze)
211
- (ssl || socket).write "GET #{url.path}#{url.query.to_s.empty? ? ''.freeze : ("?#{url.query}")} HTTP/1.1\r\nHost: #{url.host}#{url.port ? (":#{url.port.to_s}") : ''.freeze}\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nOrigin: #{ssl ? 'https'.freeze : 'http'.freeze}://#{url.host}\r\nSec-WebSocket-Key: #{websocket_key}\r\nSec-WebSocket-Version: 13\r\n#{custom_headers}\r\n"
212
- # wait for answer - make sure we don't over-read
213
- # (a websocket message might be sent immidiately after connection is established)
214
- reply = String.new.force_encoding(::Encoding::ASCII_8BIT)
215
- stop_time = Time.now + (@options[:timeout] || 5)
216
- stop_reply = "\r\n\r\n".freeze
217
- until reply[-4..-1] == stop_reply
218
- begin
219
- reply << ( ssl ? ssl.read_nonblock(1) : socket.recv_nonblock(1) )
220
- rescue Errno::EWOULDBLOCK, OpenSSL::SSL::SSLErrorWaitReadable => e
221
- raise "Websocket client handshake timed out (HTTP reply not recieved)\n\n Got Only: #{reply}" if Time.now >= stop_time
222
- IO.select [socket], nil, nil, (@options[:timeout] || 5)
223
- retry
224
- end
225
- raise "Connection failed" if socket.closed?
226
- end
227
- # review reply
228
- raise "Connection Refused. Reply was:\r\n #{reply}" unless reply.lines[0].match(/^HTTP\/[\d\.]+ 101/i.freeze)
229
- raise 'Websocket Key Authentication failed.' unless reply.match(/^Sec-WebSocket-Accept:[\s]*([^\s]*)/i.freeze) && reply.match(/^Sec-WebSocket-Accept:[\s]*([^\s]*)/i.freeze)[1] == Digest::SHA1.base64digest(websocket_key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
230
- # read the body's data and parse any incoming data.
231
- @request = Iodine::Http::Request.new
232
- @request[:method] = 'GET'.freeze
233
- @request['host'.freeze] = "#{url.host}:#{url.port}"
234
- @request[:query] = url.path
235
- @request[:version] = '1.1'.freeze
236
- reply = StringIO.new reply
237
- reply.gets
238
-
239
- until reply.eof?
240
- until @request[:headers_complete] || (l = reply.gets).nil?
241
- if l.include? ':'
242
- l = l.strip.split(/:[\s]?/.freeze, 2)
243
- l[0].strip! ; l[0].downcase!;
244
- @request[l[0]] ? (@request[l[0]].is_a?(Array) ? (@request[l[0]] << l[1]) : @request[l[0]] = [@request[l[0]], l[1] ]) : (@request[l[0]] = l[1])
245
- elsif l =~ /^[\r]?\n/.freeze
246
- @request[:headers_complete] = true
247
- else
248
- #protocol error
249
- raise 'Protocol Error, closing connection.'
250
- return close
251
- end
252
- end
253
- end
254
- reply.string.clear
255
-
256
- return Iodine::Http::Websockets.new( ( ssl || socket), handler: self, request: @request )
257
-
258
- rescue => e
259
- (ssl || socket).tap {|io| next if io.nil?; io.close unless io.closed?}
260
- if @options[:on_error]
261
- @options[:on_error].call(e)
262
- return false
263
- end
264
- raise e unless @io
265
- end
266
-
267
- # Create a simple Websocket Client(!).
268
- #
269
- # This method accepts two parameters:
270
- # url:: a String representing the URL of the websocket. i.e.: 'ws://foo.bar.com:80/ws/path'
271
- # options:: a Hash with options to be used. The options will be used to define the connection's details (i.e. ssl etc') and the Websocket callbacks (i.e. on_open(ws), on_close(ws), on_message(ws))
272
- # &block:: an optional block that accepts one parameter (data) and will be used as the `#on_message(data)`
273
- #
274
- # Acceptable options are:
275
- # on_open:: the on_open callback - Must be an objects that answers `call()`, usually a Proc.
276
- # on_message:: the on_message callback - Must be an objects that answers `call(data)`, usually a Proc.
277
- # on_close:: the on_close callback - Must be an objects that answers `call()`, usually a Proc. The method is called when the connection is closed and isn't renewed.
278
- # on_error:: the on_error callback - Must be an objects that answers `call(err)`, usually a Proc. This is called whenever a connection fails to be established or an exception is raised by any of the callbacks. This is NOT the disconnection websocket message. dafaults to raising the error (error pass-through).
279
- # headers:: a Hash of custom HTTP headers to be sent with the request. Header data, including cookie headers, should be correctly encoded.
280
- # cookies:: a Hash of cookies to be sent with the request. cookie data will be encoded before being sent.
281
- # timeout:: the number of seconds to wait before the connection is established. Defaults to 5 seconds.
282
- # every:: this option, together with `:send` and `:renew`, implements a polling websocket. :every is the number of seconds between each polling event. without `:send`, this option will be ignored. defaults to nil.
283
- # send:: a String to be sent or a Proc to be performed each polling interval. This option, together with `:every` and `:renew`, implements a polling websocket. without `:every`, this option will be ignored. defaults to nil. If `:send` is a Proc, it will be executed within the context of the websocket client object, with acess to the websocket client's instance variables and methods.
284
- # renew:: the number of times to attempt to renew the connection if the connection is terminated by the remote server. Attempts are made in 2 seconds interval. The default for a polling websocket is 5 attempts to renew. For all other clients, the default is 0 (no renewal).
285
- #
286
- # The method will block until the connection is established or until 5 seconds have passed (the timeout). The method will either return a WebsocketClient instance object or raise an exception it the connection was unsuccessful.
287
- #
288
- # Use Iodine::Http.ws_connect for a non-blocking initialization.
289
- #
290
- # An #on_close callback will only be called if the connection isn't or cannot be renewed. If the connection is renewed,
291
- # the #on_open callback will be called again for a new Websocket client instance - but the #on_close callback will NOT be called.
292
- #
293
- # Due to this design, the #on_open and #on_close methods should NOT be used for opening IO resources (i.e. file handles) nor for cleanup IF the `:renew` option is enabled.
294
- #
295
- # An on_message Proc must be defined, or the method will fail.
296
- #
297
- # The on_message Proc can be defined using the optional block:
298
- #
299
- # Iodine::Http::WebsocketClient.connect("ws://localhost:3000/") {|data| write data} #echo example
300
- #
301
- # OR, the on_message Proc can be defined using the options Hash:
302
- #
303
- # Iodine::Http::WebsocketClient.connect("ws://localhost:3000/", on_open: -> {}, on_message: -> {|data| write data })
304
- #
305
- # The #on_message(data), #on_open and #on_close methods will be executed within the context of the WebsocketClient
306
- # object, and will have native acess to the Websocket response object.
307
- #
308
- # After the WebsocketClient had been created, it's possible to update the #on_message and #on_close methods:
309
- #
310
- # # updates #on_message
311
- # wsclient.on_message do |data|
312
- # response << "I'll disconnect on the next message!"
313
- # # updates #on_message again.
314
- # on_message {|data| disconnect }
315
- # end
316
- #
317
- #
318
- # !!please be aware that the Websockt Client will not attempt to verify SSL certificates,
319
- # so that even SSL connections are vulnerable to a possible man in the middle attack.
320
- #
321
- # @return [Iodine::Http::WebsocketClient] this method returns the connected {Iodine::Http::WebsocketClient} or raises an exception if something went wrong (such as a connection timeout).
322
- def self.connect url, options={}, &block
323
- options = url if url.is_a?(Hash) && options.empty?
324
- options[:renew] ||= 5 if options[:every] && options[:send]
325
- options[:url] ||= url
326
- options[:on_message] ||= block
327
- client = self.new(options)
328
- return client unless client.closed?
329
- false
330
- end
331
- end
332
- end
333
- end
334
-
335
-