Skip to content

Instantly share code, notes, and snippets.

@nbibler
Created June 15, 2012 03:59
Show Gist options
  • Save nbibler/2934602 to your computer and use it in GitHub Desktop.
Save nbibler/2934602 to your computer and use it in GitHub Desktop.
A demo of using a testing beacon to determine body manipulation and state.
require 'rack'
module Rack
# This would be Rack::Lint. The demonstrable functionality here is to call
# up the stack, then inspect the env['rack.linkbeacon'] object to determine
# whether or not it was left in an unclosed state.
#
class LintDemo
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
if env['rack.lintbeacon'].closed?
puts "Good."
else
puts "OMG FAIL."
end
[status, headers, body]
end
end
# This basically wraps the body it receives with a proxy that acts as a
# beacon. The beacon watches to see if the body is acted upon and attempt
# to determine whether the user's middleware (that sits between LintDemo and
# LintBeacon) unintentionally modified or left the body object unclosed.
#
class LintBeacon
attr_reader :beacon
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
@beacon = Beacon.new(body)
env['rack.lintbeacon'] = @beacon
[status, headers, @beacon]
end
end
# The beacon proxy which wraps the response body.
#
class Beacon
def initialize(body)
@closed = false
@body = body
end
def close
@closed = true
@body.close if @body.respond_to?(:close)
end
def closed?
@closed || !@operated_upon
end
private
def method_missing(method, *args, &block)
@closed = false # consider any operation to be a re-open
@operated_upon = true
@body.send(method, *args, &block)
end
end
end
# This is a middleware which does not modify the response objects.
#
class MyGoodMiddleware
def initialize(app)
@app = app
end
def call(env)
@app.call(env)
end
end
stack = Rack::Builder.new do
use Rack::LintDemo
use MyGoodMiddleware
use Rack::LintBeacon
run ->(env) { [200, {'Content-Type' => 'text/plain'}, ['Success']] }
end
env = Rack::MockRequest.env_for("http://test.local/")
stack.call(env)
# => "Good."
# This is a middleware which reads the response body without closing it.
#
class MyBadMiddleware
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
body.each {}
[status, headers, body]
end
end
stack = Rack::Builder.new do
use Rack::LintDemo
use MyBadMiddleware
use Rack::LintBeacon
run ->(env) { [200, {'Content-Type' => 'text/plain'}, ['Success']] }
end
env = Rack::MockRequest.env_for("http://test.local/")
stack.call(env)
# => "OMG FAIL."
# This is a middleware which reads the response body but then closes it before
# returning.
#
class MyAcceptableMiddleware
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
body.each {}
body.close if body.respond_to?(:close)
[status, headers, body]
end
end
stack = Rack::Builder.new do
use Rack::LintDemo
use MyAcceptableMiddleware
use Rack::LintBeacon
run ->(env) { [200, {'Content-Type' => 'text/plain'}, ['Success']] }
end
env = Rack::MockRequest.env_for("http://test.local/")
stack.call(env)
# => "Good."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment