sham_rack-1.4.1/ 0000755 0000041 0000041 00000000000 13146333120 013471 5 ustar www-data www-data sham_rack-1.4.1/Rakefile 0000644 0000041 0000041 00000000334 13146333116 015143 0 ustar www-data www-data require "rubygems"
require "rake"
task "default" => "spec"
require "rspec/core/rake_task"
RSpec::Core::RakeTask.new do |t|
t.rspec_opts = ["--format", "doc"]
end
require 'bundler'
Bundler::GemHelper.install_tasks
sham_rack-1.4.1/spec/ 0000755 0000041 0000041 00000000000 13146333116 014430 5 ustar www-data www-data sham_rack-1.4.1/spec/spec_helper.rb 0000644 0000041 0000041 00000000226 13146333116 017246 0 ustar www-data www-data require "rubygems"
require "rspec"
require "rspec/mocks"
require "test_apps"
RSpec.configure do |config|
config.raise_errors_for_deprecations!
end
sham_rack-1.4.1/spec/sham_rack_spec.rb 0000644 0000041 0000041 00000022403 13146333116 017720 0 ustar www-data www-data require "spec_helper"
require "sham_rack"
require "sham_rack/patron"
require "open-uri"
require "restclient"
require "mechanize"
require "rack"
RSpec.describe ShamRack do
class NetHttpProhibited < StandardError; end
before do
allow_any_instance_of(Net::HTTP).to receive(:start) do
raise NetHttpProhibited, "real network calls are not allowed"
end
end
after(:each) do
ShamRack.reset
end
describe "mounted Rack application" do
before(:each) do
ShamRack.at("www.greetings.com").mount(GreetingApp.new)
end
it "can be accessed using Net::HTTP" do
response = Net::HTTP.start("www.greetings.com") do |http|
http.request(Net::HTTP::Get.new("/"))
end
expect(response.body).to eq("Hello, world")
end
it "can be accessed using Net::HTTP#get_response" do
response = Net::HTTP.get_response(URI.parse("http://www.greetings.com/"))
expect(response.body).to eq("Hello, world")
end
it "can be accessed using open-uri" do
response = open("http://www.greetings.com")
expect(response.status).to eq(["200", "OK"])
expect(response.read).to eq("Hello, world")
end
it "can be accessed using RestClient" do
response = RestClient.get("http://www.greetings.com")
expect(response.code).to eq(200)
expect(response.to_s).to eq("Hello, world")
end
it "can be accessed using Mechanize" do
response = Mechanize.new.get("http://www.greetings.com")
expect(response.body).to eq("Hello, world")
end
it "can be accessed using Patron" do
patron = Patron::Session.new
response = patron.get("http://www.greetings.com/foo/bar")
expect(response.body).to eq("Hello, world")
end
end
describe ".at" do
context "with a block" do
it "mounts associated block as an app" do
ShamRack.at("simple.xyz") do |env|
["200 OK", { "Content-type" => "text/plain" }, ["Easy, huh?"]]
end
expect(open("http://simple.xyz").read).to eq("Easy, huh?")
end
end
context "with a URL" do
it "raises an ArgumentError" do
expect do
ShamRack.at("http://www.greetings.com")
end.to raise_error(ArgumentError, "invalid address")
end
end
describe "#mount" do
it "mounts an app" do
ShamRack.at("hello.xyz").mount(GreetingApp.new)
expect(open("http://hello.xyz").read).to eq("Hello, world")
end
end
describe "#unmount" do
it "deregisters a mounted app" do
ShamRack.at("gone.xyz").mount(GreetingApp.new)
ShamRack.at("gone.xyz").unmount
expect do
open("http://gone.xyz").read
end.to raise_error(NetHttpProhibited)
end
end
describe "#rackup" do
before do
@return_value = ShamRack.at("rackup.xyz").rackup do
use UpcaseBody
run GreetingApp.new
end
end
it "mounts an app created using Rack::Builder" do
expect(open("http://rackup.xyz").read).to eq("HELLO, WORLD")
end
it "returns the app" do
expect(@return_value).to respond_to(:call)
end
end
describe "#sinatra" do
before do
@return_value = ShamRack.at("sinatra.xyz").sinatra do
get "/hello/:subject" do
"Hello, #{params[:subject]}"
end
end
end
it "mounts associated block as a Sinatra app" do
expect(open("http://sinatra.xyz/hello/stranger").read).to eq("Hello, stranger")
end
it "returns the app" do
expect(@return_value).to respond_to(:call)
end
end
describe "#stub" do
before do
@return_value = ShamRack.at("stubbed.xyz").stub
end
it "mounts a StubWebService" do
expect(ShamRack.application_for("stubbed.xyz")).to be_kind_of(ShamRack::StubWebService)
end
it "returns the StubWebService" do
expect(@return_value).to eq(ShamRack.application_for("stubbed.xyz"))
end
end
end
describe "response" do
before(:each) do
ShamRack.at("www.greetings.com") do
[
"456 Foo Bar",
{ "Content-Type" => "text/plain", "X-Foo" => "bar" },
["BODY"]
]
end
end
let(:response) { Net::HTTP.get_response(URI.parse("http://www.greetings.com/")) }
it "has status returned by app" do
expect(response.code).to eq("456")
end
it "has status message returned by app" do
expect(response.message).to eq("Foo Bar")
end
it "has body returned by app" do
expect(response.body).to eq("BODY")
end
it "has Content-Type returned by app" do
expect(response.content_type).to eq("text/plain")
end
it "has other headers returned by app" do
expect(response["x-foo"]).to eq("bar")
end
context "when the app returns a numeric status" do
before(:each) do
ShamRack.at("www.greetings.com") do
[
201,
{ "Content-Type" => "text/plain" },
["BODY"]
]
end
@response = Net::HTTP.get_response(URI.parse("http://www.greetings.com/"))
end
it "has status returned by app" do
expect(response.code).to eq("201")
end
it "derives a status message" do
expect(response.message).to eq("Created")
end
end
end
describe ".allow_network_connections" do
context "when false" do
before do
ShamRack.prevent_network_connections
end
after do
ShamRack.allow_network_connections
end
it "prevents Net::HTTP requests" do
expect {
Net::HTTP.get_response(URI.parse("http://www.example.com/"))
}.to raise_error(ShamRack::NetworkConnectionPrevented)
end
it "prevents Patron requests" do
expect {
Patron::Session.new.get("http://www.example.com/")
}.to raise_error(ShamRack::NetworkConnectionPrevented)
end
end
context "when true" do
before do
ShamRack.allow_network_connections
end
it "allows Net::HTTP requests" do
expect {
Net::HTTP.get_response(URI.parse("http://www.example.com/"))
}.to raise_error(NetHttpProhibited)
end
end
end
describe "Rack environment" do
before(:each) do
@env_recorder = recorder = EnvRecorder.new(GreetingApp.new)
ShamRack.at("env.xyz").rackup do
use Rack::Lint
run recorder
end
end
def env
@env_recorder.last_env
end
it "is valid" do
open("http://env.xyz/blah?q=abc")
expect(env["REQUEST_METHOD"]).to eq("GET")
expect(env["SCRIPT_NAME"]).to eq("")
expect(env["PATH_INFO"]).to eq("/blah")
expect(env["QUERY_STRING"]).to eq("q=abc")
expect(env["SERVER_NAME"]).to eq("env.xyz")
expect(env["SERVER_PORT"]).to eq("80")
expect(env["rack.version"]).to be_kind_of(Array)
expect(env["rack.url_scheme"]).to eq("http")
expect(env["rack.multithread"]).to eq(true)
expect(env["rack.multiprocess"]).to eq(true)
expect(env["rack.run_once"]).to eq(false)
end
it "provides request headers" do
Net::HTTP.start("env.xyz") do |http|
request = Net::HTTP::Get.new("/")
request["Foo-bar"] = "baz"
http.request(request)
end
expect(env["HTTP_FOO_BAR"]).to eq("baz")
end
it "supports POST" do
RestClient.post("http://env.xyz/resource", "q" => "rack")
expect(env["REQUEST_METHOD"]).to eq("POST")
expect(env["CONTENT_TYPE"]).to eq("application/x-www-form-urlencoded")
expect(env["rack.input"].read).to eq("q=rack")
end
it "supports POST using Net::HTTP" do
Net::HTTP.start("env.xyz") do |http|
http.post("/resource", "q=rack")
end
expect(env["REQUEST_METHOD"]).to eq("POST")
expect(env["rack.input"].read).to eq("q=rack")
end
it "supports POST using Patron" do
patron = Patron::Session.new
response = patron.post("http://env.xyz/resource", "", "Content-Type" => "application/xml")
expect(response.status).to eq(200)
expect(env["REQUEST_METHOD"]).to eq("POST")
expect(env["rack.input"].read).to eq("")
expect(env["CONTENT_TYPE"]).to eq("application/xml")
end
it "supports PUT" do
RestClient.put("http://env.xyz/thing1", "stuff", :content_type => "text/plain")
expect(env["REQUEST_METHOD"]).to eq("PUT")
expect(env["CONTENT_TYPE"]).to eq("text/plain")
expect(env["rack.input"].read).to eq("stuff")
end
it "supports PUT using Patron" do
patron = Patron::Session.new
response = patron.put("http://env.xyz/resource", "stuff", "Content-Type" => "text/plain")
expect(env["REQUEST_METHOD"]).to eq("PUT")
expect(env["CONTENT_TYPE"]).to eq("text/plain")
expect(env["rack.input"].read).to eq("stuff")
end
it "supports DELETE" do
RestClient.delete("http://env.xyz/thing/1")
expect(env["REQUEST_METHOD"]).to eq("DELETE")
expect(env["PATH_INFO"]).to eq("/thing/1")
end
it "supports DELETE using Patron" do
patron = Patron::Session.new
response = patron.delete("http://env.xyz/resource")
expect(env["REQUEST_METHOD"]).to eq("DELETE")
expect(env["PATH_INFO"]).to eq("/resource")
end
end
end
sham_rack-1.4.1/spec/sham_rack/ 0000755 0000041 0000041 00000000000 13146333116 016360 5 ustar www-data www-data sham_rack-1.4.1/spec/sham_rack/stub_web_service_spec.rb 0000644 0000041 0000041 00000004152 13146333116 023253 0 ustar www-data www-data require "spec_helper"
require "sham_rack/stub_web_service"
require "rack/test"
RSpec.describe ShamRack::StubWebService do
include Rack::Test::Methods
attr_reader :app
before(:each) do
@app = ShamRack::StubWebService.new
end
describe "#last_request" do
it "returns the last request" do
get '/foo/bar'
expect(@app.last_request.path_info).to eq("/foo/bar")
end
end
describe "with no handlers registered" do
describe "any request" do
before do
get "/foo/456"
end
it "returns a 404" do
expect(last_response.status).to eq(404)
end
end
end
describe "with two handlers registered" do
before(:each) do
@app.handle do |request|
[200, {}, ["response from first handler"]] if request.get?
end
@app.handle do |request|
[200, {}, ["response from second handler"]] if request.path_info == "/stuff"
end
end
describe "a request matching the first handler" do
before do
get "/foo/456"
end
it "receives a response from the first handler" do
expect(last_response.body).to eq("response from first handler")
end
end
describe "a request matching the second handler" do
before do
post "/stuff"
end
it "receives a response from the second handler" do
expect(last_response.body).to eq("response from second handler")
end
end
describe "a request matching both handlers" do
before do
get "/stuff"
end
it "receives a response from the second handler" do
expect(last_response.body).to eq("response from second handler")
end
end
end
describe ".register_resource" do
before do
@app.register_resource("/stuff?foo=bar", "STUFF", "text/plain", 202)
get "/stuff?foo=bar"
end
it "sets body" do
expect(last_response.body).to eq("STUFF")
end
it "sets content-type" do
expect(last_response.content_type).to eq("text/plain")
end
it "sets status code" do
expect(last_response.status).to eq(202)
end
end
end
sham_rack-1.4.1/spec/test_apps.rb 0000644 0000041 0000041 00000001414 13146333116 016757 0 ustar www-data www-data require "rack"
class GreetingApp
include Rack::Utils
def call(env)
params = parse_nested_query(env["QUERY_STRING"])
salutation = params[:salutation] || "Hello"
subject = params[:subject] || "world"
message = "#{salutation}, #{subject}"
[
"200 OK",
{ "Content-Type" => "text/plain", "Content-Length" => message.length.to_s },
[message]
]
end
end
class EnvRecorder
def initialize(app)
@app = app
end
def call(env)
@last_env = env
@app.call(env)
end
attr_reader :last_env
end
class UpcaseBody
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
upcased_body = Array(body).map { |x| x.upcase }
[status, headers, upcased_body]
end
end
sham_rack-1.4.1/benchmark/ 0000755 0000041 0000041 00000000000 13146333116 015430 5 ustar www-data www-data sham_rack-1.4.1/benchmark/benchmark.rb 0000644 0000041 0000041 00000002125 13146333116 017707 0 ustar www-data www-data require "rubygems"
require "pathname"
dir = Pathname(__FILE__).parent
$LOAD_PATH.unshift(dir.parent + "lib")
require dir + "hello_app"
require "sham_rack"
require "restclient"
# mount an instance of the app using ShamRack
ShamRack.mount(HelloApp.new, "hello.sham")
# run another instance in a separate process
server_pid = fork do
puts "Starting HTTP server on port 3333"
$stdout = File.new('/dev/null', 'w')
HelloApp.run!(:port => 3333)
end
at_exit do
puts "Killing HTTP server"
Process.kill("TERM", server_pid)
Process.wait(server_pid)
end
puts "Waiting for server to come up"
begin
puts RestClient.get("http://localhost:3333/hello/stranger")
rescue SystemCallError => e
retry
end
iterations = (ARGV.shift || 1000).to_i
%w(hello.sham localhost:3333).each do |site|
start = Time.now
iterations.times do
x = RestClient.get("http://#{site}/hello/stranger").to_s
end
elapsed_time = (Time.now - start)
requests_per_second = iterations / elapsed_time.to_f
printf "%-20s #{iterations} requests in %f; %f per second\n", site, elapsed_time, requests_per_second
end
sham_rack-1.4.1/benchmark/hello_app.rb 0000644 0000041 0000041 00000000234 13146333116 017717 0 ustar www-data www-data require "rubygems"
require "sinatra/base"
class HelloApp < Sinatra::Base
get "/hello/:subject" do
"Hello, #{params[:subject]}"
end
end
sham_rack-1.4.1/lib/ 0000755 0000041 0000041 00000000000 13146333116 014244 5 ustar www-data www-data sham_rack-1.4.1/lib/sham_rack/ 0000755 0000041 0000041 00000000000 13146333116 016174 5 ustar www-data www-data sham_rack-1.4.1/lib/sham_rack/patron.rb 0000644 0000041 0000041 00000003316 13146333116 020027 0 ustar www-data www-data require "patron"
require "sham_rack"
require "uri"
module Patron
class Session
alias :handle_request_without_sham_rack :handle_request
def handle_request(patron_request)
uri = URI.parse(patron_request.url)
rack_app = ShamRack.application_for(uri.host, uri.port)
if rack_app
handle_request_with_rack(patron_request, rack_app)
else
handle_request_without_sham_rack(patron_request)
end
end
private
def handle_request_with_rack(patron_request, rack_app)
env = rack_env_for(patron_request)
rack_response = rack_app.call(env)
patron_response(rack_response)
end
def rack_env_for(patron_request)
env = Rack::MockRequest.env_for(patron_request.url, :method => patron_request.action, :input => patron_request.upload_data)
env.merge!(header_env(patron_request))
env
end
def patron_response(rack_response)
status, headers, body = rack_response
Patron::Response.new("", status.to_i, 0, assemble_headers(status, headers), assemble_body(body))
end
def header_env(patron_request)
env = {}
patron_request.headers.each do |header, content|
key = header.upcase.gsub('-', '_')
key = "HTTP_" + key unless key =~ /^CONTENT_(TYPE|LENGTH)$/
env[key] = content
end
env
end
def assemble_headers(status, headers)
status_code = Rack::Utils::HTTP_STATUS_CODES[status.to_i]
content = "HTTP/1.1 #{status} #{status_code}\r\n"
headers.each do |k, v|
content << "#{k}: #{v}\r\n"
end
content
end
def assemble_body(body)
content = ""
body.each { |fragment| content << fragment }
content
end
end
end
sham_rack-1.4.1/lib/sham_rack/net_http.rb 0000644 0000041 0000041 00000005272 13146333116 020354 0 ustar www-data www-data require "net/http"
require "rack"
class << Net::HTTP
alias :new_without_sham_rack :new
def new(address, port = nil, *proxy_args)
port ||= Net::HTTP.default_port
rack_app = ShamRack.application_for(address, port)
http_object = new_without_sham_rack(address, port, *proxy_args)
if rack_app
http_object.extend(ShamRack::NetHttp::Extensions)
http_object.rack_app = rack_app
end
http_object
end
end
module ShamRack
module NetHttp
module Extensions
attr_accessor :rack_app
def start
if block_given?
yield self
else
self
end
end
def request(request, body = nil)
rack_response = @rack_app.call(rack_env(request, body))
net_http_response = build_response(rack_response)
yield net_http_response if block_given?
return net_http_response
end
private
def rack_env(request, body)
rack_env = request_env(request, body)
rack_env.merge!(header_env(request))
rack_env.merge!(server_env)
end
def server_env
{
"SERVER_NAME" => @address,
"SERVER_PORT" => @port.to_s
}
end
def header_env(request)
env = {}
request.each_header do |header, content|
key = header.upcase.gsub('-', '_')
key = "HTTP_" + key unless key =~ /^CONTENT_(TYPE|LENGTH)$/
env[key] = content
end
env
end
def request_env(request, body)
body ||= request_body(request)
Rack::MockRequest.env_for(request.path, :method => request.method, :input => body)
end
def request_body(request)
return request.body unless request.body.nil?
return request.body_stream.read unless request.body_stream.nil?
""
end
def build_response(rack_response)
status, headers, body = rack_response
code, message = status.to_s.split(" ", 2)
message ||= Rack::Utils::HTTP_STATUS_CODES[code.to_i]
response = Net::HTTPResponse.send(:response_class, code).new("Sham", code, message)
response.instance_variable_set(:@body, assemble_body(body))
response.instance_variable_set(:@read, true)
headers.each do |k,v|
response.add_field(k, v)
end
response.extend ShamRack::NetHttp::ResponseExtensions
return response
end
def assemble_body(body)
content = ""
body.each { |fragment| content << fragment }
content
end
end
module ResponseExtensions
def read_body(dest = nil)
yield @body if block_given?
dest << @body if dest
return @body
end
end
end
end
sham_rack-1.4.1/lib/sham_rack/registration.rb 0000644 0000041 0000041 00000003223 13146333116 021233 0 ustar www-data www-data require "sham_rack/allowances"
module ShamRack
module Registration
ADDRESS_PATTERN = /^[a-z0-9-]+(\.[a-z0-9-]+)*$/i
def unmount_all
registry.clear
end
def at(address, port = nil, &app_block)
mount_point = mount_point_for(address, port)
if app_block
mount_point.mount(app_block)
else
mount_point
end
end
def application_for(address, port = nil)
port ||= Net::HTTP.default_port
mount_point_for(address, port).app.tap do |app|
return app unless app.nil?
unless ShamRack.network_connections_allowed?
raise NetworkConnectionPrevented, "connection to #{address}:#{port} not allowed"
end
end
end
private
def mount_point_for(address, port)
registry[mount_key(address, port)]
end
def registry
@registry ||= Hash.new do |hash, key|
hash[key] = MountPoint.new
end
end
def mount_key(address, port)
unless address =~ ADDRESS_PATTERN
raise ArgumentError, "invalid address"
end
port ||= Net::HTTP.default_port
port = Integer(port)
[address, port]
end
end
class MountPoint
attr_reader :app
def mount(app)
@app = app
end
def unmount
@app = nil
end
def rackup(&block)
require "rack"
mount(Rack::Builder.new(&block).to_app)
end
def sinatra(&block)
require "sinatra/base"
sinatra_app = Class.new(Sinatra::Base)
sinatra_app.class_eval(&block)
mount(sinatra_app.new)
end
def stub
require "sham_rack/stub_web_service"
mount(StubWebService.new)
end
end
end
sham_rack-1.4.1/lib/sham_rack/version.rb 0000644 0000041 0000041 00000000050 13146333116 020201 0 ustar www-data www-data module ShamRack
VERSION = "1.4.1"
end
sham_rack-1.4.1/lib/sham_rack/allowances.rb 0000644 0000041 0000041 00000000603 13146333116 020650 0 ustar www-data www-data module ShamRack
class << self
def network_connections_allowed?
@allow_network_connections
end
def allow_network_connections
@allow_network_connections = true
end
def prevent_network_connections
@allow_network_connections = false
end
end
class NetworkConnectionPrevented < StandardError
end
end
ShamRack.allow_network_connections
sham_rack-1.4.1/lib/sham_rack/stub_web_service.rb 0000644 0000041 0000041 00000002016 13146333116 022052 0 ustar www-data www-data require "rack"
module ShamRack
# A simple Rack app that stubs out a web service, for testing.
class StubWebService
def initialize
@handlers = []
end
def last_request
@request
end
def call(env)
@request = Rack::Request.new(env)
@handlers.each do |handler|
response = handler.call(@request)
return response if response
end
return default_response
end
def handle(&block)
@handlers.unshift(block)
end
def register_resource(path, content, content_type = "application/xml", status = 200)
handle do |request|
request_path = request.path_info
unless request.query_string.to_s.empty?
request_path += "?" + request.query_string
end
[status, {"Content-Type" => content_type}, [content]] if request_path == path
end
end
def reset
@handlers.clear
end
protected
def default_response
[404, {"Content-Type" => "text/plain"}, ["Not found"]]
end
end
end
sham_rack-1.4.1/lib/sham_rack.rb 0000644 0000041 0000041 00000000557 13146333116 016530 0 ustar www-data www-data require "sham_rack/net_http"
require "sham_rack/registration"
require "sham_rack/version"
# ShamRack allows access to Rack applications using Net::Http, but without network traffic.
#
# For more detail, see http://github.com/mdub/sham_rack
#
module ShamRack
extend ShamRack::Registration
def self.reset
unmount_all
allow_network_connections
end
end
sham_rack-1.4.1/sham_rack.gemspec 0000644 0000041 0000041 00000003210 13146333120 016762 0 ustar www-data www-data #########################################################
# This file has been automatically generated by gem2tgz #
#########################################################
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = "sham_rack"
s.version = "1.4.1"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Mike Williams"]
s.date = "2017-08-20"
s.description = "ShamRack plumbs Net::HTTP directly into Rack, for quick and easy HTTP testing."
s.email = "mdub@dogbiscuit.org"
s.files = ["Rakefile", "benchmark/benchmark.rb", "benchmark/hello_app.rb", "lib/sham_rack.rb", "lib/sham_rack/allowances.rb", "lib/sham_rack/net_http.rb", "lib/sham_rack/patron.rb", "lib/sham_rack/registration.rb", "lib/sham_rack/stub_web_service.rb", "lib/sham_rack/version.rb", "spec/sham_rack/stub_web_service_spec.rb", "spec/sham_rack_spec.rb", "spec/spec_helper.rb", "spec/test_apps.rb"]
s.homepage = "http://github.com/mdub/sham_rack"
s.require_paths = ["lib"]
s.rubyforge_project = "shamrack"
s.rubygems_version = "1.8.23"
s.summary = "Net::HTTP-to-Rack plumbing"
s.test_files = ["Rakefile", "benchmark/benchmark.rb", "benchmark/hello_app.rb", "spec/sham_rack/stub_web_service_spec.rb", "spec/sham_rack_spec.rb", "spec/spec_helper.rb", "spec/test_apps.rb"]
if s.respond_to? :specification_version then
s.specification_version = 4
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q, [">= 0"])
else
s.add_dependency(%q, [">= 0"])
end
else
s.add_dependency(%q, [">= 0"])
end
end