Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intruduce some framework #5

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added CHANGELOG.md
Empty file.
14 changes: 14 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -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
56 changes: 56 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -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
11 changes: 11 additions & 0 deletions Guardfile
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -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.
70 changes: 48 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,83 @@
# 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
```

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
2 changes: 2 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require "bundler"
Bundler::GemHelper.install_tasks
34 changes: 34 additions & 0 deletions bin/vcr_proxy
Original file line number Diff line number Diff line change
@@ -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 }
123 changes: 123 additions & 0 deletions lib/vcr_proxy.rb
Original file line number Diff line number Diff line change
@@ -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
13 changes: 13 additions & 0 deletions lib/vcr_proxy/constants.rb
Original file line number Diff line number Diff line change
@@ -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
Loading