miniproxy 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3f44f0daaa7c8c5897ee86428737bb7bafba78d60935943056d8daed9baa2111
4
+ data.tar.gz: 4ec7b5cf714b7afe7d81dda425a6a8f3095caefac4c9511405a189678df71910
5
+ SHA512:
6
+ metadata.gz: 5410429d11d0cdb1f97cf5423d97d8f3d3d88b80a39bab27de5fd265d2bd02e779c5946d7e28d0ac9ab618758afc1900e1cc294b3b9c7f19ff6c800bd96d0c71
7
+ data.tar.gz: 2c987fde657f840ae2e6c976ea6bae574aa9ec31dfff19a48fe128f348b97d3aa89a6dedc2d2cc82fd8f497183065b3e02fb277ff2da2ffebe63d337a4995f91
data/lib/miniproxy.rb ADDED
@@ -0,0 +1,53 @@
1
+ require "drb"
2
+ require "timeout"
3
+ require "miniproxy/remote"
4
+
5
+ module MiniProxy
6
+ DRB_SERVICE_TIMEOUT = 5
7
+
8
+ def self.reset
9
+ remote.clear
10
+ end
11
+
12
+ def self.stop
13
+ remote.stop if Remote.drb_process_alive?
14
+ end
15
+
16
+ def self.port
17
+ remote.port
18
+ end
19
+
20
+ def self.host
21
+ "127.0.0.1"
22
+ end
23
+
24
+ def self.ignore_all_requests
25
+ reset
26
+
27
+ %w(GET POST PUT PATCH DELETE).each do |method|
28
+ stub_request(method: method, url: /.*/)
29
+ end
30
+ end
31
+
32
+ def self.stub_request(method:, url:, response: {})
33
+ remote.stub_request(method: method, url: url, response: response)
34
+ end
35
+
36
+ private_class_method def self.remote
37
+ Timeout.timeout(DRB_SERVICE_TIMEOUT) do
38
+ begin
39
+ remote = DRbObject.new(nil, Remote.server)
40
+
41
+ until remote.started?
42
+ sleep 0.01
43
+ end
44
+
45
+ remote.drain_messages.each(&method(:puts))
46
+
47
+ remote
48
+ rescue
49
+ retry
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,38 @@
1
+ require "webrick/https" # This is required to create a HTTPS server
2
+ # https://ruby-doc.org/stdlib-2.0.0/libdoc/webrick/rdoc/WEBrick.html#module-WEBrick-label-HTTPS
3
+
4
+ module MiniProxy
5
+ # MiniProxy fake SSL enabled server, which receives relayed requests from the ProxyServer
6
+ #
7
+ class FakeSSLServer < WEBrick::HTTPServer
8
+ ALLOWED_HOSTS = ["127.0.0.1", "localhost"].freeze
9
+
10
+ def initialize(config = {}, default = WEBrick::Config::HTTP)
11
+ config = config.merge({
12
+ Logger: WEBrick::Log.new(nil, 0), # silence logging
13
+ AccessLog: [], # silence logging
14
+ SSLEnable: true,
15
+ SSLCertificate: OpenSSL::X509::Certificate.new(certificate_file("cert.pem")),
16
+ SSLPrivateKey: OpenSSL::PKey::RSA.new(certificate_file("cert.key")),
17
+ SSLVerifyClient: OpenSSL::SSL::VERIFY_NONE,
18
+ })
19
+
20
+ super(config, default)
21
+ end
22
+
23
+ def service(req, res)
24
+ if ALLOWED_HOSTS.include?(req.host)
25
+ super(req, res)
26
+ else
27
+ self.config[:MockHandlerCallback].call(req, res)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def certificate_file(filename)
34
+ filename = File.join( File.dirname(__FILE__), "../../ssl/#{filename}")
35
+ File.open(filename).read
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,55 @@
1
+ require "webrick/httpproxy"
2
+
3
+ module MiniProxy
4
+ # MiniProxy server, which boots a WEBrick proxy server
5
+ #
6
+ class ProxyServer < WEBrick::HTTPProxyServer
7
+ ALLOWED_HOSTS = ["127.0.0.1", "localhost"].freeze
8
+
9
+ attr_accessor :requests
10
+
11
+ def initialize(config = {}, default = WEBrick::Config::HTTP)
12
+ config = config.merge({
13
+ Logger: WEBrick::Log.new(nil, 0), # silence logging
14
+ AccessLog: [], # silence logging
15
+ })
16
+
17
+ super(config, default)
18
+ end
19
+
20
+ def do_PUT(req, res)
21
+ perform_proxy_request(req, res) do |http, path, header|
22
+ http.put(path, req.body || "", header)
23
+ end
24
+ end
25
+
26
+ def do_DELETE(req, res)
27
+ perform_proxy_request(req, res) do |http, path, header|
28
+ http.delete(path, header)
29
+ end
30
+ end
31
+
32
+ def do_PATCH(req, res)
33
+ perform_proxy_request(req, res) do |http, path, header|
34
+ http.patch(path, req.body || "", header)
35
+ end
36
+ end
37
+
38
+ def service(req, res)
39
+ if ALLOWED_HOSTS.include?(req.host)
40
+ super(req, res)
41
+ else
42
+ if req.request_method == "CONNECT"
43
+ # If something is trying to initiate an SSL connection, rewrite
44
+ # the URI to point to our fake server.
45
+ req.instance_variable_set(:@unparsed_uri, "localhost:#{self.config[:FakeServerPort]}")
46
+ super(req, res)
47
+ else
48
+ # Otherwise, call our handler to respond with an appropriate
49
+ # mock for the request.
50
+ self.config[:MockHandlerCallback].call(req, res)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,127 @@
1
+ require "miniproxy/stub/request"
2
+ require "miniproxy/stub/response"
3
+ require "miniproxy/proxy_server"
4
+ require "miniproxy/fake_ssl_server"
5
+
6
+ module MiniProxy
7
+ # Controls the remote DRb service, which provides a communcation mechanism
8
+ #
9
+ class Remote
10
+ SERVER_DYNAMIC_PORT_RANGE = (12345..32768).to_a.freeze
11
+ SERVER_START_TIMEOUT = 10
12
+
13
+ def self.pid
14
+ @pid
15
+ end
16
+
17
+ def self.drb_process_alive?
18
+ pid && Process.kill(0, pid) == 1
19
+ rescue Errno::ESRCH
20
+ false
21
+ end
22
+
23
+ def self.server
24
+ @unix_socket_uri ||= begin
25
+ tempfile = Tempfile.new("mini_proxy")
26
+ socket_path = tempfile.path
27
+ tempfile.close!
28
+ "drbunix:///#{socket_path}"
29
+ end
30
+
31
+ return @unix_socket_uri if drb_process_alive?
32
+
33
+ @pid = fork do
34
+ remote = Remote.new
35
+
36
+ Timeout.timeout(SERVER_START_TIMEOUT) do
37
+ begin
38
+ fake_server_port = SERVER_DYNAMIC_PORT_RANGE.sample
39
+ fake_server = FakeSSLServer.new(
40
+ Port: fake_server_port,
41
+ MockHandlerCallback: remote.method(:handler),
42
+ )
43
+ Thread.new { fake_server.start }
44
+ rescue Errno::EADDRINUSE
45
+ retry
46
+ end
47
+
48
+ begin
49
+ remote.port = ENV["MINI_PROXY_PORT"] || SERVER_DYNAMIC_PORT_RANGE.sample
50
+ proxy = MiniProxy::ProxyServer.new(
51
+ Port: remote.port,
52
+ FakeServerPort: fake_server_port,
53
+ MockHandlerCallback: remote.method(:handler),
54
+ )
55
+ Thread.new { proxy.start }
56
+ rescue Errno::EADDRINUSE
57
+ retry
58
+ end
59
+ end
60
+
61
+ DRb.start_service(@unix_socket_uri, remote)
62
+ DRb.thread.join
63
+
64
+ Process.exit!
65
+ end
66
+
67
+ Process.detach(@pid)
68
+ @unix_socket_uri
69
+ end
70
+
71
+ def handler(req, res)
72
+ if (request = @stubs.detect { |mock_request| mock_request.match?(req) })
73
+ response = request.response
74
+ res.status = response.code
75
+ response.headers.each { |key, value| res[key] = value }
76
+ res.body = response.body
77
+ else
78
+ res.status = 200
79
+ res.body = ""
80
+ queue_message "WARN: external request to #{req.host}#{req.path} not mocked"
81
+ queue_message %Q{Stub with: MiniProxy.stub_request(method: "#{req.request_method}", url: "#{req.host}#{req.path}")}
82
+ end
83
+ end
84
+
85
+ def stub_request(method:, url:, response:)
86
+ response = MiniProxy::Stub::Response.new(headers: response[:headers], body: response[:body])
87
+ request = MiniProxy::Stub::Request.new(method: method, url: url, response: response)
88
+ @stubs.push(request)
89
+ end
90
+
91
+ def port
92
+ @port
93
+ end
94
+
95
+ def port=(value)
96
+ @port = value
97
+ end
98
+
99
+ def stop
100
+ DRb.stop_service
101
+ end
102
+
103
+ def clear
104
+ @stubs.clear
105
+ end
106
+
107
+ def drain_messages
108
+ @messages.slice!(0, @messages.length)
109
+ end
110
+
111
+ def started?
112
+ current_server = DRb.current_server()
113
+ current_server && current_server.alive?
114
+ end
115
+
116
+ private
117
+
118
+ def queue_message(msg)
119
+ @messages.push msg
120
+ end
121
+
122
+ def initialize
123
+ @stubs = []
124
+ @messages = []
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,24 @@
1
+ module MiniProxy
2
+ module Stub
3
+ # MiniProxy stub request to match and stub external URLs with a stubbed response
4
+ #
5
+ class Request
6
+ attr_reader :response
7
+
8
+ # @param [String] method
9
+ # @param [Regexp, String] url
10
+ # @param [MiniProxy::Response] response
11
+ def initialize(method:, url:, response:)
12
+ @method = method
13
+ @response = response
14
+ @url = url
15
+ end
16
+
17
+ # @param [WEBrick::HTTPRequest] http_request
18
+ def match?(http_request)
19
+ request_uri = http_request.host + http_request.path
20
+ http_request.request_method == @method && request_uri.match?(@url)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module MiniProxy
2
+ module Stub
3
+ # MiniProxy stub response, so stubbed requests can return a custom response
4
+ #
5
+ class Response
6
+ def initialize(headers:, body:)
7
+ @body = body
8
+ @headers = headers
9
+ end
10
+
11
+ def body
12
+ @body || ""
13
+ end
14
+
15
+ def code
16
+ 200
17
+ end
18
+
19
+ def headers
20
+ @headers || { "Content-Type" => "text/html" }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ module MiniProxy
2
+ VERSION = "0.2.0".freeze
3
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: miniproxy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - The Conversation Dev Team
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-07-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: capybara
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: selenium-webdriver
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: A tool which allows you to easily stub requests to external sites for
56
+ your browser tests.
57
+ email:
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - lib/miniproxy.rb
63
+ - lib/miniproxy/fake_ssl_server.rb
64
+ - lib/miniproxy/proxy_server.rb
65
+ - lib/miniproxy/remote.rb
66
+ - lib/miniproxy/stub/request.rb
67
+ - lib/miniproxy/stub/response.rb
68
+ - lib/miniproxy/version.rb
69
+ homepage: https://github.com/conversation/miniproxy
70
+ licenses:
71
+ - MIT
72
+ metadata: {}
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 2.7.3
90
+ signing_key:
91
+ specification_version: 4
92
+ summary: Easily stub external requests for your browser tests.
93
+ test_files: []