middleware: split out proxy class
[raindrops.git] / lib / raindrops / middleware.rb
blob1ea486346ae48871d7294934245dabee67b9895e
1 # -*- encoding: binary -*-
2 require 'raindrops'
4 # Raindrops middleware should be loaded at the top of Rack
5 # middleware stack before other middlewares for maximum accuracy.
6 class Raindrops::Middleware
7   attr_accessor :app, :stats, :path, :tcp, :unix
9   # :stopdoc:
10   Stats = Raindrops::Struct.new(:calling, :writing)
11   PATH_INFO = "PATH_INFO"
12   require "raindrops/middleware/proxy"
13   # :startdoc:
15   def initialize(app, opts = {})
16     @app = app
17     @stats = opts[:stats] || Stats.new
18     @path = opts[:path] || "/_raindrops"
19     tmp = opts[:listeners]
20     if tmp.nil? && defined?(Unicorn) && Unicorn.respond_to?(:listener_names)
21       tmp = Unicorn.listener_names
22     end
23     @tcp = @unix = nil
25     if tmp
26       @tcp = tmp.grep(/\A.+:\d+\z/)
27       @unix = tmp.grep(%r{\A/})
28       @tcp = nil if @tcp.empty?
29       @unix = nil if @unix.empty?
30     end
31   end
33   # standard Rack endpoint
34   def call(env)
35     env[PATH_INFO] == @path and return stats_response
36     begin
37       @stats.incr_calling
39       status, headers, body = @app.call(env)
40       rv = [ status, headers, Proxy.new(body, @stats) ]
42       # the Rack server will start writing headers soon after this method
43       @stats.incr_writing
44       rv
45     ensure
46       @stats.decr_calling
47     end
48   end
50   def stats_response
51     body = "calling: #{@stats.calling}\n" \
52            "writing: #{@stats.writing}\n"
54     if defined?(Raindrops::Linux)
55       Raindrops::Linux.tcp_listener_stats(@tcp).each do |addr,stats|
56         body << "#{addr} active: #{stats.active}\n" \
57                 "#{addr} queued: #{stats.queued}\n"
58       end if @tcp
59       Raindrops::Linux.unix_listener_stats(@unix).each do |addr,stats|
60         body << "#{addr} active: #{stats.active}\n" \
61                 "#{addr} queued: #{stats.queued}\n"
62       end if @unix
63     end
65     headers = {
66       "Content-Type" => "text/plain",
67       "Content-Length" => body.size.to_s,
68     }
69     [ 200, headers, [ body ] ]
70   end
71 end