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 +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
|