iodine 0.1.7 → 0.1.8

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: 5fa1a23166127d28f5115a2a26d9f1065a0ebbe6
4
- data.tar.gz: a649b5fcb56decc43c8de19af7d6e8f60a594769
3
+ metadata.gz: 015d21e31dd393be87f10ecb19fe9d94f565255c
4
+ data.tar.gz: 8919757b4d844e02b110496fff6d8e84dc87f60f
5
5
  SHA512:
6
- metadata.gz: fea7a37cad21a33383709ba16af7d871eb6478fec9809aa1a4a562e965bb4f0b2a7193b76548b6c4a5940ac2ef0d0a11561d0c64f0e0d5aa15241b4d81a9764e
7
- data.tar.gz: dfd9f61e71a8b37d5e76feb234f544797585bd6b46cfecf2ecb253247ed34cb3bc9abc032a1e30e771d15a76e7180bd46a2c28ffa18f244438075df666f2084a
6
+ metadata.gz: 6198a6eaa862ce000905693bad9afb670359ab20b80987713f95df11eefe8d1b951f243f761bac2546701eb10c215e5cea157f215eb3fd48f4bc229d50927e6a
7
+ data.tar.gz: e96c344aad2a559a5af5b4f2f6365f876335ad3fc741f5cf212eede1002ec3a87751dc78f51d3b0228fdf16d729369f05cde90aaa573af521e25315fb5086ff4
@@ -8,6 +8,22 @@ 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.8
12
+
13
+ **Fix**: Websocket broadcasts are now correctly executed within the IO's mutex locker. This maintains the idea that only one thread at a time should be executing code on behald of any given Protocol object ("yes" to concurrency between objects but "no" to concurrency within objects).
14
+
15
+ **Fix** fixed an issue where manually setting the number of threads for Rack applications (when using Iodine as a Rack server), the setting was mistakenly ignored.
16
+
17
+ **Fix** fixed an issue where sometimes extractin the Http response's body would fail (if body is `nil`).
18
+
19
+ **Feature**: session objects are now aware of the session id. The seesion id is available by calling `response.session.id`
20
+
21
+ **Fix** fixed an issue where Http streaming wasn't chunk encoding after connection error handling update.
22
+
23
+ **Fix** fixed an issue where Http streaming would disconnect while still processing. Streaming timeout now extended to 15 seconds between response writes.
24
+
25
+ ***
26
+
11
27
  Change log v.0.1.7
12
28
 
