wreq 1.2.11 → 1.2.13
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 +4 -4
- data/Cargo.lock +88 -146
- data/Cargo.toml +10 -7
- data/crates/wreq-util/src/emulate/profile/chrome/http2.rs +1 -1
- data/crates/wreq-util/src/emulate/profile/firefox/http2.rs +2 -2
- data/crates/wreq-util/src/emulate/profile/opera/http2.rs +1 -1
- data/docs/fork-safety.md +51 -0
- data/docs/interrupt-handling.md +134 -0
- data/examples/tls_info.rb +27 -0
- data/lib/wreq.rb +37 -0
- data/lib/wreq_ruby/body.rb +9 -0
- data/lib/wreq_ruby/client.rb +24 -1
- data/lib/wreq_ruby/cookie.rb +9 -0
- data/lib/wreq_ruby/error.rb +15 -0
- data/lib/wreq_ruby/response.rb +44 -0
- data/lib/wreq_ruby/tls.rb +73 -0
- data/src/arch.rs +174 -0
- data/src/client/body/stream.rs +28 -22
- data/src/client/req.rs +119 -122
- data/src/client/resp.rs +120 -47
- data/src/client.rs +103 -44
- data/src/cookie.rs +47 -11
- data/src/error.rs +28 -0
- data/src/lib.rs +3 -1
- data/src/macros.rs +2 -3
- data/src/rt.rs +46 -25
- data/src/tls.rs +51 -0
- data/test/fork_test.rb +106 -0
- data/test/scripts/fork_safety.rb +133 -0
- data/test/scripts/prefork_runtime.rb +95 -0
- data/test/support/tls_server.rb +95 -0
- data/test/tls_info_test.rb +59 -0
- metadata +11 -1
data/test/fork_test.rb
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
require "rbconfig"
|
|
5
|
+
require "tempfile"
|
|
6
|
+
require "timeout"
|
|
7
|
+
|
|
8
|
+
class ForkTest < Minitest::Test
|
|
9
|
+
FORK_ERROR_LABELS = %w[
|
|
10
|
+
module_request
|
|
11
|
+
fresh_client_request
|
|
12
|
+
fresh_body_sender_push
|
|
13
|
+
inherited_body_sender_push
|
|
14
|
+
inherited_body_sender_close
|
|
15
|
+
inherited_body_sender_closed
|
|
16
|
+
inherited_client
|
|
17
|
+
inherited_jar
|
|
18
|
+
inherited_cookie_provider
|
|
19
|
+
inherited_response_metadata
|
|
20
|
+
inherited_response
|
|
21
|
+
inherited_response_text
|
|
22
|
+
inherited_response_chunks
|
|
23
|
+
inherited_response_close
|
|
24
|
+
].freeze
|
|
25
|
+
|
|
26
|
+
def test_fork_error_is_a_runtime_error
|
|
27
|
+
assert_operator Wreq::ForkError, :<, RuntimeError
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_loaded_extension_can_initialize_runtime_after_fork
|
|
31
|
+
skip "fork is not supported on this platform" unless Process.respond_to?(:fork)
|
|
32
|
+
|
|
33
|
+
stdout, stderr, status = run_fork_script("prefork_runtime.rb")
|
|
34
|
+
|
|
35
|
+
assert status.success?, "subprocess failed with #{status.inspect}: #{stderr}"
|
|
36
|
+
assert_equal "ok\n", stdout
|
|
37
|
+
assert_match(/loaded_only=ok/, stderr)
|
|
38
|
+
assert_match(/before_runtime=ok/, stderr)
|
|
39
|
+
assert_match(/parent_after_children=ok/, stderr)
|
|
40
|
+
%w[
|
|
41
|
+
inherited_client_before_runtime
|
|
42
|
+
inherited_sender_before_runtime
|
|
43
|
+
inherited_jar_before_runtime
|
|
44
|
+
inherited_cookie_provider_before_runtime
|
|
45
|
+
].each do |label|
|
|
46
|
+
assert_match(/#{label}=Wreq::ForkError:.*cannot be used after fork/, stderr)
|
|
47
|
+
end
|
|
48
|
+
refute_match(/\[BUG\]|segmentation fault|panicked/i, stderr)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def test_initialized_runtime_is_rejected_after_fork
|
|
52
|
+
skip "fork is not supported on this platform" unless Process.respond_to?(:fork)
|
|
53
|
+
|
|
54
|
+
stdout, stderr, status = run_fork_script("fork_safety.rb")
|
|
55
|
+
|
|
56
|
+
assert status.success?, "subprocess failed with #{status.inspect}: #{stderr}"
|
|
57
|
+
assert_equal "ok\n", stdout
|
|
58
|
+
assert_match(/non_runtime_construction=ok/, stderr)
|
|
59
|
+
assert_match(/inherited_snapshots=ok/, stderr)
|
|
60
|
+
FORK_ERROR_LABELS.each do |label|
|
|
61
|
+
assert_match(/#{label}=Wreq::ForkError:.*cannot be used after fork/, stderr)
|
|
62
|
+
assert_match(/#{label}_retry=Wreq::ForkError:.*cannot be used after fork/, stderr)
|
|
63
|
+
end
|
|
64
|
+
assert_match(/inherited_gc=ok/, stderr)
|
|
65
|
+
refute_match(/\[BUG\]|segmentation fault|panicked/i, stderr)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def run_fork_script(name)
|
|
71
|
+
lib_dir = File.expand_path("../lib", __dir__)
|
|
72
|
+
script = File.expand_path("scripts/#{name}", __dir__)
|
|
73
|
+
|
|
74
|
+
Tempfile.create("wreq-fork-stdout") do |stdout|
|
|
75
|
+
Tempfile.create("wreq-fork-stderr") do |stderr|
|
|
76
|
+
pid = Process.spawn(
|
|
77
|
+
RbConfig.ruby,
|
|
78
|
+
"-I",
|
|
79
|
+
lib_dir,
|
|
80
|
+
script,
|
|
81
|
+
out: stdout,
|
|
82
|
+
err: stderr,
|
|
83
|
+
pgroup: true
|
|
84
|
+
)
|
|
85
|
+
status = Timeout.timeout(30) { Process.wait2(pid).last }
|
|
86
|
+
stdout.rewind
|
|
87
|
+
stderr.rewind
|
|
88
|
+
return [stdout.read, stderr.read, status]
|
|
89
|
+
rescue Timeout::Error
|
|
90
|
+
begin
|
|
91
|
+
Process.kill("KILL", -pid)
|
|
92
|
+
rescue Errno::ESRCH
|
|
93
|
+
nil
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
begin
|
|
97
|
+
Process.wait(pid)
|
|
98
|
+
rescue Errno::ECHILD
|
|
99
|
+
nil
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
flunk "#{name} timed out"
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "socket"
|
|
4
|
+
require "timeout"
|
|
5
|
+
require "weakref"
|
|
6
|
+
require "wreq"
|
|
7
|
+
|
|
8
|
+
$stdout.sync = true
|
|
9
|
+
$stderr.sync = true
|
|
10
|
+
|
|
11
|
+
def expect_fork_error(label)
|
|
12
|
+
2.times do |attempt|
|
|
13
|
+
attempt_label = attempt.zero? ? label : "#{label}_retry"
|
|
14
|
+
|
|
15
|
+
begin
|
|
16
|
+
yield
|
|
17
|
+
rescue Wreq::ForkError => error
|
|
18
|
+
warn "#{attempt_label}=#{error.class}: #{error.message}"
|
|
19
|
+
next
|
|
20
|
+
rescue => error
|
|
21
|
+
abort "#{attempt_label}=unexpected #{error.class}: #{error.message}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
abort "#{attempt_label}=missing Wreq::ForkError"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
server = TCPServer.new("127.0.0.1", 0)
|
|
29
|
+
port = server.addr[1]
|
|
30
|
+
server_pid = fork do
|
|
31
|
+
3.times do
|
|
32
|
+
ready = IO.select([server], nil, nil, 10)
|
|
33
|
+
exit! 4 unless ready
|
|
34
|
+
|
|
35
|
+
socket = server.accept
|
|
36
|
+
begin
|
|
37
|
+
while (line = socket.gets)
|
|
38
|
+
break if line == "\r\n"
|
|
39
|
+
end
|
|
40
|
+
socket.write("HTTP/1.1 200 OK\r\nX-Fork-Test: ok\r\nContent-Length: 2\r\nConnection: close\r\n\r\nok")
|
|
41
|
+
ensure
|
|
42
|
+
socket.close
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
exit! 0
|
|
46
|
+
ensure
|
|
47
|
+
server.close
|
|
48
|
+
end
|
|
49
|
+
server.close
|
|
50
|
+
|
|
51
|
+
url = "http://127.0.0.1:#{port}/"
|
|
52
|
+
|
|
53
|
+
# Start the server process before Tokio creates worker threads in the parent.
|
|
54
|
+
runtime_probe = Wreq::BodySender.new(1)
|
|
55
|
+
runtime_probe.push("warmup")
|
|
56
|
+
|
|
57
|
+
client = Wreq::Client.new
|
|
58
|
+
abort "parent warm-up failed" unless client.get(url).bytes == "ok"
|
|
59
|
+
|
|
60
|
+
inherited_objects = {
|
|
61
|
+
client: Wreq::Client.new,
|
|
62
|
+
sender: Wreq::BodySender.new,
|
|
63
|
+
response: client.get(url),
|
|
64
|
+
jar: Wreq::Jar.new
|
|
65
|
+
}
|
|
66
|
+
inherited_weak_refs = inherited_objects.values.map { |object| WeakRef.new(object) }
|
|
67
|
+
status_snapshot = inherited_objects[:response].status
|
|
68
|
+
headers_snapshot = inherited_objects[:response].headers
|
|
69
|
+
|
|
70
|
+
guard_pid = fork do
|
|
71
|
+
Timeout.timeout(10) do
|
|
72
|
+
jar = Wreq::Jar.new
|
|
73
|
+
jar.add("child=1; Path=/", url)
|
|
74
|
+
abort "fresh child jar failed" unless jar.get_all.one?
|
|
75
|
+
|
|
76
|
+
Wreq::Client.new(cookie_provider: jar)
|
|
77
|
+
Wreq::BodySender.new
|
|
78
|
+
warn "non_runtime_construction=ok"
|
|
79
|
+
|
|
80
|
+
abort "status snapshot changed" unless status_snapshot.to_i == 200
|
|
81
|
+
abort "headers snapshot changed" unless headers_snapshot["X-Fork-Test"] == "ok"
|
|
82
|
+
warn "inherited_snapshots=ok"
|
|
83
|
+
|
|
84
|
+
expect_fork_error("module_request") { Wreq.get(url) }
|
|
85
|
+
expect_fork_error("fresh_client_request") { Wreq::Client.new.get(url) }
|
|
86
|
+
expect_fork_error("fresh_body_sender_push") { Wreq::BodySender.new.push("chunk") }
|
|
87
|
+
expect_fork_error("inherited_body_sender_push") do
|
|
88
|
+
inherited_objects[:sender].push("chunk")
|
|
89
|
+
end
|
|
90
|
+
expect_fork_error("inherited_body_sender_close") { inherited_objects[:sender].close }
|
|
91
|
+
expect_fork_error("inherited_body_sender_closed") { inherited_objects[:sender].closed? }
|
|
92
|
+
expect_fork_error("inherited_client") { inherited_objects[:client].get(url) }
|
|
93
|
+
expect_fork_error("inherited_jar") { inherited_objects[:jar].get_all }
|
|
94
|
+
expect_fork_error("inherited_cookie_provider") do
|
|
95
|
+
Wreq::Client.new(cookie_provider: inherited_objects[:jar])
|
|
96
|
+
end
|
|
97
|
+
expect_fork_error("inherited_response_metadata") { inherited_objects[:response].status }
|
|
98
|
+
expect_fork_error("inherited_response") { inherited_objects[:response].bytes }
|
|
99
|
+
expect_fork_error("inherited_response_text") { inherited_objects[:response].text }
|
|
100
|
+
expect_fork_error("inherited_response_chunks") { inherited_objects[:response].chunks { nil } }
|
|
101
|
+
expect_fork_error("inherited_response_close") { inherited_objects[:response].close }
|
|
102
|
+
end
|
|
103
|
+
exit! 0
|
|
104
|
+
rescue => error
|
|
105
|
+
warn "guard_checks=unexpected #{error.class}: #{error.message}"
|
|
106
|
+
exit! 2
|
|
107
|
+
end
|
|
108
|
+
_, guard_status = Process.wait2(guard_pid)
|
|
109
|
+
abort "guard checks child failed with #{guard_status.inspect}" unless guard_status.success?
|
|
110
|
+
|
|
111
|
+
# The hash is now the only strong reference to the inherited native objects.
|
|
112
|
+
GC.start(full_mark: true, immediate_sweep: true)
|
|
113
|
+
gc_pid = fork do
|
|
114
|
+
inherited_objects = nil
|
|
115
|
+
3.times { GC.start(full_mark: true, immediate_sweep: true) }
|
|
116
|
+
alive = inherited_weak_refs.each_index.select do |index|
|
|
117
|
+
inherited_weak_refs[index].weakref_alive?
|
|
118
|
+
end
|
|
119
|
+
abort "inherited objects were not collected: #{alive.join(", ")}" unless alive.empty?
|
|
120
|
+
warn "inherited_gc=ok"
|
|
121
|
+
exit! 0
|
|
122
|
+
rescue => error
|
|
123
|
+
warn "inherited_gc=unexpected #{error.class}: #{error.message}"
|
|
124
|
+
exit! 5
|
|
125
|
+
end
|
|
126
|
+
_, gc_status = Process.wait2(gc_pid)
|
|
127
|
+
abort "inherited GC child failed with #{gc_status.inspect}" unless gc_status.success?
|
|
128
|
+
|
|
129
|
+
abort "parent request after fork failed" unless client.get(url).bytes == "ok"
|
|
130
|
+
_, server_status = Process.wait2(server_pid)
|
|
131
|
+
abort "server failed with #{server_status.inspect}" unless server_status.success?
|
|
132
|
+
|
|
133
|
+
puts "ok"
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "socket"
|
|
4
|
+
require "timeout"
|
|
5
|
+
require "wreq"
|
|
6
|
+
|
|
7
|
+
$stdout.sync = true
|
|
8
|
+
$stderr.sync = true
|
|
9
|
+
|
|
10
|
+
def expect_fork_error(label)
|
|
11
|
+
yield
|
|
12
|
+
abort "#{label}=missing Wreq::ForkError"
|
|
13
|
+
rescue Wreq::ForkError => error
|
|
14
|
+
warn "#{label}=#{error.class}: #{error.message}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def run_child(label)
|
|
18
|
+
child_pid = fork do
|
|
19
|
+
Timeout.timeout(10) { yield }
|
|
20
|
+
warn "#{label}=ok"
|
|
21
|
+
exit! 0
|
|
22
|
+
rescue => error
|
|
23
|
+
warn "#{label}=unexpected #{error.class}: #{error.message}"
|
|
24
|
+
exit! 2
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
_, status = Process.wait2(child_pid)
|
|
28
|
+
abort "#{label} child failed with #{status.inspect}" unless status.success?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
server = TCPServer.new("127.0.0.1", 0)
|
|
32
|
+
url = "http://127.0.0.1:#{server.addr[1]}/"
|
|
33
|
+
server_pid = fork do
|
|
34
|
+
3.times do
|
|
35
|
+
ready = IO.select([server], nil, nil, 10)
|
|
36
|
+
exit! 4 unless ready
|
|
37
|
+
|
|
38
|
+
socket = server.accept
|
|
39
|
+
begin
|
|
40
|
+
while (line = socket.gets)
|
|
41
|
+
break if line == "\r\n"
|
|
42
|
+
end
|
|
43
|
+
socket.write("HTTP/1.1 200 OK\r\nContent-Length: 2\r\nConnection: close\r\n\r\nok")
|
|
44
|
+
ensure
|
|
45
|
+
socket.close
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
exit! 0
|
|
49
|
+
ensure
|
|
50
|
+
server.close
|
|
51
|
+
end
|
|
52
|
+
server.close
|
|
53
|
+
|
|
54
|
+
# Requiring the extension is the only parent-side Wreq operation before this fork.
|
|
55
|
+
run_child("loaded_only") do
|
|
56
|
+
abort "module request failed" unless Wreq.get(url).bytes == "ok"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
inherited_client = Wreq::Client.new
|
|
60
|
+
inherited_sender = Wreq::BodySender.new
|
|
61
|
+
inherited_jar = Wreq::Jar.new
|
|
62
|
+
|
|
63
|
+
run_child("before_runtime") do
|
|
64
|
+
expect_fork_error("inherited_client_before_runtime") do
|
|
65
|
+
inherited_client.get(url)
|
|
66
|
+
end
|
|
67
|
+
expect_fork_error("inherited_sender_before_runtime") { inherited_sender.closed? }
|
|
68
|
+
expect_fork_error("inherited_jar_before_runtime") { inherited_jar.get_all }
|
|
69
|
+
expect_fork_error("inherited_cookie_provider_before_runtime") do
|
|
70
|
+
Wreq::Client.new(cookie_provider: inherited_jar)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
sender = Wreq::BodySender.new
|
|
74
|
+
sender.push("child")
|
|
75
|
+
|
|
76
|
+
jar = Wreq::Jar.new
|
|
77
|
+
jar.add("child=1; Path=/", url)
|
|
78
|
+
abort "child jar failed" unless jar.get_all.one?
|
|
79
|
+
|
|
80
|
+
client = Wreq::Client.new(cookie_provider: jar)
|
|
81
|
+
abort "client request failed" unless client.get(url).bytes == "ok"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
Timeout.timeout(10) do
|
|
85
|
+
abort "parent client failed" unless inherited_client.get(url).bytes == "ok"
|
|
86
|
+
inherited_sender.push("parent")
|
|
87
|
+
inherited_jar.add("parent=1; Path=/", url)
|
|
88
|
+
abort "parent jar failed" unless inherited_jar.get_all.one?
|
|
89
|
+
end
|
|
90
|
+
warn "parent_after_children=ok"
|
|
91
|
+
|
|
92
|
+
_, server_status = Process.wait2(server_pid)
|
|
93
|
+
abort "server failed with #{server_status.inspect}" unless server_status.success?
|
|
94
|
+
|
|
95
|
+
puts "ok"
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "openssl"
|
|
4
|
+
require "socket"
|
|
5
|
+
require "timeout"
|
|
6
|
+
|
|
7
|
+
# A small HTTPS server that serves every expected request on one TLS connection.
|
|
8
|
+
module TlsTestServer
|
|
9
|
+
RESPONSE_BODY = "ok"
|
|
10
|
+
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
def with_connection(request_count:)
|
|
14
|
+
tcp_server = TCPServer.new("127.0.0.1", 0)
|
|
15
|
+
context, certificate_der = server_context
|
|
16
|
+
ssl_server = OpenSSL::SSL::SSLServer.new(tcp_server, context)
|
|
17
|
+
outcome = Queue.new
|
|
18
|
+
server_thread = Thread.new do
|
|
19
|
+
socket = ssl_server.accept
|
|
20
|
+
request_lines = []
|
|
21
|
+
|
|
22
|
+
request_count.times do |index|
|
|
23
|
+
request_lines << read_request(socket)
|
|
24
|
+
connection = (index == request_count - 1) ? "close" : "keep-alive"
|
|
25
|
+
socket.write(response(connection))
|
|
26
|
+
socket.flush
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
outcome << {connections: 1, requests: request_lines}
|
|
30
|
+
rescue => error
|
|
31
|
+
outcome << error
|
|
32
|
+
ensure
|
|
33
|
+
socket&.close
|
|
34
|
+
end
|
|
35
|
+
server_thread.report_on_exception = false
|
|
36
|
+
|
|
37
|
+
yield "https://127.0.0.1:#{tcp_server.addr[1]}/", certificate_der
|
|
38
|
+
|
|
39
|
+
result = Timeout.timeout(5) { outcome.pop }
|
|
40
|
+
raise result if result.is_a?(StandardError)
|
|
41
|
+
|
|
42
|
+
result
|
|
43
|
+
ensure
|
|
44
|
+
tcp_server&.close
|
|
45
|
+
server_thread&.join(5)
|
|
46
|
+
if server_thread&.alive?
|
|
47
|
+
server_thread.kill
|
|
48
|
+
server_thread.join
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def read_request(socket)
|
|
53
|
+
request_line = socket.gets
|
|
54
|
+
raise EOFError, "client closed before sending a request" unless request_line
|
|
55
|
+
|
|
56
|
+
loop do
|
|
57
|
+
line = socket.gets
|
|
58
|
+
raise EOFError, "client closed while sending headers" unless line
|
|
59
|
+
break if line == "\r\n"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
request_line
|
|
63
|
+
end
|
|
64
|
+
private_class_method :read_request
|
|
65
|
+
|
|
66
|
+
def response(connection)
|
|
67
|
+
[
|
|
68
|
+
"HTTP/1.1 200 OK",
|
|
69
|
+
"Content-Length: #{RESPONSE_BODY.bytesize}",
|
|
70
|
+
"Connection: #{connection}",
|
|
71
|
+
"",
|
|
72
|
+
RESPONSE_BODY
|
|
73
|
+
].join("\r\n")
|
|
74
|
+
end
|
|
75
|
+
private_class_method :response
|
|
76
|
+
|
|
77
|
+
def server_context
|
|
78
|
+
key = OpenSSL::PKey::RSA.new(2048)
|
|
79
|
+
certificate = OpenSSL::X509::Certificate.new
|
|
80
|
+
certificate.version = 2
|
|
81
|
+
certificate.serial = 1
|
|
82
|
+
certificate.subject = certificate.issuer = OpenSSL::X509::Name.parse("/CN=127.0.0.1")
|
|
83
|
+
certificate.public_key = key.public_key
|
|
84
|
+
certificate.not_before = Time.now - 60
|
|
85
|
+
certificate.not_after = Time.now + 3600
|
|
86
|
+
certificate.sign(key, OpenSSL::Digest.new("SHA256"))
|
|
87
|
+
|
|
88
|
+
context = OpenSSL::SSL::SSLContext.new.tap do |ssl_context|
|
|
89
|
+
ssl_context.cert = certificate
|
|
90
|
+
ssl_context.key = key
|
|
91
|
+
end
|
|
92
|
+
[context, certificate.to_der]
|
|
93
|
+
end
|
|
94
|
+
private_class_method :server_context
|
|
95
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
require_relative "support/tls_server"
|
|
5
|
+
|
|
6
|
+
class TlsInfoTest < Minitest::Test
|
|
7
|
+
HTTPBIN_HTTP_URL = ENV.fetch("HTTPBIN_HTTP_URL", HTTPBIN_URL.sub(/\Ahttps:/, "http:"))
|
|
8
|
+
|
|
9
|
+
def test_tls_info_is_nil_when_disabled_or_request_is_plain_http
|
|
10
|
+
default_response = Wreq::Client.new.get("#{HTTPBIN_URL}/get")
|
|
11
|
+
plain_response = Wreq::Client.new(tls_info: true).get("#{HTTPBIN_HTTP_URL}/get")
|
|
12
|
+
|
|
13
|
+
assert_nil default_response.tls_info
|
|
14
|
+
assert_nil plain_response.tls_info
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_certificate_data_survives_body_lifecycle_on_a_reused_connection
|
|
18
|
+
fixture = TlsTestServer.with_connection(request_count: 2) do |base_url, certificate_der|
|
|
19
|
+
client = Wreq::Client.new(
|
|
20
|
+
tls_info: true,
|
|
21
|
+
verify: false,
|
|
22
|
+
http1_only: true,
|
|
23
|
+
no_proxy: true,
|
|
24
|
+
timeout: 5
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
read_response = client.get("#{base_url}read")
|
|
28
|
+
assert_equal "ok", read_response.text
|
|
29
|
+
read_tls = read_response.tls_info
|
|
30
|
+
|
|
31
|
+
closed_response = client.get("#{base_url}close")
|
|
32
|
+
closed_response.close
|
|
33
|
+
closed_tls = closed_response.tls_info
|
|
34
|
+
|
|
35
|
+
assert_instance_of Wreq::TlsInfo, read_tls
|
|
36
|
+
certificate = read_tls.peer_certificate
|
|
37
|
+
chain = read_tls.peer_certificate_chain
|
|
38
|
+
assert_equal certificate_der, certificate
|
|
39
|
+
assert_equal Encoding::BINARY, certificate.encoding
|
|
40
|
+
assert_equal [certificate_der], chain
|
|
41
|
+
assert_equal Encoding::BINARY, chain.first.encoding
|
|
42
|
+
assert_predicate chain, :frozen?
|
|
43
|
+
assert_equal(
|
|
44
|
+
"#<Wreq::TlsInfo peer_certificate=#{certificate_der.bytesize}B peer_certificate_chain=1>",
|
|
45
|
+
read_tls.inspect
|
|
46
|
+
)
|
|
47
|
+
assert_empty Wreq::TlsInfo.instance_methods(false) & %i[to_h to_s]
|
|
48
|
+
|
|
49
|
+
certificate.clear
|
|
50
|
+
assert_equal certificate_der, read_tls.peer_certificate
|
|
51
|
+
assert_equal certificate_der, closed_tls.peer_certificate
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
assert_equal(
|
|
55
|
+
{connections: 1, requests: ["GET /read HTTP/1.1\r\n", "GET /close HTTP/1.1\r\n"]},
|
|
56
|
+
fixture
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: wreq
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.2.
|
|
4
|
+
version: 1.2.13
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- SearchApi
|
|
@@ -80,6 +80,8 @@ files:
|
|
|
80
80
|
- crates/wreq-util/tests/emulate_safari.rs
|
|
81
81
|
- crates/wreq-util/tests/support/mod.rs
|
|
82
82
|
- crates/wreq-util/tests/support/server.rs
|
|
83
|
+
- docs/fork-safety.md
|
|
84
|
+
- docs/interrupt-handling.md
|
|
83
85
|
- docs/windows-gnu-tokio-crash.md
|
|
84
86
|
- examples/body.rb
|
|
85
87
|
- examples/client.rb
|
|
@@ -90,6 +92,7 @@ files:
|
|
|
90
92
|
- examples/send_stream.rb
|
|
91
93
|
- examples/stream.rb
|
|
92
94
|
- examples/thread_interrupt.rb
|
|
95
|
+
- examples/tls_info.rb
|
|
93
96
|
- extconf.rb
|
|
94
97
|
- lib/wreq.rb
|
|
95
98
|
- lib/wreq_ruby/body.rb
|
|
@@ -100,6 +103,7 @@ files:
|
|
|
100
103
|
- lib/wreq_ruby/header.rb
|
|
101
104
|
- lib/wreq_ruby/http.rb
|
|
102
105
|
- lib/wreq_ruby/response.rb
|
|
106
|
+
- lib/wreq_ruby/tls.rb
|
|
103
107
|
- script/build_platform_gem.rb
|
|
104
108
|
- script/rust_env.rb
|
|
105
109
|
- src/arch.rs
|
|
@@ -142,12 +146,14 @@ files:
|
|
|
142
146
|
- src/serde/ser/struct_variant_serializer.rs
|
|
143
147
|
- src/serde/ser/tuple_variant_serializer.rs
|
|
144
148
|
- src/serde/tests.rs
|
|
149
|
+
- src/tls.rs
|
|
145
150
|
- test/body_sender_test.rb
|
|
146
151
|
- test/client_cookie_test.rb
|
|
147
152
|
- test/client_test.rb
|
|
148
153
|
- test/cookie_test.rb
|
|
149
154
|
- test/emulation_test.rb
|
|
150
155
|
- test/error_handling_test.rb
|
|
156
|
+
- test/fork_test.rb
|
|
151
157
|
- test/header_test.rb
|
|
152
158
|
- test/inspect_test.rb
|
|
153
159
|
- test/json_precision_test.rb
|
|
@@ -157,8 +163,12 @@ files:
|
|
|
157
163
|
- test/request_parameters_test.rb
|
|
158
164
|
- test/request_test.rb
|
|
159
165
|
- test/response_test.rb
|
|
166
|
+
- test/scripts/fork_safety.rb
|
|
167
|
+
- test/scripts/prefork_runtime.rb
|
|
160
168
|
- test/stream_test.rb
|
|
169
|
+
- test/support/tls_server.rb
|
|
161
170
|
- test/test_helper.rb
|
|
171
|
+
- test/tls_info_test.rb
|
|
162
172
|
- test/value_semantics_test.rb
|
|
163
173
|
- wreq.gemspec
|
|
164
174
|
homepage: https://github.com/SearchApi/wreq-ruby
|