iodine 0.1.19 → 0.1.20
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 +10 -0
- data/README.md +8 -2
- data/lib/iodine/core_init.rb +2 -2
- data/lib/iodine/http.rb +15 -5
- data/lib/iodine/http/http1.rb +3 -3
- data/lib/iodine/http/http2.rb +2 -2
- data/lib/iodine/http/request.rb +23 -16
- data/lib/iodine/http/websockets.rb +1 -1
- data/lib/iodine/protocol.rb +1 -1
- data/lib/iodine/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b5ee2e6d64a08ec88c75a4e0eaed1d15586310e8
|
4
|
+
data.tar.gz: fe61017b3b92e43efb5cf1ca9331e5143940ec8c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a9e83552a00e1799096ef2b0515ed417a18eab511c0460743a4f6f003613ae47114b55abe14ec0dd1243a9dd846032ad30f59b3786c71673192e504fa0a2ebed
|
7
|
+
data.tar.gz: 40989499a5b70e09fb35dcd29ebcc8fd3326ff5b9e886ea7800b6e9f01c4ef01fd99b587ebff92aad9ff03d7d5e5ada3489bc18c9887864be4148e2263cdc3eb
|
data/CHANGELOG.md
CHANGED
@@ -8,6 +8,16 @@ 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.20
|
12
|
+
|
13
|
+
**Update/Fix**: Updated the `x-forwarded-for` header recognition, to accommodate an Array formatting sometimes used (`["ip1", "ip2", ...]`).
|
14
|
+
|
15
|
+
**Update**: native support for the `Forwarded` header Http.
|
16
|
+
|
17
|
+
**API Changes**: `Iodine::Http.max_http_buffer` was replaced with `Iodine::Http.max_body_size`, for a better understanding of the method's result.
|
18
|
+
|
19
|
+
***
|
20
|
+
|
11
21
|
Change log v.0.1.19
|
12
22
|
|
13
23
|
**Update**: added the `go_away` method to the Http/1 peorotocol, for seamless connection closeing across Http/2, Http/1 and Websockets.
|
data/README.md
CHANGED
@@ -9,6 +9,8 @@ In fact, it's so fun to write network protocols that mix and match together, tha
|
|
9
9
|
|
10
10
|
To use Iodine, you just set up your tasks - including a single server, if you want one. Iodine will start running once your application is finished and it won't stop runing until all the scheduled tasks have completed.
|
11
11
|
|
12
|
+
Iodine is used by [the Plezi Ruby framework for real-time applications](http://www.plezi.io).
|
13
|
+
|
12
14
|
## Installation
|
13
15
|
|
14
16
|
Add this line to your application's Gemfile:
|
@@ -140,9 +142,9 @@ Having said that, Iodine is built with certain security measures in mind:
|
|
140
142
|
|
141
143
|
- 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.
|
142
144
|
|
143
|
-
- Iodine limits every Http request body-size (file upload data, form data, etc') to ~0.5GB. This setting can be changed using `Iodine::Http.
|
145
|
+
- 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_body_size`. This safeguard is meant to prevent Ruby from crashing due to insufficient memory (an error Iodine cannot, and should not, recover from).
|
144
146
|
|
145
|
-
It is recommended that this number will be lowered substantially whenever possible, by using `Iodine::Http.
|
147
|
+
It is recommended that this number will be lowered substantially whenever possible, by using `Iodine::Http.max_body_size = new_value`
|
146
148
|
|
147
149
|
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.
|
148
150
|
|
@@ -262,6 +264,10 @@ For instance, in Iodine's implementation for the Websocket protocol: Websocket m
|
|
262
264
|
|
263
265
|
The exception to the rule is the `ping` implementation. Your protocol's `ping` method will execute in parallel with other parts of your protocol's code. Pinging is a machanism that is often time sensitive and is required to maintain an open connection. For this reason, if your code is working hard on a long task, a ping will still occure automatically in the background and offset any connection timeout.
|
264
266
|
|
267
|
+
If your code is short and efficient (no blocking tasks), it is best to run Iodine in a single threaded mode (you get better performance AND safer code, as long as you don't block):
|
268
|
+
|
269
|
+
Iodine.threads = 1
|
270
|
+
|
265
271
|
## Contributing
|
266
272
|
|
267
273
|
Bug reports and pull requests are welcome on GitHub at https://github.com/boazsegev/iodine.
|
data/lib/iodine/core_init.rb
CHANGED
@@ -109,8 +109,8 @@ module Iodine
|
|
109
109
|
next
|
110
110
|
end
|
111
111
|
on_shutdown do
|
112
|
-
|
113
|
-
|
112
|
+
@server.close unless @server.nil? || @server.closed?
|
113
|
+
log "Stopped listening to port #{@port}.\n"
|
114
114
|
end
|
115
115
|
::Iodine::Base::Listener.accept(@server, false)
|
116
116
|
log "Iodine #{VERSION} is listening on port #{@port}#{ ' to SSL/TLS connections.' if @ssl}\n"
|
data/lib/iodine/http.rb
CHANGED
@@ -100,13 +100,23 @@ module Iodine
|
|
100
100
|
@session_token
|
101
101
|
end
|
102
102
|
|
103
|
-
#
|
103
|
+
# `max_http_buffer` is deprecated. Use `max_body_size` instead.
|
104
104
|
def max_http_buffer= size
|
105
|
-
|
105
|
+
Iodine.warn "`max_http_buffer` is deprecated. Use `max_body_size` instead."
|
106
|
+
max_body_size = size
|
106
107
|
end
|
107
|
-
#
|
108
|
+
# `max_http_buffer` is deprecated. Use `max_body_size` instead.
|
108
109
|
def max_http_buffer
|
109
|
-
|
110
|
+
Iodine.warn "`max_http_buffer` is deprecated. Use `max_body_size` instead."
|
111
|
+
@max_body_size
|
112
|
+
end
|
113
|
+
# Sets the maximum bytes allowed for an HTTP's body request (file upload data). Defaults to the command line `-limit` argument, or ~0.5GB.
|
114
|
+
def max_body_size= size
|
115
|
+
@max_body_size = size
|
116
|
+
end
|
117
|
+
# Gets the maximum bytes allowed for an HTTP's body request (file upload data). Defaults to the command line `-limit` argument, or ~0.5GB.
|
118
|
+
def max_body_size
|
119
|
+
@max_body_size
|
110
120
|
end
|
111
121
|
|
112
122
|
# Sets whether Iodine will allow connections to the experiemntal Http2 protocol. Defaults to false unless the `http2` command line flag is present.
|
@@ -159,7 +169,7 @@ module Iodine
|
|
159
169
|
protected
|
160
170
|
|
161
171
|
@http2 = (ARGV.index('http2') && true)
|
162
|
-
@
|
172
|
+
@max_body_size = ((ARGV.index('-limit') && ARGV[ARGV.index('-limit') + 1]) || 536_870_912).to_i
|
163
173
|
|
164
174
|
@websocket_app = @http_app = NOT_IMPLEMENTED = Proc.new { |i,o| false }
|
165
175
|
@session_token = "#{File.basename($0, '.*')}_uuid"
|
data/lib/iodine/http/http1.rb
CHANGED
@@ -23,7 +23,7 @@ module Iodine
|
|
23
23
|
end
|
24
24
|
next if l.empty?
|
25
25
|
request[:method], request[:query], request[:version] = l.split(/[\s]+/.freeze, 3)
|
26
|
-
return (Iodine.warn('
|
26
|
+
return (Iodine.warn('Http1 Protocol Error, closing connection.'.freeze, l, request) && close) unless request[:method] =~ HTTP_METHODS_REGEXP
|
27
27
|
request[:version] = (request[:version] || '1.1'.freeze).match(/[\d\.]+/.freeze)[0]
|
28
28
|
request[:time_recieved] = Time.now
|
29
29
|
end
|
@@ -82,8 +82,8 @@ module Iodine
|
|
82
82
|
request[:body_complete] = true
|
83
83
|
end
|
84
84
|
end
|
85
|
-
if request[:body] && request[:body].size > ::Iodine::Http.
|
86
|
-
Iodine.warn("Http1 message body too big, closing connection (Iodine::Http.
|
85
|
+
if request[:body] && request[:body].size > ::Iodine::Http.max_body_size
|
86
|
+
Iodine.warn("Http1 message body too big, closing connection (Iodine::Http.max_body_size == #{::Iodine::Http.max_body_size} bytes) - #{request[:body].size} bytes.")
|
87
87
|
request.delete(:body).tap {|f| f.close unless f.closed? } rescue false
|
88
88
|
write "HTTP/1.0 413 Payload Too Large\r\ncontent-length: 17\r\n\r\nPayload Too Large".freeze
|
89
89
|
return close
|
data/lib/iodine/http/http2.rb
CHANGED
@@ -337,8 +337,8 @@ module Iodine
|
|
337
337
|
(frame[:stream][:body] ||= Tempfile.new('iodine'.freeze, :encoding => 'binary'.freeze) ) << frame[:body]
|
338
338
|
|
339
339
|
# check request size
|
340
|
-
if frame[:stream][:body].size > ::Iodine::Http.
|
341
|
-
Iodine.warn("Http2 payload (message size) too big (Iodine::Http.
|
340
|
+
if frame[:stream][:body].size > ::Iodine::Http.max_body_size
|
341
|
+
Iodine.warn("Http2 payload (message size) too big (Iodine::Http.max_body_size == #{::Iodine::Http.max_body_size} bytes) - #{frame[:stream][:body].size} bytes.")
|
342
342
|
return connection_error( ENHANCE_YOUR_CALM )
|
343
343
|
end
|
344
344
|
|
data/lib/iodine/http/request.rb
CHANGED
@@ -180,12 +180,12 @@ module Iodine
|
|
180
180
|
def patch?
|
181
181
|
self[:method] == HTTP_PATCH
|
182
182
|
end
|
183
|
-
HTTP_CTYPE = 'content-type'.freeze; HTTP_JSON = /application\/json
|
183
|
+
HTTP_CTYPE = 'content-type'.freeze; HTTP_JSON = /application\/json/.freeze
|
184
184
|
# returns true if the request is of type JSON.
|
185
185
|
def json?
|
186
186
|
self[HTTP_CTYPE] =~ HTTP_JSON
|
187
187
|
end
|
188
|
-
HTTP_XML = /text\/xml
|
188
|
+
HTTP_XML = /text\/xml/.freeze
|
189
189
|
# returns true if the request is of type XML.
|
190
190
|
def xml?
|
191
191
|
self[HTTP_CTYPE].match HTTP_XML
|
@@ -214,13 +214,20 @@ module Iodine
|
|
214
214
|
|
215
215
|
request.delete :headers_size
|
216
216
|
|
217
|
-
|
218
|
-
request[
|
217
|
+
# 2014 RFC 7239 Forwarded: for=192.0.2.60; proto=http; by=203.0.113.43
|
218
|
+
if tmp = request['forwarded'.freeze]
|
219
|
+
tmp = tmp.join(';'.freeze) if tmp.is_a?(Array)
|
220
|
+
tmp.match(/proto=([^\s;]+)/i.freeze).tap {|m| request[:scheme] = m[1].downcase if m } unless request[:scheme]
|
221
|
+
tmp.match(/for=[\[]?[\"]?([^\s;\",]+)/i.freeze).tap {|m| request[:client_ip] = m[1] if m } unless request[:client_ip]
|
222
|
+
end
|
223
|
+
|
224
|
+
request[:client_ip] ||= (request['x-forwarded-for'.freeze].to_s.match(/[^\s\[\"\,]+/.freeze) || request[:io].io.to_io.remote_address.ip_address).to_s rescue 'unknown'.freeze
|
225
|
+
request[:version] ||= '1'.freeze
|
219
226
|
|
220
227
|
request[:scheme] ||= request['x-forwarded-proto'.freeze] ? request['x-forwarded-proto'.freeze].downcase : ( request[:io].ssl? ? 'https'.freeze : 'http'.freeze)
|
221
228
|
tmp = (request['host'.freeze] || request[:authority] || ''.freeze).split(':'.freeze)
|
222
229
|
request[:host_name] = tmp[0]
|
223
|
-
request[:port] = tmp[1]
|
230
|
+
request[:port] = tmp[1]
|
224
231
|
|
225
232
|
tmp = (request[:query] ||= request[:path] ).split('?'.freeze, 2)
|
226
233
|
request[:path] = tmp[0].chomp('/'.freeze)
|
@@ -266,7 +273,7 @@ module Iodine
|
|
266
273
|
|
267
274
|
# encodes URL data.
|
268
275
|
def self.encode_url str
|
269
|
-
(str.to_s.gsub(/[^a-z0-9\*\.\_\-]/i) {|m| '%%%02x'.freeze % m.ord }).force_encoding(::Encoding::ASCII_8BIT)
|
276
|
+
(str.to_s.gsub(/[^a-z0-9\*\.\_\-]/i.freeze) {|m| '%%%02x'.freeze % m.ord }).force_encoding(::Encoding::ASCII_8BIT)
|
270
277
|
end
|
271
278
|
|
272
279
|
# Adds paramaters to a Hash object, according to the Iodine's server conventions.
|
@@ -284,7 +291,7 @@ module Iodine
|
|
284
291
|
c = (h[n] ||= {})
|
285
292
|
end
|
286
293
|
n = a.last
|
287
|
-
n.chomp!(']'); n.strip!;
|
294
|
+
n.chomp!(']'.freeze); n.strip!;
|
288
295
|
n = n.empty? ? nil : ( (n.to_i.to_s == n) ? n.to_i : n.to_sym )
|
289
296
|
if n
|
290
297
|
if c[n]
|
@@ -354,7 +361,7 @@ module Iodine
|
|
354
361
|
request[:body].rewind
|
355
362
|
case request['content-type'.freeze].to_s
|
356
363
|
when /x-www-form-urlencoded/.freeze
|
357
|
-
extract_params request[:body].read.split(/[&;]
|
364
|
+
extract_params request[:body].read.split(/[&;]/.freeze), request[:params] #, :form # :uri
|
358
365
|
when /multipart\/form-data/.freeze
|
359
366
|
read_multipart request, request
|
360
367
|
when /text\/xml/.freeze
|
@@ -387,12 +394,12 @@ module Iodine
|
|
387
394
|
boundary_length = nil
|
388
395
|
true until ( (line = body.gets) ) && line =~ /\A--(#{boundary.join '|'})(--)?[\r]?\n/
|
389
396
|
until body.eof?
|
390
|
-
return if line =~ /--[\r]?\n
|
397
|
+
return if line =~ /--[\r]?\n/.freeze
|
391
398
|
return boundary.pop if boundary.count > 1 && line.match(/--(#{boundary.join '|'})/)[1] != boundary.last
|
392
399
|
boundary_length = line.bytesize
|
393
|
-
line = body.gets until line.nil? || line =~
|
394
|
-
until line.nil? || line =~ /^[\r]?\n
|
395
|
-
tmp = line.strip.split ':', 2
|
400
|
+
line = body.gets until line.nil? || line =~ /\:/.freeze
|
401
|
+
until line.nil? || line =~ /^[\r]?\n/.freeze
|
402
|
+
tmp = line.strip.split ':'.freeze, 2
|
396
403
|
return Iodine.error "Http multipart parsing error (multipart header data malformed): #{line}" unless tmp && tmp.count == 2
|
397
404
|
tmp[0].strip!; tmp[0].downcase!; tmp[1].strip!;
|
398
405
|
part_headers[tmp[0]] = tmp[1]
|
@@ -403,7 +410,7 @@ module Iodine
|
|
403
410
|
Iodine.error "Wrong multipart format with headers: #{part_headers}"
|
404
411
|
return
|
405
412
|
end
|
406
|
-
extract_header part_headers['content-disposition'.freeze].split(/[;,][\s]
|
413
|
+
extract_header part_headers['content-disposition'.freeze].split(/[;,][\s]?/.freeze), part_headers
|
407
414
|
if name_prefix.empty?
|
408
415
|
name = part_headers[:name][1..-2]
|
409
416
|
else
|
@@ -420,7 +427,7 @@ module Iodine
|
|
420
427
|
end_part_pos += 1 unless body.getc =~ /[\r\n]/.freeze
|
421
428
|
end_part_pos += 1 unless body.getc =~ /[\r\n\-]/.freeze
|
422
429
|
if part_headers['content-type'.freeze]
|
423
|
-
if part_headers['content-type'.freeze] =~ /multipart/i
|
430
|
+
if part_headers['content-type'.freeze] =~ /multipart/i.freeze
|
424
431
|
body.pos = start_part_pos
|
425
432
|
read_multipart request, part_headers, boundary, name_prefix
|
426
433
|
else
|
@@ -428,13 +435,13 @@ module Iodine
|
|
428
435
|
add_param_to_hash "#{name}[type]", make_utf8!(part_headers['content-type'.freeze]), request[:params]
|
429
436
|
part_headers.each {|k,v| add_param_to_hash "#{name}[#{k.to_s}]", make_utf8!(v[0] == '"' ? v[1..-2].to_s : v), request[:params] if v}
|
430
437
|
|
431
|
-
tmp = Tempfile.new 'upload', encoding: 'binary'
|
438
|
+
tmp = Tempfile.new 'upload'.freeze, encoding: 'binary'.freeze
|
432
439
|
body.pos = start_part_pos
|
433
440
|
((end_part_pos - start_part_pos)/65_536).to_i.times {tmp << body.read(65_536)}
|
434
441
|
tmp << body.read(end_part_pos - body.pos)
|
435
442
|
add_param_to_hash "#{name}[size]", tmp.size, request[:params]
|
436
443
|
add_param_to_hash "#{name}[file]", tmp, request[:params] do |hash, key|
|
437
|
-
if key == :data || key == "data" && hash.has_key?(:file) && hash[:file].is_a?(::Tempfile)
|
444
|
+
if key == :data || key == "data".freeze && hash.has_key?(:file) && hash[:file].is_a?(::Tempfile)
|
438
445
|
hash[:file].rewind
|
439
446
|
(hash[:data] = hash[:file].read)
|
440
447
|
end
|
@@ -177,7 +177,7 @@ module Iodine
|
|
177
177
|
# Although memory will be allocated for the latest TCP/IP frame,
|
178
178
|
# this allows the websocket to disconnect if the incoming expected message size exceeds the allowed maximum size.
|
179
179
|
#
|
180
|
-
# If the
|
180
|
+
# 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
181
|
def self.message_size_limit=val
|
182
182
|
@message_size_limit = val
|
183
183
|
end
|
data/lib/iodine/protocol.rb
CHANGED
@@ -83,7 +83,7 @@ module Iodine
|
|
83
83
|
# This method is called whenever a timeout has occurred. Either implement a ping or close the connection.
|
84
84
|
# The default implementation closes the connection unless the protocol is still processing information received before timeout occurred.
|
85
85
|
def ping
|
86
|
-
@locker.locked?
|
86
|
+
close unless @locker.locked?
|
87
87
|
end
|
88
88
|
|
89
89
|
#############
|
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.20
|
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-12-
|
11
|
+
date: 2015-12-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|