rack-timeout 0.0.4 → 0.1.0beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YjU4ZjQzOTJjZTcwNDdiZGZiZmU0YWI5NDMyMDRlYzU3Y2M3MTRjNQ==
4
+ ZmIyM2ZmYTI0MGY2MWM2YjViZDYwNDZjZjEyNGEwODM5OGY5MGUzYw==
5
5
  data.tar.gz: !binary |-
6
- ZGRlNjU2ZGExNWY2NTYxODU5ZTgyYmMxYWM4MmM2NmYxZDZlNzQ2Ng==
6
+ YmE0ZjYyZGNjOWE1NTljN2VhYjBjM2E5ZmI3NWIzMjIyY2YzZGJiYQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- ZWIyMTg0ODVmM2JkMDE0YzUxZjcxZmNiZTYzMjQxZWZmMzc2YTJlMGQ4OTAz
10
- NDBlMzczYTY5MDM4YTlkY2JiMTYwOGNjNDNhYTE3YzhiZDQyNjEwOTgxZjVi
11
- ZjYxZDQxZWNiZDMxNzM5NzI4ZmUwMDU2NWJlNzJjNWM0YzM5M2Q=
9
+ NjQxODhjN2JlNjNjM2E2NzI1NDA4NzRhNDFjMmUxYmVmMTFlOGI1NWU3ZWNk
10
+ NDNkMDYwZjc5YjRiZTE3NGViYTY1YjQyYWJiMjcyZWJhNGMzMjQ0ZTk3M2E1
11
+ NGI2YTk3YzZjZmRlMmUyNGFkYmQ3OWM2MjdkYTZhZGY1ZWMwZDA=
12
12
  data.tar.gz: !binary |-
13
- YzQzMzU3MDAxMWY3Y2U4YzY3MjZhYmU4YmRlOGEzOTVjOTNkZWZjNjRiMDlk
14
- NWZmMWQ1Zjk4ZjdiNThhOTJiMjQ0N2RhODYwNmQyZTYxM2I0NTM4ZTAzYjQ3
15
- M2EwNmEzMzc1OWM4NmY3ZjhkYTgzMGJiYjNlODJmZGEyNWM2MWI=
13
+ ODg1ODEzZjRhY2FiYmY1NjBmYzkzZDZlZTAxZjRmZWU5MDk3NGY5OWMyN2Q4
14
+ ZDcyN2QwMzM2NjM5N2RjYzIwYWU3MDE3OWI0NGMyNGRhOTNhYTQxNzMxNTcz
15
+ YzAwMzE1OTZmMTA5ZmU2OTdmMWNjN2JlYTI3MmVjMjIzMTVjNmM=
data/README.markdown CHANGED
@@ -1,64 +1,62 @@
1
1
  Rack::Timeout
2
2
  =============
3
3
 
4
- Abort requests that are taking too long; a Timeout::Error will be raised.
4
+ Abort requests that are taking too long; a `Rack::Timeout::Error` will be raised.
5
5
 
6
6
 
7
7
  Usage
8
8
  -----
9
9
 
10
- ### Rails 3 app or Rails 2.3 app with Ruby 1.9 and Bundler
10
+ Setup for current versions of Rails, Rack, Ruby, and Bundler. See next section for legacy versions.
11
11
 
12
- # Gemfile
13
- gem "rack-timeout"
14
-
15
- ### Rails 3 app or Rails 2.3 app with Ruby 1.8 and Bundler
12
+ ### Rails apps
16
13
 
17
14
  # Gemfile
18
- gem "SystemTimer", :require => "system_timer", :platforms => :ruby_18
19
15
  gem "rack-timeout"
20
16
 
17
+ That's all that's required if you want to use the default timeout of 15s. To use a custom timeout,
18
+ create an initializer file:
21
19
 
22
- ### Rails 2.3 app without Bundler
23
-
24
- # config/environment.rb
25
- config.gem "SystemTimer", :lib => "system_timer" if RUBY_VERSION < "1.9"
26
- config.gem "rack-timeout"
27
-
20
+ # config/initializers/timeout.rb
21
+ Rack::Timeout.timeout = 10 # seconds
28
22
 
29
23
  ### Sinatra and other Rack apps
30
24
 
31
25
  # config.ru
32
26
  require "rack/timeout"
