iodine 0.1.4 → 0.1.5
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +4 -4
- data/bin/hello_world +4 -0
- data/lib/iodine.rb +9 -0
- data/lib/iodine/client.rb +12 -0
- data/lib/iodine/http/http1.rb +12 -12
- data/lib/iodine/http/http2.rb +36 -22
- data/lib/iodine/http/response.rb +38 -11
- data/lib/iodine/http/websocket_client.rb +1 -1
- data/lib/iodine/http/websocket_handler.rb +16 -5
- data/lib/iodine/http/websockets.rb +5 -9
- data/lib/iodine/io.rb +1 -0
- data/lib/iodine/protocol.rb +25 -1
- data/lib/iodine/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a9f87061376cb31f4e3d6b1cc7fd9dcca401151
|
4
|
+
data.tar.gz: 2cde195704434f6ff3af7b4a98a35c3b06f637af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0bb1203ab2d5d5ab58ec4b6dd97146b0328802db7b426031319a5e8be6936826811e43a86ae6326bbe86f0811a3013656d15a56fdfb0f29a1d0046d6bf9b485a
|
7
|
+
data.tar.gz: 773e868701784aca9b5a5899f0d027cafab8e586f50acf43a40f2efb51252350d689f0af6bc90caeff6f79a6a1e4a0d0bc5a683ca4b0003dde1be3182326d866
|
data/CHANGELOG.md
CHANGED
@@ -8,6 +8,14 @@ Please notice that this change log contains changes for upcoming releases as wel
|
|
8
8
|
|
9
9
|
***
|
10
10
|
|
11
|
+
Change log v.0.1.5
|
12
|
+
|
13
|
+
**Feature**: The Response#body can now be set to a File object, allowing Iodine to preserve memory when serving large static files from disc. Limited Range requests are also supported - together, these changes allow Iodine to serve media files (such as movies) while suffering a smaller memory penalty and supporting a wider variaty of players (Safari requires Range request support for it's media player).
|
14
|
+
|
15
|
+
**Fix**: Fixed an issue where Iodine might take a long time to shut down after a Fatal Error during the server initialization.
|
16
|
+
|
17
|
+
***
|
18
|
+
|
11
19
|
Change log v.0.1.4
|
12
20
|
|
13
21
|
**Fix**: fixed an issue with where the WebsocketClient#on_close wouldn't be called for a renewable Websocket connection during shutdown.
|
data/README.md
CHANGED
@@ -57,7 +57,7 @@ exit
|
|
57
57
|
|
58
58
|
In this mode, Iodine will continue running until all the tasks have completed and than it will quite. Timer based tasks will be ignored.
|
59
59
|
|
60
|
-
## Simple Usage: Task polling
|
60
|
+
## Simple Usage: Task polling
|
61
61
|
|
62
62
|
This mode of operation is effective if want Iodine to periodically initiates new tasks, for instance if you cannot use `cron`.
|
63
63
|
|
@@ -129,7 +129,7 @@ Iodine::Http.on_websocket WSChatServer
|
|
129
129
|
|
130
130
|
### Security and limits
|
131
131
|
|
132
|
-
Nobody wants their server to crash... Security measures are a fact of life as an internet
|
132
|
+
Nobody wants their server to crash... Security measures are a fact of life as an internet entity. It is not only the theoretical malicious attacker from which a server must protect itself, but also from the unaware user or client.
|
133
133
|
|
134
134
|
Mostly, it is assumed that Iodine will run behind a proxy (i.e. within a Heroku Dyno or viaduct.io process), as such it is assumed that the proxy will protect the Iodine Http server from undue stress.
|
135
135
|
|
@@ -137,13 +137,13 @@ Having said that, Iodine is built with certain security measures in mind:
|
|
137
137
|
|
138
138
|
- Iodine will not accept IO data (neither from new connections nor form existing ones) while still answering existing requests and performing tasks. This safeguards against task overloading and DoS attacks causing a global crash, allowing the server to resume normal operation once a DoS attack had run it's course (and potentially allowing legitimate requests to be answered while the attack is still underway).
|
139
139
|
|
140
|
-
- Iodine will limit the query length
|
140
|
+
- Iodine will limit the query length, header count and header data size as well as well as react to header overloading by immediate disconnections. Iodine's limits are hardcoded to be slightly more than double those of common Proxies, so this counter-measure will only take effect should an attacker manage to bypass the Proxy.
|
141
141
|
|
142
142
|
- Iodine limits every Http request body-size (file upload data, form data, etc') to ~0.5GB. This setting can be changed using `Iodine::Http.max_http_buffer`. This safeguard is meant to prevent Ruby from crashing due to insufficient memory (an error Iodine cannot, and should not, recover from).
|
143
143
|
|
144
144
|
It is recommended that this number will be lowered substantially whenever possible, by using `Iodine::Http.max_http_buffer = new_value`
|
145
145
|
|
146
|
-
Do be aware that, at the moment, file
|
146
|
+
Do be aware that, at the moment, file upload data must passed through the memory on it's way to the temporary file. The parser's memory consumption will hopefully decrese in future releases, however, it is always recomended that large data be avoided when possible or handled using download/upload management protocols and services.
|
147
147
|
|
148
148
|
## Server Usage: Plug in your network protocol
|
149
149
|
|
data/bin/hello_world
CHANGED
data/lib/iodine.rb
CHANGED
@@ -39,10 +39,19 @@ require 'openssl'
|
|
39
39
|
#
|
40
40
|
# # setting up the server is as easy as plugging in your Protocol class:
|
41
41
|
# Iodine.protocol = MyProtocol
|
42
|
+
#
|
43
|
+
# # setting up cuncurrency can be done with threads - the default is a single thread):
|
44
|
+
# Iodine.threads = 8
|
45
|
+
#
|
46
|
+
# # setting up cuncurrency can also be done with processes (forking) - the default is a single process:
|
47
|
+
# Iodine.processes = 4
|
42
48
|
#
|
43
49
|
# # if you are excecuting this script from IRB, exit IRB to start Iodine.
|
44
50
|
# exit
|
45
51
|
#
|
52
|
+
# You can test the example using `telnet`
|
53
|
+
#
|
54
|
+
# Before you write your own protocol, make sure you learn more about the {Iodine::Protocol} and all it's got to offer.
|
46
55
|
module Iodine
|
47
56
|
extend self
|
48
57
|
end
|
data/lib/iodine/http/http1.rb
CHANGED
@@ -95,11 +95,11 @@ module Iodine
|
|
95
95
|
def send_response response
|
96
96
|
return false if response.headers.frozen?
|
97
97
|
|
98
|
+
body = response.extract_body
|
98
99
|
request = response.request
|
99
100
|
headers = response.headers
|
100
|
-
body = response.extract_body
|
101
101
|
|
102
|
-
headers['content-length'.freeze] ||= body.
|
102
|
+
headers['content-length'.freeze] ||= body.size if body
|
103
103
|
|
104
104
|
keep_alive = response.keep_alive
|
105
105
|
if (request[:version].to_f > 1 && request['connection'.freeze].nil?) || request['connection'.freeze].to_s =~ /ke/i || (headers['connection'.freeze] && headers['connection'.freeze] =~ /^ke/i)
|
@@ -111,13 +111,13 @@ module Iodine
|
|
111
111
|
end
|
112
112
|
|
113
113
|
send_headers response
|
114
|
-
return log_finished(response) if request.head?
|
115
|
-
|
116
|
-
written = write(body)
|
117
|
-
return Iodine.warn
|
114
|
+
return log_finished(response) && (body && body.close) if request.head? || body.nil?
|
115
|
+
until body.eof?
|
116
|
+
written = write(body.read 65_536)
|
117
|
+
return Iodine.warn("Http/1 couldn't send response because connection was lost.") && body.close unless written
|
118
118
|
response.bytes_written += written
|
119
|
-
(body.frozen? || body.clear)
|
120
119
|
end
|
120
|
+
body.close
|
121
121
|
close unless keep_alive
|
122
122
|
log_finished response
|
123
123
|
end
|
@@ -130,17 +130,17 @@ module Iodine
|
|
130
130
|
end
|
131
131
|
return if response.request.head?
|
132
132
|
body = response.extract_body
|
133
|
-
|
134
|
-
written =
|
135
|
-
return Iodine.warn
|
133
|
+
until body.eof?
|
134
|
+
written = write(body.read 65_536)
|
135
|
+
return Iodine.warn("Http/1 couldn't send response because connection was lost.") && body.close unless written
|
136
136
|
response.bytes_written += written
|
137
|
-
end
|
137
|
+
end if body
|
138
138
|
if finish
|
139
139
|
response.bytes_written += stream_data('')
|
140
140
|
log_finished response
|
141
141
|
close unless response.keep_alive
|
142
142
|
end
|
143
|
-
|
143
|
+
body.close if body
|
144
144
|
true
|
145
145
|
end
|
146
146
|
|
data/lib/iodine/http/http2.rb
CHANGED
@@ -1,18 +1,6 @@
|
|
1
1
|
module Iodine
|
2
2
|
module Http
|
3
3
|
class Http2 < ::Iodine::Protocol
|
4
|
-
def initialize io, original_request = nil
|
5
|
-
super(io)
|
6
|
-
return unless original_request
|
7
|
-
::Iodine.warn "Http/2: upgrade handshake settings not implemented. upgrade request:\n#{original_request}"
|
8
|
-
@last_stream = original_request[:stream_id] = 1
|
9
|
-
original_request[:io] = self
|
10
|
-
# deal with the request['http2-settings'] - NO ACK
|
11
|
-
# HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>
|
12
|
-
|
13
|
-
# dispatch the original request
|
14
|
-
::Iodine.run original_request, &(::Iodine::Http::Http2.dispatch)
|
15
|
-
end
|
16
4
|
def on_open
|
17
5
|
# not fully fanctional.
|
18
6
|
::Iodine.warn "Http/2 requested - support is still experimental."
|
@@ -52,6 +40,18 @@ module Iodine
|
|
52
40
|
# 0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a
|
53
41
|
# == PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
|
54
42
|
# + SETTINGS frame
|
43
|
+
|
44
|
+
# The @options variable contains the original Http1 request, if exists.
|
45
|
+
return unless @options
|
46
|
+
::Iodine.warn "Http/2: upgrade handshake settings not implemented. upgrade request:\n#{@options}"
|
47
|
+
@last_stream = @options[:stream_id] = 1
|
48
|
+
@options[:io] = self
|
49
|
+
# deal with the request['http2-settings'] - NO ACK
|
50
|
+
# HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>
|
51
|
+
|
52
|
+
# dispatch the original request
|
53
|
+
::Iodine.run @options, &(::Iodine::Http::Http2.dispatch)
|
54
|
+
@options = nil
|
55
55
|
end
|
56
56
|
def on_message data
|
57
57
|
data = ::StringIO.new data
|
@@ -61,21 +61,35 @@ module Iodine
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def send_response response
|
64
|
-
|
65
|
-
return false unless send_headers response, request
|
66
|
-
return nil if request.head?
|
64
|
+
return false if response.headers.frozen?
|
67
65
|
body = response.extract_body
|
68
|
-
|
69
|
-
|
66
|
+
request = response.request
|
67
|
+
return body && body.close unless send_headers response, request
|
68
|
+
return log_finished(response) && body && body.close if request.head?
|
69
|
+
if body
|
70
|
+
until body.eof?
|
71
|
+
response.bytes_written += emit_payload(body.read(@settings[SETTINGS_MAX_FRAME_SIZE]), request[:sid], 0, (body.eof? ? 1 : 0))
|
72
|
+
end
|
73
|
+
body.close
|
74
|
+
else
|
75
|
+
emit_payload('', request[:sid], 0, 1)
|
76
|
+
end
|
70
77
|
log_finished response
|
71
78
|
end
|
79
|
+
|
72
80
|
def stream_response response, finish = false
|
73
81
|
request = response.request
|
74
|
-
send_headers response, request
|
75
|
-
return nil if request.head?
|
76
82
|
body = response.extract_body
|
77
|
-
|
78
|
-
|
83
|
+
send_headers response, request
|
84
|
+
return body && body.close if request.head?
|
85
|
+
if body
|
86
|
+
until body.eof?
|
87
|
+
response.bytes_written += emit_payload(body.read(@settings[SETTINGS_MAX_FRAME_SIZE]), request[:sid], 0, ((finish && body.eof?) ? 1 : 0))
|
88
|
+
end
|
89
|
+
body.close
|
90
|
+
elsif finish
|
91
|
+
emit_payload('', request[:sid], 0, 1)
|
92
|
+
end
|
79
93
|
log_finished response if finish
|
80
94
|
end
|
81
95
|
|
@@ -132,8 +146,8 @@ module Iodine
|
|
132
146
|
end
|
133
147
|
|
134
148
|
def send_headers response, request
|
149
|
+
return false if response.headers.frozen?
|
135
150
|
headers = response.headers
|
136
|
-
return false if headers.frozen?
|
137
151
|
# headers[:status] = response.status.to_s
|
138
152
|
headers['set-cookie'] = response.extract_cookies
|
139
153
|
headers.freeze
|
data/lib/iodine/http/response.rb
CHANGED
@@ -307,27 +307,54 @@ module Iodine
|
|
307
307
|
511=>"Network Authentication Required".freeze
|
308
308
|
}
|
309
309
|
|
310
|
-
# This will return the Body object as
|
310
|
+
# This will return the Body object as an IO like object, such as StringIO (or File)... And set the body to `nil` (seeing as it was extracted from the response).
|
311
|
+
#
|
312
|
+
# This method will also attempts to set headers and update the response status in relation to the body, if applicable. Call this BEFORE getting any final data about the response or sending the headers.
|
311
313
|
def extract_body
|
312
|
-
if @body.is_a?(Array)
|
314
|
+
body_io = if @body.is_a?(Array)
|
313
315
|
return (@body = nil) if body.empty?
|
314
|
-
|
315
|
-
extract_body
|
316
|
+
StringIO.new @body.join
|
316
317
|
elsif @body.is_a?(String)
|
317
318
|
return (@body = nil) if body.empty?
|
318
|
-
|
319
|
-
@body = nil
|
320
|
-
tmp
|
319
|
+
StringIO.new @body
|
321
320
|
elsif body.nil?
|
322
321
|
nil
|
323
|
-
elsif body.
|
322
|
+
elsif @body.is_a?(File) || @body.is_a?(Tempfile) || @body.is_a?(StringIO)
|
323
|
+
@body
|
324
|
+
elsif @body.respond_to? :each
|
324
325
|
tmp = ''
|
325
|
-
body.each {|s| tmp << s}
|
326
|
-
body.close if body.respond_to? :close
|
326
|
+
@body.each {|s| tmp << s}
|
327
|
+
@body.close if @body.respond_to? :close
|
327
328
|
@body = nil
|
328
329
|
return nil if tmp.empty?
|
329
|
-
tmp
|
330
|
+
StringIO.new tmp
|
330
331
|
end
|
332
|
+
@body = nil
|
333
|
+
body_io.rewind
|
334
|
+
|
335
|
+
if !(@headers.frozen?) && @request['range'.freeze] && @request.get? && @status == 200 && @headers['content-length'.freeze].nil?
|
336
|
+
r = @request['range'.freeze].match(/^bytes=([\d]+)\-([\d]+)?$/i)
|
337
|
+
if r
|
338
|
+
old_size = body_io.size
|
339
|
+
start_pos = r[1].to_i
|
340
|
+
end_pos = (r[2] || (old_size - 1)).to_i
|
341
|
+
read_length = end_pos-start_pos+ 1
|
342
|
+
@status = 206 unless old_size == read_length
|
343
|
+
body_io.pos = start_pos
|
344
|
+
unless end_pos == old_size-1
|
345
|
+
new_body = body_io.read(read_length)
|
346
|
+
body_io.close
|
347
|
+
body_io = StringIO.new new_body
|
348
|
+
body_io.rewind
|
349
|
+
end
|
350
|
+
@headers['content-range'.freeze] = "bytes #{start_pos}-#{end_pos}/#{old_size}"
|
351
|
+
@headers['accept-ranges'.freeze] ||= 'bytes'
|
352
|
+
else
|
353
|
+
@headers['accept-ranges'.freeze] ||= 'none'
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
body_io
|
331
358
|
end
|
332
359
|
|
333
360
|
# This will return an array of cookie settings to be appended to `set-cookie` headers.
|
@@ -258,7 +258,7 @@ module Iodine
|
|
258
258
|
|
259
259
|
request[:ws_client_params] = options
|
260
260
|
client = self.new(request)
|
261
|
-
Iodine::Http::Websockets.new( ( ssl || socket), client, request )
|
261
|
+
Iodine::Http::Websockets.new( ( ssl || socket), handler: client, request: request )
|
262
262
|
|
263
263
|
return client
|
264
264
|
|
@@ -28,9 +28,16 @@ module Iodine
|
|
28
28
|
# cleanup, if needed, using this callback.
|
29
29
|
def on_close
|
30
30
|
end
|
31
|
+
# extra cleanup, if needed, when server is shutting down while the websocket is connected.
|
32
|
+
#
|
33
|
+
# You can use on_close unless some "going away" cleanup is required.
|
34
|
+
def on_shutdown
|
35
|
+
end
|
31
36
|
|
32
37
|
# This method allows the class itself to act as the Websocket handler, usable with:
|
33
|
-
#
|
38
|
+
#
|
39
|
+
# # Iodine::Http::WebsocketHandler's default implementation does nothing.
|
40
|
+
# Iodine::Http.on_websocket Iodine::Http::WebsocketHandler
|
34
41
|
def self.call request, response
|
35
42
|
self.new request, response
|
36
43
|
end
|
@@ -43,7 +50,11 @@ module Iodine
|
|
43
50
|
def write data
|
44
51
|
# We leverage the fact that the Http response can be used to send Websocket data.
|
45
52
|
#
|
46
|
-
# you can also use Websocket#send_data or it's alias Websocket#<<
|
53
|
+
# you can also use Websocket#send_data or it's alias Websocket#<< for example:
|
54
|
+
#
|
55
|
+
# # @request[:io] contains the Websockets Protocol instance
|
56
|
+
# @request[:io] << data
|
57
|
+
#
|
47
58
|
# do NOT use Websocket#write (which writes the data directly, bypassing the protocol).
|
48
59
|
@response << data
|
49
60
|
end
|
@@ -53,18 +64,18 @@ module Iodine
|
|
53
64
|
# This implementation is limited to a single process on a single server.
|
54
65
|
# Consider using Redis for a scalable implementation.
|
55
66
|
def unicast id, data
|
56
|
-
|
57
|
-
@request[:io].unicast id, data
|
67
|
+
::Iodine::Http::Websockets.unicast id, data
|
58
68
|
end
|
59
69
|
# Broadcast to all Websockets, except self.
|
60
70
|
#
|
61
71
|
# This implementation is limited to a single process on a single server.
|
62
72
|
# Consider using Redis for a scalable implementation.
|
63
73
|
def broadcast data
|
64
|
-
|
74
|
+
::Iodine::Http::Websockets.broadcast data, self
|
65
75
|
end
|
66
76
|
# Closes the connection
|
67
77
|
def close
|
78
|
+
# @request[:io] contains the Websockets Protocol instance
|
68
79
|
@request[:io].go_away
|
69
80
|
end
|
70
81
|
end
|
@@ -1,15 +1,11 @@
|
|
1
1
|
module Iodine
|
2
2
|
module Http
|
3
3
|
class Websockets < ::Iodine::Protocol
|
4
|
-
# initialize the websocket protocol.
|
5
|
-
def initialize io, handler, request, ws_extentions = nil
|
6
|
-
@handler = handler
|
7
|
-
@ws_extentions = ws_extentions
|
8
|
-
request[:io] = self
|
9
|
-
super(io)
|
10
|
-
end
|
11
4
|
# continue to initialize the websocket protocol.
|
12
5
|
def on_open
|
6
|
+
@handler = @options[:handler]
|
7
|
+
@ws_extentions = @options[:ext]
|
8
|
+
@options[:request][:io] = self
|
13
9
|
@parser = {body: '', stage: 0, step: 0, mask_key: [], len_bytes: []}
|
14
10
|
set_timeout = self.class.default_timeout
|
15
11
|
@handler.on_open if @handler.respond_to? :on_open
|
@@ -46,7 +42,7 @@ module Iodine
|
|
46
42
|
# allow Http responses to be used for sending Websocket data.
|
47
43
|
def send_response response, finish = false
|
48
44
|
body = response.extract_body
|
49
|
-
send_data body
|
45
|
+
send_data body.read
|
50
46
|
end
|
51
47
|
alias :stream_response :send_response
|
52
48
|
|
@@ -164,7 +160,7 @@ module Iodine
|
|
164
160
|
response.session
|
165
161
|
# Iodine.log "#{@request[:client_ip]} [#{Time.now.utc}] - #{@connection.object_id} Upgraded HTTP to WebSockets.\n"
|
166
162
|
response.finish
|
167
|
-
self.new(io.io, handler, request, ws_extentions)
|
163
|
+
self.new(io.io, handler: handler, request: request, ext: ws_extentions)
|
168
164
|
return true
|
169
165
|
end
|
170
166
|
|
data/lib/iodine/io.rb
CHANGED
data/lib/iodine/protocol.rb
CHANGED
@@ -4,6 +4,26 @@ module Iodine
|
|
4
4
|
#
|
5
5
|
# A new protocol instance will be created for every network connection.
|
6
6
|
#
|
7
|
+
# A new protocol might be initialized also when switching between protocols. In this use-case, the
|
8
|
+
# protocol can be initialized with an optional second `options` parameter (the first parameter MUST be the IO object used),
|
9
|
+
# allowing this data to be accessed within the {#on_open} method using the `@options` instance variable or the `options` accessor.
|
10
|
+
#
|
11
|
+
# For example, when switching protocols midstream (i.e. for implementing an Http Upgrade to another protocol such as Websockets):
|
12
|
+
#
|
13
|
+
# class MyNextProtocol
|
14
|
+
# def on_open
|
15
|
+
# @secret = options[:secret]
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# # in the old protocol:
|
20
|
+
#
|
21
|
+
# class MyOriginalProtocol
|
22
|
+
# def switch_to_next_protocol
|
23
|
+
# MyNextProtocol.new @io, secret: "my secret data"
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
7
27
|
# The recommended use is to inherit this class and override any of the following:
|
8
28
|
# on_open:: called whenever the Protocol is initialized. Override this to initialize the Protocol object.
|
9
29
|
# on_message(data):: called whenever data is received from the IO. Override this to implement the actual network protocol.
|
@@ -24,6 +44,9 @@ module Iodine
|
|
24
44
|
#
|
25
45
|
# Using one of the Protocol methods {#write}, {#read}, {#close} is prefered over direct access.
|
26
46
|
attr_reader :io
|
47
|
+
# the argument or options Hash passed to the initializer as a second argument (the first argument MUST be the IO object).
|
48
|
+
# the value is usually `nil` unless the protocol instance was created by a different protocol while "upgrading" from one protocol to the next.
|
49
|
+
attr_reader :options
|
27
50
|
|
28
51
|
# Sets the timeout in seconds for IO activity (set timeout within {#on_open}).
|
29
52
|
#
|
@@ -131,11 +154,12 @@ module Iodine
|
|
131
154
|
# A new Protocol instance set itself up as the IO's protocol (replacing any previous protocol).
|
132
155
|
#
|
133
156
|
# Normally you won't need to override this method. Override {#on_open} instead.
|
134
|
-
def initialize io
|
157
|
+
def initialize io, options = nil
|
135
158
|
@timeout ||= nil
|
136
159
|
@send_locker = Mutex.new
|
137
160
|
@locker = Mutex.new
|
138
161
|
@io = io
|
162
|
+
@options = options
|
139
163
|
touch
|
140
164
|
@locker.synchronize do
|
141
165
|
Iodine.switch_protocol @io.to_io, self
|
data/lib/iodine/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iodine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Boaz Segev
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-10-
|
11
|
+
date: 2015-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -74,6 +74,7 @@ files:
|
|
74
74
|
- bin/setup
|
75
75
|
- iodine.gemspec
|
76
76
|
- lib/iodine.rb
|
77
|
+
- lib/iodine/client.rb
|
77
78
|
- lib/iodine/core.rb
|
78
79
|
- lib/iodine/http.rb
|
79
80
|
- lib/iodine/http/hpack.rb
|