liquid-proxy 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1,9 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ script: "bundle exec rspec --color spec && bundle exec cucumber --format progress"
5
+ branches:
6
+ only:
7
+ - master
8
+ notifications:
9
+ email: false
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in liquid-proxy.gemspec
4
+ gemspec
5
+
6
+ gem 'rspec'
7
+ gem 'cucumber'
8
+ gem 'awesome_print'
9
+ gem 'thin'
10
+ gem 'rake'
@@ -0,0 +1,80 @@
1
+ # Liquid Proxy
2
+
3
+ [![Build status](https://secure.travis-ci.org/artemave/liquid-proxy.png)](https://secure.travis-ci.org/artemave/liquid-proxy)
4
+
5
+ ## Overview
6
+
7
+ Start http proxy that you can control via api in order to inject http headers into requests passing through.
8
+
9
+ Why? I needed it to recreate parts of infrustructure in test environment (namely, the app under test was behind the proxy that takes care of authenticating users and passes on its checks in the form of http header).
10
+
11
+ ## Usage
12
+
13
+ This is a ruby gem and it requires ruby >= 1.9.2.
14
+
15
+ ```ruby
16
+ # Gemfile
17
+ gem 'liquid-proxy'
18
+ ```
19
+
20
+ The following example shows usage with cucumber/capybara/selenium:
21
+
22
+ ```ruby
23
+ # env.rb
24
+ require 'capybara'
25
+ require 'selenium-webdriver'
26
+ require 'liquid-proxy'
27
+
28
+ LiquidProxy.start(:port => 9889)
29
+
30
+ Capybara.configure do |config|
31
+ config.default_driver = :selenium
32
+ config.run_server = false
33
+ config.app_host = "http://test.example.com"
34
+ end
35
+
36
+ Capybara.register_driver :selenium do |app|
37
+ profile = Selenium::WebDriver::Firefox::Profile.new
38
+ profile.proxy = Selenium::WebDriver::Proxy.new(http: 'localhost:9889', type: :manual)
39
+
40
+ Capybara::Selenium::Driver.new(app, profile: profile)
41
+ end
42
+
43
+ Before do
44
+ LiquidProxy.clear
45
+ end
46
+ ```
47
+
48
+ Then in step definitions you can:
49
+
50
+ ```ruby
51
+ # set headers to inject
52
+ LiquidProxy.headers_to_inject = {'Foo' => 'Bar', 'Accept' => 'Cash'}
53
+
54
+ # add to headers to inject
55
+ LiquidProxy.headers_to_inject['X_HACKERY'] = 'BOOM'
56
+
57
+ # clear headers to inject
58
+ LiquidProxy.clear
59
+ ```
60
+
61
+ ## Standalone usage
62
+
63
+ If you are not using ruby, it is possible to run liquid-proxy as a standalone process and controll it via REST api:
64
+
65
+ # install
66
+ bash$ gem install liquid-proxy
67
+
68
+ # start
69
+ bash$ liquid-proxy 8998
70
+
71
+ # add headers
72
+ bash$ curl --data-binary '{"Foo":"Bar","Accept":"Cash"}' http://localhost:8998
73
+
74
+ # clear headers
75
+ bash$ curl -X DELETE http://localhost:8998
76
+
77
+
78
+ ## Author
79
+
80
+ [Artem Avetisyan](https://github.com/artemave)
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.expand_path('../../lib', __FILE__)
4
+
5
+ require 'rubygems'
6
+ require 'em-proxy'
7
+ require 'liquid-proxy/connection'
8
+
9
+ LIQUID_PROXY_PORT = ARGV[0] || 8998
10
+ HEADERS_TO_INJECT = {}
11
+
12
+ Proxy.start(:host => 'localhost', :port => LIQUID_PROXY_PORT) do |conn|
13
+ LiquidProxy::Connection.setup(conn)
14
+ end
@@ -0,0 +1,18 @@
1
+ Feature: change HTTP headers
2
+ In order to create test scenarios that involve changing HTTP headers
3
+ As developer in test
4
+ I want to run the app under test via API controllable proxy
5
+ that can change headers of requests passing through
6
+
7
+ Background:
8
+ Given liquid-proxy is running
9
+ When I instruct liquid-proxy to add header "BOOM" with value "KABOOM"
10
+ And an http client makes request to a server via liquid-proxy
11
+
12
+ Scenario: add HTTP header
13
+ Then that server should see header "BOOM" with value "KABOOM" in incoming requests
14
+
15
+ Scenario: clear custom headers
16
+ When I instruct liquid-proxy to clear custom headers
17
+ And an http client makes request to a server via liquid-proxy
18
+ Then that server should not see header "BOOM" in incoming requests
@@ -0,0 +1,27 @@
1
+ Given /^liquid-proxy is running$/ do
2
+ LiquidProxy.start(:port => 9889)
3
+ end
4
+
5
+ When /^I instruct liquid\-proxy to add header "([^"]*)" with value "([^"]*)"$/ do |header, value|
6
+ LiquidProxy.headers_to_inject[header] = value
7
+ end
8
+
9
+ When /^an http client makes request to a server via liquid-proxy$/ do
10
+ proxy_class = Net::HTTP::Proxy('localhost', LiquidProxy.port)
11
+ res = proxy_class.start("localhost", TEST_APP_SERVER_PORT) do |http|
12
+ http.get "/something"
13
+ end
14
+ @request = JSON.parse(res.body)
15
+ end
16
+
17
+ Then /^that server should see header "([^"]*)" with value "([^"]*)" in incoming requests$/ do |header, value|
18
+ @request["HTTP_#{header}"].should == value
19
+ end
20
+
21
+ When /^I instruct liquid\-proxy to clear custom headers$/ do
22
+ LiquidProxy.headers_to_inject.clear
23
+ end
24
+
25
+ Then /^that server should not see header "([^"]*)" in incoming requests$/ do |header|
26
+ @request.should_not have_key("HTTP_#{header}")
27
+ end
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+ require "rack"
6
+ require 'json'
7
+
8
+ app = Proc.new do |env|
9
+ resp = env.select do |k,v|
10
+ v.respond_to?(:to_s)
11
+ end
12
+
13
+ [200, {"Content-Type" => "application/json"}, [resp.to_json]]
14
+ end
15
+
16
+ Rack::Handler::Thin.run(app, {:Host => "0.0.0.0", :Port => ARGV[0]})
@@ -0,0 +1,35 @@
1
+ $: << File.expand_path("../../../lib", __FILE__)
2
+ require 'liquid-proxy'
3
+ require 'net/http'
4
+ require 'childprocess'
5
+ require 'awesome_print'
6
+ require 'json'
7
+
8
+ TEST_APP_SERVER_PORT=8999
9
+
10
+ def start_test_appserver
11
+ appserver = ChildProcess.build(File.expand_path("../bin/appserver", __FILE__), TEST_APP_SERVER_PORT.to_s)
12
+ appserver.io.inherit!
13
+ appserver.start
14
+
15
+ while true
16
+ begin
17
+ Net::HTTP.get "localhost", '/', TEST_APP_SERVER_PORT
18
+ break
19
+ rescue Errno::ECONNREFUSED
20
+ puts "Waiting for test appserver to start..."
21
+ sleep 0.5
22
+ end
23
+ end
24
+
25
+ def self.up?
26
+ RestClient.get server_address
27
+ rescue => e
28
+ !e.is_a?(Errno::ECONNREFUSED)
29
+ end
30
+ at_exit do
31
+ appserver.stop
32
+ end
33
+ end
34
+
35
+ start_test_appserver
@@ -0,0 +1,55 @@
1
+ require 'singleton'
2
+ require 'liquid-proxy/service'
3
+ require 'json'
4
+ require 'net/http'
5
+
6
+ class LiquidProxy
7
+ include Singleton
8
+
9
+ class HeadersToInject
10
+ def []=(key, value)
11
+ http.post '/', {key => value}.to_json
12
+ end
13
+
14
+ def set_hash(hash = {})
15
+ clear
16
+ http.post '/', hash.to_json unless hash.empty?
17
+ end
18
+
19
+ def clear
20
+ http.delete '/'
21
+ end
22
+
23
+ private
24
+
25
+ def http
26
+ @http ||= Net::HTTP.new('127.0.0.1', LiquidProxy.port)
27
+ end
28
+ end
29
+
30
+ attr_reader :port
31
+
32
+ def headers_to_inject
33
+ @headers_to_inject ||= HeadersToInject.new
34
+ end
35
+
36
+ def headers_to_inject=(hash = {})
37
+ headers_to_inject.set_hash(hash)
38
+ end
39
+
40
+ def start(opts = {:port => 8998})
41
+ @port = opts[:port]
42
+
43
+ Service.start(opts)
44
+
45
+ Kernel.sleep 0.5
46
+ while not Service.up?
47
+ puts "Waiting for LiquidProxy to start..."
48
+ Kernel.sleep 0.5
49
+ end
50
+ end
51
+
52
+ def self.method_missing(*args)
53
+ instance.send(*args)
54
+ end
55
+ end
@@ -0,0 +1,23 @@
1
+ require 'json'
2
+ require 'http_tools'
3
+
4
+ class LiquidProxy
5
+ module ApiController
6
+ def process_api_call
7
+ if parser.http_method =~ /delete/i
8
+ headers_to_inject.clear
9
+ else
10
+ new_headers = JSON.parse(body) rescue {}
11
+ headers_to_inject.merge!(new_headers)
12
+ end
13
+
14
+ send_data HTTPTools::Builder.response(:ok)
15
+ close_connection_after_writing
16
+ end
17
+
18
+ def api_call?
19
+ host, port = parser.headers['Host'].split(':')
20
+ host =~ /^(localhost|127.0.0.1)$/ && port == ::LIQUID_PROXY_PORT.to_s
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ require 'liquid-proxy/connection_processor'
2
+
3
+ class LiquidProxy
4
+ class Connection
5
+ def self.setup(conn)
6
+ conn.extend(ConnectionProcessor)
7
+
8
+ conn.on_data do |data|
9
+ conn.process_data(data)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,47 @@
1
+ require 'http/parser'
2
+ require 'liquid-proxy/server_relay'
3
+ require 'liquid-proxy/request_builder'
4
+ require 'liquid-proxy/api_controller'
5
+
6
+ class LiquidProxy
7
+ module ConnectionProcessor
8
+ include ServerRelay
9
+ include ApiController
10
+
11
+ def process_data(data)
12
+ parser << data
13
+ end
14
+
15
+ def parser
16
+ @parser ||= Http::Parser.new(self)
17
+ end
18
+
19
+ def body
20
+ @body ||= ''
21
+ end
22
+
23
+ def on_body(chunk)
24
+ body << chunk
25
+ end
26
+
27
+ def request_builder
28
+ @request_builder ||= RequestBuilder.new
29
+ end
30
+
31
+ def on_message_complete
32
+ if api_call?
33
+ process_api_call
34
+ else
35
+ parser.headers.merge!(headers_to_inject)
36
+
37
+ new_request = request_builder.build(parser, body)
38
+ body.clear
39
+ pass_to_server new_request
40
+ end
41
+ end
42
+
43
+ def headers_to_inject
44
+ @headers_to_inject ||= HEADERS_TO_INJECT
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,15 @@
1
+ require 'http_tools'
2
+
3
+ class LiquidProxy
4
+ class RequestBuilder
5
+ def build(parser, body = '')
6
+ new_request = HTTPTools::Builder.request(
7
+ parser.http_method,
8
+ parser.headers['Host'],
9
+ parser.request_url,
10
+ parser.headers
11
+ )
12
+ new_request+= body
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ class LiquidProxy
2
+ module ServerRelay
3
+ def pass_to_server request
4
+ host, port = parser.headers['Host'].split(':')
5
+ server :test, :host => host, :port => (port || 80).to_i
6
+ relay_to_servers request
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,27 @@
1
+ require 'singleton'
2
+ require 'liquid-proxy/subprocess'
3
+ require 'utils/port_explorer'
4
+
5
+ class LiquidProxy
6
+ class Service
7
+ include Singleton
8
+
9
+ def start(opts = {})
10
+ if @child and @child.alive?
11
+ return
12
+ end
13
+
14
+ @port = opts[:port]
15
+
16
+ @child = Subprocess.new(opts)
17
+ end
18
+
19
+ def up?
20
+ !!(@child && @child.alive? && Utils::PortExplorer.port_occupied?(@port))
21
+ end
22
+
23
+ def self.method_missing(*args)
24
+ instance.send(*args)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ require 'childprocess'
2
+
3
+ class LiquidProxy
4
+ class Subprocess
5
+ def initialize(opts = {})
6
+ @child = ChildProcess.build(File.expand_path("../../../bin/liquid-proxy", __FILE__), opts[:port].to_s || "8998")
7
+ @child.io.inherit!
8
+ @child.start
9
+
10
+ at_exit do
11
+ @child.stop
12
+ end
13
+ end
14
+
15
+ def alive?
16
+ @child.alive?
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ class LiquidProxy
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,13 @@
1
+ require 'socket'
2
+
3
+ module Utils
4
+ class PortExplorer
5
+ def self.port_occupied?(port)
6
+ !!Net::HTTP.get('127.0.0.1', '/', port)
7
+ rescue => e
8
+ !e.is_a?(Errno::ECONNREFUSED)
9
+ else
10
+ true
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "liquid-proxy/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "liquid-proxy"
7
+ s.version = LiquidProxy::VERSION
8
+ s.authors = ["artemave"]
9
+ s.email = ["artemave@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{http proxy with api for modifying requests passing through}
12
+ #s.description = %q{http proxy with api for modifying requests passing through}
13
+
14
+ s.rubyforge_project = "liquid-proxy"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_runtime_dependency "em-proxy"
23
+ s.add_runtime_dependency "http_tools"
24
+ s.add_runtime_dependency "http_parser.rb"
25
+ s.add_runtime_dependency 'childprocess'
26
+ end
@@ -0,0 +1,51 @@
1
+ require_relative "spec_helper"
2
+ require 'liquid-proxy/api_controller'
3
+
4
+ describe LiquidProxy::ApiController do
5
+ let :conn do
6
+ o = Object.new
7
+ o.extend(LiquidProxy::ApiController)
8
+ o
9
+ end
10
+
11
+ it 'knows when request is an api call' do
12
+ LIQUID_PROXY_PORT = 9876 unless defined?(LIQUID_PROXY_PORT)
13
+
14
+ conn.stub_chain('parser.headers').and_return('Host' => "localhost:#{LIQUID_PROXY_PORT}")
15
+ conn.api_call?.should == true
16
+ end
17
+
18
+ it 'knows when request is NOT an api call' do
19
+ conn.stub_chain('parser.headers').and_return('Host' => "localhost:1111")
20
+ conn.api_call?.should == false
21
+ end
22
+
23
+ it 'adds headers to inject' do
24
+ new_headers = {'X_HACK' => 'Boom'}
25
+ conn.stub(:send_data => nil, :close_connection_after_writing => nil, :parser => stub.as_null_object)
26
+ conn.stub(:body => new_headers.to_json)
27
+ conn.stub(:headers_to_inject => (headers_to_inject = mock))
28
+
29
+ headers_to_inject.should_receive(:merge!).with(new_headers)
30
+ conn.process_api_call
31
+ end
32
+
33
+ it 'clears off headers to inject if api request method is DELETE' do
34
+ conn.stub(:send_data => nil, :close_connection_after_writing => nil)
35
+ conn.stub_chain('parser.http_method').and_return('DELETE')
36
+ conn.stub(:headers_to_inject => (headers_to_inject = mock))
37
+ headers_to_inject.should_receive(:clear)
38
+ conn.process_api_call
39
+ end
40
+
41
+ it 'relays back to client' do
42
+ response = "response_#{Time.now}"
43
+ HTTPTools::Builder.stub(:response).with(:ok).and_return(response)
44
+ conn.stub(:headers_to_inject => stub.as_null_object, :parser => stub.as_null_object)
45
+
46
+ conn.should_receive(:send_data).with(response).ordered
47
+ conn.should_receive(:close_connection_after_writing).ordered
48
+
49
+ conn.process_api_call
50
+ end
51
+ end
@@ -0,0 +1,60 @@
1
+ require_relative "spec_helper"
2
+ require 'liquid-proxy/connection_processor'
3
+ require 'liquid-proxy/server_relay'
4
+ require 'liquid-proxy/api_controller'
5
+
6
+ describe LiquidProxy::ConnectionProcessor do
7
+ let :conn do
8
+ o = Object.new
9
+ o.extend(LiquidProxy::ConnectionProcessor)
10
+ o.extend(LiquidProxy::ServerRelay)
11
+ o.extend(LiquidProxy::ApiController)
12
+ o
13
+ end
14
+
15
+ it 'feeds connection data to parser' do
16
+ data = 'data'
17
+ Http::Parser.stub(:new).and_return(parser = mock)
18
+ parser.should_receive(:<<).with(data)
19
+ conn.process_data(data)
20
+ end
21
+
22
+ it 'passes request through' do
23
+ HEADERS_TO_INJECT = {} unless defined?(HEADERS_TO_INJECT)
24
+ new_request = "new_request#{Time.now}"
25
+ parser = stub(:headers => {'Host' => 'localhost'})
26
+ conn.stub(
27
+ :body => (body = mock),
28
+ :parser => parser,
29
+ :request_builder => (request_builder = stub),
30
+ :api_call? => false
31
+ )
32
+ request_builder.stub(:build).with(conn.parser, conn.body).and_return(new_request)
33
+
34
+ body.should_receive(:clear)
35
+ conn.should_receive(:pass_to_server).with(new_request)
36
+
37
+ conn.on_message_complete
38
+ end
39
+
40
+ it 'adds custom headers to requests passing through' do
41
+ headers_to_inject = {'blah' => 'val1', 'boom' => 'val2'}
42
+
43
+ conn.stub(:server => nil, :relay_to_servers => nil, :api_call? => false)
44
+ conn.stub(:headers_to_inject => headers_to_inject)
45
+ conn.parser.stub(:headers).and_return(headers = mock.as_null_object)
46
+
47
+ headers.should_receive(:merge!).with(headers_to_inject)
48
+
49
+ conn.on_message_complete
50
+ end
51
+
52
+ it 'treats requests to itself as API calls' do
53
+ conn.stub(:server => nil, :relay_to_servers => nil)
54
+ conn.stub(:api_call? => true)
55
+
56
+ conn.should_receive(:process_api_call)
57
+ conn.should_not_receive(:pass_to_server)
58
+ conn.on_message_complete
59
+ end
60
+ end
@@ -0,0 +1,17 @@
1
+ require_relative "spec_helper"
2
+ require 'liquid-proxy/connection'
3
+ require 'liquid-proxy/connection_processor'
4
+
5
+ describe LiquidProxy::Connection do
6
+ context "em-proxy connection setup" do
7
+ it 'feeds data to connection processor' do
8
+ conn = mock
9
+ conn.should_receive(:extend).with(LiquidProxy::ConnectionProcessor) # this assert stinks?
10
+ conn.should_receive(:on_data) do |&block|
11
+ conn.should_receive(:process_data).with("data")
12
+ block.call("data")
13
+ end
14
+ LiquidProxy::Connection.setup(conn)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,59 @@
1
+ require_relative "spec_helper"
2
+ require 'liquid-proxy'
3
+
4
+ describe LiquidProxy do
5
+ before do
6
+ LiquidProxy::Service.reset_instance
7
+ LiquidProxy.reset_instance
8
+ end
9
+
10
+ it 'starts' do
11
+ LiquidProxy::Service.stub(:up?).and_return(true)
12
+ LiquidProxy::Service.should_receive(:start).with(:port => 1234)
13
+
14
+ LiquidProxy.start(:port => 1234)
15
+ end
16
+
17
+ it 'knows its port' do
18
+ LiquidProxy::Service.stub(:up?).and_return(true)
19
+ LiquidProxy::Service.stub(:start)
20
+
21
+ LiquidProxy.start(:port => 1234)
22
+ LiquidProxy.port.should == 1234
23
+ end
24
+
25
+ context 'when starting' do
26
+ it 'waits for proxy service to come up' do
27
+ LiquidProxy::Service.stub(:start)
28
+ LiquidProxy::Service.stub(:up?).and_return(false, false, true)
29
+ Kernel.should_receive(:sleep).with(0.5).exactly(3).times
30
+
31
+ LiquidProxy.start
32
+ end
33
+ end
34
+
35
+ context 'header injection' do
36
+ let :headers_to_inject do
37
+ mock('headers_to_inject')
38
+ end
39
+
40
+ before do
41
+ LiquidProxy::HeadersToInject.stub(:new).and_return(headers_to_inject)
42
+ end
43
+
44
+ it 'allows specify header as hash key' do
45
+ headers_to_inject.should_receive(:[]=).with('X_HACK', 'KABOOM')
46
+ LiquidProxy.headers_to_inject['X_HACK'] = 'KABOOM'
47
+ end
48
+
49
+ it 'allows specify headers by assigning hash' do
50
+ headers_to_inject.should_receive(:set_hash).with('X_HACK' => 'KABOOM')
51
+ LiquidProxy.headers_to_inject = {'X_HACK' => 'KABOOM'}
52
+ end
53
+
54
+ it 'clears off list headers to inject' do
55
+ headers_to_inject.should_receive(:clear)
56
+ LiquidProxy.headers_to_inject.clear
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,12 @@
1
+ require_relative "spec_helper"
2
+ require 'liquid-proxy/request_builder'
3
+
4
+ describe LiquidProxy::RequestBuilder do
5
+ it 'builds HTTP request' do
6
+ body = '_with_body'
7
+ parser = stub(:http_method => 'method', :headers => {'Host' => 'host'}, :request_url => 'url')
8
+ HTTPTools::Builder.stub(:request).with('method', 'host', 'url', {'Host' => 'host'}).and_return('new_request')
9
+
10
+ LiquidProxy::RequestBuilder.new.build(parser, body).should == 'new_request_with_body'
11
+ end
12
+ end
@@ -0,0 +1,26 @@
1
+ require_relative "spec_helper"
2
+ require 'liquid-proxy/server_relay'
3
+
4
+ describe LiquidProxy::ServerRelay do
5
+ let :conn do
6
+ Object.new.extend(LiquidProxy::ServerRelay)
7
+ end
8
+
9
+ it 'relays to server usging host and port from incoming request' do
10
+ host, port = 'remotehost', 9876
11
+ conn.stub(:parser => stub(:headers => {'Host' => "#{host}:#{port}"}))
12
+
13
+ conn.should_receive(:server).with(anything, :host => host, :port => port).ordered
14
+ conn.should_receive(:relay_to_servers).with('new_request').ordered
15
+
16
+ conn.pass_to_server('new_request')
17
+ end
18
+
19
+ it 'uses port 80 by default' do
20
+ conn.stub(:relay_to_servers => nil, :parser => stub(:headers => {'Host' => "localhost"}))
21
+
22
+ conn.should_receive(:server).with(anything, :host => 'localhost', :port => 80)
23
+
24
+ conn.pass_to_server('new_request')
25
+ end
26
+ end
@@ -0,0 +1,62 @@
1
+ require_relative 'spec_helper'
2
+ require 'utils/port_explorer'
3
+ require 'liquid-proxy/service'
4
+
5
+ describe LiquidProxy::Service do
6
+ before do
7
+ LiquidProxy::Service.reset_instance
8
+ end
9
+
10
+ context 'when started' do
11
+ it 'starts subprocess' do
12
+ opts = {:host => 'localhost'}
13
+
14
+ LiquidProxy::Subprocess.should_receive(:new).with(opts)
15
+ LiquidProxy::Service.start(opts)
16
+ end
17
+
18
+ it 'does nothing if subprocess already running' do
19
+ LiquidProxy::Subprocess.should_receive(:new).once.and_return(stub(:alive? => true))
20
+
21
+ LiquidProxy::Service.start
22
+ LiquidProxy::Service.start
23
+ end
24
+ end
25
+
26
+ it 'knows when it is up if subprocess is alive and service port is occupied' do
27
+ port = 1234
28
+ LiquidProxy::Subprocess.stub(:new).and_return(sp = stub(:alive? => true))
29
+
30
+ sp.should_receive(:alive?)
31
+ Utils::PortExplorer.should_receive(:port_occupied?).with(port).and_return(true)
32
+
33
+ LiquidProxy::Service.start(:port => port)
34
+ LiquidProxy::Service.up?.should == true
35
+ end
36
+
37
+ context 'it knows that it is NOT up' do
38
+ it 'if it has not been started' do
39
+ LiquidProxy::Service.up?.should == false
40
+ end
41
+
42
+ it 'if subprocess is not alive' do
43
+ LiquidProxy::Subprocess.stub(:new).and_return(sp = stub(:alive? => false))
44
+
45
+ sp.should_receive(:alive?)
46
+ LiquidProxy::Service.start
47
+ LiquidProxy::Service.up?.should == false
48
+ end
49
+
50
+ it 'if service port is not occupied' do
51
+ port = 1234
52
+ LiquidProxy::Subprocess.stub(:new).and_return(sp = stub(:alive? => true))
53
+
54
+ sp.should_receive(:alive?)
55
+ Utils::PortExplorer.should_receive(:port_occupied?).with(port).and_return(false)
56
+
57
+ LiquidProxy::Service.start(:port => port)
58
+ LiquidProxy::Service.up?.should == false
59
+ end
60
+ end
61
+
62
+ end
@@ -0,0 +1,17 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper.rb"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+
8
+
9
+ $: << File.expand_path("../../../lib", __FILE__)
10
+
11
+ require_relative 'support/reset-singleton'
12
+
13
+ RSpec.configure do |config|
14
+ config.treat_symbols_as_metadata_keys_with_true_values = true
15
+ config.run_all_when_everything_filtered = true
16
+ config.filter_run :focus
17
+ end
@@ -0,0 +1,49 @@
1
+ require 'tempfile'
2
+ require_relative 'spec_helper'
3
+ require 'liquid-proxy/subprocess'
4
+
5
+ describe LiquidProxy::Subprocess do
6
+ let :child do
7
+ double.as_null_object
8
+ end
9
+
10
+ before do
11
+ ChildProcess.stub(:build).and_return(child)
12
+ end
13
+
14
+ context 'ChildProcess start' do
15
+ it 'builds ChildProcess with cmd args from opts' do
16
+ opts = {:port => 1234}
17
+ ChildProcess.should_receive(:build) do |cmd, *params|
18
+ cmd.should =~ %r{bin/liquid-proxy}
19
+ params.should == ["1234"]
20
+ end
21
+ LiquidProxy::Subprocess.new(opts)
22
+ end
23
+ it 'inherits io and then starts child' do
24
+ child.should_receive(:io).ordered.and_return(io = mock)
25
+ io.should_receive(:inherit!)
26
+ child.should_receive(:start).ordered
27
+ LiquidProxy::Subprocess.new
28
+ end
29
+ end
30
+
31
+ it 'knows if it is alive' do
32
+ child.stub(:alive? => true)
33
+ p = LiquidProxy::Subprocess.new
34
+ p.alive?.should == true
35
+ end
36
+
37
+ it 'makes sure ChildProcess does not outlive parent' do
38
+ res_file = Tempfile.new('res')
39
+ child.stub(:stop) do
40
+ res_file.write "stopped"
41
+ res_file.rewind
42
+ end
43
+ fork do
44
+ LiquidProxy::Subprocess.new
45
+ end
46
+ Process.wait
47
+ res_file.read.should == 'stopped'
48
+ end
49
+ end
@@ -0,0 +1,15 @@
1
+ require 'singleton'
2
+
3
+ class <<Singleton
4
+ def included_with_reset(klass)
5
+ included_without_reset(klass)
6
+ class <<klass
7
+ def reset_instance
8
+ Singleton.send :__init__, self
9
+ self
10
+ end
11
+ end
12
+ end
13
+ alias_method :included_without_reset, :included
14
+ alias_method :included, :included_with_reset
15
+ end
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: liquid-proxy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - artemave
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-07 00:00:00.000000000 +00:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: em-proxy
17
+ requirement: &2161476040 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *2161476040
26
+ - !ruby/object:Gem::Dependency
27
+ name: http_tools
28
+ requirement: &2161475620 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *2161475620
37
+ - !ruby/object:Gem::Dependency
38
+ name: http_parser.rb
39
+ requirement: &2161475200 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: *2161475200
48
+ - !ruby/object:Gem::Dependency
49
+ name: childprocess
50
+ requirement: &2161474780 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :runtime
57
+ prerelease: false
58
+ version_requirements: *2161474780
59
+ description:
60
+ email:
61
+ - artemave@gmail.com
62
+ executables:
63
+ - liquid-proxy
64
+ extensions: []
65
+ extra_rdoc_files: []
66
+ files:
67
+ - .gitignore
68
+ - .rspec
69
+ - .travis.yml
70
+ - Gemfile
71
+ - README.markdown
72
+ - Rakefile
73
+ - bin/liquid-proxy
74
+ - features/change_headers.feature
75
+ - features/step_definition/proxy_steps.rb
76
+ - features/support/bin/appserver
77
+ - features/support/env.rb
78
+ - lib/liquid-proxy.rb
79
+ - lib/liquid-proxy/api_controller.rb
80
+ - lib/liquid-proxy/connection.rb
81
+ - lib/liquid-proxy/connection_processor.rb
82
+ - lib/liquid-proxy/request_builder.rb
83
+ - lib/liquid-proxy/server_relay.rb
84
+ - lib/liquid-proxy/service.rb
85
+ - lib/liquid-proxy/subprocess.rb
86
+ - lib/liquid-proxy/version.rb
87
+ - lib/utils/port_explorer.rb
88
+ - liquid-proxy.gemspec
89
+ - spec/api_controller_spec.rb
90
+ - spec/connection_processor_spec.rb
91
+ - spec/connection_spec.rb
92
+ - spec/liquid-proxy_spec.rb
93
+ - spec/request_builder_spec.rb
94
+ - spec/server_relay_spec.rb
95
+ - spec/service_spec.rb
96
+ - spec/spec_helper.rb
97
+ - spec/subprocess_spec.rb
98
+ - spec/support/reset-singleton.rb
99
+ has_rdoc: true
100
+ homepage: ''
101
+ licenses: []
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ! '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ requirements: []
119
+ rubyforge_project: liquid-proxy
120
+ rubygems_version: 1.6.2
121
+ signing_key:
122
+ specification_version: 3
123
+ summary: http proxy with api for modifying requests passing through
124
+ test_files:
125
+ - features/change_headers.feature
126
+ - features/step_definition/proxy_steps.rb
127
+ - features/support/bin/appserver
128
+ - features/support/env.rb
129
+ - spec/api_controller_spec.rb
130
+ - spec/connection_processor_spec.rb
131
+ - spec/connection_spec.rb
132
+ - spec/liquid-proxy_spec.rb
133
+ - spec/request_builder_spec.rb
134
+ - spec/server_relay_spec.rb
135
+ - spec/service_spec.rb
136
+ - spec/spec_helper.rb
137
+ - spec/subprocess_spec.rb
138
+ - spec/support/reset-singleton.rb