33
- use Rack::Timeout # call as early as possible so rack-timeout runs before other middlewares.
34
- Rack::Timeout.timeout = 10 # this line is optional. if omitted, default is 15 seconds.
27
+ use Rack::Timeout # Call as early as possible so rack-timeout runs before other middleware.
28
+ Rack::Timeout.timeout = 10 # This line is optional. If omitted, timeout defaults to 15 seconds.
35
29
 
36
- ### Setting a custom timeout for Rails apps
37
30
 
38
- # config/initializers/timeout.rb
39
- Rack::Timeout.timeout = 10 # seconds
31
+ Compatibility
32
+ -------------
40
33
 
34
+ This version of Rack::Timeout is compatible with Ruby 1.9.1 and up, and, for Rails apps, Rails 3.x
35
+ and up.
41
36
 
42
- ### Here be dragons
37
+ For applications running Ruby 1.8.x and/or Rails 2.x, use [version 0.0.4][v0.0.4].
43
38
 
44
- SystemTimer/timeout rely on threads. If your app or any of the libraries it depends on is not thread-safe,
45
- you may run into issues using rack-timeout.
39
+ [v0.0.4]: https://github.com/kch/rack-timeout/tree/v0.0.4
46
40
 
47
- Concurrent web servers such as [Unicorn][] and [Puma][] should work fine with rack-timeout.
48
41
 
49
- [Unicorn]: http://unicorn.bogomips.org/
50
- [Puma]: http://puma.io/
42
+ Here be dragons
43
+ ---------------
44
+
45
+ * Ruby's Timeout rely on threads. If your app or any of the libraries it depends on is not
46
+ thread-safe, you may run into issues using Rack::Timeout.
51
47
 
52
- #### A note about testing timeouts in Rails apps
48
+ Concurrent web servers such as [Unicorn][] and [Puma][] should work fine with Rack::Timeout.
53
49
 
54
- If you're trying to test that a `Timeout::Error` is being raised in your Rails application, please note that
55
- it's **not possible in functional tests**. You *can*, however, test `assert_raises Rack::Timeout::Error`
56
- in integration tests.
50
+ * If you're trying to test that a `Rack::Timeout::Error` is raised in an action in your Rails
51
+ application, you **must do so in integration tests**. Please note that Rack::Timeout will not
52
+ kick in for functional tests as they bypass the rack middleware stack.
57
53
 
