proxy_tester 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|