Skip to content

Instantly share code, notes, and snippets.

@filiptepper
Created November 10, 2009 17:31
Show Gist options
  • Save filiptepper/231057 to your computer and use it in GitHub Desktop.
Save filiptepper/231057 to your computer and use it in GitHub Desktop.
require "erb"
require "redis"
module Rack
class Out
DEFAULTS = { :secret => "secretive-secret", :key => "rack:out",
:host => "127.0.0.1", :port => 6379, :db => 0, :timeout => 5, :password => nil,
:logger => nil, :thread_safe => nil }
def initialize(app, options = {})
@app, @options = app, DEFAULTS.merge(options)
@redis = Redis.new :host => @options[:host], :port => @options[:port], :db => @options[:db],
:timeout => @options[:timeout], :password => @options[:password], :logger => @options[:logger],
:thread_safe => @options[:thread_safe]
end
def call(env); dup._call env; end
def _call(env)
@path_info = env["PATH_INFO"]
begin
match = (Regexp.new "/__rack_out__/#{@options[:secret]}/(list|add|remove)").match @path_info
return (self.send match[1]) if match
return (respond 403, "403 Forbidden") if @redis.sismember @options[:key], env["REMOTE_ADDR"]
rescue Errno::ECONNREFUSED
end
@app.call env
end
private
def remote_addr
match = (Regexp.new "/__rack_out__/#{@options[:secret]}/(add|remove)/(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)").match @path_info
return [match[2..5]].join(".") if match
nil
end
def list
@list = @redis.smembers @options[:key]
@content = Rack::Out::Template::LIST.result binding
@form = Rack::Out::Template::FORM.result binding
respond 200, (Rack::Out::Template::LAYOUT.result binding)
end
def add
ip = remote_addr
if ip
@redis.sadd @options[:key], ip
return 302, { "Location" => "../list" }, [""]
end
respond 400, "400 Bad Request"
end
def remove
ip = remote_addr
if ip
@redis.srem @options[:key], ip
return 302, { "Location" => "../list" }, [""]
end
respond 400, "400 Bad Request"
end
def respond(status, body)
return status, { "Content-Type" => "text/html" }, [body]
end
end
end
module Rack
class Out
class Template
FORM = ERB.new <<-EOF
<form onsubmit="window.location.href = '/__rack_out__/<%= @options[:secret] %>/add/' + document.getElementById('rack-out-ip').value; return false;">
<label for="rack-out-ip">IP:</label>
<input type="text" id="rack-out-ip" />
<input type="submit" value="Add!" />
</form>
EOF
LIST = ERB.new <<-EOF
<% if !@list.nil? && @list.any? %>
<table class="ips">
<% @list.each do |ip| %>
<tr>
<td class="ip"><%= ip %></td>
<td class="remove"><a href="/__rack_out__/<%= @options[:secret] %>/remove/<%= ip %>">remove</a></td>
</tr>
<% end %>
</table>
<% else %>
All clear!
<% end %>
EOF
LAYOUT = ERB.new <<-EOF
<!DOCTYPE html>
<html>
<head>
<title>Rack::Out</title>
<style type="text/css">
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var, dl, dt, dd, ul, li,
form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { margin: 0;
padding: 0; border: 0; outline: 0; font-weight: inherit; font-style: normal;
font-size: 100%; font-family: inherit; }
:focus { outline: 0; }
body { line-height: 1; }
ul { list-style: none; }
table { border-collapse: collapse; border-spacing: 0; }
caption, th, td { text-align: left; font-weight: normal; }
blockquote:before, blockquote:after, q:before, q:after { content: "";}
blockquote, q { quotes: "" ""; }
html { background:#efefef; font-family:Arial, Verdana, sans-serif; font-size:13px; }
body { padding:0; margin:0; }
.header { background:#000; padding:8px 5% 0 5%; border-bottom:1px solid #444;border-bottom:5px solid #ce1212;}
.header ul li { display:inline;}
.header ul li a { color:#fff; text-decoration:none; margin-right:10px; display:inline-block; padding:8px; -webkit-border-top-right-radius:6px; -webkit-border-top-left-radius:6px; }
.header ul li a:hover { background:#333;}
.header ul li.current a { background:#ce1212; font-weight:bold; color:#fff;}
#main { padding:10px 5%; background:#fff; overflow:hidden; }
#main .logo { float:right; margin:10px;}
#main span.hl { background:#efefef; padding:2px;}
#main table { width:100%; margin:10px 0;}
#main table tr td, #main table tr th { border:1px solid #ccc; padding:6px;}
#main a { color:#111;}
#main p { margin:5px 0;}
#main table.ips td.remove { background:#b1d2e9; padding:2px; margin:0 3px; font-size:80%; text-decoration:none; text-transform:uppercase; font-weight:bold; color:#3274a2;}
#main table.ips td.ip { font-family:Monaco, "Courier New", monospace; font-size:90%;}
#footer { padding:10px 5%; background:#efefef; color:#999; font-size:85%; line-height:1.5; border-top:5px solid #ccc; padding-top:10px;}
#footer p a { color:#999;}
</style>
</head>
<body>
<div class="header">
<ul class="nav">
<li class="current"><a href="">Rack::Out</a></li>
</ul>
</div>
<div id="main">
<%= @content %>
<%= @form %>
</div>
<div id="footer">
<p>You're running Rack::Out.</p>
<p>Layout? Thank you, <a href="http://github.com/defunkt/resque">Resque</a>!</p>
</div>
</body>
</html>
EOF
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment