agile-proxy-jruby 0.1.25-jruby
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.bowerrc +3 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.rubocop.yml +36 -0
- data/.travis.yml +10 -0
- data/Gemfile +4 -0
- data/Guardfile +20 -0
- data/LICENSE +22 -0
- data/README.md +131 -0
- data/Rakefile +15 -0
- data/agile-proxy.gemspec +60 -0
- data/assets/index.html +39 -0
- data/assets/ui/app/AgileProxyApi.js +31 -0
- data/assets/ui/app/app.js +1 -0
- data/assets/ui/app/controller/Stubs.js +64 -0
- data/assets/ui/app/controller/main.js +12 -0
- data/assets/ui/app/directive/AppEnhancedFormElement.js +21 -0
- data/assets/ui/app/directive/AppFor.js +16 -0
- data/assets/ui/app/directive/AppResponseEditor.js +54 -0
- data/assets/ui/app/model/RequestSpec.js +6 -0
- data/assets/ui/app/routes.js +11 -0
- data/assets/ui/app/service/Dialog.js +49 -0
- data/assets/ui/app/service/DomId.js +10 -0
- data/assets/ui/app/service/Error.js +7 -0
- data/assets/ui/app/service/Stub.js +36 -0
- data/assets/ui/app/view/404.html +2 -0
- data/assets/ui/app/view/dialog/error.html +10 -0
- data/assets/ui/app/view/dialog/yesNo.html +8 -0
- data/assets/ui/app/view/responses/editForm.html +78 -0
- data/assets/ui/app/view/status.html +1 -0
- data/assets/ui/app/view/stubs.html +19 -0
- data/assets/ui/app/view/stubs/edit.html +58 -0
- data/assets/ui/css/main.css +3 -0
- data/bin/agile_proxy +4 -0
- data/bower.json +27 -0
- data/config.yml +6 -0
- data/db.yml +10 -0
- data/db/migrations/20140818110800_create_users.rb +9 -0
- data/db/migrations/20140818134700_create_applications.rb +10 -0
- data/db/migrations/20140818135200_create_request_specs.rb +13 -0
- data/db/migrations/20140821115300_create_responses.rb +14 -0
- data/db/migrations/20140823082900_add_method_to_request_specs.rb +7 -0
- data/db/migrations/20140823083900_rename_request_spec_columns.rb +8 -0
- data/db/migrations/20141031072100_add_url_type_to_request_specs.rb +8 -0
- data/db/migrations/20141105125600_add_conditions_to_request_specs.rb +7 -0
- data/db/migrations/20141106083100_add_username_and_password_to_applications.rb +8 -0
- data/db/migrations/20141119143800_add_record_to_applications.rb +7 -0
- data/db/migrations/20141119174300_create_recordings.rb +18 -0
- data/db/migrations/20150221152500_add_record_requests_to_request_specs.rb +7 -0
- data/db/schema.rb +78 -0
- data/db/seed.rb +26 -0
- data/echo_server.rb +19 -0
- data/examples/README.md +1 -0
- data/examples/facebook_api.html +59 -0
- data/examples/tumblr_api.html +22 -0
- data/lib/agile_proxy.rb +8 -0
- data/lib/agile_proxy/api/applications.rb +77 -0
- data/lib/agile_proxy/api/recordings.rb +52 -0
- data/lib/agile_proxy/api/request_spec_recordings.rb +52 -0
- data/lib/agile_proxy/api/request_specs.rb +86 -0
- data/lib/agile_proxy/api/root.rb +45 -0
- data/lib/agile_proxy/cli.rb +116 -0
- data/lib/agile_proxy/config.rb +66 -0
- data/lib/agile_proxy/handlers/handler.rb +43 -0
- data/lib/agile_proxy/handlers/proxy_handler.rb +111 -0
- data/lib/agile_proxy/handlers/request_handler.rb +75 -0
- data/lib/agile_proxy/handlers/stub_handler.rb +146 -0
- data/lib/agile_proxy/mitm.crt +22 -0
- data/lib/agile_proxy/mitm.key +27 -0
- data/lib/agile_proxy/model/application.rb +20 -0
- data/lib/agile_proxy/model/recording.rb +17 -0
- data/lib/agile_proxy/model/request_spec.rb +48 -0
- data/lib/agile_proxy/model/response.rb +51 -0
- data/lib/agile_proxy/model/user.rb +17 -0
- data/lib/agile_proxy/proxy_connection.rb +112 -0
- data/lib/agile_proxy/rack/get_only_cache.rb +30 -0
- data/lib/agile_proxy/route.rb +106 -0
- data/lib/agile_proxy/router.rb +99 -0
- data/lib/agile_proxy/server.rb +119 -0
- data/lib/agile_proxy/servers/api.rb +40 -0
- data/lib/agile_proxy/servers/request_spec.rb +40 -0
- data/lib/agile_proxy/servers/request_spec_direct.rb +35 -0
- data/lib/agile_proxy/version.rb +6 -0
- data/load_proxy.js +39 -0
- data/log/.gitkeep +0 -0
- data/spec/common_helper.rb +32 -0
- data/spec/fixtures/example_static_file.html +1 -0
- data/spec/fixtures/test-server.crt +15 -0
- data/spec/fixtures/test-server.key +15 -0
- data/spec/integration/helpers/request_spec_helper.rb +84 -0
- data/spec/integration/specs/lib/server_spec.rb +474 -0
- data/spec/integration_spec_helper.rb +16 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/test_server.rb +105 -0
- data/spec/unit/agile_proxy/api/applications_spec.rb +102 -0
- data/spec/unit/agile_proxy/api/common_helper.rb +31 -0
- data/spec/unit/agile_proxy/api/recordings_spec.rb +115 -0
- data/spec/unit/agile_proxy/api/request_spec_recordings_spec.rb +119 -0
- data/spec/unit/agile_proxy/api/request_specs_spec.rb +159 -0
- data/spec/unit/agile_proxy/handlers/handler_spec.rb +8 -0
- data/spec/unit/agile_proxy/handlers/proxy_handler_spec.rb +138 -0
- data/spec/unit/agile_proxy/handlers/request_handler_spec.rb +76 -0
- data/spec/unit/agile_proxy/handlers/stub_handler_spec.rb +177 -0
- data/spec/unit/agile_proxy/model/recording_spec.rb +0 -0
- data/spec/unit/agile_proxy/model/request_spec_spec.rb +45 -0
- data/spec/unit/agile_proxy/model/response_spec.rb +38 -0
- data/spec/unit/agile_proxy/server_spec.rb +91 -0
- data/spec/unit/agile_proxy/servers/api_spec.rb +35 -0
- data/spec/unit/agile_proxy/servers/request_spec_direct_spec.rb +51 -0
- data/spec/unit/agile_proxy/servers/request_spec_spec.rb +35 -0
- metadata +736 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'flavour_saver'
|
2
|
+
require 'flavour_saver/runtime'
|
3
|
+
module AgileProxy
|
4
|
+
#
|
5
|
+
# = The associated response for the RequestSpec
|
6
|
+
#
|
7
|
+
# An instance of this class is expected to be stored alongside every RequestSpec.
|
8
|
+
#
|
9
|
+
# It is responsible for the following :-
|
10
|
+
#
|
11
|
+
# 1. Retrieving response
|
12
|
+
# 2. Persisting responses
|
13
|
+
# 3. Providing a 'rack' ouput of the response
|
14
|
+
# 4. Convenient setters for code, body and content_type
|
15
|
+
# 5. Template parsing
|
16
|
+
class Response < ActiveRecord::Base
|
17
|
+
PROTECTED_HEADERS = ['Content-Type']
|
18
|
+
has_many :request_specs
|
19
|
+
serialize :headers, JSON
|
20
|
+
# A convenient setter for the content_type within the header
|
21
|
+
# @param val [String] The content type
|
22
|
+
def content_type=(val)
|
23
|
+
write_attribute(:content_type, val)
|
24
|
+
headers.merge!('Content-Type' => val)
|
25
|
+
val
|
26
|
+
end
|
27
|
+
# Provides the response as a 'rack' response
|
28
|
+
#
|
29
|
+
# If the response is a template (by specifying is_template as true), the output will
|
30
|
+
# have its template values parsed and replaced with
|
31
|
+
# data from the input_params, input_headers and input_body
|
32
|
+
# Otherwise, the body of the output is sent as is.
|
33
|
+
# @param input_params [Hash] The input parameters as a hash
|
34
|
+
# @param _input_headers [Hash] The input headers as a hash
|
35
|
+
# @param _input_body [String] The input body
|
36
|
+
# @return [Array] A 'rack' response array (status, headers, body)
|
37
|
+
def to_rack(input_params, _input_headers, _input_body)
|
38
|
+
output_headers = headers.clone
|
39
|
+
output_headers.merge! 'Cache-Control' => 'no-store'
|
40
|
+
output_content = content
|
41
|
+
output_status_code = status_code
|
42
|
+
if is_template
|
43
|
+
data = OpenStruct.new input_params
|
44
|
+
template = Tilt['handlebars'].new { output_content }
|
45
|
+
output_content = template.render data
|
46
|
+
end
|
47
|
+
EventMachine::Synchrony.sleep(delay) if delay > 0
|
48
|
+
[output_status_code, output_headers, [output_content]]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative 'application'
|
2
|
+
require 'active_record'
|
3
|
+
module AgileProxy
|
4
|
+
#
|
5
|
+
# = An API User
|
6
|
+
#
|
7
|
+
# The API access to the system is multi user in that each user
|
8
|
+
# can have many applications and each application can have many stubs etc...
|
9
|
+
#
|
10
|
+
# This class is responsible for :-
|
11
|
+
# 1. Retrieving users from storage
|
12
|
+
# 2. Persisting users to storage
|
13
|
+
# 3. Providing a list of applications that the user owns
|
14
|
+
class User < ActiveRecord::Base
|
15
|
+
has_many :applications
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'stringio'
|
4
|
+
require 'rack'
|
5
|
+
require 'goliath/request'
|
6
|
+
require 'goliath/env'
|
7
|
+
|
8
|
+
module AgileProxy
|
9
|
+
#
|
10
|
+
# = The Proxy Connection
|
11
|
+
#
|
12
|
+
# This class is the event machine connection used by the proxy. Every request creates a new instance of this
|
13
|
+
class ProxyConnection < Goliath::Connection
|
14
|
+
def logger
|
15
|
+
::AgileProxy.config.logger
|
16
|
+
end
|
17
|
+
def post_init
|
18
|
+
super
|
19
|
+
#@proxy_parser = Http::Parser.new(self)
|
20
|
+
@proxy_parser = Http::Parser.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def receive_data(data)
|
24
|
+
#@proxy_parser << data
|
25
|
+
@parser << data
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_message_begin
|
29
|
+
@headers = nil
|
30
|
+
@body = ''
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_headers_complete(headers)
|
34
|
+
@headers = headers
|
35
|
+
end
|
36
|
+
|
37
|
+
def on_body(chunk)
|
38
|
+
@body << chunk
|
39
|
+
end
|
40
|
+
|
41
|
+
def on_message_complete
|
42
|
+
if @proxy_parser.http_method == 'CONNECT'
|
43
|
+
restart_with_ssl(@proxy_parser.request_url)
|
44
|
+
else
|
45
|
+
if @ssl
|
46
|
+
uri = URI.parse(@proxy_parser.request_url)
|
47
|
+
@url = "https://#{@ssl}#{[uri.path, uri.query].compact.join('?')}"
|
48
|
+
else
|
49
|
+
@url = @proxy_parser.request_url
|
50
|
+
end
|
51
|
+
handle_request
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
def restart_with_ssl(url)
|
59
|
+
@ssl = url
|
60
|
+
@proxy_parser = Http::Parser.new(self)
|
61
|
+
@original_headers = @headers.clone
|
62
|
+
send_data("HTTP/1.0 200 Connection established\r\nProxy-agent: Http-Flexible-Proxy/0.0.0\r\n\r\n")
|
63
|
+
start_tls(
|
64
|
+
private_key_file: File.expand_path('../mitm.key', __FILE__),
|
65
|
+
cert_chain_file: File.expand_path('../mitm.crt', __FILE__)
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
def handle_request
|
70
|
+
EM.synchrony do
|
71
|
+
request = ::Goliath::Request.new(handler, self, env)
|
72
|
+
request.set_deferred_status :succeeded
|
73
|
+
request.process
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def env
|
80
|
+
return @__env if @__env
|
81
|
+
fake_input_buffer = StringIO.new(@body)
|
82
|
+
fake_error_buffer = StringIO.new
|
83
|
+
url_parsed = URI.parse(@url)
|
84
|
+
@__env = ::Goliath::Env.new
|
85
|
+
@__env.merge!({
|
86
|
+
'rack.input' => Rack::Lint::InputWrapper.new(fake_input_buffer),
|
87
|
+
'rack.errors' => Rack::Lint::ErrorWrapper.new(fake_error_buffer),
|
88
|
+
'REQUEST_METHOD' => @proxy_parser.http_method,
|
89
|
+
'REQUEST_PATH' => url_parsed.path,
|
90
|
+
'PATH_INFO' => url_parsed.path,
|
91
|
+
'QUERY_STRING' => url_parsed.query || '',
|
92
|
+
'REQUEST_URI' => url_parsed.path + (url_parsed.query || ''),
|
93
|
+
'rack.url_scheme' => url_parsed.scheme,
|
94
|
+
'CONTENT_LENGTH' => @body.length,
|
95
|
+
'SERVER_NAME' => url_parsed.host,
|
96
|
+
'SERVER_PORT' => url_parsed.port,
|
97
|
+
'rack.logger' => logger
|
98
|
+
|
99
|
+
|
100
|
+
})
|
101
|
+
@headers.merge(@original_headers || {}).each do |name, value|
|
102
|
+
converted_name = "HTTP_#{name.gsub(/-/, '_').upcase}"
|
103
|
+
@__env[converted_name] = value
|
104
|
+
end
|
105
|
+
@__env['CONTENT_TYPE'] = @__env.delete('HTTP_CONTENT_TYPE') if @__env.key?('HTTP_CONTENT_TYPE')
|
106
|
+
@__env['CONTENT_LENGTH'] = @__env.delete('HTTP_CONTENT_LENGTH') if @__env.key?('HTTP_CONTENT_LENGTH')
|
107
|
+
@__env
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rack-cache'
|
2
|
+
require 'action_dispatch/http/request'
|
3
|
+
module AgileProxy
|
4
|
+
module Rack
|
5
|
+
class GetOnlyCache
|
6
|
+
def initialize(app)
|
7
|
+
@app = app
|
8
|
+
end
|
9
|
+
def call(env)
|
10
|
+
force_caching(env)
|
11
|
+
request = ::ActionDispatch::Request.new(env)
|
12
|
+
if request.request_method_symbol == :get
|
13
|
+
cache_app.call(env)
|
14
|
+
else
|
15
|
+
@app.call(env)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def force_caching(env)
|
21
|
+
env.delete 'HTTP_PRAGMA' if env['HTTP_PRAGMA'] == 'no-cache'
|
22
|
+
env.delete 'HTTP_CACHE_CONTROL' if env['HTTP_CACHE_CONTROL'] == 'no-cache'
|
23
|
+
end
|
24
|
+
def cache_app
|
25
|
+
@cache_app ||= ::Rack::Cache.new(@app)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module AgileProxy
|
2
|
+
#
|
3
|
+
# An instance of this class represents a route within the system.
|
4
|
+
#
|
5
|
+
class Route
|
6
|
+
attr_accessor :request_method, :pattern, :app, :constraints, :name
|
7
|
+
|
8
|
+
PATH_INFO = 'PATH_INFO'.freeze
|
9
|
+
ROUTE_PARAMS = 'rack.route_params'.freeze
|
10
|
+
QUERY_STRING = 'QUERY_STRING'.freeze
|
11
|
+
FORM_HASH = 'rack.request.form_hash'.freeze
|
12
|
+
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
13
|
+
HEAD = 'HEAD'.freeze
|
14
|
+
GET = 'GET'.freeze
|
15
|
+
POST = 'POST'.freeze
|
16
|
+
PUT = 'PUT'.freeze
|
17
|
+
DELETE = 'DELETE'.freeze
|
18
|
+
|
19
|
+
DEFAULT_WILDCARD_NAME = :paths
|
20
|
+
WILDCARD_PATTERN = /\/\*(.*)/.freeze
|
21
|
+
NAMED_SEGMENTS_PATTERN = /\/:([^$\/]+)/.freeze
|
22
|
+
NAMED_SEGMENTS_REPLACEMENT_PATTERN = /\/:([^$\/]+)/.freeze
|
23
|
+
DOT = '.'.freeze
|
24
|
+
|
25
|
+
def initialize(request_method, pattern, app, options = {})
|
26
|
+
fail ArgumentError, 'pattern cannot be blank' if pattern.to_s.strip.empty?
|
27
|
+
fail ArgumentError, 'app must be callable' unless app.respond_to?(:call)
|
28
|
+
@request_method = request_method
|
29
|
+
@pattern = pattern
|
30
|
+
@app = app
|
31
|
+
@constraints = options && options[:constraints]
|
32
|
+
@name = options && options[:as]
|
33
|
+
end
|
34
|
+
|
35
|
+
def regexp
|
36
|
+
@regexp ||= compile
|
37
|
+
end
|
38
|
+
|
39
|
+
def compile
|
40
|
+
pattern_match = pattern.match(WILDCARD_PATTERN)
|
41
|
+
src = if pattern_match
|
42
|
+
@wildcard_name = if pattern_match[1].to_s.strip.empty?
|
43
|
+
DEFAULT_WILDCARD_NAME
|
44
|
+
else
|
45
|
+
pattern_match[1].to_sym
|
46
|
+
end
|
47
|
+
pattern.gsub(WILDCARD_PATTERN, '(?:/(.*)|)')
|
48
|
+
else
|
49
|
+
pattern_match = pattern.match(NAMED_SEGMENTS_PATTERN)
|
50
|
+
p = if pattern_match
|
51
|
+
pattern.gsub(NAMED_SEGMENTS_REPLACEMENT_PATTERN, '/(?<\1>[^.$/]+)')
|
52
|
+
else
|
53
|
+
pattern
|
54
|
+
end
|
55
|
+
p + '(?:\.(?<format>.*))?'
|
56
|
+
end
|
57
|
+
Regexp.new("\\A#{src}\\Z")
|
58
|
+
end
|
59
|
+
|
60
|
+
def match(env)
|
61
|
+
request_method = env[REQUEST_METHOD]
|
62
|
+
request_method = GET if request_method == HEAD
|
63
|
+
path = env[PATH_INFO]
|
64
|
+
qs = env[QUERY_STRING]
|
65
|
+
return nil unless request_method == self.request_method
|
66
|
+
fail ArgumentError, 'path is required' if path.to_s.strip.empty?
|
67
|
+
path_match = path.match(regexp)
|
68
|
+
return unless path_match
|
69
|
+
params = if @wildcard_name
|
70
|
+
{ @wildcard_name => path_match[1].to_s.split('/') }
|
71
|
+
else
|
72
|
+
Hash[path_match.names.map(&:to_sym).zip(path_match.captures)]
|
73
|
+
end
|
74
|
+
params.merge!(::Rack::Utils.parse_nested_query(qs).symbolize_keys) unless qs.nil? || qs.empty?
|
75
|
+
params.merge! env[FORM_HASH] if env.key? FORM_HASH
|
76
|
+
params.delete(:format) if params.key?(:format) && params[:format].nil?
|
77
|
+
|
78
|
+
params if meets_constraints(params)
|
79
|
+
end
|
80
|
+
|
81
|
+
def meets_constraints(params)
|
82
|
+
if constraints
|
83
|
+
constraints.each do |param, constraint|
|
84
|
+
unless params.symbolize_keys[param.to_sym].to_s.match(constraint)
|
85
|
+
return false
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
true
|
90
|
+
end
|
91
|
+
|
92
|
+
def eql?(other)
|
93
|
+
other.is_a?(self.class) &&
|
94
|
+
other.request_method == request_method &&
|
95
|
+
other.pattern == pattern &&
|
96
|
+
other.app == app &&
|
97
|
+
other.constraints == constraints
|
98
|
+
end
|
99
|
+
|
100
|
+
alias_method :==, :eql?
|
101
|
+
|
102
|
+
def hash
|
103
|
+
request_method.hash ^ pattern.hash ^ app.hash ^ constraints.hash
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'agile_proxy/route'
|
2
|
+
|
3
|
+
module AgileProxy
|
4
|
+
#
|
5
|
+
# A rack router used for selecting a 'request spec' from a shortlist
|
6
|
+
#
|
7
|
+
class Router
|
8
|
+
VERSION = '0.4.0'
|
9
|
+
|
10
|
+
HEAD = 'HEAD'.freeze
|
11
|
+
GET = 'GET'.freeze
|
12
|
+
POST = 'POST'.freeze
|
13
|
+
PUT = 'PUT'.freeze
|
14
|
+
DELETE = 'DELETE'.freeze
|
15
|
+
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
16
|
+
PATH_INFO = 'PATH_INFO'.freeze
|
17
|
+
ROUTE_PARAMS = 'rack.route_params'.freeze
|
18
|
+
QUERY_STRING = 'QUERY_STRING'.freeze
|
19
|
+
FORM_HASH = 'rack.request.form_hash'.freeze
|
20
|
+
|
21
|
+
def initialize(&block)
|
22
|
+
@named_routes = {}
|
23
|
+
routes(&block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](route_name)
|
27
|
+
@named_routes[route_name]
|
28
|
+
end
|
29
|
+
|
30
|
+
def routes(&block)
|
31
|
+
instance_eval(&block) if block
|
32
|
+
@routes
|
33
|
+
end
|
34
|
+
|
35
|
+
def get(route_spec)
|
36
|
+
route(GET, route_spec)
|
37
|
+
end
|
38
|
+
|
39
|
+
def post(route_spec)
|
40
|
+
route(POST, route_spec)
|
41
|
+
end
|
42
|
+
|
43
|
+
def put(route_spec)
|
44
|
+
route(PUT, route_spec)
|
45
|
+
end
|
46
|
+
|
47
|
+
def delete(route_spec)
|
48
|
+
route(DELETE, route_spec)
|
49
|
+
end
|
50
|
+
|
51
|
+
def route(method, route_spec)
|
52
|
+
route = Route.new(
|
53
|
+
method,
|
54
|
+
route_spec.first.first,
|
55
|
+
route_spec.first.last,
|
56
|
+
route_spec.reject { |k, _| k == route_spec.first.first }
|
57
|
+
)
|
58
|
+
@routes ||= []
|
59
|
+
@routes << route
|
60
|
+
if route_spec && route_spec[:as]
|
61
|
+
# Using ||= so the first route with that name will be returned
|
62
|
+
@named_routes[route_spec[:as].to_sym] ||= route_spec.first.first
|
63
|
+
end
|
64
|
+
route
|
65
|
+
end
|
66
|
+
|
67
|
+
def call(env)
|
68
|
+
app = match(env)
|
69
|
+
if app
|
70
|
+
app.call(env)
|
71
|
+
else
|
72
|
+
not_found(env)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def match(env)
|
77
|
+
routes.each do |route|
|
78
|
+
params = route.match(env)
|
79
|
+
if params
|
80
|
+
env[ROUTE_PARAMS] = params
|
81
|
+
return route.app
|
82
|
+
end
|
83
|
+
end
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
|
87
|
+
def not_found(env)
|
88
|
+
body = "<h1>Not Found</h1><p>No route matches #{env[REQUEST_METHOD]} #{env[PATH_INFO]}</p>"
|
89
|
+
[
|
90
|
+
404,
|
91
|
+
{
|
92
|
+
'Content-Type' => 'text/html',
|
93
|
+
'Content-Length' => body.length.to_s
|
94
|
+
},
|
95
|
+
[body]
|
96
|
+
]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'yaml'
|
3
|
+
require 'cgi'
|
4
|
+
require 'uri'
|
5
|
+
require 'eventmachine'
|
6
|
+
require 'grape'
|
7
|
+
require 'agile_proxy/api/root'
|
8
|
+
require 'agile_proxy/servers/api'
|
9
|
+
require 'agile_proxy/servers/request_spec'
|
10
|
+
require 'agile_proxy/servers/request_spec_direct'
|
11
|
+
require 'forwardable'
|
12
|
+
|
13
|
+
require 'goliath/api'
|
14
|
+
require 'goliath/runner'
|
15
|
+
|
16
|
+
# Example demonstrating how to use a custom Goliath runner
|
17
|
+
#
|
18
|
+
|
19
|
+
class Custom < Goliath::API
|
20
|
+
def response(env)
|
21
|
+
[200, {}, "hello!"]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
module AgileProxy
|
28
|
+
#
|
29
|
+
# This class is responsible for controlling the underlying proxy and web servers
|
30
|
+
#
|
31
|
+
class Server
|
32
|
+
ROOT = File.expand_path '../../', File.dirname(__FILE__)
|
33
|
+
extend Forwardable
|
34
|
+
attr_reader :request_handler
|
35
|
+
|
36
|
+
def_delegators :request_handler, :reset_cache, :restore_cache, :handle_request
|
37
|
+
|
38
|
+
def initialize
|
39
|
+
environment = AgileProxy.config.environment
|
40
|
+
dbconfig = YAML.load(File.read(AgileProxy.config.database_config_file)).with_indifferent_access
|
41
|
+
ActiveRecord::Base.configurations = dbconfig
|
42
|
+
ActiveRecord::Base.establish_connection dbconfig[environment.to_s]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Starts the proxy and web servers
|
46
|
+
def start
|
47
|
+
main_loop
|
48
|
+
end
|
49
|
+
|
50
|
+
# The url that the proxy server is running on
|
51
|
+
# @return [String] The URL
|
52
|
+
def url
|
53
|
+
"http://#{host}:#{port}"
|
54
|
+
end
|
55
|
+
|
56
|
+
# The url that the web server can be accessed from
|
57
|
+
# @return [String] The URL
|
58
|
+
def webserver_url
|
59
|
+
"http://#{webserver_host}:#{webserver_port}"
|
60
|
+
end
|
61
|
+
|
62
|
+
# The url that the direct web server can be accessed from
|
63
|
+
# @return [String] The URL
|
64
|
+
def server_url
|
65
|
+
"http://#{server_host}:#{server_port}"
|
66
|
+
end
|
67
|
+
|
68
|
+
# The host that the proxy server is running on
|
69
|
+
# @return [String] The host
|
70
|
+
def host
|
71
|
+
'localhost'
|
72
|
+
end
|
73
|
+
|
74
|
+
# The port that the proxy server is running on
|
75
|
+
# @return [String] The port
|
76
|
+
def port
|
77
|
+
@request_spec_server.port
|
78
|
+
end
|
79
|
+
|
80
|
+
# The host that the webserver is running on
|
81
|
+
# @return [String] The host
|
82
|
+
def webserver_host
|
83
|
+
AgileProxy.config.webserver_host
|
84
|
+
end
|
85
|
+
|
86
|
+
# The port that the webserver is running on
|
87
|
+
# @return [String] The port
|
88
|
+
def webserver_port
|
89
|
+
AgileProxy.config.webserver_port
|
90
|
+
end
|
91
|
+
|
92
|
+
# The host that the direct server is running on
|
93
|
+
# @return [String] The host
|
94
|
+
def server_host
|
95
|
+
AgileProxy.config.server_host
|
96
|
+
end
|
97
|
+
|
98
|
+
# The port that the direct server is running on
|
99
|
+
# @return [String] The port
|
100
|
+
def server_port
|
101
|
+
AgileProxy.config.server_port
|
102
|
+
end
|
103
|
+
|
104
|
+
protected
|
105
|
+
|
106
|
+
def main_loop
|
107
|
+
EM.run do
|
108
|
+
EM.error_handler do |e|
|
109
|
+
puts e.class.name, e
|
110
|
+
puts e.backtrace.join("\n")
|
111
|
+
end
|
112
|
+
AgileProxy::Servers::Api.start(webserver_host, webserver_port)
|
113
|
+
AgileProxy::Servers::RequestSpecDirect.start(server_host, server_port)
|
114
|
+
@request_spec_server = AgileProxy::Servers::RequestSpec.start enable_cache: AgileProxy.config.enable_cache
|
115
|
+
AgileProxy.log(:info, "agile-proxy: Proxy listening on #{url}, API webserver listening on #{webserver_url} and Direct webserver listening on #{server_url}")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|