13
29
  Removed a deprecation notice for blocking API. Client API will remain blocking due to use-case requirements.
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Iodine
2
2
  [![Gem Version](https://badge.fury.io/rb/iodine.svg)](https://badge.fury.io/rb/iodine)
3
3
  [![Inline docs](http://inch-ci.org/github/boazsegev/iodine.svg?branch=master)](http://www.rubydoc.info/github/boazsegev/iodine/master/frames)
4
+ [![GitHub](https://img.shields.io/badge/GitHub-Open%20Source-blue.svg)](https://github.com/boazsegev/iodine)
4
5
 
5
6
  Iodine makes writing Object Oriented evented server applications easy to write.
6
7
 
@@ -249,6 +250,16 @@ end if Process.respond_to? :fork
249
250
  exit
250
251
  ```
251
252
 
253
+ ## Cuncurrency?
254
+
255
+ Iodine maintains the idea of: "yes" to concurrency between objects but "no" to concurrency within objects.
256
+
257
+ Iodine applies this concept when running in Task mode (or timer mode), by defaulting to single threaded mode, preventing multi-threading race conditions in an unknown environment. Also, each task runs in a single thread from the thread-pool, so unless it tries to set or manipulate global data, it's safe from race conditions.
258
+
259
+ Iodine applies this concept when running in Server mode by locking the Protocol instance whenever Iodine calls for actions related to that Protocol.
260
+
261
+ For instance, in Iodine's implementation for the Websocket protocol: Websocket messages to different connections can run concurrently, however multiple messages to the same connection are only executed one at a time, maintaining their order (lately a fix in version 0.1.8 made sure that also websocket broadcasting will be executed within the Protocol lock, preventing concurrency within the same connection).
262
+
252
263
  ## Development
253
264
 
254
265
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -51,6 +51,14 @@ Process.fork do
51
51
  Iodine.ssl = true
52
52
  Iodine.port = 3030
53
53
  Iodine::Http.on_http do |req, res|
54
+ if req.path == '/stream'
55
+ res.stream_async { sleep 0.2; res << "I was sleeping..."}
56
+ next
57
+ end
58
+ if req.path == '/stream2'
59
+ res.stream_async { sleep 2; res << "I was sleeping..."}
60
+ next
61
+ end
54
62
  res.session[:count] ||= 0
55
63
  res.session[:count] += 1
56
64
  res['content-type'] = 'text/plain'
@@ -45,7 +45,8 @@ module Iodine
45
45
  @stop = true
46
46
  @done = false
47
47
  @logger = Logger.new(STDOUT)
48
- @spawn_count = @thread_count = 1
48
+ @spawn_count = 1
49
+ @thread_count = nil
49
50
  @ios = {}
50
51
  @io_in = Queue.new
51
52
  @io_out = Queue.new
@@ -73,7 +74,7 @@ module Iodine
73
74
 
74
75
  def startup use_rescue = false, hide_message = false
75
76
  threads = []
76
- @thread_count.times { threads << Thread.new { cycle } }
77
+ (@thread_count ||= 1).times { threads << Thread.new { cycle } }
77
78
  unless @stop
78
79
  if use_rescue
79
80
  sleep rescue true
@@ -122,6 +122,7 @@ module Iodine
122
122
  log_finished response
123
123
  end
124
124
  def stream_response response, finish = false
125
+ timeout = 15
125
126
  unless response.headers.frozen?
126
127
  response['transfer-encoding'.freeze] = 'chunked'
127
128
  response.headers['connection'.freeze] = 'close'.freeze
@@ -131,7 +132,7 @@ module Iodine
131
132
  return if response.request.head?
132
133
  body = response.extract_body
133
134
  until body.eof?
134
- written = write(body.read 65_536)
135
+ written = stream_data(body.read 65_536)
135
136
  return Iodine.warn("Http/1 couldn't send response because connection was lost.") && body.close unless written
136
137
  response.bytes_written += written
137
138
  end if body
@@ -7,10 +7,12 @@ module Iodine
7
7
  def run(app, options = {})
8
8
  @app = app
9
9
 
10
- Iodine.threads = 18
10
+ puts "reading threads = #{Iodine.threads.to_s}"
11
+
12
+ Iodine.threads = 18 unless Iodine.threads
11
13
  Iodine.port = options[:Port]
12
14
  Iodine.protocol ||= Iodine::Http::Http1
13
- @pre_rack_handler = Iodine::Http.on_http
15
+ @pre_rack_handler = Iodine::Http.on_http unless Iodine::Http.on_http == Iodine::Http::NOT_IMPLEMENTED
14
16
  Iodine::Http.on_http self
15
17
  true
16
18
  end
@@ -317,7 +317,8 @@ module Iodine
317
317
  elsif @body.is_a?(String)
318
318
  return (@body = nil) if body.empty?
319
319
  StringIO.new @body
320
- elsif body.nil?
320
+ elsif @body.nil?
321
+ return nil
321
322
  nil
322
323
  elsif @body.is_a?(File) || @body.is_a?(Tempfile) || @body.is_a?(StringIO)
323
324
  @body
@@ -4,9 +4,19 @@ module Iodine
4
4
  module SessionManager
5
5
 
6
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
7
17
  @mem_storage = {}
8
18
  def self.fetch key
9
- @mem_storage[key] ||= {}
19
+ @mem_storage[key] ||= SessionObject.new(key)
10
20
  end
11
21
  end
12
22
  module FileSessionStorage
@@ -15,6 +25,11 @@ module Iodine
15
25
  def initialize id
16
26
  @filename = File.join Dir.tmpdir, "iodine_#{Iodine::Http.session_token}_#{id}"
17
27
  @data ||= {}
28
+ @id = id
29
+ end
30
+ # returns the session id (the session cookie value).
31
+ def id
32
+ @id
18
33
  end
19
34
  # Get a key from the session data store.
20
35
  #
@@ -94,9 +109,8 @@ module Iodine
94
109
  # A Session Storage system must answer only one methods:
95
110
  # 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
96
111
  #
97
- # The Session Object should update itself in the storage whenever data is saved to the session Object.
98
- # This is important also because websocket 'session' could exist simultaneously with other HTTP requests and the data should be kept updated at all times.
99
- # If there are race conditions that apply for multi-threading / multi processing, the Session Object should manage them as well as possible.
112
+ # The Session Object should update the storage whenever data is saved to the session Object.
113
+ # 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.
100
114
  def storage= session_storage = nil
101
115
  case session_storage
102
116
  when :file, nil
@@ -116,6 +130,9 @@ module Iodine
116
130
  end
117
131
  end
118
132
  # A hash like interface for storing request session data.
119
- # The store must implement: store(key, value) (aliased as []=);
133
+ # The store must implement:
134
+ # store(key, value) (aliased as []=);
120
135
  # fetch(key, default = nil) (aliased as []);
121
- # delete(key); clear;
136
+ # delete(key);
137
+ # clear;
138
+ # id(); (returns the session id)
@@ -96,9 +96,9 @@ module Iodine
96
96
  @io.close if @io
97
97
  end
98
98
 
99
- # checks if the socket is open (if the websocket was terminated abnormally, this might returs true when it should be false).
99
+ # checks if the socket is open (if the websocket was terminated abnormally, this might return true for a while after it should be false).
100
100
  def closed?
101
- @io.io.closed? if @io && @io.io
101
+ (@io && @io.io) ? @io.io.closed? : true
102
102
  end
103
103
 
104
104
  # checks if this is an SSL websocket connection.
@@ -16,7 +16,7 @@ module Iodine
16
16
  end
17
17
  # handle broadcasts.
18
18
  def on_broadcast data
19
- @handler.on_broadcast(data) if @handler.respond_to? :on_broadcast
19
+ @locker.synchronize { @handler.on_broadcast(data) if @handler.respond_to? :on_broadcast }
20
20
  end
21
21
  # cleanup after closing.
22
22
  def on_close
@@ -47,6 +47,11 @@ module Iodine
47
47
  # the argument or options Hash passed to the initializer as a second argument (the first argument MUST be the IO object).
48
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
49
  attr_reader :options
50
+ # The protocol's Mutex locker. It should be locked whenever your code is runing, unless you are
51
+ # writing asynchronouse code.
52
+ #
53
+ # Use with care (or, better yet, don't use).
54
+ attr_reader :locker
50
55
 
51
56
  # Sets the timeout in seconds for IO activity (set timeout within {#on_open}).
52
57
  #
@@ -14,6 +14,11 @@ module Iodine
14
14
  @thread_count = count
15
15
  end
16
16
 
17
+ # Gets the number of threads in the thread pool used for executing the tasks. Returns `nil` unless previously set or Iodine is running.
18
+ def threads
19
+ @thread_count
20
+ end
21
+
17
22
  # Sets the number of processes that should be spawned in Server mode. Defaults to 1 (no processes spawned).
18
23
  #
19
24
  # * Forking (spwaning processes) might NOT work on all systems (forking is supported by Ruby on Unix systems).
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = "0.1.7"
2
+ VERSION = "0.1.8"
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.7
4
+ version: 0.1.8
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-28 00:00:00.000000000 Z
11
+ date: 2015-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler