diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..be5b77a --- /dev/null +++ b/Gemfile @@ -0,0 +1,14 @@ +source :rubygems + +gem 'vcr' + +group :development, :test do + gem 'guard', '>= 1.6.2' + gem 'guard-bundler' + gem 'guard-rspec' + gem 'rb-fsevent' + gem 'pry', '>= 0.9.10' + gem 'pry-doc', '>= 0.4.4' + gem 'rake', '>= 10.0.4' + gem 'rspec', '>= 2.13' +end diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..be4e15f --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,56 @@ +GEM + remote: http://rubygems.org/ + specs: + coderay (1.0.9) + diff-lcs (1.2.2) + formatador (0.2.4) + guard (1.7.0) + formatador (>= 0.2.4) + listen (>= 0.6.0) + lumberjack (>= 1.0.2) + pry (>= 0.9.10) + thor (>= 0.14.6) + guard-bundler (1.0.0) + bundler (~> 1.0) + guard (~> 1.1) + guard-rspec (2.5.0) + guard (>= 1.1) + rspec (~> 2.11) + listen (0.7.3) + lumberjack (1.0.3) + method_source (0.8.1) + pry (0.9.12) + coderay (~> 1.0.5) + method_source (~> 0.8) + slop (~> 3.4) + pry-doc (0.4.5) + pry (>= 0.9) + yard (>= 0.8) + rake (10.0.4) + rb-fsevent (0.9.3) + rspec (2.13.0) + rspec-core (~> 2.13.0) + rspec-expectations (~> 2.13.0) + rspec-mocks (~> 2.13.0) + rspec-core (2.13.1) + rspec-expectations (2.13.0) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.13.0) + slop (3.4.4) + thor (0.18.1) + vcr (2.4.0) + yard (0.8.5.2) + +PLATFORMS + ruby + +DEPENDENCIES + guard (>= 1.6.2) + guard-bundler + guard-rspec + pry (>= 0.9.10) + pry-doc (>= 0.4.4) + rake (>= 10.0.4) + rb-fsevent + rspec (>= 2.13) + vcr diff --git a/Guardfile b/Guardfile new file mode 100644 index 0000000..5e7fa46 --- /dev/null +++ b/Guardfile @@ -0,0 +1,11 @@ +# A sample Guardfile +# More info at https://github.com/guard/guard#readme + +guard :bundler do + watch('Gemfile') +end + +guard :rspec, :cli => '--drb --format progress --color' do + watch(%r{^spec/.+_spec\.rb$}) + watch(%r{^lib/.+\.rb$}) do |m| "spec/#{m[1]}_spec.rb" end +end diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a54610a --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2013 Nikola Chochkov + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 9859957..09c88c5 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,72 @@ # VCRProxy -A first try for a very basic implementation of a VCR proxy. VCR is an awesome tool to record and -replay HTTP interactions for your test suite (or other use cases) in Ruby. That means, when you -call an external site, VCR records the request at the first time, and replays at later requests. -The problem is, that VCR is pure Ruby, that means, it can hook into webmock or fakeweb, but not +A basic implementation of a VCR proxy. VCR is an awesome tool to record and +replay HTTP interactions for your test suite (or other use cases) in Ruby. That means, when you +call an external site, VCR records the request at the first time, and replays at later requests. +The problem is, that VCR is pure Ruby, that means, it can hook into webmock or fakeweb, but not into requests made by external applications. -Such an external application could be an automated browser for your testsuite (e.g. selenium, phantomjs...). -When you try to do ajax calls from the frontend, that calls aren't recorded, because VCR can't know about them. -But: If you can configure your browser (or any other application) to use a proxy on a specific port, it would +Such an external application could be an automated browser for your testsuite (e.g. selenium, phantomjs...). +When you try to do ajax calls from the frontend, that calls aren't recorded, because VCR can't know about them. +But: If you can configure your browser (or any other application) to use a proxy on a specific port, it would be possible for VCR to record the request, when the proxy is written in Ruby. -And so I did: VCRProxy is a proxy server based on WEBrick (which is included in ruby by default), that hooks +And so I did: VCRProxy is a proxy server based on WEBrick (which is included in ruby by default), that hooks into the calls, records them with VCR and let VCR replay the records the second time. -If you have SSL calls (and that is the tricky part), a second MITM proxy is started. You just have to ensure -that you ignore SSL errors/warnings in your application, because WEBrick generates a self-signed SSL certificate +If you have SSL calls (and that is the tricky part), a second MITM proxy is started. You just have to ensure +that you ignore SSL errors/warnings in your application, because WEBrick generates a self-signed SSL certificate on the fly, and this certificate isn't signed. -# Usage +# Installation -Clone the repo +Install the gem either through your `Gemfile`: ``` -git clone git://github.com/23tux/vcr_proxy.git +gem 'vcr_proxy' ``` -Have a look into the `vcr_proxy.rb` and look at the bottom to make sure, port `9999` is available at your machine. +or by: -Start the server with +``` +gem install vcr_proxy +``` + +# Rspec usage + +TODO: `Rspec` integration + +# Configuration +TODO: Allow custom VCR configuration through VCRProxy so that people could use +only VCRPRoxy ( without explicitly citing the vcr gem itself ). However we +should respect any existing VCR configuration. + +# Manual usage + +## Starting the server + +If you're on a Rails app, enjoy the rake tasks: + +``` +bundle exec rake vcr_proxy:start VCR_PROXY_PORT=9999 +``` + +or if you're not, see the: ``` -ruby vcr_proxy.rb +vcr_proxy --help ``` -Test the server with +and then a quick test: ``` curl --proxy localhost:9999 http://blekko.com/ws/?q=rails+/json ``` -For now VCR records the calls into `cassettes/records.yml`, this should be configurable in the future. If you start +For now VCR records the calls into `cassettes/records.yml`, this should be configurable in the future. If you start the command a second time, VCR replays the interaction. -If you want to mock out HTTPS calls, try this +If you want to mock out HTTPS calls, try this: ``` curl --proxy localhost:9999 --insecure https://blekko.com/ws/?q=rails+/json @@ -51,7 +74,10 @@ curl --proxy localhost:9999 --insecure https://blekko.com/ws/?q=rails+/json The `--insecure` option tells curl to ignore SSL warnings. -# Further Development +# Contributing + +Check back the current GitHub issues list, then: -There is a lot more to improve this thing. The issue sites tracks some already known issues from my site. -This thing should also be converted into a gem, and should provide some nice helper to hook into RSpec... +* clone +* create in a feature branch with specs +* create a Pull Request diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..2480d0e --- /dev/null +++ b/Rakefile @@ -0,0 +1,2 @@ +require "bundler" +Bundler::GemHelper.install_tasks diff --git a/bin/vcr_proxy b/bin/vcr_proxy new file mode 100755 index 0000000..9610d60 --- /dev/null +++ b/bin/vcr_proxy @@ -0,0 +1,34 @@ +#!/usr/bin/env ruby + +require 'optparse' +require 'vcr_proxy' + +hash = {} + +OptionParser.new do |opts| + opts.banner = "Usage: vcr_proxy [options]" + + opts.on('-c', '--cassettes CASSETTES', 'location of cassettes folder') do |r| + hash[:cassettes] = r + end + + opts.on('-p', '--port PORT', 'VCRProxy server port') do |r| + hash[:port] = r.to_i + end +end.parse! + +# enable modifications to unparsed_uri +# FIXME - lets not open HTTPRequest class and do sth else instead +class WEBrick::HTTPRequest + def unparsed_uri=(str) + @unparsed_uri = str + end +end + +# FIXME - we don't want to have custom VCR configuration because most apps will +# already have that. Or we should at least +VCRProxy.configure(hash) + +server = VCRProxy.start({ :Port => hash[:port] }) + +trap("INT") { server.shutdown } diff --git a/lib/vcr_proxy.rb b/lib/vcr_proxy.rb new file mode 100644 index 0000000..d805258 --- /dev/null +++ b/lib/vcr_proxy.rb @@ -0,0 +1,123 @@ +# stdlib modules +require 'net/http' +require 'webrick' +require 'webrick/https' +require 'webrick/httpproxy' + +require 'vcr' + +require 'vcr_proxy/server' +require 'vcr_proxy/constants' +require 'vcr_proxy/dependency' +require 'vcr_proxy/driver' + +if VCRProxy::Dependency.rails3? + require 'vcr_proxy/railtie' +end + +require 'capybara/poltergeist' + +VCRProxy.register_poltergeist_driver + +if VCRProxy::Dependency.rspec2? + require 'vcr_proxy/rspec' +end + +module VCRProxy + include Constants + + class << self + def prepare(opts = {}) + VCR.configure do |c| + c.default_cassette_options = { :record => :new_episodes } + end + Server.new(opts) + end + + def log + @logger ||= begin + if Dependency.rails? + Rails.logger + else + Logger.new(STDOUT) + end + end + end + + def port + ENV['VCR_PROXY_PORT'] || VCRProxy::DEFAULT_PORT + end + + def host + ENV['VCR_PROXY_HOST'] || VCRProxy::DEFAULT_HOST + end + + # start http proxy server and write the pid under tmp/pids + def start_with_pid + stop_with_pid + + Process.fork do + path = get_pid_root.join(VCRProxy::PID_FILE_PATH) + path.mkdir unless path.directory? + + path = path.join(VCRProxy::PID_FILE_NAME) + + opts = { + :Port => VCRProxy.port, + :RequestTimeout => 300, + :ProxyTimeout => true, + } + + server = VCRProxy.prepare(opts) + + log.info "VCRProxy starting on #{VCRProxy.port}, pid #{Process.pid}" + + trap('INT') { server.shutdown } + + path.open('w') do |file| + file.puts Process.pid + end + + server.start + end + end + + # send INT to VCRProxy pid if file found under tmp/pids/ + def stop_with_pid + path = get_pid_path + + if path.exist? + `kill -s INT #{path.read.chomp}` + path.delete + else + log.info "Looked in tmp/pids/; no VCRProxy pids found; nothing happened" + end + end + + # FIXME how do we implement configuraion + def configure(opts = {}) + VCR.configure do |c| + c.hook_into :webmock + c.cassette_library_dir = opts[:cassettes] ||= DEFAULT_CASSETTES + c.default_cassette_options = { :record => :new_episodes } + c.ignore_localhost = true + c.ignore_hosts "127.0.0.1" + end + end + + private + + def get_pid_path + get_pid_root.join(VCRProxy::PID_FILE_PATH).join(VCRProxy::PID_FILE_NAME) + end + + def get_pid_root + if Dependency.rails? + Rails.root + else + require 'pathname' + path Pathname.new '/' + end + end + end +end diff --git a/lib/vcr_proxy/constants.rb b/lib/vcr_proxy/constants.rb new file mode 100644 index 0000000..b3ff5b9 --- /dev/null +++ b/lib/vcr_proxy/constants.rb @@ -0,0 +1,13 @@ +module VCRProxy + module Constants + DEFAULT_PORT = 9994 + DEFAULT_HOST = 'localhost' + + # this setting will not be used if overwritten by VCR config + DEFAULT_CASSETTES = '/tmp/cassettes' + + # the location of the http proxy pid + PID_FILE_PATH = 'tmp/pids' + PID_FILE_NAME = 'vcr_proxy_server.pid' + end +end diff --git a/lib/vcr_proxy/dependency.rb b/lib/vcr_proxy/dependency.rb new file mode 100644 index 0000000..cc3b1f3 --- /dev/null +++ b/lib/vcr_proxy/dependency.rb @@ -0,0 +1,38 @@ +module VCRProxy + module Dependency + class << self + + def rails3? + safe_check_gem('rails', '>= 3.0') && running_rails3? + end + + def rails? + running_rails? + end + + def rspec2? + !! safe_check_gem('rspec', '>= 2.0') + end + + private + + def running_rails3? + running_rails? && Rails.version.to_i == 3 + end + + def running_rails? + !! defined?(Rails) + end + + def safe_check_gem(gem_name, version_string) + if Gem::Specification.respond_to?(:find_by_name) + Gem::Specification.find_by_name(gem_name, version_string) + elsif Gem.respond_to?(:available?) + Gem.available?(gem_name, version_string) + end + rescue Gem::LoadError + false + end + end + end +end diff --git a/lib/vcr_proxy/driver.rb b/lib/vcr_proxy/driver.rb new file mode 100644 index 0000000..f2091d6 --- /dev/null +++ b/lib/vcr_proxy/driver.rb @@ -0,0 +1,16 @@ +module VCRProxy + class << self + def register_poltergeist_driver + ::Capybara.register_driver :poltergeist_billy do |app| + options = { + phantomjs_options: [ + '--ignore-ssl-errors=yes', + "--proxy=#{VCRProxy.host}:#{VCRProxy.port}", + ] + } + + ::Capybara::Poltergeist::Driver.new(app, options) + end + end + end +end diff --git a/lib/vcr_proxy/railtie.rb b/lib/vcr_proxy/railtie.rb new file mode 100644 index 0000000..ab63444 --- /dev/null +++ b/lib/vcr_proxy/railtie.rb @@ -0,0 +1,7 @@ +module VCRProxy + class Railtie < Rails::Railtie + rake_tasks do + load 'vcr_proxy/tasks/vcr_proxy.rake' + end + end +end diff --git a/lib/vcr_proxy/rspec.rb b/lib/vcr_proxy/rspec.rb new file mode 100644 index 0000000..48c76e1 --- /dev/null +++ b/lib/vcr_proxy/rspec.rb @@ -0,0 +1,11 @@ +require 'rspec' + +RSpec.configure do |config| + config.before(:suite) do + VCRProxy.start_with_pid + end + + config.after(:suite) do + VCRProxy.stop_with_pid + end +end diff --git a/lib/vcr_proxy/server.rb b/lib/vcr_proxy/server.rb new file mode 100644 index 0000000..ed5aff6 --- /dev/null +++ b/lib/vcr_proxy/server.rb @@ -0,0 +1,110 @@ +module VCRProxy + class Server < ::WEBrick::HTTPProxyServer + def initialize(opts = {}) + super + @mitm_port = opts[:MITMPort] || 12322 + end + + # Starts the MITM server + # + # @param host [String] + # @param port [Integer] + # + # @return Thread object + def start_ssl_mitm(host, port) + # WORKAROUND for "adress is already in use", just increase + # the port number and kill the old webrick + @mitm_port += 1 + @mitm_server.stop if @mitm_server + @mitm_thread.kill if @mitm_thread + + @mitm_server = ::WEBrick::HTTPServer.new({ + :Port => @mitm_port, + :SSLEnable => true, + :SSLVerifyClient => ::OpenSSL::SSL::VERIFY_NONE, + :SSLCertName => [ ["C", "US"], ["O", host], ["CN", host] ] + }) + + @mitm_server.mount_proc('/') do |req, res| + method, url, version = req.request_line.split(" ") + + remote_request = case method.upcase + when 'GET' + ::Net::HTTP::Get.new(req.unparsed_uri) + when 'POST' + ::Net::HTTP::Post.new(req.unparsed_uri) + when 'PUT' + ::Net::HTTP::Put.new(req.unparsed_uri) + when 'DELETE' + ::Net::HTTP::Delete.new(req.unparsed_uri) + when 'HEAD' + ::Net::HTTP::Head.new(req.unparsed_uri) + when 'OPTIONS' + ::Net::HTTP::Options.new(req.unparsed_uri) + else + puts "HTTP method '#{method}' not supported!" + end + + remote_request.body = req.body + remote_request.body = req.body + remote_request.initialize_http_header(transform_header(req.header)) + + uri = req.request_uri + http = ::Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + http.verify_mode = ::OpenSSL::SSL::VERIFY_NONE + + remote_response = http.request(remote_request) + + remote_response.code + res.body = remote_response.body + res.status = remote_response.code + + remote_response.header.each do |k| + res.header[k] = remote_response.header[k] + end + end + + @mitm_thread = ::Thread.new { @mitm_server.start } + end + + # transforms the webrick header format into the ruby net http format + # webrick: {"agent"=>["blabla"]} + # net http: {"agent"=>"blabla"} + def transform_header(header) + # header.inject({}) do |memo, pair| + # if Array === value + # h[key] = value.first + # else + # h[key] = value + # end + # end + + h = {} + header.each do |key, value| + if ::Array === value + h[key] = value.first + else + h[key] = value + end + end + h + end + + # the proxy tries to just forward SSL connections with a "CONNECT" + # catch that forwarding, and call ssl_mitm + def do_CONNECT(req, res) + host, port = req.unparsed_uri.split(":") + port = 443 unless port + start_ssl_mitm(host, port) + req.unparsed_uri = "127.0.0.1:#{@mitm_port}" + super req, res + end + + def service(req, res) + ::VCR.use_cassette("vcr_proxy_records", :record => :new_episodes) do + super(req, res) + end + end + end + end diff --git a/lib/vcr_proxy/tasks/vcr_proxy.rake b/lib/vcr_proxy/tasks/vcr_proxy.rake new file mode 100644 index 0000000..8467600 --- /dev/null +++ b/lib/vcr_proxy/tasks/vcr_proxy.rake @@ -0,0 +1,17 @@ +namespace :vcr_proxy do + namespace :server do + desc 'Start the VCR Proxy server. Default VCR_PROXY_PORT=9999.' + task :start do + require 'vcr_proxy' + + VCRProxy.start_with_pid + end + + desc 'Stop the VCR Proxy server if tmp/pids/ pid files are found' + task :start do + require 'vcr_proxy' + + VCRProxy.stop_with_pid + end + end +end diff --git a/lib/vcr_proxy/version.rb b/lib/vcr_proxy/version.rb new file mode 100644 index 0000000..0493b8e --- /dev/null +++ b/lib/vcr_proxy/version.rb @@ -0,0 +1,3 @@ +module VCRProxy + VERSION = '0.0.1' +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..86307bf --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1 @@ +require 'vcr_proxy' diff --git a/spec/vcr_proxy_spec.rb b/spec/vcr_proxy_spec.rb new file mode 100644 index 0000000..d0df5f7 --- /dev/null +++ b/spec/vcr_proxy_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe VCRProxy do + before :each do + @server = VCRProxy.start(:Port => 9999) + end + + after :each do + @server.shutdown + end + + it "should work" do + @server.should be_kind_of(VCRProxy::Server) + end +end diff --git a/vcr_proxy.gemspec b/vcr_proxy.gemspec new file mode 100644 index 0000000..2b6c727 --- /dev/null +++ b/vcr_proxy.gemspec @@ -0,0 +1,33 @@ +require File.expand_path("../lib/vcr_proxy/version", __FILE__) + +Gem::Specification.new do |s| + s.name = "vcr_proxy" + s.version = VCRProxy::VERSION + s.platform = Gem::Platform::RUBY + s.authors = [""] + s.email = [""] + s.homepage = "http://github.com/23tux/vcr_proxy" + s.summary = "" + s.description = "" + + s.add_runtime_dependency 'capybara' + s.add_runtime_dependency 'poltergeist' + + s.required_rubygems_version = ">= 1.3.6" + + s.add_development_dependency 'guard', '>= 1.6.2' + s.add_development_dependency 'pry', '>= 0.9.10' + s.add_development_dependency 'pry-doc', '>= 0.4.4' + s.add_development_dependency 'rake', '>= 10.0.4' + s.add_development_dependency 'rspec', '>= 2.13' + + s.require_paths = ['lib'] + + s.files = `git ls-files`.split("\n") + s.test_files = `git ls-files -- {spec}/*`.split("\n") + s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } + + # If you have C extensions, uncomment these: + # s.extensions = "ext/extconf.rb" + # s.require_paths << ['ext'] +end diff --git a/vcr_proxy.rb b/vcr_proxy.rb deleted file mode 100644 index 79429f8..0000000 --- a/vcr_proxy.rb +++ /dev/null @@ -1,119 +0,0 @@ -require 'net/http' -require 'webrick' -require 'webrick/https' -require 'webrick/httpproxy' -require 'vcr' -require 'debugger' - -# enable modifications to unparsed_uri -class WEBrick::HTTPRequest - def unparsed_uri=(str) - @unparsed_uri = str - end -end - -class VCRProxy < WEBrick::HTTPProxyServer - def initialize(options) - super(options) - @mitm_port = options[:MITMPort] || 12322 - end - - # starts the MITM server - def start_ssl_mitm(host, port) - # WORKAROUND for "adress is already in use", just increase - # the port number and kill the old webrick - @mitm_port += 1 - @mitm_server.stop if @mitm_server - @mitm_thread.kill if @mitm_thread - - @mitm_server = WEBrick::HTTPServer.new(:Port => @mitm_port, - :SSLEnable => true, - :SSLVerifyClient => ::OpenSSL::SSL::VERIFY_NONE, - :SSLCertName => [["C", "US"], ["O", host], ["CN", host] ]) - - @mitm_server.mount_proc('/') do |req,res| - method, url, version = req.request_line.split(" ") - - remote_request = case method.upcase - when 'GET' - Net::HTTP::Get.new(req.unparsed_uri) - when 'POST' - Net::HTTP::Post.new(req.unparsed_uri) - when 'PUT' - Net::HTTP::Put.new(req.unparsed_uri) - when 'DELETE' - Net::HTTP::Delete.new(req.unparsed_uri) - when 'HEAD' - Net::HTTP::Head.new(req.unparsed_uri) - when 'OPTIONS' - Net::HTTP::Options.new(req.unparsed_uri) - else - puts "HTTP method '#{method}' not supported!" - end - - remote_request.body = req.body - remote_request.body = req.body - remote_request.initialize_http_header(transform_header(req.header)) - - uri = req.request_uri - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - - remote_response = http.request(remote_request) - - remote_response.code - res.body = remote_response.body - res.status = remote_response.code - - remote_response.header.each do |k| - res.header[k] = remote_response.header[k] - end - end - - @mitm_thread = Thread.new { @mitm_server.start } - end - - # transforms the webrick header format into the ruby net http format - # webrick: {"agent"=>["blabla"]} - # net http: {"agent"=>"blabla"} - def transform_header header - h = {} - header.each do |key, value| - if value.class == Array - h[key] = value.first - else - h[key] = value - end - end - h - end - - # the proxy tries to just forward SSL connections with a "CONNECT" - # catch that forwarding, and call ssl_mitm - def do_CONNECT(req, res) - host, port = req.unparsed_uri.split(":") - port = 443 unless port - start_ssl_mitm(host, port) - req.unparsed_uri = "127.0.0.1:#{@mitm_port}" - super req, res - end - - def service(req, res) - VCR.use_cassette("records") do - super(req, res) - end - end -end - -VCR.configure do |c| - c.hook_into :webmock - c.cassette_library_dir = 'cassettes' - c.default_cassette_options = { :record => :new_episodes } - c.ignore_localhost = true - c.ignore_hosts "127.0.0.1" -end - -server = VCRProxy.new(:Port => 9999) -trap("INT"){ server.shutdown } -server.start