rack-timeout 0.0.4 → 0.1.0beta

Sign up to get free protection for your applications and to get access to all the features.
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