ethon 0.15.0 → 0.17.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 +4 -4
- data/CHANGELOG.md +29 -1
- data/README.md +23 -0
- data/ethon.gemspec +6 -3
- data/lib/ethon/curls/classes.rb +12 -2
- data/lib/ethon/curls/codes.rb +3 -2
- data/lib/ethon/curls/options.rb +4 -3
- data/lib/ethon/easy/callbacks.rb +2 -1
- data/lib/ethon/easy/informations.rb +3 -0
- data/lib/ethon/easy/response_callbacks.rb +6 -1
- data/lib/ethon/version.rb +1 -1
- metadata +6 -85
- data/.github/workflows/ruby.yml +0 -41
- data/.gitignore +0 -8
- data/.rspec +0 -3
- data/Gemfile +0 -43
- data/Guardfile +0 -10
- data/Rakefile +0 -40
- data/profile/benchmarks.rb +0 -104
- data/profile/memory_leaks.rb +0 -114
- data/profile/perf_spec_helper.rb +0 -37
- data/profile/support/memory_test_helpers.rb +0 -76
- data/profile/support/os_memory_leak_tracker.rb +0 -48
- data/profile/support/ruby_object_leak_tracker.rb +0 -49
- data/spec/ethon/curl_spec.rb +0 -38
- data/spec/ethon/easy/callbacks_spec.rb +0 -59
- data/spec/ethon/easy/debug_info_spec.rb +0 -54
- data/spec/ethon/easy/features_spec.rb +0 -24
- data/spec/ethon/easy/form_spec.rb +0 -104
- data/spec/ethon/easy/header_spec.rb +0 -79
- data/spec/ethon/easy/http/custom_spec.rb +0 -177
- data/spec/ethon/easy/http/delete_spec.rb +0 -21
- data/spec/ethon/easy/http/get_spec.rb +0 -126
- data/spec/ethon/easy/http/head_spec.rb +0 -80
- data/spec/ethon/easy/http/options_spec.rb +0 -51
- data/spec/ethon/easy/http/patch_spec.rb +0 -51
- data/spec/ethon/easy/http/post_spec.rb +0 -317
- data/spec/ethon/easy/http/put_spec.rb +0 -168
- data/spec/ethon/easy/http_spec.rb +0 -64
- data/spec/ethon/easy/informations_spec.rb +0 -120
- data/spec/ethon/easy/mirror_spec.rb +0 -47
- data/spec/ethon/easy/operations_spec.rb +0 -268
- data/spec/ethon/easy/options_spec.rb +0 -193
- data/spec/ethon/easy/queryable_spec.rb +0 -235
- data/spec/ethon/easy/response_callbacks_spec.rb +0 -152
- data/spec/ethon/easy/util_spec.rb +0 -28
- data/spec/ethon/easy_spec.rb +0 -203
- data/spec/ethon/libc_spec.rb +0 -14
- data/spec/ethon/loggable_spec.rb +0 -22
- data/spec/ethon/multi/operations_spec.rb +0 -298
- data/spec/ethon/multi/options_spec.rb +0 -182
- data/spec/ethon/multi/stack_spec.rb +0 -80
- data/spec/ethon/multi_spec.rb +0 -152
- data/spec/spec_helper.rb +0 -28
- data/spec/support/localhost_server.rb +0 -95
- data/spec/support/server.rb +0 -115
@@ -1,182 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe Ethon::Multi::Options do
|
5
|
-
let(:multi) { Ethon::Multi.new }
|
6
|
-
|
7
|
-
[
|
8
|
-
:maxconnects, :pipelining, :socketdata, :socketfunction,
|
9
|
-
:timerdata, :timerfunction, :max_total_connections
|
10
|
-
].each do |name|
|
11
|
-
describe "#{name}=" do
|
12
|
-
it "responds_to" do
|
13
|
-
expect(multi).to respond_to("#{name}=")
|
14
|
-
end
|
15
|
-
|
16
|
-
it "sets option" do
|
17
|
-
expect(Ethon::Curl).to receive(:set_option).with(name, anything, anything, anything)
|
18
|
-
multi.method("#{name}=").call(1)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
context "socket_action mode" do
|
24
|
-
let(:multi) { Ethon::Multi.new(execution_mode: :socket_action) }
|
25
|
-
|
26
|
-
describe "#socketfunction callbacks" do
|
27
|
-
it "allows multi_code return values" do
|
28
|
-
calls = []
|
29
|
-
multi.socketfunction = proc do |handle, sock, what, userp, socketp|
|
30
|
-
calls << what
|
31
|
-
:ok
|
32
|
-
end
|
33
|
-
|
34
|
-
easy = Ethon::Easy.new
|
35
|
-
easy.url = "http://localhost:3001/?delay=1"
|
36
|
-
multi.add(easy)
|
37
|
-
expect(calls).to eq([])
|
38
|
-
5.times do
|
39
|
-
multi.socket_action
|
40
|
-
break unless calls.empty?
|
41
|
-
sleep 0.1
|
42
|
-
end
|
43
|
-
expect(calls.last).to eq(:in).or(eq(:out))
|
44
|
-
multi.delete(easy)
|
45
|
-
expect(calls.last).to eq(:remove)
|
46
|
-
end
|
47
|
-
|
48
|
-
it "allows integer return values (compatibility)" do
|
49
|
-
called = false
|
50
|
-
multi.socketfunction = proc do |handle, sock, what, userp, socketp|
|
51
|
-
called = true
|
52
|
-
0
|
53
|
-
end
|
54
|
-
|
55
|
-
easy = Ethon::Easy.new
|
56
|
-
easy.url = "http://localhost:3001/?delay=1"
|
57
|
-
multi.add(easy)
|
58
|
-
5.times do
|
59
|
-
multi.socket_action
|
60
|
-
break if called
|
61
|
-
sleep 0.1
|
62
|
-
end
|
63
|
-
multi.delete(easy)
|
64
|
-
|
65
|
-
expect(called).to be_truthy
|
66
|
-
end
|
67
|
-
|
68
|
-
it "errors on invalid return codes" do
|
69
|
-
called = false
|
70
|
-
multi.socketfunction = proc do |handle, sock, what, userp, socketp|
|
71
|
-
called = true
|
72
|
-
"hi"
|
73
|
-
end
|
74
|
-
|
75
|
-
easy = Ethon::Easy.new
|
76
|
-
easy.url = "http://localhost:3001/?delay=1"
|
77
|
-
multi.add(easy)
|
78
|
-
expect {
|
79
|
-
5.times do
|
80
|
-
multi.socket_action
|
81
|
-
break if called
|
82
|
-
sleep 0.1
|
83
|
-
end
|
84
|
-
}.to raise_error(ArgumentError)
|
85
|
-
expect { multi.delete(easy) }.to raise_error(ArgumentError)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
describe "#timerfunction callbacks" do
|
90
|
-
it "allows multi_code return values" do
|
91
|
-
calls = []
|
92
|
-
multi.timerfunction = proc do |handle, timeout_ms, userp|
|
93
|
-
calls << timeout_ms
|
94
|
-
:ok
|
95
|
-
end
|
96
|
-
|
97
|
-
easy = Ethon::Easy.new
|
98
|
-
easy.url = "http://localhost:3001/?delay=1"
|
99
|
-
multi.add(easy)
|
100
|
-
expect(calls.last).to be >= 0 # adds an immediate timeout
|
101
|
-
|
102
|
-
multi.delete(easy)
|
103
|
-
expect(calls.last).to eq(-1) # cancels the timer
|
104
|
-
end
|
105
|
-
|
106
|
-
it "allows integer return values (compatibility)" do
|
107
|
-
called = false
|
108
|
-
multi.timerfunction = proc do |handle, timeout_ms, userp|
|
109
|
-
called = true
|
110
|
-
0
|
111
|
-
end
|
112
|
-
|
113
|
-
easy = Ethon::Easy.new
|
114
|
-
easy.url = "http://localhost:3001/?delay=1"
|
115
|
-
multi.add(easy)
|
116
|
-
multi.socket_action
|
117
|
-
multi.delete(easy)
|
118
|
-
|
119
|
-
expect(called).to be_truthy
|
120
|
-
end
|
121
|
-
|
122
|
-
it "errors on invalid return codes" do
|
123
|
-
called = false
|
124
|
-
multi.timerfunction = proc do |handle, timeout_ms, userp|
|
125
|
-
called = true
|
126
|
-
"hi"
|
127
|
-
end
|
128
|
-
|
129
|
-
easy = Ethon::Easy.new
|
130
|
-
easy.url = "http://localhost:3001/?delay=1"
|
131
|
-
expect { multi.add(easy) }.to raise_error(ArgumentError)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
describe "#value_for" do
|
137
|
-
context "when option in bool" do
|
138
|
-
context "when value true" do
|
139
|
-
let(:value) { true }
|
140
|
-
|
141
|
-
it "returns 1" do
|
142
|
-
expect(multi.method(:value_for).call(value, :bool)).to eq(1)
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
context "when value false" do
|
147
|
-
let(:value) { false }
|
148
|
-
|
149
|
-
it "returns 0" do
|
150
|
-
expect(multi.method(:value_for).call(value, :bool)).to eq(0)
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
|
156
|
-
context "when value in int" do
|
157
|
-
let(:value) { "2" }
|
158
|
-
|
159
|
-
it "returns value casted to int" do
|
160
|
-
expect(multi.method(:value_for).call(value, :int)).to eq(2)
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
context "when value in unspecific_options" do
|
165
|
-
context "when value a string" do
|
166
|
-
let(:value) { "www.example.\0com" }
|
167
|
-
|
168
|
-
it "returns zero byte escaped string" do
|
169
|
-
expect(multi.method(:value_for).call(value, nil)).to eq("www.example.\\0com")
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
context "when value not a string" do
|
174
|
-
let(:value) { 1 }
|
175
|
-
|
176
|
-
it "returns value" do
|
177
|
-
expect(multi.method(:value_for).call(value, nil)).to eq(1)
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
@@ -1,80 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe Ethon::Multi::Stack do
|
5
|
-
let(:multi) { Ethon::Multi.new }
|
6
|
-
let(:easy) { Ethon::Easy.new }
|
7
|
-
|
8
|
-
describe "#add" do
|
9
|
-
context "when easy already added" do
|
10
|
-
before { multi.add(easy) }
|
11
|
-
|
12
|
-
it "returns nil" do
|
13
|
-
expect(multi.add(easy)).to be_nil
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
context "when easy new" do
|
18
|
-
it "adds easy to multi" do
|
19
|
-
expect(Ethon::Curl).to receive(:multi_add_handle).and_return(:ok)
|
20
|
-
multi.add(easy)
|
21
|
-
end
|
22
|
-
|
23
|
-
it "adds easy to easy_handles" do
|
24
|
-
multi.add(easy)
|
25
|
-
expect(multi.easy_handles).to include(easy)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
context "when multi_add_handle fails" do
|
30
|
-
it "raises multi add error" do
|
31
|
-
expect(Ethon::Curl).to receive(:multi_add_handle).and_return(:bad_easy_handle)
|
32
|
-
expect{ multi.add(easy) }.to raise_error(Ethon::Errors::MultiAdd)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
context "when multi cleaned up before" do
|
37
|
-
it "raises multi add error" do
|
38
|
-
Ethon::Curl.multi_cleanup(multi.handle)
|
39
|
-
expect{ multi.add(easy) }.to raise_error(Ethon::Errors::MultiAdd)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
describe "#delete" do
|
45
|
-
context "when easy in easy_handles" do
|
46
|
-
before { multi.add(easy) }
|
47
|
-
|
48
|
-
it "deletes easy from multi" do
|
49
|
-
expect(Ethon::Curl).to receive(:multi_remove_handle).and_return(:ok)
|
50
|
-
multi.delete(easy)
|
51
|
-
end
|
52
|
-
|
53
|
-
it "deletes easy from easy_handles" do
|
54
|
-
multi.delete(easy)
|
55
|
-
expect(multi.easy_handles).to_not include(easy)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
context "when easy is not in easy_handles" do
|
60
|
-
it "does nothing" do
|
61
|
-
expect(Ethon::Curl).to receive(:multi_add_handle).and_return(:ok)
|
62
|
-
multi.add(easy)
|
63
|
-
end
|
64
|
-
|
65
|
-
it "adds easy to easy_handles" do
|
66
|
-
multi.add(easy)
|
67
|
-
expect(multi.easy_handles).to include(easy)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
context "when multi_remove_handle fails" do
|
72
|
-
before { multi.add(easy) }
|
73
|
-
|
74
|
-
it "raises multi remove error" do
|
75
|
-
expect(Ethon::Curl).to receive(:multi_remove_handle).and_return(:bad_easy_handle)
|
76
|
-
expect{ multi.delete(easy) }.to raise_error(Ethon::Errors::MultiRemove)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
data/spec/ethon/multi_spec.rb
DELETED
@@ -1,152 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe Ethon::Multi do
|
5
|
-
describe ".new" do
|
6
|
-
it "inits curl" do
|
7
|
-
expect(Ethon::Curl).to receive(:init)
|
8
|
-
Ethon::Multi.new
|
9
|
-
end
|
10
|
-
|
11
|
-
context "with default options" do
|
12
|
-
it "allows running #perform with the default execution_mode" do
|
13
|
-
Ethon::Multi.new.perform
|
14
|
-
end
|
15
|
-
|
16
|
-
it "refuses to run #socket_action" do
|
17
|
-
expect { Ethon::Multi.new.socket_action }.to raise_error(ArgumentError)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
context "when options not empty" do
|
22
|
-
context "when pipelining is set" do
|
23
|
-
let(:options) { { :pipelining => true } }
|
24
|
-
|
25
|
-
it "sets pipelining" do
|
26
|
-
expect_any_instance_of(Ethon::Multi).to receive(:pipelining=).with(true)
|
27
|
-
Ethon::Multi.new(options)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
context "when execution_mode option is :socket_action" do
|
32
|
-
let(:options) { { :execution_mode => :socket_action } }
|
33
|
-
let(:multi) { Ethon::Multi.new(options) }
|
34
|
-
|
35
|
-
it "refuses to run #perform" do
|
36
|
-
expect { multi.perform }.to raise_error(ArgumentError)
|
37
|
-
end
|
38
|
-
|
39
|
-
it "allows running #socket_action" do
|
40
|
-
multi.socket_action
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
describe "#socket_action" do
|
47
|
-
let(:options) { { :execution_mode => :socket_action } }
|
48
|
-
let(:select_state) { { :readers => [], :writers => [], :timeout => 0 } }
|
49
|
-
let(:multi) {
|
50
|
-
multi = Ethon::Multi.new(options)
|
51
|
-
multi.timerfunction = proc do |handle, timeout_ms, userp|
|
52
|
-
timeout_ms = nil if timeout_ms == -1
|
53
|
-
select_state[:timeout] = timeout_ms
|
54
|
-
:ok
|
55
|
-
end
|
56
|
-
multi.socketfunction = proc do |handle, sock, what, userp, socketp|
|
57
|
-
case what
|
58
|
-
when :remove
|
59
|
-
select_state[:readers].delete(sock)
|
60
|
-
select_state[:writers].delete(sock)
|
61
|
-
when :in
|
62
|
-
select_state[:readers].push(sock) unless select_state[:readers].include? sock
|
63
|
-
select_state[:writers].delete(sock)
|
64
|
-
when :out
|
65
|
-
select_state[:readers].delete(sock)
|
66
|
-
select_state[:writers].push(sock) unless select_state[:writers].include? sock
|
67
|
-
when :inout
|
68
|
-
select_state[:readers].push(sock) unless select_state[:readers].include? sock
|
69
|
-
select_state[:writers].push(sock) unless select_state[:writers].include? sock
|
70
|
-
else
|
71
|
-
raise ArgumentError, "invalid value for 'what' in socketfunction callback"
|
72
|
-
end
|
73
|
-
:ok
|
74
|
-
end
|
75
|
-
multi
|
76
|
-
}
|
77
|
-
|
78
|
-
def fds_to_ios(fds)
|
79
|
-
fds.map do |fd|
|
80
|
-
IO.for_fd(fd).tap { |io| io.autoclose = false }
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def perform_socket_action_until_complete
|
85
|
-
multi.socket_action # start things off
|
86
|
-
|
87
|
-
while multi.ongoing?
|
88
|
-
readers, writers, _ = IO.select(
|
89
|
-
fds_to_ios(select_state[:readers]),
|
90
|
-
fds_to_ios(select_state[:writers]),
|
91
|
-
[],
|
92
|
-
select_state[:timeout]
|
93
|
-
)
|
94
|
-
|
95
|
-
to_notify = Hash.new { |hash, key| hash[key] = [] }
|
96
|
-
unless readers.nil?
|
97
|
-
readers.each do |reader|
|
98
|
-
to_notify[reader] << :in
|
99
|
-
end
|
100
|
-
end
|
101
|
-
unless writers.nil?
|
102
|
-
writers.each do |writer|
|
103
|
-
to_notify[writer] << :out
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
to_notify.each do |io, readiness|
|
108
|
-
multi.socket_action(io, readiness)
|
109
|
-
end
|
110
|
-
|
111
|
-
# if we didn't have anything to notify, then we timed out
|
112
|
-
multi.socket_action if to_notify.empty?
|
113
|
-
end
|
114
|
-
ensure
|
115
|
-
multi.easy_handles.dup.each do |h|
|
116
|
-
multi.delete(h)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
it "supports an end-to-end request" do
|
121
|
-
easy = Ethon::Easy.new
|
122
|
-
easy.url = "http://localhost:3001/"
|
123
|
-
multi.add(easy)
|
124
|
-
|
125
|
-
perform_socket_action_until_complete
|
126
|
-
|
127
|
-
expect(multi.ongoing?).to eq(false)
|
128
|
-
end
|
129
|
-
|
130
|
-
it "supports multiple concurrent requests" do
|
131
|
-
handles = []
|
132
|
-
10.times do
|
133
|
-
easy = Ethon::Easy.new
|
134
|
-
easy.url = "http://localhost:3001/?delay=1"
|
135
|
-
multi.add(easy)
|
136
|
-
handles << easy
|
137
|
-
end
|
138
|
-
|
139
|
-
start = Time.now
|
140
|
-
perform_socket_action_until_complete
|
141
|
-
duration = Time.now - start
|
142
|
-
|
143
|
-
# these should have happened concurrently
|
144
|
-
expect(duration).to be < 2
|
145
|
-
expect(multi.ongoing?).to eq(false)
|
146
|
-
|
147
|
-
handles.each do |handle|
|
148
|
-
expect(handle.response_code).to eq(200)
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
4
|
-
|
5
|
-
require 'bundler'
|
6
|
-
Bundler.setup
|
7
|
-
require "ethon"
|
8
|
-
require 'rspec'
|
9
|
-
|
10
|
-
if defined? require_relative
|
11
|
-
require_relative 'support/localhost_server'
|
12
|
-
require_relative 'support/server'
|
13
|
-
else
|
14
|
-
require 'support/localhost_server'
|
15
|
-
require 'support/server'
|
16
|
-
end
|
17
|
-
|
18
|
-
# Ethon.logger = Logger.new($stdout).tap do |log|
|
19
|
-
# log.level = Logger::DEBUG
|
20
|
-
# end
|
21
|
-
|
22
|
-
RSpec.configure do |config|
|
23
|
-
# config.order = :rand
|
24
|
-
|
25
|
-
config.before(:suite) do
|
26
|
-
LocalhostServer.new(TESTSERVER.new, 3001)
|
27
|
-
end
|
28
|
-
end
|
@@ -1,95 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'rack'
|
3
|
-
require 'rack/handler/webrick'
|
4
|
-
require 'net/http'
|
5
|
-
|
6
|
-
# The code for this is inspired by Capybara's server:
|
7
|
-
# http://github.com/jnicklas/capybara/blob/0.3.9/lib/capybara/server.rb
|
8
|
-
class LocalhostServer
|
9
|
-
READY_MESSAGE = "Server ready"
|
10
|
-
|
11
|
-
class Identify
|
12
|
-
def initialize(app)
|
13
|
-
@app = app
|
14
|
-
end
|
15
|
-
|
16
|
-
def call(env)
|
17
|
-
if env["PATH_INFO"] == "/__identify__"
|
18
|
-
[200, {}, [LocalhostServer::READY_MESSAGE]]
|
19
|
-
else
|
20
|
-
@app.call(env)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
attr_reader :port
|
26
|
-
|
27
|
-
def initialize(rack_app, port = nil)
|
28
|
-
@port = port || find_available_port
|
29
|
-
@rack_app = rack_app
|
30
|
-
concurrently { boot }
|
31
|
-
wait_until(10, "Boot failed.") { booted? }
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
def find_available_port
|
37
|
-
server = TCPServer.new('127.0.0.1', 0)
|
38
|
-
server.addr[1]
|
39
|
-
ensure
|
40
|
-
server.close if server
|
41
|
-
end
|
42
|
-
|
43
|
-
def boot
|
44
|
-
# Use WEBrick since it's part of the ruby standard library and is available on all ruby interpreters.
|
45
|
-
options = { :Port => port }
|
46
|
-
options.merge!(:AccessLog => [], :Logger => WEBrick::BasicLog.new(StringIO.new)) unless ENV['VERBOSE_SERVER']
|
47
|
-
Rack::Handler::WEBrick.run(Identify.new(@rack_app), **options)
|
48
|
-
end
|
49
|
-
|
50
|
-
def booted?
|
51
|
-
res = ::Net::HTTP.get_response("localhost", '/__identify__', port)
|
52
|
-
if res.is_a?(::Net::HTTPSuccess) or res.is_a?(::Net::HTTPRedirection)
|
53
|
-
return res.body == READY_MESSAGE
|
54
|
-
end
|
55
|
-
rescue Errno::ECONNREFUSED, Errno::EBADF
|
56
|
-
return false
|
57
|
-
end
|
58
|
-
|
59
|
-
def concurrently
|
60
|
-
if should_use_subprocess?
|
61
|
-
pid = Process.fork do
|
62
|
-
trap(:INT) { ::Rack::Handler::WEBrick.shutdown }
|
63
|
-
yield
|
64
|
-
exit # manually exit; otherwise this sub-process will re-run the specs that haven't run yet.
|
65
|
-
end
|
66
|
-
|
67
|
-
at_exit do
|
68
|
-
Process.kill('INT', pid)
|
69
|
-
begin
|
70
|
-
Process.wait(pid)
|
71
|
-
rescue Errno::ECHILD
|
72
|
-
# ignore this error...I think it means the child process has already exited.
|
73
|
-
end
|
74
|
-
end
|
75
|
-
else
|
76
|
-
Thread.new { yield }
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def should_use_subprocess?
|
81
|
-
# !ENV['THREADED']
|
82
|
-
false
|
83
|
-
end
|
84
|
-
|
85
|
-
def wait_until(timeout, error_message, &block)
|
86
|
-
start_time = Time.now
|
87
|
-
|
88
|
-
while true
|
89
|
-
return if yield
|
90
|
-
raise TimeoutError.new(error_message) if (Time.now - start_time) > timeout
|
91
|
-
sleep(0.05)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
data/spec/support/server.rb
DELETED
@@ -1,115 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
require 'json'
|
4
|
-
require 'zlib'
|
5
|
-
require 'sinatra/base'
|
6
|
-
|
7
|
-
TESTSERVER = Sinatra.new do
|
8
|
-
set :logging, nil
|
9
|
-
|
10
|
-
fail_count = 0
|
11
|
-
|
12
|
-
post '/file' do
|
13
|
-
{
|
14
|
-
'content-type' => params[:file][:type],
|
15
|
-
'filename' => params[:file][:filename],
|
16
|
-
'content' => params[:file][:tempfile].read,
|
17
|
-
'request-content-type' => request.env['CONTENT_TYPE']
|
18
|
-
}.to_json
|
19
|
-
end
|
20
|
-
|
21
|
-
get '/multiple-headers' do
|
22
|
-
[200, { 'Set-Cookie' => %w[ foo bar ], 'Content-Type' => 'text/plain' }, ['']]
|
23
|
-
end
|
24
|
-
|
25
|
-
get '/fail/:number' do
|
26
|
-
if fail_count >= params[:number].to_i
|
27
|
-
"ok"
|
28
|
-
else
|
29
|
-
fail_count += 1
|
30
|
-
error 500, "oh noes!"
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
get '/fail_forever' do
|
35
|
-
error 500, "oh noes!"
|
36
|
-
end
|
37
|
-
|
38
|
-
get '/redirect' do
|
39
|
-
redirect '/'
|
40
|
-
end
|
41
|
-
|
42
|
-
post '/redirect' do
|
43
|
-
redirect '/'
|
44
|
-
end
|
45
|
-
|
46
|
-
get '/bad_redirect' do
|
47
|
-
redirect '/bad_redirect'
|
48
|
-
end
|
49
|
-
|
50
|
-
get '/auth_basic/:username/:password' do
|
51
|
-
@auth ||= Rack::Auth::Basic::Request.new(request.env)
|
52
|
-
# Check that we've got a basic auth, and that it's credentials match the ones
|
53
|
-
# provided in the request
|
54
|
-
if @auth.provided? && @auth.basic? && @auth.credentials == [ params[:username], params[:password] ]
|
55
|
-
# auth is valid - confirm it
|
56
|
-
true
|
57
|
-
else
|
58
|
-
# invalid auth - request the authentication
|
59
|
-
response['WWW-Authenticate'] = %(Basic realm="Testing HTTP Auth")
|
60
|
-
throw(:halt, [401, "Not authorized\n"])
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
get '/auth_ntlm' do
|
65
|
-
# we're just checking for the existence if NTLM auth header here. It's validation
|
66
|
-
# is too troublesome and really doesn't bother is much, it's up to libcurl to make
|
67
|
-
# it valid
|
68
|
-
response['WWW-Authenticate'] = 'NTLM'
|
69
|
-
is_ntlm_auth = /^NTLM/ =~ request.env['HTTP_AUTHORIZATION']
|
70
|
-
true if is_ntlm_auth
|
71
|
-
throw(:halt, [401, "Not authorized\n"]) if !is_ntlm_auth
|
72
|
-
end
|
73
|
-
|
74
|
-
get '/gzipped' do
|
75
|
-
req_env = request.env.to_json
|
76
|
-
z = Zlib::Deflate.new
|
77
|
-
gzipped_env = z.deflate(req_env, Zlib::FINISH)
|
78
|
-
z.close
|
79
|
-
response['Content-Encoding'] = 'gzip'
|
80
|
-
gzipped_env
|
81
|
-
end
|
82
|
-
|
83
|
-
get '/**' do
|
84
|
-
sleep params["delay"].to_i if params.has_key?("delay")
|
85
|
-
request.env.merge!(:body => request.body.read).to_json
|
86
|
-
end
|
87
|
-
|
88
|
-
head '/**' do
|
89
|
-
sleep params["delay"].to_i if params.has_key?("delay")
|
90
|
-
end
|
91
|
-
|
92
|
-
put '/**' do
|
93
|
-
request.env.merge!(:body => request.body.read).to_json
|
94
|
-
end
|
95
|
-
|
96
|
-
post '/**' do
|
97
|
-
request.env.merge!(:body => request.body.read).to_json
|
98
|
-
end
|
99
|
-
|
100
|
-
delete '/**' do
|
101
|
-
request.env.merge!(:body => request.body.read).to_json
|
102
|
-
end
|
103
|
-
|
104
|
-
patch '/**' do
|
105
|
-
request.env.merge!(:body => request.body.read).to_json
|
106
|
-
end
|
107
|
-
|
108
|
-
options '/**' do
|
109
|
-
request.env.merge!(:body => request.body.read).to_json
|
110
|
-
end
|
111
|
-
|
112
|
-
route 'PURGE', '/**' do
|
113
|
-
request.env.merge!(:body => request.body.read).to_json
|
114
|
-
end
|
115
|
-
end
|