miniproxy 0.2.0
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.
- checksums.yaml +7 -0
- data/lib/miniproxy.rb +53 -0
- data/lib/miniproxy/fake_ssl_server.rb +38 -0
- data/lib/miniproxy/proxy_server.rb +55 -0
- data/lib/miniproxy/remote.rb +127 -0
- data/lib/miniproxy/stub/request.rb +24 -0
- data/lib/miniproxy/stub/response.rb +24 -0
- data/lib/miniproxy/version.rb +3 -0
- metadata +93 -0
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
|
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: []
|