Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Replace monkey-patch with proxy server

EphemeralResponse is really just a caching proxy-server, so why not make
it work that way? The library is now more portable. It could be made to
work with other Ruby http libraries, or even your favorite web browser.
  • Loading branch information...
commit ef7ace83e6ffa16fa2f9a656fef3d83411dbe350 1 parent 1f90e43
@sandro authored
View
3  lib/ephemeral_response.rb
@@ -1,8 +1,9 @@
module EphemeralResponse
+ require 'ephemeral_response/proxy'
+ autoload :CacheService, 'ephemeral_response/cache_service'
autoload :Commands, 'ephemeral_response/commands'
autoload :Configuration, 'ephemeral_response/configuration'
autoload :Fixture, 'ephemeral_response/fixture'
- autoload :NullOutput, 'ephemeral_response/null_output'
autoload :Request, 'ephemeral_response/request'
VERSION = "0.4.0".freeze
View
29 lib/ephemeral_response/cache_service.rb
@@ -0,0 +1,29 @@
+module EphemeralResponse
+ class CacheService
+ attr_accessor :request
+
+ def cached?
+ Fixture.find(uri, http_request)
+ end
+
+ def get_cached_response
+ fixture = Fixture.find(uri, http_request)
+ fixture.raw_response
+ end
+
+ def cache(raw_response)
+ fixture = Fixture.new(uri, http_request)
+ fixture.raw_response = raw_response
+ fixture.register
+ end
+
+ def http_request
+ request.http_request
+ end
+
+ def uri
+ request.uri
+ end
+
+ end
+end
View
14 lib/ephemeral_response/certificate.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICKTCCAZICCQCxJv27YPhTITANBgkqhkiG9w0BAQUFADBZMQswCQYDVQQGEwJB
+VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMTIwNDA2MTk1MTAxWhcN
+MjIwNDA0MTk1MTAxWjBZMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0
+ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRIwEAYDVQQDEwls
+b2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKjy90rauaubevAJ
+YeyaOJRMejC10IOxGXgNOpqdL9fAloOwLs+5yufRuYO8KngZ4eapQvwDojdGDONm
+2Aojp6NS28onxzRRA8QHu25ImGi+S/fPqH9mnP7qmdNgLqIQDJxIflo3XKrmiXkK
+1P6GP6vBDdWF5GotK/iXhcGtUbuvAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAhST/
+zULfkCcfRPOAiNoixKtzY4w/gtYNOoG4znrEoAJxfMLdV3mZrzeqE29PbPApV/Yx
+8Io772IHE18HTZlHx01K3VyuBDOngrkEyqXxZrekBA00K8b0se1IKpo5s7+LLdrO
+QTaau8BfvSiPCuD9syYlhe4MHeKHyEMEqDMeVNk=
+-----END CERTIFICATE-----
View
33 lib/ephemeral_response/commands.rb
@@ -2,10 +2,26 @@ module EphemeralResponse
module Commands
def activate
deactivate
- load 'ephemeral_response/net_http.rb'
+ server.start unless server && server.running?
+ ::Net.module_eval do
+ if const_defined?(:HTTP) && !const_defined?(:OHTTP)
+ const_set(:OHTTP, remove_const(:HTTP))
+ const_set(:HTTP, Net::ProxyHTTP)
+ end
+ end
Fixture.load_all
end
+ def server
+ @server ||= new_server
+ end
+
+ def new_server
+ s = ProxyServer.new
+ s.cache_service = CacheService.new
+ s
+ end
+
def configure
yield Configuration if block_given?
Configuration
@@ -21,15 +37,12 @@ def fixture_set=(name)
end
def deactivate
- Net::HTTP.class_eval do
- remove_method(:generate_uri) if method_defined?(:generate_uri)
- remove_method(:uri) if method_defined?(:uri)
- alias_method(:connect, :connect_without_ephemeral_response) if private_method_defined?(:connect_without_ephemeral_response)
- alias_method(:request, :request_without_ephemeral_response) if method_defined?(:request_without_ephemeral_response)
- end
- Net::HTTPResponse.class_eval do
- alias_method(:procdest, :procdest_without_ephemeral_response) if private_method_defined?(:procdest_without_ephemeral_response)
- alias_method(:read_body, :read_body_without_ephemeral_response) if method_defined?(:read_body_without_ephemeral_response)
+ server.stop
+ ::Net.module_eval do
+ if const_defined?(:OHTTP)
+ remove_const(:HTTP)
+ const_set(:HTTP, remove_const(:OHTTP))
+ end
end
end
View
4 lib/ephemeral_response/configuration.rb
@@ -1,3 +1,5 @@
+require 'stringio'
+
module EphemeralResponse
module Configuration
extend self
@@ -27,7 +29,7 @@ def debug_output=(io)
end
def debug_output
- @debug_output ||= NullOutput.new
+ @debug_output ||= StringIO.new
end
def expiration=(expiration)
View
77 lib/ephemeral_response/fixture.rb
@@ -2,12 +2,10 @@
require 'time'
require 'digest/sha1'
require 'yaml'
+require 'stringio'
module EphemeralResponse
class Fixture
- attr_accessor :response
- attr_reader :request, :uri, :created_at
-
def self.fixtures
@fixtures ||= {}
end
@@ -17,7 +15,8 @@ def self.clear
end
def self.find(uri, request)
- fixtures[Fixture.new(uri, request).identifier]
+ f = Fixture.new(uri, request)
+ fixtures[f.identifier]
end
def self.load_all
@@ -49,25 +48,29 @@ def self.register(fixture)
end
end
- def self.respond_to(uri, request, request_block)
- fixture = find_or_initialize(uri, request)
- if fixture.new?
- fixture.response = yield
- fixture.response.instance_variable_set(:@body, fixture.response.body.to_s)
- fixture.register
- elsif request_block
- request_block.call fixture.response
- end
- fixture.response
- end
+ attr_accessor :raw_response
+ attr_reader :uri, :created_at, :raw_request
def initialize(uri, request)
@uri = uri.normalize
- @request = deep_dup request
@created_at = Time.now
+ self.request = request
yield self if block_given?
end
+ def request=(request)
+ if Net::HTTPGenericRequest === request
+ @request = request
+ @raw_request = extract_raw_request
+ else
+ @raw_request = request
+ end
+ end
+
+ def request
+ @request ||= build_request
+ end
+
def expired?
!Configuration.skip_expiration && (created_at + Configuration.expiration) < Time.now
end
@@ -89,7 +92,7 @@ def new?
end
def normalized_name
- [uri.host, http_method, fs_path].compact.join("_").gsub(/[\/]/, '-')
+ [uri.host, http_method, fs_path].compact.join("_").tr('/', '-')
end
def fs_path
@@ -100,6 +103,14 @@ def path
File.join(Configuration.effective_directory, file_name)
end
+ def response
+ s = StringIO.new(raw_response)
+ b = Net::BufferedIO.new(s)
+ response = Net::HTTPResponse.read_new(b)
+ response.reading_body(b, request.response_body_permitted?) {}
+ response
+ end
+
def register
unless Configuration.white_list.include? uri.host
EphemeralResponse::Configuration.debug_output.puts "#{http_method} #{uri} saved as #{path}"
@@ -108,13 +119,6 @@ def register
end
end
- def save
- FileUtils.mkdir_p Configuration.effective_directory
- File.open(path, 'w') do |f|
- f.write to_yaml
- end
- end
-
def uri_identifier
if uri.query
parts = uri.to_s.split("?", 2)
@@ -125,8 +129,17 @@ def uri_identifier
end
end
+ def to_yaml_properties
+ %w(@uri @raw_request @raw_response @created_at)
+ end
+
protected
+ def build_request
+ r = ProxyRequest.new(raw_request)
+ r.http_request
+ end
+
def deep_dup(object)
Marshal.load(Marshal.dump(object))
end
@@ -135,6 +148,14 @@ def default_identifier
"#{uri_identifier}#{http_method}#{request.body}"
end
+ def extract_raw_request
+ s = StringIO.new
+ b = Net::BufferedIO.new(s)
+ request.exec(b, Net::HTTP::HTTPVersion, request.path)
+ s.rewind
+ b.read_all
+ end
+
def generate_file_name
"#{normalized_name}_#{identifier[0..6]}.yml"
end
@@ -142,5 +163,13 @@ def generate_file_name
def registered_identifier
identity = Configuration.host_registry[uri.host].call(Request.new(uri, request)) and identity.to_s
end
+
+ def save
+ FileUtils.mkdir_p Configuration.effective_directory
+ File.open(path, 'w') do |f|
+ f.write to_yaml
+ end
+ end
+
end
end
View
15 lib/ephemeral_response/key.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQCo8vdK2rmrm3rwCWHsmjiUTHowtdCDsRl4DTqanS/XwJaDsC7P
+ucrn0bmDvCp4GeHmqUL8A6I3RgzjZtgKI6ejUtvKJ8c0UQPEB7tuSJhovkv3z6h/
+Zpz+6pnTYC6iEAycSH5aN1yq5ol5CtT+hj+rwQ3VheRqLSv4l4XBrVG7rwIDAQAB
+AoGANUR9sa0qsy+PYFUk+ctaIW/Haso4Vv0kkZRiMNN0fSrsidKnv7jNf6/BNQbD
+wSAv+GDPjNO8dn7wm1YWsYOyW6okVrgqle3lF/OBnUS2rUnsZdjgd5seXz+nRY6+
+9Zi7aV8FPHgDMAKWYilzytNGHtWjCAOtLHB7ejv6nDRUbmECQQDRNer1tHUcV6AJ
+FyXvAn193S3Xu3bQuN59q5+v1QJDUcFYmIsg1M3IFQDbzGkD850PpXPfNVHMFnAU
+b307dw6xAkEAzrvtMHJKvYyP7DyTGlbFS7xWHN3szSwkHgEUoPGcocODmX/moxEW
+WRBXoG7ttSGMYqm9QtxwSRl2065n13nIXwJABXfkUVHLMdd0fmhVfH7TKuQKG7Zx
+r5j1b9F5lg36Riov5JHwKQaG7nDmGdio8gp/E3aepbnuDmiTu2UCn/hHsQJATey6
+QBuknoQgL9y5WiFA5wZLsz/XpZKw3npryyqnbrYiobZ7OhYTxWiKjxehFDhcEUiH
+5W7wCC3IA4xm6eqmowJBAIgSYoSxWyL1rUF1hIptc4/LJqjMtRjTPCojCjc4k0BP
+5MOvgYFLiBoJyoZKvkmU4hI3SZkoB224v/suSkGUJiw=
+-----END RSA PRIVATE KEY-----
View
72 lib/ephemeral_response/net_http.rb
@@ -1,72 +0,0 @@
-require 'net/http'
-
-module Net
- class HTTP
- alias request_without_ephemeral_response request
- alias connect_without_ephemeral_response connect
-
- attr_reader :uri
-
- def connect
- end
- private :connect
-
- def do_start_with_ephemeral_response
- connect_without_ephemeral_response
- @started = true
- end
- private :do_start_with_ephemeral_response
-
- def generate_uri(request)
- scheme = use_ssl? ? "https" : "http"
- @uri = URI.parse("#{scheme}://#{conn_address}:#{conn_port}#{request.path}")
- end
-
- def request(request, body = nil, &block)
- generate_uri(request)
- request.set_body_internal body
- EphemeralResponse::Fixture.respond_to(uri, request, block) do
- do_start_with_ephemeral_response
- request_without_ephemeral_response(request, nil, &block)
- end
- end
- end
-
- module PersistentReadAdapter
- def _buffer
- @_buffer ||= ""
- end
-
- def <<(str)
- _buffer << str
- super
- end
-
- def to_yaml(opts = {})
- _buffer.to_yaml opts
- end
-
- def to_s
- _buffer
- end
- end
-
- class HTTPResponse
- alias procdest_without_ephemeral_response procdest
- alias read_body_without_ephemeral_response read_body
-
- def procdest(dest, block)
- to = procdest_without_ephemeral_response(dest, block)
- to.extend PersistentReadAdapter
- end
-
- def read_body(dest = nil, &block)
- if @read
- yield @body if block_given?
- @body
- else
- read_body_without_ephemeral_response(dest, &block)
- end
- end
- end
-end
View
6 lib/ephemeral_response/null_output.rb
@@ -1,6 +0,0 @@
-module EphemeralResponse
- class NullOutput
- def puts(*args)
- end
- end
-end
View
352 lib/ephemeral_response/proxy.rb
@@ -0,0 +1,352 @@
+require 'socket'
+require 'uri'
+require 'net/http'
+require 'openssl'
+require 'thread'
+
+module Net
+
+ class ProxyHTTP < HTTP
+ include HTTP::ProxyDelta
+ @is_proxy_class = true
+ @proxy_address = 'localhost'
+ @proxy_port = 44567
+
+ def initialize(*args)
+ super
+ self.verify_mode = nil
+ end
+
+ def verify_mode=(*args)
+ @verify_mode = OpenSSL::SSL::VERIFY_NONE
+ end
+ alias verify_mode verify_mode=
+ end
+
+end
+
+module EphemeralResponse
+
+ class ProxyReader
+ attr_reader :socket
+
+ def initialize(socket)
+ @socket = socket
+ end
+
+ def read
+ request = catch(:request_done) do
+ buf = ProxyRequest.new
+ buf.tunnel_host = socket.tunnel_host if socket.respond_to?(:tunnel_host)
+ while line = socket.gets
+ buf << line
+ if buf.complete?
+ if buf.content_length?
+ buf << socket.read(buf.content_length)
+ end
+ throw :request_done, buf
+ end
+ end
+ end
+ end
+ end
+
+ class ProxyRequest
+ attr_accessor :tunnel_host
+ attr_reader :raw
+
+ def initialize(raw = "")
+ @raw = raw || ""
+ end
+
+ def <<(str)
+ raw << str
+ end
+
+ def complete?
+ raw =~ /\r?\n\r?\n$/
+ end
+
+ def host
+ uri.host
+ end
+
+ def port
+ uri.port
+ end
+
+ def http_method
+ @http_method ||= first_line[0]
+ end
+
+ def uri
+ @uri ||= parse_uri
+ end
+
+ def parse_uri
+ h = first_line[1]
+ if tunnel_host
+ h = "https://#{tunnel_host}#{h}"
+ elsif h !~ /^https?:\/\//
+ h = "http://#{h}"
+ end
+ begin
+ URI.parse(h)
+ rescue URI::InvalidURIError
+ URI.parse(URI.escape(h))
+ end
+ end
+
+ def http_version
+ first_line[2]
+ end
+
+ def http_method_class
+ ::Net::HTTP.const_get(http_method.downcase.capitalize)
+ end
+
+ def http_request
+ req = http_method_class.new(uri.request_uri)
+ req.set_form_data(form_data) unless form_data.empty?
+ headers.each {|k,v| req[k] = v}
+ req
+ end
+
+ def ssl_tunnel?
+ http_method.upcase == "CONNECT"
+ end
+
+ def ssl?
+ uri.scheme == "https" || uri.port == URI::HTTPS::DEFAULT_PORT
+ end
+
+ def to_s
+ raw
+ end
+
+ def first_line
+ @first_line ||= lines[0].split(" ", 3)
+ end
+
+ def lines
+ @lines ||= raw.split(/\r?\n/)
+ end
+
+ def headers
+ @headers ||= parse_headers
+ end
+
+ def parse_headers
+ h = {}
+ lines[1..-1].each do |header|
+ k,v = header.split(": ", 2)
+ h[k] = v if k && v
+ end
+ h
+ end
+
+ def content_length
+ headers['Content-Length'].to_i
+ end
+
+ def content_length?
+ content_length > 0
+ end
+
+ def form_data
+ @form_data ||= parse_form_data
+ end
+
+ def parse_form_data
+ h = {}
+ if content_length?
+ data = raw.split(/\r?\n\r?\n/, 2).last
+ data.split("&").each do |set|
+ k,v = set.split('=', 2)
+ h[k] = v
+ end
+ end
+ h
+ end
+ end
+
+ class ProxyForwarder
+ attr_reader :proxy_req, :response, :cache_service
+
+ def initialize(proxy_req, cache_service=nil)
+ @proxy_req = proxy_req
+ @raw = ""
+ self.cache_service = cache_service
+ end
+
+ def cache_service=(cache_service)
+ if cache_service
+ @cache_service = cache_service
+ cache_service.request = proxy_req
+ cache_service
+ end
+ end
+
+ def start
+ if cached?
+ yield get_cached_response
+ else
+ make_request
+ cache_response
+ yield @raw
+ end
+ end
+
+ def cached?
+ if cache_service
+ cache_service.cached?
+ end
+ end
+
+ def cache_response
+ if cache_service
+ cache_service.cache(@raw)
+ end
+ end
+
+ def get_cached_response
+ cache_service.get_cached_response
+ end
+
+ def http_class
+ Net.const_defined?(:OHTTP) ? Net::OHTTP : Net::HTTP
+ end
+
+ def make_request
+ if proxy_req.ssl_tunnel?
+ @raw = "#{proxy_req.http_version} 200 Connection established\r\n\r\n"
+ else
+ http = http_class.new(proxy_req.host, proxy_req.port)
+ http.use_ssl = proxy_req.ssl?
+ http.start do |http|
+ http.request(proxy_req.http_request) do |response|
+ @response = response
+ @raw << response_headers
+ response.read_body do |data|
+ @raw << data
+ end
+ end
+ end
+ end
+ end
+
+ def response_headers
+ h = ["HTTP/#{response.http_version} #{response.code} #{response.message}"]
+ response.each_capitalized do |k,v|
+ unless ['Transfer-Encoding'].include?(k)
+ h << "#{k}: #{v}"
+ end
+ end
+ h.join("\r\n") << "\r\n\r\n"
+ end
+ end
+
+ module SSLTunnel
+ attr_accessor :tunnel_host
+ end
+
+ class ProxyServer
+ Thread.abort_on_exception = true
+
+ attr_reader :ios, :server, :server_thread, :mutex
+ attr_accessor :port, :cache_service
+
+ def initialize(port=nil)
+ @ios = []
+ @server_thread = []
+ @mutex = Mutex.new
+ self.port = port || 44567
+ end
+
+ def dir
+ File.expand_path(File.dirname(__FILE__))
+ end
+
+ def certificate_path
+ File.join(dir, "certificate.pem")
+ end
+
+ def key_path
+ File.join(dir, "key.pem")
+ end
+
+ def ssl_sock(sock)
+ context = OpenSSL::SSL::SSLContext.new
+ context.cert = OpenSSL::X509::Certificate.new(File.open(certificate_path))
+ context.key = OpenSSL::PKey::RSA.new(File.open(key_path))
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, context)
+ ssl.sync_close = true
+ ssl
+ end
+
+ def server
+ @server ||= TCPServer.new(port)
+ end
+
+ def start
+ ios << server
+ @running = true
+ @stopping = false
+ @server_thread = Thread.new do
+ while true
+ selection = select(ios, [], [], 0.1)
+ if selection
+ selection.first.each do |socket_ready|
+ if socket_ready.closed?
+ $stderr.puts "#{self.class.name}: socket closed: #{socket_ready.inspect}"
+ else
+ handle(socket_ready)
+ end
+ end
+ end
+ break if @stopping
+ end
+ end
+ self
+ end
+
+ def running?
+ @running
+ end
+
+ def join
+ server_thread.join
+ end
+
+ def stop
+ ios.clear
+ @stopping = true
+ @running = false
+ end
+
+ def handle(socket_ready)
+ s = socket_ready.accept
+ reader = ProxyReader.new(s)
+ request = reader.read
+
+ if request
+ forwarder = ProxyForwarder.new(request, cache_service)
+ forwarder.start do |str|
+ s.print(str) unless s.closed?
+ end
+ if request.ssl_tunnel?
+ ssl = ssl_sock(s)
+ ssl.extend SSLTunnel
+ ssl.tunnel_host = request.host
+ mutex.synchronize { ios << ssl }
+ else
+ s.close
+ mutex.synchronize { ios.delete(s) }
+ end
+ else
+ puts "No request #{request.inspect}"
+ s.close
+ end
+ end
+ end
+end
View
6 spec/ephemeral_response/configuration_spec.rb
@@ -17,8 +17,8 @@
end
describe ".debug_output" do
- it "defaults to being off (NullOutput)" do
- subject.debug_output.should be_instance_of(EphemeralResponse::NullOutput)
+ it "defaults to being off (StringIO)" do
+ subject.debug_output.should be_instance_of(StringIO)
end
it "returns the the result of the setter" do
@@ -150,7 +150,7 @@
it "resets debug_output" do
subject.debug_output = $stderr
subject.reset
- subject.debug_output.should be_instance_of(EphemeralResponse::NullOutput)
+ subject.debug_output.should be_instance_of(StringIO)
end
end
View
94 spec/ephemeral_response/fixture_spec.rb
@@ -6,9 +6,10 @@
let(:fixture_directory) { File.expand_path EphemeralResponse::Configuration.fixture_directory }
let(:request) { Net::HTTP::Get.new '/' }
let(:uri) { URI.parse("http://example.com/") }
+ let(:raw_response) { %(HTTP/1.1 200 OK\r\n\r\nRaw Body\r\n) }
let(:fixture) do
EphemeralResponse::Fixture.new(uri, request) do |f|
- f.response = OpenStruct.new(:body => "hello world")
+ f.raw_response = raw_response
end
end
@@ -59,7 +60,7 @@
describe ".load_fixture" do
it "loads the yamlized fixture into the fixtures hash" do
- fixture.save
+ fixture.send :save
EphemeralResponse::Fixture.load_fixture(fixture.path)
EphemeralResponse::Fixture.fixtures.should have_key(fixture.identifier)
end
@@ -123,8 +124,8 @@
context "when the fixture doesn't exist" do
it "processes the block" do
EphemeralResponse::Fixture.find_or_initialize(uri, request) do |fixture|
- fixture.response = "bah"
- end.response.should == "bah"
+ fixture.raw_response = raw_response
+ end.response.body.should == "Raw Body\r\n"
end
it "returns the new fixture" do
@@ -134,80 +135,6 @@
end
end
- describe ".respond_to" do
- context "host included in white list" do
- before do
- EphemeralResponse::Configuration.white_list = uri.host
- end
-
- it "returns flow back to net/http" do
- 2.times do
- EphemeralResponse::Fixture.respond_to(fixture.uri, request, nil) do
- stub(:body => "")
- end
- EphemeralResponse::Fixture.fixtures[fixture.identifier].should be_nil
- end
- end
-
- context "uri not normalized" do
- let(:uri) { URI.parse("HtTP://ExaMplE.Com/") }
- let(:fixture) { EphemeralResponse::Fixture.new(uri, request) }
-
- before do
- EphemeralResponse::Configuration.white_list = "example.com"
- end
-
- it "returns flow to net/http when host is not normalized" do
- EphemeralResponse::Fixture.respond_to(uri, request, nil) do
- stub(:body => "")
- end
- EphemeralResponse::Fixture.fixtures[fixture.identifier].should be_nil
- end
- end
-
- end
-
- context "fixture loaded" do
- it "returns the fixture response" do
- fixture.register
- response = EphemeralResponse::Fixture.respond_to(fixture.uri, request, nil)
- response.body.should == "hello world"
- end
-
- it "yields the response body to the request block" do
- fixture.register
- reverse_body = nil
- request_block = lambda {|response| reverse_body = response.body.reverse}
- response = EphemeralResponse::Fixture.respond_to(fixture.uri, request, request_block)
- reverse_body.should == response.body.reverse
- end
- end
-
- context "fixture not loaded" do
- it "sets the response to the block" do
- EphemeralResponse::Fixture.respond_to(fixture.uri, request, nil) do
- stub(:body => "new response")
- end
- EphemeralResponse::Fixture.fixtures[fixture.identifier].response.body.should == "new response"
- end
-
- it "sets @body to the body string" do
- EphemeralResponse::Fixture.respond_to(fixture.uri, request, nil) do
- stub(:body => :hello_world)
- end
- response = EphemeralResponse::Fixture.fixtures[fixture.identifier].response
- response.instance_variable_get(:@body).should == "hello_world"
- end
-
- it "saves the fixture" do
- EphemeralResponse::Fixture.respond_to(fixture.uri, request, nil) do
- stub(:body => "new response")
- end
- File.exists?(fixture.path).should be_true
- end
- end
- end
-
describe "#initialize" do
let(:uri) { URI.parse("HtTP://ExaMplE.Com/") }
subject { EphemeralResponse::Fixture }
@@ -217,13 +144,6 @@
fixture.uri.should == uri.normalize
end
- it "duplicates the request" do
- fixture = subject.new(uri, request)
- request['something'] = "anything"
- fixture.request['something'].should be_nil
- request['something'].should == "anything"
- end
-
it "sets created_at to the current time" do
Time.travel "2010-01-15 10:11:12" do
fixture = subject.new(uri, request)
@@ -233,9 +153,9 @@
it "yields itself" do
fixture = subject.new(uri, request) do |f|
- f.response = "yielded self"
+ f.raw_response = raw_response
end
- fixture.response.should == "yielded self"
+ fixture.response.body.should == "Raw Body\r\n"
end
end
View
101 spec/ephemeral_response/net_http_spec.rb
@@ -1,101 +0,0 @@
-require 'spec_helper'
-
-describe Net::HTTP do
- subject { Net::HTTP.new('example.com') }
- let(:request) { Net::HTTP::Get.new("/foo?q=1") }
- let(:uri) { URI.parse("http://example.com/foo?q=1") }
- let(:response) { OpenStruct.new(:body => "Hello") }
-
- before do
- subject.stub(:connect_without_ephemeral_response)
- subject.stub(:request_without_ephemeral_response => response)
- end
-
- describe "#connect" do
- it "does nothing" do
- subject.send(:connect).should be_nil
- end
- end
-
- describe "#generate_uri" do
- context "when HTTP" do
- subject { Net::HTTP.new('example.com') }
-
- it "returns the proper http uri object" do
- subject.generate_uri(request).should == URI.parse("http://example.com/foo?q=1")
- end
-
- it "sets the uri instance variable" do
- subject.generate_uri(request)
- subject.uri.should == URI.parse("http://example.com/foo?q=1")
- end
- end
-
- context "when HTTPS" do
- subject do
- https = Net::HTTP.new('example.com', 443)
- https.use_ssl = true
- https
- end
-
- it "returns the proper http uri object" do
- subject.generate_uri(request).should == URI.parse("https://example.com/foo?q=1")
- end
- end
- end
-
- describe "#request" do
- context "fixture does not exist" do
- before do
- EphemeralResponse::Fixture.stub(:respond_to).and_yield
- end
-
- it "connects" do
- subject.should_receive(:connect_without_ephemeral_response)
- subject.request(request)
- end
-
- it "calls #request_without_ephemeral_response" do
- subject.should_receive(:request_without_ephemeral_response).with(request, nil).and_return(response)
- subject.request(request)
- end
- end
-
- context "fixture exists" do
- before do
- fixture = EphemeralResponse::Fixture.new(uri, request) do |f|
- f.response = response
- end
- fixture.register
- end
-
- after do
- clear_fixtures
- end
-
- it "does not connect" do
- subject.should_not_receive(:connect_without_ephemeral_response)
- subject.request(request)
- end
-
- it "does not call #request_without_ephemeral_response" do
- subject.should_not_receive(:request_without_ephemeral_response).with(request, nil).and_return(response)
- subject.request(request)
- end
-
- it "yields the response to the block" do
- subject.request(request) do |response|
- response.should == response
- end
- end
- end
-
- context "connection not started" do
- it "starts the connection" do
- subject.should_not be_started
- subject.request(request)
- subject.should be_started
- end
- end
- end
-end
View
51 spec/ephemeral_response_spec.rb
@@ -2,49 +2,48 @@
describe EphemeralResponse do
describe ".activate" do
- it "loads all fixtures" do
- EphemeralResponse::Fixture.should_receive(:load_all)
+ it "deactivates" do
+ EphemeralResponse.should_receive(:deactivate)
EphemeralResponse.activate
end
- end
- describe ".deactivate" do
- before do
- Net::HTTP.stub(:alias_method)
- Net::HTTPResponse.stub(:alias_method)
+ it "starts the proxy server" do
+ server = mock(:stop => nil, :running? => false)
+ EphemeralResponse.stub(:server => server)
+ server.should_receive(:start)
+ EphemeralResponse.activate
end
- it "restores the original connection method" do
- Net::HTTP.should_receive(:alias_method).with(:connect, :connect_without_ephemeral_response).once
+ it "switches Net::HTTP to return a proxied HTTP class" do
EphemeralResponse.deactivate
+ real_ancestors = Net::HTTP.ancestors
+ EphemeralResponse.activate
+ Net::HTTP.ancestors.should include(Net::ProxyHTTP)
+ Net::OHTTP.ancestors.should == real_ancestors
end
- it "restores the original request method" do
- Net::HTTP.should_receive(:alias_method).with(:request, :request_without_ephemeral_response)
- EphemeralResponse.deactivate
+ it "loads all fixtures" do
+ EphemeralResponse::Fixture.should_receive(:load_all)
+ EphemeralResponse.activate
end
- it "restores #procdest" do
- Net::HTTPResponse.should_receive(:alias_method).with(:procdest, :procdest_without_ephemeral_response)
- EphemeralResponse.deactivate
- end
+ end
- it "restores #read_body" do
- Net::HTTPResponse.should_receive(:alias_method).with(:read_body, :read_body_without_ephemeral_response)
- EphemeralResponse.deactivate
- end
+ describe ".deactivate" do
- it "removes #generate_uri" do
- Net::HTTP.method_defined?(:generate_uri).should be_true
+ it "stops the proxy server" do
+ server = mock(:stop => nil, :running? => false)
+ EphemeralResponse.stub(:server => server)
+ server.should_receive(:stop)
EphemeralResponse.deactivate
- Net::HTTP.method_defined?(:generate_uri).should be_false
end
- it "removes #uri" do
- Net::HTTP.method_defined?(:uri).should be_true
+ it "restores the orignal net http object" do
EphemeralResponse.deactivate
- Net::HTTP.method_defined?(:uri).should be_false
+ Net::HTTP.ancestors.should_not include(Net::ProxyHTTP)
+ Net.const_defined?(:OHTTP).should be_false
end
+
end
describe ".fixtures" do
View
5 spec/integration/normal_flow_spec.rb
@@ -23,8 +23,6 @@ def send_request(req)
real_response = send_request(get)
fixture = EphemeralResponse::Fixture.new(uri, get)
File.exists?(fixture.path).should be_true
- Net::HTTP.should_not_receive(:connect_without_ephemeral_response)
- Net::HTTP.should_not_receive(:request_without_ephemeral_response)
fixture_response = send_request(get)
real_response.should == fixture_response
end
@@ -32,6 +30,7 @@ def send_request(req)
it "generates a new fixture when the initial fixture expires" do
send_request(get)
old_fixture = EphemeralResponse::Fixture.find(uri, get)
+ send_request(get)
Time.travel((Time.now + EphemeralResponse::Configuration.expiration * 2).to_s) do
EphemeralResponse::Fixture.load_all
send_request(get)
@@ -40,8 +39,6 @@ def send_request(req)
old_fixture.created_at.should < new_fixture.created_at
# use the new fixture
- Net::HTTP.should_not_receive(:connect_without_ephemeral_response)
- Net::HTTP.should_not_receive(:request_without_ephemeral_response)
send_request(get)
end
View
13 spec/integration/read_body_compatibility_spec.rb
@@ -8,7 +8,7 @@
clear_fixtures
end
- let(:uri) { URI.parse('http://example.com/') }
+ let(:uri) { URI.parse('http://duckduckgo.com/') }
def http
Net::HTTP.new uri.host, uri.port
@@ -38,12 +38,21 @@ def send_request(req, body=nil)
context "Net::HTTP#get" do
it "generates a fixture, then uses the fixture" do
+ begin
real_response = nil
- http.get('/') {|s| real_response = s}
+ http.start do |h|
+ h.request(get) do |r|
+ r.read_body {|s| real_response = s}
+ end
+ end
fixture = EphemeralResponse::Fixture.find(uri, get)
File.exists?(fixture.path).should be_true
fixture_response = send_request(get).body
real_response.should == fixture_response
+ rescue Exception => e
+ p e
+ puts e.backtrace
+ end
end
end
View
8 spec/integration/unique_fixtures_spec.rb
@@ -35,7 +35,6 @@ def set_up_responses
def perform(request, body=nil)
http = Net::HTTP.new uri.host, uri.port
- # http.set_debug_output $stdout
http.start do |http|
http.request(request, body)
end
@@ -148,12 +147,15 @@ def get
clear_fixtures
http = Net::HTTP.new uri.host, uri.port
-
EphemeralResponse::RackReflector.while_running do
http.request(get)
end
- http.request(get).body.should == EphemeralResponse::Fixture.find(http.uri, get).response.body
+ fixture_uri = uri.dup
+ fixture_uri.path = get.path
+ body = http.request(get).body
+ fixture_body = EphemeralResponse::Fixture.find(fixture_uri, get).response.body
+ body.should == fixture_body
end
end
View
16 spec/spec_helper.rb
@@ -3,11 +3,23 @@
require 'ephemeral_response'
require 'fakefs/safe'
require 'fakefs/spec_helpers'
-require 'spec/autorun'
+require 'rspec/autorun'
+require 'debugger'
Dir.glob("spec/support/*.rb") {|f| require File.expand_path(f, '.')}
-Spec::Runner.configure do |config|
+class Net::HTTPResponse
+ def equality_test
+ [http_version, code, message, body]
+ end
+
+ def ==(other)
+ equality_test == other.equality_test
+ end
+end
+
+RSpec.configure do |config|
+ config.color = true
config.include ClearFixtures
config.before(:suite) do
View
4 spec/support/clear_fixtures.rb
@@ -1,7 +1,9 @@
module ClearFixtures
module_function
def clear_fixtures
- FileUtils.rm_rf EphemeralResponse::Configuration.fixture_directory
+ if Dir.exists?(EphemeralResponse::Configuration.fixture_directory)
+ FileUtils.rm_rf(EphemeralResponse::Configuration.fixture_directory)
+ end
EphemeralResponse::Fixture.clear
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.