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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 26fc8c539c88696980b52287e3cbe646333fd1de
4
- data.tar.gz: 3b0fbe71cc301108ccfe9a92b9e651062c1679a5
3
+ metadata.gz: b5ee2e6d64a08ec88c75a4e0eaed1d15586310e8
4
+ data.tar.gz: fe61017b3b92e43efb5cf1ca9331e5143940ec8c
5
5
  SHA512:
6
- metadata.gz: b7f048ef6135b596cc0041a28e52e0889a384df22094027058f4b3ef8bc7b132d21f2b48336b7b9fe22c0fb27cf626f62fd162379d563520259572653df5a084
7
- data.tar.gz: 3860f9850f6a53688d5fbf5920900f7852404047cb4daad09ee46f49a4debf60b48a0da015e2bde5f7626018fb79275a4c93d0419951eab2b7a95d90c2b26280
6
+ metadata.gz: a9e83552a00e1799096ef2b0515ed417a18eab511c0460743a4f6f003613ae47114b55abe14ec0dd1243a9dd846032ad30f59b3786c71673192e504fa0a2ebed
7
+ data.tar.gz: 40989499a5b70e09fb35dcd29ebcc8fd3326ff5b9e886ea7800b6e9f01c4ef01fd99b587ebff92aad9ff03d7d5e5ada3489bc18c9887864be4148e2263cdc3eb
@@ -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.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).
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.max_http_buffer = new_value`
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.
@@ -109,8 +109,8 @@ module Iodine
109
109
  next
110
110
  end
111
111
  on_shutdown do
112
- log "Stopping to listen on port #{@port} and shutting down.\n"
113
- @server.close unless @server.closed?
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"
@@ -100,13 +100,23 @@ module Iodine
100
100
  @session_token
101
101
  end
102
102
 
103
- # Sets the maximum bytes allowed for an HTTP's body request (file upload data). Defaults to the command line `-limit` argument, or ~0.25GB.
103
+ # `max_http_buffer` is deprecated. Use `max_body_size` instead.
104
104
  def max_http_buffer= size
105
- @max_http_buffer = size
105
+ Iodine.warn "`max_http_buffer` is deprecated. Use `max_body_size` instead."
106
+ max_body_size = size
106
107
  end
107
- # Gets the maximum bytes allowed for an HTTP's body request (file upload data). Defaults to the command line `-limit` argument, or ~0.25GB.
108
+ # `max_http_buffer` is deprecated. Use `max_body_size` instead.
108
109
  def max_http_buffer
109
- @max_http_buffer
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
- @max_http_buffer = ((ARGV.index('-limit') && ARGV[ARGV.index('-limit') + 1]) || 536_870_912).to_i
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"
@@ -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('Htt1 Protocol Error, closing connection.'.freeze) && close) unless request[:method] =~ HTTP_METHODS_REGEXP
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.max_http_buffer
86
- Iodine.warn("Http1 message body too big, closing connection (Iodine::Http.max_http_buffer == #{::Iodine::Http.max_http_buffer} bytes) - #{request[:body].size} bytes.")
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
@@ -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.max_http_buffer
341
- Iodine.warn("Http2 payload (message size) too big (Iodine::Http.max_http_buffer == #{::Iodine::Http.max_http_buffer} bytes) - #{frame[:stream][:body].size} bytes.")
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
 
@@ -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
- request[:client_ip] = request['x-forwarded-for'.freeze].to_s.split(/,[\s]?/)[0] || (request[:io].io.to_io.remote_address.ip_address) rescue 'unknown IP'.freeze
218
- request[:version] ||= '1'
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] || nil
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(/[&;]/), request[:params] #, :form # :uri
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]?/), part_headers
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 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.
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
@@ -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? ? touch : close
86
+ close unless @locker.locked?
87
87
  end
88
88
 
89
89
  #############
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = "0.1.19"
2
+ VERSION = "0.1.20"
3
3
  end
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.19
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-02 00:00:00.000000000 Z
11
+ date: 2015-12-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler