rack_after_reply 0.0.1
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.
- data/CHANGELOG +3 -0
- data/LICENSE +20 -0
- data/README.markdown +46 -0
- data/Rakefile +1 -0
- data/lib/rack_after_reply.rb +39 -0
- data/lib/rack_after_reply/adapter.rb +10 -0
- data/lib/rack_after_reply/adapter/base.rb +15 -0
- data/lib/rack_after_reply/adapter/mongrel.rb +25 -0
- data/lib/rack_after_reply/adapter/passenger.rb +23 -0
- data/lib/rack_after_reply/adapter/thin.rb +17 -0
- data/lib/rack_after_reply/adapter/unicorn.rb +23 -0
- data/lib/rack_after_reply/adapter/webrick.rb +47 -0
- data/lib/rack_after_reply/app_proxy.rb +32 -0
- data/lib/rack_after_reply/request_handler.rb +18 -0
- data/lib/rack_after_reply/version.rb +11 -0
- metadata +82 -0
data/CHANGELOG
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) George Ogata
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# Rack After Reply
|
2
|
+
|
3
|
+
A hook for Rack apps which fires after the response has been sent, and
|
4
|
+
the socket to the client has been closed.
|
5
|
+
|
6
|
+
This is the ideal time to perform delayable, non-backgroundable tasks,
|
7
|
+
such as garbage collection, stats gathering, flushing logs, etc.
|
8
|
+
without affecting response times at all.
|
9
|
+
|
10
|
+
## Usage
|
11
|
+
|
12
|
+
Simply add your callbacks to `env['rack_after_reply.callbacks']`.
|
13
|
+
|
14
|
+
use Rack::ContentLength
|
15
|
+
use Rack::ContentType, 'text/plain'
|
16
|
+
run lambda { |env|
|
17
|
+
env['rack_after_reply.callbacks'] << lambda { ... }
|
18
|
+
[200, {}, ['hi']]
|
19
|
+
}
|
20
|
+
|
21
|
+
## Support
|
22
|
+
|
23
|
+
Rack After Request works with these web servers:
|
24
|
+
|
25
|
+
* [Mongrel](https://github.com/fauna/mongrel)
|
26
|
+
* [Passenger](http://www.modrails.com)
|
27
|
+
* [Thin](https://github.com/macournoyer/thin)
|
28
|
+
* [Unicorn](http://unicorn.bogomips.org)
|
29
|
+
* WEBrick (distributed with Ruby)
|
30
|
+
|
31
|
+
To request support for other web servers, [open a ticket][issues] or
|
32
|
+
submit a patch.
|
33
|
+
|
34
|
+
[issues]: http://github.com/oggy/rack_after_reply/issues
|
35
|
+
|
36
|
+
## Contributing
|
37
|
+
|
38
|
+
* [Bug reports](https://github.com/oggy/rack_after_reply/issues)
|
39
|
+
* [Source](https://github.com/oggy/rack_after_reply)
|
40
|
+
* Patches: Fork on Github, send pull request.
|
41
|
+
* Ensure patch includes tests.
|
42
|
+
* Leave the version alone, or bump it in a separate commit.
|
43
|
+
|
44
|
+
## Copyright
|
45
|
+
|
46
|
+
Copyright (c) George Ogata. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'ritual'
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
module RackAfterReply
|
4
|
+
CALLBACKS_KEY = 'rack_after_reply.callbacks'.freeze
|
5
|
+
|
6
|
+
autoload :AppProxy, 'rack_after_reply/app_proxy'
|
7
|
+
autoload :Adapter, 'rack_after_reply/adapter'
|
8
|
+
autoload :RequestHandler, 'rack_after_reply/request_handler'
|
9
|
+
|
10
|
+
class << self
|
11
|
+
#
|
12
|
+
# Apply extensions for all loaded web servers.
|
13
|
+
#
|
14
|
+
def apply
|
15
|
+
Adapter::Thin.apply if defined?(::Thin)
|
16
|
+
Adapter::Mongrel.apply if defined?(::Mongrel)
|
17
|
+
Adapter::Passenger.apply if defined?(::PhusionPassenger)
|
18
|
+
Adapter::WEBrick.apply if defined?(::WEBrick)
|
19
|
+
Adapter::Unicorn.apply if defined?(::Unicorn)
|
20
|
+
end
|
21
|
+
|
22
|
+
def freedom_patch(mod, method) # :nodoc:
|
23
|
+
# Prevent infinite recursion if we've already done it.
|
24
|
+
return if mod.method_defined?("#{method}_without_rack_after_reply")
|
25
|
+
|
26
|
+
mod.module_eval do
|
27
|
+
alias_method "#{method}_without_rack_after_reply", method
|
28
|
+
alias_method method, "#{method}_with_rack_after_reply"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def freedom_extend(object, method) # :nodoc:
|
33
|
+
klass = (class << object; self; end)
|
34
|
+
freedom_patch(klass, method)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
RackAfterReply.apply
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module RackAfterReply
|
2
|
+
module Adapter
|
3
|
+
autoload :Base, 'rack_after_reply/adapter/base'
|
4
|
+
autoload :Mongrel, 'rack_after_reply/adapter/mongrel'
|
5
|
+
autoload :Passenger, 'rack_after_reply/adapter/passenger'
|
6
|
+
autoload :Thin, 'rack_after_reply/adapter/thin'
|
7
|
+
autoload :Unicorn, 'rack_after_reply/adapter/unicorn'
|
8
|
+
autoload :WEBrick, 'rack_after_reply/adapter/webrick'
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RackAfterReply
|
2
|
+
module Adapter
|
3
|
+
class Mongrel < Base
|
4
|
+
def apply
|
5
|
+
Rack::Handler::Mongrel.module_eval do
|
6
|
+
include RackAfterReply::RequestHandler
|
7
|
+
|
8
|
+
def initialize_with_rack_after_reply(app)
|
9
|
+
app = AppProxy.new(self, app)
|
10
|
+
initialize_without_rack_after_reply(app)
|
11
|
+
end
|
12
|
+
RackAfterReply.freedom_patch self, :initialize
|
13
|
+
|
14
|
+
def process_with_rack_after_reply(request, response)
|
15
|
+
process_without_rack_after_reply(request, response)
|
16
|
+
ensure
|
17
|
+
response.socket.close
|
18
|
+
fire_rack_after_reply
|
19
|
+
end
|
20
|
+
RackAfterReply.freedom_patch self, :process
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module RackAfterReply
|
2
|
+
module Adapter
|
3
|
+
class Passenger < Base
|
4
|
+
def apply
|
5
|
+
PhusionPassenger::Rack::RequestHandler.module_eval do
|
6
|
+
include RackAfterReply::RequestHandler
|
7
|
+
|
8
|
+
def initialize_with_rack_after_reply(owner_pipe, app, options = {})
|
9
|
+
app = AppProxy.new(self, app)
|
10
|
+
initialize_without_rack_after_reply(owner_pipe, app, options)
|
11
|
+
end
|
12
|
+
RackAfterReply.freedom_patch self, :initialize
|
13
|
+
|
14
|
+
def accept_and_process_next_request_with_rack_after_reply(socket_wrapper, channel, buffer)
|
15
|
+
accept_and_process_next_request_without_rack_after_reply(socket_wrapper, channel, buffer)
|
16
|
+
fire_rack_after_reply
|
17
|
+
end
|
18
|
+
RackAfterReply.freedom_patch self, :accept_and_process_next_request
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RackAfterReply
|
2
|
+
module Adapter
|
3
|
+
class Thin < Base
|
4
|
+
def apply
|
5
|
+
::Thin::Connection.module_eval do
|
6
|
+
def pre_process_with_rack_after_reply
|
7
|
+
callbacks = []
|
8
|
+
@request.env[RackAfterReply::CALLBACKS_KEY] = callbacks
|
9
|
+
EM.next_tick { callbacks.each {|c| c.call} }
|
10
|
+
pre_process_without_rack_after_reply
|
11
|
+
end
|
12
|
+
RackAfterReply.freedom_patch self, :pre_process
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module RackAfterReply
|
2
|
+
module Adapter
|
3
|
+
class Unicorn < Base
|
4
|
+
def apply
|
5
|
+
::Unicorn::HttpServer.module_eval do
|
6
|
+
include RackAfterReply::RequestHandler
|
7
|
+
|
8
|
+
def process_client_with_rack_after_reply(client)
|
9
|
+
# We can't install the AppProxy in #initialize, because
|
10
|
+
# the HttpServer is already instantiated by the time we
|
11
|
+
# typically run. Wrap it here exactly once.
|
12
|
+
self.app = AppProxy.new(self, app) unless @rack_after_reply_wrapped
|
13
|
+
@rack_after_reply_wrapped = true
|
14
|
+
|
15
|
+
process_client_without_rack_after_reply(client)
|
16
|
+
fire_rack_after_reply
|
17
|
+
end
|
18
|
+
RackAfterReply.freedom_patch(self, :process_client)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module RackAfterReply
|
2
|
+
module Adapter
|
3
|
+
class WEBrick < Base
|
4
|
+
def apply
|
5
|
+
# Rack::Handler::WEBrick#service returns before the socket is closed,
|
6
|
+
# and if we close it ourselves, WEBrick will close it again causing a
|
7
|
+
# bomb. We can access the socket through the response argument, though,
|
8
|
+
# so we hook into its #close method.
|
9
|
+
Rack::Handler::WEBrick.module_eval do
|
10
|
+
include RackAfterReply::RequestHandler
|
11
|
+
|
12
|
+
def initialize_with_rack_after_reply(server, app)
|
13
|
+
app = AppProxy.new(self, app)
|
14
|
+
initialize_without_rack_after_reply(server, app)
|
15
|
+
end
|
16
|
+
RackAfterReply.freedom_patch self, :initialize
|
17
|
+
|
18
|
+
def service_with_rack_after_reply(request, response)
|
19
|
+
response.extend ResponseExtension
|
20
|
+
response.rack_after_reply_handler = self
|
21
|
+
service_without_rack_after_reply(request, response)
|
22
|
+
end
|
23
|
+
RackAfterReply.freedom_patch self, :service
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module ResponseExtension
|
28
|
+
def send_response(socket)
|
29
|
+
socket.extend SocketExtension
|
30
|
+
socket.rack_after_reply_handler = rack_after_reply_handler
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_accessor :rack_after_reply_handler
|
35
|
+
end
|
36
|
+
|
37
|
+
module SocketExtension
|
38
|
+
def close
|
39
|
+
super
|
40
|
+
rack_after_reply_handler.fire_rack_after_reply
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_accessor :rack_after_reply_handler
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module RackAfterReply
|
2
|
+
#
|
3
|
+
# Wraps a Rack app to intercept the rack environment passed to #call
|
4
|
+
# for access by the request handler after the socket is closed.
|
5
|
+
#
|
6
|
+
class AppProxy
|
7
|
+
def initialize(request_handler, app)
|
8
|
+
@request_handler = request_handler
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
callbacks = []
|
14
|
+
env[RackAfterReply::CALLBACKS_KEY] = callbacks
|
15
|
+
@request_handler.rack_after_reply_callbacks = callbacks
|
16
|
+
@app.call(env)
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_missing(name, *args, &block)
|
20
|
+
class_eval <<-EOS
|
21
|
+
def #{name}(*args, &block)
|
22
|
+
@app.#{name}(*args, &block)
|
23
|
+
end
|
24
|
+
EOS
|
25
|
+
send(name, *args, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def respond_to?(name, include_private=false)
|
29
|
+
super || @app.respond_to?(name, include_private)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module RackAfterReply
|
2
|
+
module RequestHandler
|
3
|
+
attr_accessor :rack_after_reply_callbacks
|
4
|
+
|
5
|
+
def fire_rack_after_reply
|
6
|
+
# Ensure we only fire the hook once. Passenger runs its request
|
7
|
+
# handler when shutting down, causing an infinite loop if we
|
8
|
+
# don't check for this.
|
9
|
+
rack_after_reply_callbacks or
|
10
|
+
return
|
11
|
+
|
12
|
+
rack_after_reply_callbacks.each do |callback|
|
13
|
+
callback.call
|
14
|
+
end
|
15
|
+
self.rack_after_reply_callbacks = nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rack_after_reply
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- George Ogata
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-05-10 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description:
|
22
|
+
email:
|
23
|
+
- george.ogata@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- LICENSE
|
30
|
+
- README.markdown
|
31
|
+
files:
|
32
|
+
- lib/rack_after_reply/adapter/base.rb
|
33
|
+
- lib/rack_after_reply/adapter/mongrel.rb
|
34
|
+
- lib/rack_after_reply/adapter/passenger.rb
|
35
|
+
- lib/rack_after_reply/adapter/thin.rb
|
36
|
+
- lib/rack_after_reply/adapter/unicorn.rb
|
37
|
+
- lib/rack_after_reply/adapter/webrick.rb
|
38
|
+
- lib/rack_after_reply/adapter.rb
|
39
|
+
- lib/rack_after_reply/app_proxy.rb
|
40
|
+
- lib/rack_after_reply/request_handler.rb
|
41
|
+
- lib/rack_after_reply/version.rb
|
42
|
+
- lib/rack_after_reply.rb
|
43
|
+
- LICENSE
|
44
|
+
- README.markdown
|
45
|
+
- Rakefile
|
46
|
+
- CHANGELOG
|
47
|
+
has_rdoc: true
|
48
|
+
homepage: http://github.com/oggy/rack_after_reply
|
49
|
+
licenses: []
|
50
|
+
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options:
|
53
|
+
- --charset=UTF-8
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
segments:
|
70
|
+
- 1
|
71
|
+
- 3
|
72
|
+
- 6
|
73
|
+
version: 1.3.6
|
74
|
+
requirements: []
|
75
|
+
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.3.7
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: Rack hook which fires after the socket to the client is closed.
|
81
|
+
test_files: []
|
82
|
+
|