rack_after_reply 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,3 @@
1
+ == 0.0.1 2011-05-10
2
+
3
+ * Hi.
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,15 @@
1
+ module RackAfterReply
2
+ module Adapter
3
+ class Base
4
+ def self.apply
5
+ return if defined?(@applied)
6
+ instance.apply
7
+ @applied = true
8
+ end
9
+
10
+ def self.instance
11
+ @instance ||= new
12
+ end
13
+ end
14
+ end
15
+ 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
@@ -0,0 +1,11 @@
1
+ module RackAfterReply
2
+ VERSION = [0, 0, 1]
3
+
4
+ class << VERSION
5
+ include Comparable
6
+
7
+ def to_s
8
+ join('.')
9
+ end
10
+ end
11
+ 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
+