initial
[raindrops.git] / lib / raindrops / middleware.rb
blob4ef6368b85a3075dc108641955d96525c536b9eb
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
7 class Middleware < ::Struct.new(:app, :stats, :path, :tcp, :unix)
9   # :stopdoc:
10   Stats = Raindrops::Struct.new(:calling, :writing)
11   PATH_INFO = "PATH_INFO"
12   # :startdoc:
14   def initialize(app, opts = {})
15     super(app, opts[:stats] || Stats.new, opts[:path] || "/_raindrops")
16     if tmp = opts[:listeners]
17       self.tcp = tmp.grep(/\A[^:]+:\d+\z/)
18       self.unix = tmp.grep(%r{\A/})
19       self.tcp = nil if tcp.empty?
20       self.unix = nil if unix.empty?
21     end
22   end
24   # standard Rack endpoing
25   def call(env)
26     env[PATH_INFO] == path ? stats_response : dup._call(env)
27   end
29   def _call(env)
30     stats.incr_calling
31     status, headers, self.app = app.call(env)
33     # the Rack server will start writing headers soon after this method
34     stats.incr_writing
35     [ status, headers, self ]
36     ensure
37       stats.decr_calling
38   end
40   # yield to the Rack server here for writing
41   def each(&block)
42     app.each(&block)
43   end
45   # the Rack server should call this after #each (usually ensure-d)
46   def close
47     stats.decr_writing
48     ensure
49       app.close if app.respond_to?(:close)
50   end
52   def stats_response
53     body = "calling: #{stats.calling}\n" \
54            "writing: #{stats.writing}\n"
56     if defined?(Linux)
57       Linux.tcp_listener_stats(tcp).each do |addr,stats|
58         body << "#{addr} active: #{stats.active}\n" \
59                 "#{addr} queued: #{stats.queued}\n"
60       end if tcp
61       Linux.unix_listener_stats(unix).each do |addr,stats|
62         body << "#{addr} active: #{stats.active}\n" \
63                 "#{addr} queued: #{stats.queued}\n"
64       end if unix
65     end
67     headers = {
68       "Content-Type" => "text/plain",
69       "Content-Length" => body.size.to_s,
70     }
71     [ 200, headers, [ body ] ]
72   end
74 end
75 end