Permalink
Browse files

Add plugin support and status plugin

  • Loading branch information...
1 parent 8bd0741 commit a4f9dd6ee63c50c21d2302b51204cc3ea3ba17e4 @evanphx evanphx committed May 1, 2012
Showing with 195 additions and 4 deletions.
  1. +32 −4 lib/puma/express.rb
  2. +86 −0 lib/puma/express/status.erb
  3. +77 −0 lib/puma/express/status.rb
View
@@ -13,7 +13,21 @@ class Puma::Express
"content-length" => true
}
- def initialize
+ @plugins = {}
+
+ def self.add_plugin(name, obj)
+ @plugins[name] = obj
+ end
+
+ def self.plugin(name)
+ @plugins[name]
+ end
+
+ DefaultPlugins = ["status"]
+
+ def initialize(plugins=DefaultPlugins.dup)
+ @plugins = plugins.map { |i| self.class.plugin(i).new self }
+
@running = {}
@root = ENV['PUMA_EXPRESS_ROOT'] || File.expand_path("~/.puma_express")
@apps = {}
@@ -23,6 +37,8 @@ def initialize
monitor_apps
end
+ attr_reader :apps
+
def cleanup
@apps.each do |h,a|
a.stop
@@ -66,7 +82,7 @@ def start(env)
path = File.join @root, base
if File.exists? path
- app = App.new host, path, @unix_socket_dir, 5.0
+ app = App.new host, path, @unix_socket_dir, 180.0
app.run
@@ -189,6 +205,10 @@ def proxy_tcp(env, host, port)
end
def call(env)
+ plugin = @plugins.detect { |i| i.handle?(env) }
+
+ return plugin.call(env) if plugin
+
app = find_app(env)
app = start(env) unless app
@@ -197,14 +217,22 @@ def call(env)
begin
if sock = app.unix_socket
- proxy_unix env, sock
+ res = proxy_unix env, sock
else
- proxy_tcp env, "localhost", app.tcp_port
+ res = proxy_tcp env, "localhost", app.tcp_port
end
+
+ @plugins.each do |i|
+ i.add_result(app, env, res)
+ end
+
+ res
rescue SystemCallError => e
error env, "Error: #{e.message} (#{e.class})"
rescue Exception => e
error env, "Unknown error: #{e.message} (#{e.class})"
end
end
end
+
+require 'puma/express/status'
@@ -0,0 +1,86 @@
+<html>
+ <head>
+ <title>Puma Express Status</title>
+ <link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css">
+ <style>
+ body {
+ padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
+ }
+ </style>
+ </head>
+ <body>
+ <div class="navbar navbar-fixed-top">
+ <div class="navbar-inner">
+ <div class="container">
+ <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </a>
+ <a class="brand" href="#">Puma Express Status</a>
+ <div class="nav-collapse">
+ <ul class="nav">
+ <li class="active"><a href="#">Home</a></li>
+ <li><a href="#about">About</a></li>
+ <li><a href="#contact">Contact</a></li>
+ </ul>
+ </div><!--/.nav-collapse -->
+ </div>
+ </div>
+ </div>
+ <div class="container">
+ <% apps.each do |h,a| %>
+ <i class="icon-star"></i> <strong><%= h %></strong>
+ <ul>
+ <% hits[a].reverse_each do |hit| %>
+ <div class="hit">
+ <h1>
+ <span class="label label-success"><%= hit.code %></span>
+ <%= hit.path %>
+ </h1>
+
+ <dl>
+ <dt>Request</dt>
+ <dd>
+ <table class="table table-striped table-condensed">
+ <tbody>
+ <% hit.sorted_request.each do |k,v| %>
+ <tr><th><%= k %></th><td><code><%= v %></code></td></tr>
+ <% end %>
+ </tbody>
+ </table>
+ </dd>
+ <dt>Response</dt>
+ <dd>
+ <table class="table table-striped table-condensed">
+ <tbody>
+ <tr>
+ <th>Code</th>
+ <td><span class="label label-success"><%= hit.code %></span></td>
+ </tr>
+ <tr>
+ <th>Headers</th>
+ <td>
+ <table class="table table-striped table-condensed">
+ <% hit.sorted_response.each do |k,v| %>
+ <tr><th><%= k %></th><td><code><%= v %></td></code></tr>
+ <% end %>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <th>Body</th>
+ <td>
+ <code><%= hit.body %></code>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </dd>
+ </div>
+ <% end %>
+ </ul>
+ <% end %>
+ </div>
+ </body>
+</html>
@@ -0,0 +1,77 @@
+require 'erb'
+require 'thread'
+
+class Puma::Express
+ class Status
+
+ Puma::Express.add_plugin 'status', self
+
+ TEMPLATE = File.expand_path "../status.erb", __FILE__
+
+ def initialize(express)
+ @express = express
+ @mutex = Mutex.new
+ @hits = Hash.new { |h,k| h[k] = [] }
+ @hit_window = 10
+ end
+
+ def handle?(env)
+ env['HTTP_HOST'].split(".").first == "status"
+ end
+
+ class Hit
+ def initialize(req, res)
+ @request = req
+ @result = res
+ end
+
+ attr_reader :request, :result
+
+ def path
+ @request['PATH_INFO']
+ end
+
+ def code
+ @result.first
+ end
+
+ def body
+ @result.last.join
+ end
+
+ def sorted_request
+ headers = @request.find_all { |k,v| k[0,5] == "HTTP_" }
+ headers.map! do |k,v|
+ [k[5..-1].split("_").map { |x| x.downcase.capitalize }.join("-"), v]
+ end
+
+ headers.sort_by { |k,v| k }
+ end
+
+ def sorted_response
+ @result[1].sort_by { |k,v| k }
+ end
+ end
+
+ def add_result(app, req, res)
+ p :res => res
+
+ @mutex.synchronize do
+ hits = @hits[app]
+ if hits.size >= @hit_window
+ hits.shift
+ end
+
+ hits.push Hit.new(req, res)
+ end
+ end
+
+ def call(env)
+ apps = @express.apps.sort_by { |h,a| h }
+ hits = @hits
+
+ body = ERB.new(File.read(TEMPLATE)).result(binding)
+ [200, {}, body]
+ end
+ end
+end

0 comments on commit a4f9dd6

Please sign in to comment.