58
- There are more details about general rack middleware testing with Rails in this [@pablobm's answer on Stack Overflow][pablobm].
54
+ [More details about testing middleware with Rails here][pablobm].
59
55
 
56
+ [Unicorn]: http://unicorn.bogomips.org/
57
+ [Puma]: http://puma.io/
60
58
  [pablobm]: http://stackoverflow.com/a/8681208/13989
61
59
 
62
60
  ---
63
- Copyright © 2010 Caio Chassot, released under the MIT license
61
+ Copyright © 2010-2013 Caio Chassot, released under the MIT license
64
62
  <http://github.com/kch/rack-timeout>
data/lib/rack-timeout.rb CHANGED
@@ -1,11 +1,9 @@
1
- require File.join(File.expand_path(File.dirname(__FILE__)), 'rack/timeout')
1
+ # encoding: utf-8
2
+ require_relative 'rack/timeout'
2
3
 
3
- if defined? Rails
4
- case Rails::VERSION::MAJOR
5
- when 2; Rails.configuration.middleware.insert 0, Rack::Timeout
6
- when 3
7
- class Rack::Timeout::Railtie < Rails::Railtie
8
- initializer("rack-timeout.insert-rack-timeout") { |app| app.config.middleware.insert 0, Rack::Timeout }
9
- end
4
+ if defined?(Rails) && [3,4].include?(Rails::VERSION::MAJOR)
5
+ class Rack::Timeout::Railtie < Rails::Railtie
6
+ initializer('rack-timeout.prepend') { |app| app.config.middleware.insert 0, Rack::Timeout }
7
+ initializer('rack-timeout.timeout-trap') { |app| app.config.middleware.use Rack::Timeout::AbortionReporter }
10
8
  end
11
9
  end
data/lib/rack/timeout.rb CHANGED
@@ -1,8 +1,19 @@
1
- require RUBY_VERSION < '1.9' && RUBY_PLATFORM != 'java' ? 'system_timer' : 'timeout'
2
- SystemTimer ||= Timeout
1
+ # encoding: utf-8
2
+ require 'timeout'
3
+ require 'securerandom'
3
4
 
4
5
  module Rack
5
6
  class Timeout
7
+ class Error < RuntimeError; end
8
+ class RequestTooOldError < Error; end
9
+ class RequestAbortedError < Error; end
10
+
11
+ RequestData = Struct.new(:id, :age, :timeout, :duration, :state)
12
+ ENV_INFO_KEY = 'rack-timeout.info'
13
+ FRAMEWORK_ERROR_KEYS = %w(sinatra.error rack.exception)
14
+ FINAL_STATES = [:dropped, :aborted, :completed]
15
+ MAX_REQUEST_AGE = 30 # seconds
16
+
6
17
  @timeout = 15
7
18
  class << self
8
19
  attr_accessor :timeout
@@ -13,7 +24,65 @@ module Rack
13
24
  end
14
25
 
15
26
  def call(env)
16
- SystemTimer.timeout(self.class.timeout, ::Timeout::Error) { @app.call(env) }
27
+ info = env[ENV_INFO_KEY] ||= RequestData.new
28
+ info.id ||= env['HTTP_HEROKU_REQUEST_ID'] || SecureRandom.hex
29
+ request_start = env['HTTP_X_REQUEST_START'] # unix timestamp in ms
30
+ request_start = Time.at(request_start.to_i / 1000) if request_start
31
+ info.age = Time.now - request_start if request_start
32
+ time_left = MAX_REQUEST_AGE - info.age if info.age
33
+ info.timeout = [self.class.timeout, time_left].compact.select { |n| n >= 0 }.min
34
+
35
+ if time_left && time_left <= 0
36
+ Rack::Timeout.set_state_and_log! info, :dropped
37
+ raise RequestTooOldError
38
+ end
39
+
40
+ Rack::Timeout.set_state_and_log! info, :ready
41
+ ::Timeout.timeout(info.timeout, RequestAbortedError) do
42
+ ready_time = Time.now
43
+ response = Rack::Timeout.perform_reporting_abortion_state_in_env(env) { @app.call(env) }
44
+ info.duration = Time.now - ready_time
45
+ Rack::Timeout.set_state_and_log! info, :completed
46
+ response
47
+ end
48
+ end
49
+
50
+ def self.perform_reporting_abortion_state_in_env(env)
51
+ yield
52
+ rescue RequestAbortedError
53
+ set_aborted! env
54
+ raise
55
+ ensure
56
+ set_aborted! env if env.values_at(*FRAMEWORK_ERROR_KEYS).any? { |e| e.is_a? RequestAbortedError }
57
+ end
58
+
59
+ def self.set_aborted!(env)
60
+ set_state_and_log!(env[ENV_INFO_KEY], :aborted)
61
+ end
62
+
63
+ def self.set_state_and_log!(info, state)
64
+ return if FINAL_STATES.include? info.state
65
+ info.state = state
66
+ ms = ->(s) { '%.fms' % (s * 1000) }
67
+ s = 'source=rack-timeout'
68
+ s << ' id=' << info.id if info.id
69
+ s << ' age=' << ms[info.age] if info.age
70
+ s << ' timeout=' << ms[info.timeout] if info.timeout
71
+ s << ' duration=' << ms[info.duration] if info.duration
72
+ s << ' state=' << info.state.to_s if info.state
73
+ s << "\n"
74
+
75
+ $stderr << s
76
+ end
77
+
78
+ class AbortionReporter
79
+ def initialize(app)
80
+ @app = app
81
+ end
82
+
83
+ def call(env)
84
+ Rack::Timeout.perform_reporting_abortion_state_in_env(env) { @app.call(env) }
85
+ end
17
86
  end
18
87
 
19
88
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-timeout
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.1.0beta
5
5
  platform: ruby
6
6
  authors:
7
7
  - Caio Chassot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-04-25 00:00:00.000000000 Z
11
+ date: 2013-04-26 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Rack middleware which aborts requests that have been running for longer
14
14
  than a specified timeout.
@@ -36,9 +36,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
36
36
  version: '0'
37
37
  required_rubygems_version: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - ! '>='
39
+ - - ! '>'
40
40
  - !ruby/object:Gem::Version
41
- version: '0'
41
+ version: 1.3.1
42
42
  requirements: []
43
43
  rubyforge_project:
44
44
  rubygems_version: 2.0.3