Goliath + VCR + EM::HttpRequest = Cheapo Proxy

55 views
Skip to first unread message

Chris Le

unread,
Feb 26, 2012, 8:15:48 PM2/26/12
to Philly.rb
Result of hack-night...

I went to hack night (I think a week ago?) with something quick I
wanted to write: I wanted to take Goliath and VCR gems to create a
quick evented proxy server.

Why?
Let's say I'm writing something in NodeJS or piece in PHP that talks
to something else on the web. I simply want to have a transparent
proxy that would record the first response from the web and play it
back later. I didn't want any fancy pants stuff....

There's a nice example in Goliath that records stuff to MongoDB. But
I wanted something dumber... VCR!

The fine folks over at NeoMind quickly helped me out throwing some
ideas in the air, but I had to leave early. I finally got back to
it.. and here it is. It's as bare bones as it can get. But it
works.

Now a few lines later... now it records to YML files - plain old text
files. Now, I can hand craft server responses, break stuff on
purpose, make up my own responses in a text editor, etc. EASY.

I have to prefix my URLs with "http://localhost:9000/url/" every
time.. but I can do that easily in whatever project I need to work in.

#!/usr/bin/env ruby
#
# > ruby HttpLog.rb -sv
# > curl http://localhost:9000/url/http://www.google.com
# --> Proxies Google page for real ... Stores in VCR yml file
# > curl http://localhost:9000/url/http://www.google.com
# --> Returns yml file from VCR instead

require 'rubygems'
require 'goliath'
require 'em-http'
require 'em-synchrony/em-http'
require 'pp'
require 'vcr'

VCR.configure do |c|
c.cassette_library_dir = 'cassettes'
c.hook_into :webmock
end

class HttpLog < Goliath::API
use Goliath::Rack::Params

def on_headers(env, headers)
env.logger.info 'proxying new request: ' + headers.inspect
env['client-headers'] = headers
end

def response(env)
VCR.use_cassette('http_log') do
start_time = Time.now.to_f

params = {:head => env['client-headers'], :query => env.params}

req =
EM::HttpRequest.new(env[Goliath::Request::REQUEST_PATH].sub(/^\/url
\//, ''))
resp = case(env[Goliath::Request::REQUEST_METHOD])
when 'GET' then req.get(params)
when 'POST' then req.post(params.merge(:body =>
env[Goliath::Request::RACK_INPUT].read))
when 'HEAD' then req.head(params)
else p "UNKNOWN METHOD
#{env[Goliath::Request::REQUEST_METHOD]}"
end

process_time = Time.now.to_f - start_time

response_headers = {}
resp.response_header.each_pair do |k, v|
response_headers[to_http_header(k)] = v
end

[resp.response_header.status, response_headers, resp.response]
end
end

def to_http_header(k)
k.downcase.split('_').collect { |e| e.capitalize }.join('-')
end

end
Reply all
Reply to author
Forward
0 new messages