proxy_tester 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -0
- data/Gemfile.lock +18 -1
- data/README.md +88 -4
- data/bin/proxy_tester +11 -0
- data/config.yaml +0 -0
- data/features/.keep +0 -0
- data/features/add_test_case.feature +41 -0
- data/features/step_definitions.rb +30 -0
- data/features/support/debugging.rb +3 -0
- data/features/support/env.rb +12 -0
- data/features/support/environment.rb +22 -0
- data/features/support/filesystem.rb +19 -0
- data/lib/proxy_tester.rb +11 -1
- data/lib/proxy_tester/actions/add_test_case.rb +40 -0
- data/lib/proxy_tester/actions/clear_environment.rb +21 -0
- data/lib/proxy_tester/actions/fetch_urls.rb +94 -0
- data/lib/proxy_tester/actions/handle_error.rb +10 -6
- data/lib/proxy_tester/capybara_proxy.rb +1 -1
- data/lib/proxy_tester/cli/main.rb +10 -3
- data/lib/proxy_tester/cli/test.rb +79 -0
- data/lib/proxy_tester/config.rb +1 -1
- data/lib/proxy_tester/error_handler.rb +16 -7
- data/lib/proxy_tester/error_messages.rb +45 -3
- data/lib/proxy_tester/exceptions.rb +31 -2
- data/lib/proxy_tester/git_repository.rb +6 -7
- data/lib/proxy_tester/http_proxy.rb +48 -0
- data/lib/proxy_tester/locales/en.yml +18 -0
- data/lib/proxy_tester/main.rb +1 -1
- data/lib/proxy_tester/models/user.rb +2 -2
- data/lib/proxy_tester/remote_repository.rb +55 -0
- data/lib/proxy_tester/reporters/fetch_urls.rb +34 -0
- data/lib/proxy_tester/version.rb +2 -1
- data/proxy_tester.gemspec +2 -0
- data/script/ci +4 -0
- data/script/test_web +5 -2
- data/spec/actions/add_test_case_spec.rb +81 -0
- data/spec/actions/fetch_urls_spec.rb +52 -0
- data/spec/actions/handle_error_spec.rb +1 -1
- data/spec/capybara_proxy_spec.rb +1 -1
- data/spec/git_repository_spec.rb +22 -0
- data/spec/http_proxy_spec.rb +47 -0
- data/spec/remote_repository_spec.rb +103 -0
- data/spec/reporters/fetch_urls_spec.rb +58 -0
- data/spec/support/filesystem.rb +2 -0
- data/spec/support/reporting.rb +6 -0
- data/spec/user_spec.rb +10 -2
- metadata +66 -5
- data/lib/proxy_tester/actions/clone_repository.rb +0 -39
- data/spec/actions/clone_repository_spec.rb +0 -83
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module ProxyTester
|
3
|
+
module Actions
|
4
|
+
class ClearEnvironment
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
attr_reader :variables
|
9
|
+
|
10
|
+
public
|
11
|
+
|
12
|
+
def initialize(options = {})
|
13
|
+
@variables = options.fetch(:variables, %w{ http_proxy https_proxy HTTP_PROXY HTTPS_PROXY })
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
variables.each { |var| ENV.delete(var) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module ProxyTester
|
3
|
+
module Actions
|
4
|
+
class FetchUrls
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
attr_reader :urls, :proxy, :timeout, :count, :result, :reporter, :concurrent, :output
|
9
|
+
|
10
|
+
public
|
11
|
+
|
12
|
+
def initialize(options = {})
|
13
|
+
@urls = options.fetch(:urls).collect { |u| Addressable::URI.heuristic_parse(u).to_s }
|
14
|
+
@proxy = options.fetch(:proxy)
|
15
|
+
@timeout = options.fetch(:timeout).to_i
|
16
|
+
@count = options.fetch(:count).to_i
|
17
|
+
@concurrent = options.fetch(:concurrent)
|
18
|
+
|
19
|
+
ProxyTester.ui_logger.debug "Options: " + options.to_s
|
20
|
+
|
21
|
+
@output = options.fetch(:output)
|
22
|
+
@reporter = options.fetch(:reporter, Reporters::FetchUrls.new(output))
|
23
|
+
|
24
|
+
@result = []
|
25
|
+
end
|
26
|
+
|
27
|
+
def run
|
28
|
+
threads = []
|
29
|
+
|
30
|
+
reporter.header
|
31
|
+
|
32
|
+
urls.each do |u|
|
33
|
+
count.times do |n|
|
34
|
+
threads << Thread.new do
|
35
|
+
ProxyTester.ui_logger.info "Starting #{n + 1}-nth thread to fetch \"#{u}\" via \"#{proxy.to_string}\"."
|
36
|
+
|
37
|
+
begin
|
38
|
+
fetch_url(u)
|
39
|
+
end while concurrent
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
sleep_time = 0
|
46
|
+
while sleep_time < timeout and threads.any? { |t| t.alive? }
|
47
|
+
sleep 1
|
48
|
+
sleep_time += 1
|
49
|
+
end
|
50
|
+
|
51
|
+
if sleep_time < timeout
|
52
|
+
ProxyTester.ui_logger.info "All threads finished before waiting time \"#{timeout}\" was over."
|
53
|
+
else
|
54
|
+
ProxyTester.ui_logger.info "Waiting time #{timeout} is over. Killing all active threads..."
|
55
|
+
end
|
56
|
+
|
57
|
+
threads.each do |t|
|
58
|
+
Thread.kill(t)
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def fetch_url(url)
|
66
|
+
begin
|
67
|
+
response = Excon.get(url,
|
68
|
+
proxy: proxy.to_string(cleartext: true),
|
69
|
+
middlewares: Excon.defaults[:middlewares] + [Excon::Middleware::RedirectFollower],
|
70
|
+
read_timeout: timeout,
|
71
|
+
write_timeout: timeout,
|
72
|
+
headers: {
|
73
|
+
'User-Agent' => 'ProxyTester',
|
74
|
+
}
|
75
|
+
)
|
76
|
+
rescue Excon::Errors::SocketError
|
77
|
+
handler = ErrorHandler.find(Exceptions::ProxyNotReachable)
|
78
|
+
handler.use(host: proxy.host, port: proxy.port)
|
79
|
+
|
80
|
+
ProxyTester.ui_logger.fatal handler.details
|
81
|
+
|
82
|
+
raise Exceptions::ProxyNotReachable, JSON.dump(host: proxy.port, port: proxy.port)
|
83
|
+
end
|
84
|
+
|
85
|
+
reporter.data(
|
86
|
+
headers: response.headers,
|
87
|
+
status: response.status,
|
88
|
+
url: u,
|
89
|
+
proxy: proxy.to_string,
|
90
|
+
)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -5,19 +5,23 @@ module ProxyTester
|
|
5
5
|
|
6
6
|
private
|
7
7
|
|
8
|
-
attr_reader :
|
8
|
+
attr_reader :handler_klass, :exception
|
9
9
|
|
10
10
|
public
|
11
11
|
|
12
12
|
def initialize(exception, handler_klass = ErrorHandler)
|
13
|
-
@exception
|
14
|
-
@
|
15
|
-
@handler_klass = handler_klass
|
13
|
+
@exception = exception
|
14
|
+
@handler_klass = handler_klass
|
16
15
|
end
|
17
16
|
|
18
17
|
def run
|
19
18
|
handler = handler_klass.find exception
|
20
|
-
|
19
|
+
|
20
|
+
if exception.respond_to? :cause
|
21
|
+
handler.cause = exception.cause
|
22
|
+
else
|
23
|
+
handler.cause = exception
|
24
|
+
end
|
21
25
|
|
22
26
|
handler.execute(parsed_message)
|
23
27
|
end
|
@@ -25,7 +29,7 @@ module ProxyTester
|
|
25
29
|
private
|
26
30
|
|
27
31
|
def parsed_message
|
28
|
-
result = JSON.parse(
|
32
|
+
result = JSON.parse(exception.message)
|
29
33
|
return {} unless result.kind_of? Hash
|
30
34
|
|
31
35
|
result
|
@@ -7,10 +7,14 @@ module ProxyTester
|
|
7
7
|
class_option :debug_mode, type: :boolean, desc: 'Run application in debug mode'
|
8
8
|
|
9
9
|
desc 'test', 'Run tests'
|
10
|
+
subcommand 'test', Cli::Test
|
11
|
+
|
12
|
+
desc 'add SOURCE', 'Add new testcases by cloning them via vcs'
|
13
|
+
option :force, type: :boolean, default: false, desc: 'Overwrite existing files?'
|
10
14
|
option :test_cases_directory, type: :string, desc: 'Directory with test cases'
|
11
|
-
|
12
|
-
def test
|
15
|
+
def add(source)
|
13
16
|
ProxyTester.config = ProxyTester::Config.new(options[:config_file]) if options[:config_file]
|
17
|
+
|
14
18
|
ProxyTester.config.log_level = options[:log_level] if options[:log_level]
|
15
19
|
ProxyTester.config.debug_mode = options[:debug_mode] if options[:debug_mode]
|
16
20
|
ProxyTester.config.test_cases_directory = options[:test_cases_directory] if options[:test_cases_directory]
|
@@ -19,7 +23,10 @@ module ProxyTester
|
|
19
23
|
ProxyTester.ui_logger.level = ProxyTester.config.log_level
|
20
24
|
ProxyTester.enable_debug_mode if ProxyTester.config.debug_mode
|
21
25
|
|
22
|
-
ProxyTester
|
26
|
+
ProxyTester.ui_logger.debug('Options: ' + options.to_s)
|
27
|
+
ProxyTester.ui_logger.debug("Config:\n" + ProxyTester.config.to_s)
|
28
|
+
|
29
|
+
Actions::AddTestCase.new(RemoteRepository.new(source), ProxyTester.config.test_cases_directory, force: options[:force]).run
|
23
30
|
end
|
24
31
|
|
25
32
|
desc 'init', 'Create files/directories to use proxy_tester'
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# encoding
|
2
|
+
module ProxyTester
|
3
|
+
module Cli
|
4
|
+
class Test < Thor
|
5
|
+
desc 'rspec', 'Run rspec tests'
|
6
|
+
option :test_cases_directory, type: :string, desc: 'Directory with test cases'
|
7
|
+
option :tags, type: :array, desc: 'Filter tests based on tags'
|
8
|
+
def rspec
|
9
|
+
ProxyTester.config = ProxyTester::Config.new(options[:config_file]) if options[:config_file]
|
10
|
+
ProxyTester.config.log_level = options[:log_level] if options[:log_level]
|
11
|
+
ProxyTester.config.debug_mode = options[:debug_mode] if options[:debug_mode]
|
12
|
+
ProxyTester.config.test_cases_directory = options[:test_cases_directory] if options[:test_cases_directory]
|
13
|
+
ProxyTester.config.lock
|
14
|
+
|
15
|
+
ProxyTester.ui_logger.level = ProxyTester.config.log_level
|
16
|
+
ProxyTester.enable_debug_mode if ProxyTester.config.debug_mode
|
17
|
+
|
18
|
+
ProxyTester::RspecRunner.new.run(options[:tags])
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'url', 'Fetch url'
|
22
|
+
option :count, type: :numeric, desc: 'Number of requests', default: 10
|
23
|
+
option :timeout, type: :numeric, desc: 'Wait n seconds before kill children', default: 60
|
24
|
+
option :proxy, type: :array, desc: 'System(s) under test', required: true
|
25
|
+
option :user, type: :string, desc: 'User for authentication', default: ENV['USER']
|
26
|
+
option :log_level, type: :string, desc: 'Log level'
|
27
|
+
option :concurrent, type: :boolean, desc: 'Simulate concurrent requests', default: false
|
28
|
+
option :output, type: :string, desc: 'Write output to file'
|
29
|
+
def url(*urls)
|
30
|
+
ProxyTester.config = ProxyTester::Config.new(options[:config_file]) if options[:config_file]
|
31
|
+
ProxyTester.config.log_level = options[:log_level] if options[:log_level]
|
32
|
+
ProxyTester.config.debug_mode = options[:debug_mode] if options[:debug_mode]
|
33
|
+
ProxyTester.config.lock
|
34
|
+
|
35
|
+
ProxyTester.ui_logger.level = ProxyTester.config.log_level
|
36
|
+
ProxyTester.enable_debug_mode if ProxyTester.config.debug_mode
|
37
|
+
|
38
|
+
proxies = options[:proxy].collect { |p| HttpProxy.new(p) }
|
39
|
+
|
40
|
+
unless options[:user].blank?
|
41
|
+
user = User.new
|
42
|
+
user.name = options[:user]
|
43
|
+
|
44
|
+
while user.password.blank?
|
45
|
+
user.password = HighLine.new.ask("Enter password for proxy user \"#{user.name}\": ") { |q| q.echo = '*' }
|
46
|
+
end
|
47
|
+
|
48
|
+
puts ''
|
49
|
+
|
50
|
+
proxies.each { |p| p.use_user user }
|
51
|
+
end
|
52
|
+
|
53
|
+
output = options.fetch(:output, $stdout)
|
54
|
+
if output.kind_of? String
|
55
|
+
output = File.open(output, 'w')
|
56
|
+
else
|
57
|
+
output = $stdout
|
58
|
+
end
|
59
|
+
|
60
|
+
Actions::ClearEnvironment.new.run
|
61
|
+
|
62
|
+
proxies.each do |p|
|
63
|
+
ProxyTester.ui_logger.debug "Using proxy \"#{p.to_string}\""
|
64
|
+
ProxyTester.ui_logger.debug "Using user \"#{user.to_string}\""
|
65
|
+
|
66
|
+
action = Actions::FetchUrls.new( urls: urls,
|
67
|
+
timeout: options[:timeout],
|
68
|
+
count: options[:count],
|
69
|
+
proxy: p,
|
70
|
+
concurrent: options[:concurrent],
|
71
|
+
output: output,
|
72
|
+
)
|
73
|
+
action.run
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/proxy_tester/config.rb
CHANGED
@@ -16,7 +16,7 @@ module ProxyTester
|
|
16
16
|
public
|
17
17
|
|
18
18
|
attr_reader :exception
|
19
|
-
attr_accessor :
|
19
|
+
attr_accessor :cause
|
20
20
|
|
21
21
|
def initialize(options = {})
|
22
22
|
@exception = options.fetch(:exception)
|
@@ -36,7 +36,7 @@ module ProxyTester
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def find(exception)
|
39
|
-
handlers.find(proc { default_handler }) { |h| h.exception == exception }
|
39
|
+
handlers.find(proc { default_handler }) { |h| h.exception == exception.class }
|
40
40
|
end
|
41
41
|
|
42
42
|
private
|
@@ -44,7 +44,7 @@ module ProxyTester
|
|
44
44
|
def default_handler
|
45
45
|
mutex.synchronize do
|
46
46
|
@default_handler ||= new(
|
47
|
-
exception:
|
47
|
+
exception: Exceptions::DefaultError,
|
48
48
|
details: 'errors.default.details',
|
49
49
|
summary: 'errors.default.summary',
|
50
50
|
exit_code: 99,
|
@@ -76,17 +76,26 @@ module ProxyTester
|
|
76
76
|
end
|
77
77
|
|
78
78
|
def use(data)
|
79
|
-
data = JSON.parse(data)
|
79
|
+
data = JSON.parse(data) if data.kind_of? String
|
80
|
+
data = data.symbolize_keys
|
80
81
|
|
81
|
-
@details
|
82
|
-
@summary
|
82
|
+
@details ||= I18n.t(details_i18n, data)
|
83
|
+
@summary ||= I18n.t(summary_i18n, data)
|
83
84
|
end
|
84
85
|
|
85
86
|
def execute(data = {})
|
86
87
|
use(data)
|
87
88
|
|
88
89
|
ProxyTester.ui_logger.fatal details
|
89
|
-
ProxyTester.ui_logger.debug
|
90
|
+
ProxyTester.ui_logger.debug "Resulting Exception: #{exception}"
|
91
|
+
ProxyTester.ui_logger.debug "Resulting Exit Code: #{exit_code}"
|
92
|
+
|
93
|
+
if cause
|
94
|
+
ProxyTester.ui_logger.debug "Cause: #{cause.class}"
|
95
|
+
ProxyTester.ui_logger.debug "Cause Message: #{cause.message}"
|
96
|
+
ProxyTester.ui_logger.debug "Cause Backtrace:\n" + Array(cause.backtrace).join("\n")
|
97
|
+
end
|
98
|
+
|
90
99
|
Kernel.exit exit_code
|
91
100
|
end
|
92
101
|
|
@@ -3,7 +3,7 @@ module ProxyTester
|
|
3
3
|
module ErrorMessages
|
4
4
|
|
5
5
|
ErrorHandler.create(
|
6
|
-
exception:
|
6
|
+
exception: Exceptions::DefaultError,
|
7
7
|
summary: 'errors.default.summary',
|
8
8
|
details: 'errors.default.details',
|
9
9
|
exit_code: 99,
|
@@ -69,14 +69,56 @@ module ProxyTester
|
|
69
69
|
exception: Exceptions::PacResultInvalid,
|
70
70
|
details: 'errors.invalid_pac_result.details',
|
71
71
|
summary: 'errors.invalid_pac_result.summary',
|
72
|
-
exit_code:
|
72
|
+
exit_code: 9,
|
73
73
|
)
|
74
74
|
|
75
75
|
ErrorHandler.create(
|
76
76
|
exception: Exceptions::ProxyTypeInvalid,
|
77
77
|
details: 'errors.invalid_proxy_type.details',
|
78
78
|
summary: 'errors.invalid_proxy_type.summary',
|
79
|
-
exit_code:
|
79
|
+
exit_code: 10,
|
80
|
+
)
|
81
|
+
|
82
|
+
ErrorHandler.create(
|
83
|
+
exception: Exceptions::ProxyConnectionStringInvalid,
|
84
|
+
details: 'errors.invalid_proxy_connection_string.details',
|
85
|
+
summary: 'errors.invalid_proxy_connection_string.summary',
|
86
|
+
exit_code: 11,
|
87
|
+
)
|
88
|
+
|
89
|
+
ErrorHandler.create(
|
90
|
+
exception: Interrupt,
|
91
|
+
details: 'errors.sigint.details',
|
92
|
+
summary: 'errors.sigint.summary',
|
93
|
+
exit_code: 12,
|
94
|
+
)
|
95
|
+
|
96
|
+
ErrorHandler.create(
|
97
|
+
exception: Exceptions::SIGTERM,
|
98
|
+
details: 'errors.sigterm.details',
|
99
|
+
summary: 'errors.sigterm.summary',
|
100
|
+
exit_code: 13,
|
101
|
+
)
|
102
|
+
|
103
|
+
ErrorHandler.create(
|
104
|
+
exception: Exceptions::ProxyNotReachable,
|
105
|
+
details: 'errors.unreachable_proxy.details',
|
106
|
+
summary: 'errors.unreachable_proxy.summary',
|
107
|
+
exit_code: 14,
|
108
|
+
)
|
109
|
+
|
110
|
+
ErrorHandler.create(
|
111
|
+
exception: Exceptions::RepositoryDoesNotExist,
|
112
|
+
details: 'errors.unexisting_repository.details',
|
113
|
+
summary: 'errors.unexisting_repository.summary',
|
114
|
+
exit_code: 15,
|
115
|
+
)
|
116
|
+
|
117
|
+
ErrorHandler.create(
|
118
|
+
exception: Exceptions::RepositoryInvalid,
|
119
|
+
details: 'errors.invalid_repository.details',
|
120
|
+
summary: 'errors.invalid_repository.summary',
|
121
|
+
exit_code: 16,
|
80
122
|
)
|
81
123
|
end
|
82
124
|
end
|
@@ -2,11 +2,28 @@
|
|
2
2
|
module ProxyTester
|
3
3
|
# Exceptions
|
4
4
|
module Exceptions
|
5
|
+
# my standard error
|
6
|
+
if RUBY_VERSION < "2.1"
|
7
|
+
class MyStandardError < StandardError
|
8
|
+
attr_reader :cause
|
9
|
+
|
10
|
+
def initialize(msg, cause = $!)
|
11
|
+
super(msg)
|
12
|
+
@cause = cause;
|
13
|
+
end
|
14
|
+
end
|
15
|
+
else
|
16
|
+
class MyStandardError < StandardError; end
|
17
|
+
end
|
18
|
+
|
5
19
|
# raised if user error occurred
|
6
|
-
class UserError <
|
20
|
+
class UserError < MyStandardError; end
|
21
|
+
|
22
|
+
# raised if an internal error occurred
|
23
|
+
class InternalError < MyStandardError; end
|
7
24
|
|
8
25
|
# raised if an internal error occurred
|
9
|
-
class
|
26
|
+
class DefaultError < MyStandardError; end
|
10
27
|
|
11
28
|
# raised if config file does not exist
|
12
29
|
class ConfigFileNotReadable < UserError; end
|
@@ -26,6 +43,9 @@ module ProxyTester
|
|
26
43
|
# raised if repository does not exist
|
27
44
|
class RepositoryDoesNotExist < UserError; end
|
28
45
|
|
46
|
+
# raised if repository is not a supported vcs repository
|
47
|
+
class RepositoryInvalid < UserError; end
|
48
|
+
|
29
49
|
# raised if proxy user does not exist
|
30
50
|
class ProxyUserInvalid < UserError; end
|
31
51
|
|
@@ -49,5 +69,14 @@ module ProxyTester
|
|
49
69
|
|
50
70
|
# raised if chosen proxy type is invalid
|
51
71
|
class ProxyTypeInvalid < UserError; end
|
72
|
+
|
73
|
+
# raised if chosen proxy connection string is invalid
|
74
|
+
class ProxyConnectionStringInvalid< UserError; end
|
75
|
+
|
76
|
+
# raised if program was killed with SIGTERM
|
77
|
+
class SIGTERM < UserError; end
|
78
|
+
|
79
|
+
# raised if proxy is not reachable
|
80
|
+
class ProxyNotReachable < UserError; end
|
52
81
|
end
|
53
82
|
end
|