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 +8 -8
- data/README.markdown +28 -30
- data/lib/rack-timeout.rb +6 -8
- data/lib/rack/timeout.rb +72 -3
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZmIyM2ZmYTI0MGY2MWM2YjViZDYwNDZjZjEyNGEwODM5OGY5MGUzYw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YmE0ZjYyZGNjOWE1NTljN2VhYjBjM2E5ZmI3NWIzMjIyY2YzZGJiYQ==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NjQxODhjN2JlNjNjM2E2NzI1NDA4NzRhNDFjMmUxYmVmMTFlOGI1NWU3ZWNk
|
10
|
+
NDNkMDYwZjc5YjRiZTE3NGViYTY1YjQyYWJiMjcyZWJhNGMzMjQ0ZTk3M2E1
|
11
|
+
NGI2YTk3YzZjZmRlMmUyNGFkYmQ3OWM2MjdkYTZhZGY1ZWMwZDA=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
10
|
+
Setup for current versions of Rails, Rack, Ruby, and Bundler. See next section for legacy versions.
|
11
11
|
|
12
|
-
|
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
|
-
|
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 #
|
34
|
-
Rack::Timeout.timeout = 10 #
|
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
|
-
|
39
|
-
|
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
|
-
|
37
|
+
For applications running Ruby 1.8.x and/or Rails 2.x, use [version 0.0.4][v0.0.4].
|
43
38
|
|
44
|
-
|
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
|
-
|
50
|
-
|
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
|
-
|
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
|
55
|
-
|
56
|
-
in
|
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
|
-
|
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
|
-
|
1
|
+
# encoding: utf-8
|
2
|
+
require_relative 'rack/timeout'
|
2
3
|
|
3
|
-
if defined? Rails
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
-
|
2
|
-
|
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
|
-
|
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.
|
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-
|
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:
|
41
|
+
version: 1.3.1
|
42
42
|
requirements: []
|
43
43
|
rubyforge_project:
|
44
44
|
rubygems_version: 2.0.3
|