ethon 0.16.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 +26 -1
- data/README.md +1 -1
- data/ethon.gemspec +6 -3
- data/lib/ethon/curls/codes.rb +3 -2
- 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 -81
- 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 -126
- data/spec/ethon/easy/mirror_spec.rb +0 -47
- data/spec/ethon/easy/operations_spec.rb +0 -271
- 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,76 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require_relative 'ruby_object_leak_tracker'
|
3
|
-
require_relative 'os_memory_leak_tracker'
|
4
|
-
|
5
|
-
module MemoryTestHelpers
|
6
|
-
class << self
|
7
|
-
attr_accessor :gc_proc, :iterations, :logger
|
8
|
-
|
9
|
-
def setup
|
10
|
-
if RUBY_PLATFORM == "java"
|
11
|
-
# for leak detection
|
12
|
-
JRuby.objectspace = true if defined?(JRuby)
|
13
|
-
# for gc
|
14
|
-
require 'java'
|
15
|
-
java_import 'java.lang.System'
|
16
|
-
self.gc_proc = proc { System.gc }
|
17
|
-
else
|
18
|
-
self.gc_proc = proc { GC.start }
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
module TestMethods
|
24
|
-
def memory_leak_test(description, &block)
|
25
|
-
context(description) do
|
26
|
-
it "doesn't leak ruby objects" do
|
27
|
-
object_leak_tracker = RubyObjectLeakTracker.new
|
28
|
-
track_memory_usage(object_leak_tracker, &block)
|
29
|
-
object_leak_tracker.total_difference_between_runs.should be <= 10
|
30
|
-
end
|
31
|
-
|
32
|
-
it "doesn't leak OS memory (C interop check)" do
|
33
|
-
os_memory_leak_tracker = OSMemoryLeakTracker.new
|
34
|
-
track_memory_usage(os_memory_leak_tracker, &block)
|
35
|
-
os_memory_leak_tracker.total_difference_between_runs.should be <= 10
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def track_memory_usage(tracker)
|
42
|
-
# Intentionally do all this setup before we do any testing
|
43
|
-
logger = MemoryTestHelpers.logger
|
44
|
-
iterations = MemoryTestHelpers.iterations
|
45
|
-
|
46
|
-
checkpoint_frequency = (iterations / 10.0).to_i
|
47
|
-
gc_frequency = 20
|
48
|
-
|
49
|
-
warmup_iterations = [(iterations / 3.0).to_i, 500].min
|
50
|
-
logger.info "Performing #{warmup_iterations} warmup iterations"
|
51
|
-
warmup_iterations.times do
|
52
|
-
yield
|
53
|
-
MemoryTestHelpers.gc_proc.call
|
54
|
-
end
|
55
|
-
tracker.capture_initial_memory_usage
|
56
|
-
|
57
|
-
logger.info "Performing #{iterations} iterations (checkpoint every #{checkpoint_frequency})"
|
58
|
-
|
59
|
-
iterations.times do |i|
|
60
|
-
yield
|
61
|
-
|
62
|
-
last_iteration = (i == iterations - 1)
|
63
|
-
checkpoint = last_iteration || (i % checkpoint_frequency == 0)
|
64
|
-
|
65
|
-
if checkpoint || (i % gc_frequency == 0)
|
66
|
-
MemoryTestHelpers.gc_proc.call
|
67
|
-
end
|
68
|
-
|
69
|
-
if checkpoint
|
70
|
-
logger.info "Iteration #{i} checkpoint"
|
71
|
-
tracker.capture_memory_usage
|
72
|
-
tracker.dump_status(logger)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
class OSMemoryLeakTracker
|
3
|
-
attr_reader :current_run
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
@previous_run = @current_run = 0
|
7
|
-
end
|
8
|
-
|
9
|
-
def difference_between_runs(basis=@previous_run)
|
10
|
-
@current_run - basis
|
11
|
-
end
|
12
|
-
|
13
|
-
def total_difference_between_runs
|
14
|
-
difference_between_runs(@initial_count_run)
|
15
|
-
end
|
16
|
-
|
17
|
-
def capture_initial_memory_usage
|
18
|
-
capture_memory_usage
|
19
|
-
@initial_count_run = @current_run
|
20
|
-
end
|
21
|
-
|
22
|
-
def capture_memory_usage
|
23
|
-
@previous_run = @current_run
|
24
|
-
@current_run = rss_bytes
|
25
|
-
end
|
26
|
-
|
27
|
-
def dump_status(logger)
|
28
|
-
delta = difference_between_runs
|
29
|
-
logger.add(log_level(delta), sprintf("\tTotal memory usage (kb): %d (%+d)", current_run, delta))
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
# amount of memory the current process "is using", in RAM
|
34
|
-
# (doesn't include any swap memory that it may be using, just that in actual RAM)
|
35
|
-
# Code loosely based on https://github.com/rdp/os/blob/master/lib/os.rb
|
36
|
-
# returns 0 on windows
|
37
|
-
def rss_bytes
|
38
|
-
if ENV['OS'] == 'Windows_NT'
|
39
|
-
0
|
40
|
-
else
|
41
|
-
`ps -o rss= -p #{Process.pid}`.to_i # in kilobytes
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def log_level(delta)
|
46
|
-
delta > 0 ? Logger::WARN : Logger::DEBUG
|
47
|
-
end
|
48
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
class RubyObjectLeakTracker
|
3
|
-
attr_reader :previous_count_hash, :current_count_hash
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
@previous_count_hash = @current_count_hash = {}
|
7
|
-
end
|
8
|
-
|
9
|
-
def difference_between_runs(basis=@previous_count_hash)
|
10
|
-
@difference_between_runs ||= Hash[@current_count_hash.map do |object_class, count|
|
11
|
-
[object_class, count - (basis[object_class] || 0)]
|
12
|
-
end]
|
13
|
-
end
|
14
|
-
|
15
|
-
def total_difference_between_runs
|
16
|
-
difference_between_runs(@initial_count_hash).values.inject(0) { |sum, count| sum + count }
|
17
|
-
end
|
18
|
-
|
19
|
-
def capture_initial_memory_usage
|
20
|
-
capture_memory_usage
|
21
|
-
@initial_count_hash = @current_count_hash
|
22
|
-
end
|
23
|
-
|
24
|
-
def capture_memory_usage
|
25
|
-
@difference_between_runs = nil
|
26
|
-
@previous_count_hash = @current_count_hash
|
27
|
-
|
28
|
-
class_to_count = Hash.new { |hash, key| hash[key] = 0 }
|
29
|
-
ObjectSpace.each_object { |obj| class_to_count[obj.class] += 1 }
|
30
|
-
|
31
|
-
sorted_class_to_count = class_to_count.sort_by { |k, v| -v }
|
32
|
-
@current_count_hash = Hash[sorted_class_to_count]
|
33
|
-
end
|
34
|
-
|
35
|
-
def dump_status(logger)
|
36
|
-
diff = difference_between_runs
|
37
|
-
most_used_objects = current_count_hash.to_a.sort_by(&:last).reverse[0, 20]
|
38
|
-
|
39
|
-
most_used_objects.each do |object_class, count|
|
40
|
-
delta = diff[object_class]
|
41
|
-
logger.add(log_level(delta), sprintf("\t%s: %d (%+d)", object_class, count, delta))
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
def log_level(delta)
|
47
|
-
delta > 0 ? Logger::WARN : Logger::DEBUG
|
48
|
-
end
|
49
|
-
end
|
data/spec/ethon/curl_spec.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe Ethon::Curl do
|
5
|
-
describe ".init" do
|
6
|
-
before { Ethon::Curl.send(:class_variable_set, :@@initialized, false) }
|
7
|
-
|
8
|
-
context "when global_init fails" do
|
9
|
-
it "raises global init error" do
|
10
|
-
expect(Ethon::Curl).to receive(:global_init).and_return(1)
|
11
|
-
expect{ Ethon::Curl.init }.to raise_error(Ethon::Errors::GlobalInit)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
context "when global_init works" do
|
16
|
-
before { expect(Ethon::Curl).to receive(:global_init).and_return(0) }
|
17
|
-
|
18
|
-
it "doesn't raises global init error" do
|
19
|
-
expect{ Ethon::Curl.init }.to_not raise_error
|
20
|
-
end
|
21
|
-
|
22
|
-
it "logs" do
|
23
|
-
expect(Ethon.logger).to receive(:debug)
|
24
|
-
Ethon::Curl.init
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
context "when global_cleanup is called" do
|
29
|
-
before { expect(Ethon::Curl).to receive(:global_cleanup) }
|
30
|
-
|
31
|
-
it "logs" do
|
32
|
-
expect(Ethon.logger).to receive(:debug).twice
|
33
|
-
Ethon::Curl.init
|
34
|
-
Ethon::Curl.cleanup
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,81 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe Ethon::Easy::Callbacks do
|
5
|
-
let!(:easy) { Ethon::Easy.new }
|
6
|
-
|
7
|
-
describe "#set_callbacks" do
|
8
|
-
before do
|
9
|
-
expect(Ethon::Curl).to receive(:set_option).exactly(3).times
|
10
|
-
end
|
11
|
-
|
12
|
-
it "sets write-, debug-, and headerfunction" do
|
13
|
-
easy.set_callbacks
|
14
|
-
end
|
15
|
-
|
16
|
-
it "resets @response_body" do
|
17
|
-
easy.set_callbacks
|
18
|
-
expect(easy.instance_variable_get(:@response_body)).to eq("")
|
19
|
-
end
|
20
|
-
|
21
|
-
it "resets @response_headers" do
|
22
|
-
easy.set_callbacks
|
23
|
-
expect(easy.instance_variable_get(:@response_headers)).to eq("")
|
24
|
-
end
|
25
|
-
|
26
|
-
it "resets @debug_info" do
|
27
|
-
easy.set_callbacks
|
28
|
-
expect(easy.instance_variable_get(:@debug_info).to_a).to eq([])
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
describe "#progress_callback" do
|
33
|
-
it "returns 0" do
|
34
|
-
expect(easy.progress_callback.call(0,1,1,1,1)).to be(0)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
describe "#body_write_callback" do
|
39
|
-
let(:body_write_callback) { easy.instance_variable_get(:@body_write_callback) }
|
40
|
-
let(:stream) { double(:read_string => "") }
|
41
|
-
context "when body returns not :abort" do
|
42
|
-
it "returns number bigger than 0" do
|
43
|
-
expect(body_write_callback.call(stream, 1, 1, nil) > 0).to be(true)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
context "when body returns :abort" do
|
48
|
-
before do
|
49
|
-
easy.on_body.clear
|
50
|
-
easy.on_body { :abort }
|
51
|
-
end
|
52
|
-
let(:body_write_callback) { easy.instance_variable_get(:@body_write_callback) }
|
53
|
-
|
54
|
-
it "returns -1 to indicate abort to libcurl" do
|
55
|
-
expect(body_write_callback.call(stream, 1, 1, nil)).to eq(-1)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
describe "#header_write_callback" do
|
61
|
-
let(:header_write_callback) { easy.instance_variable_get(:@header_write_callback) }
|
62
|
-
let(:stream) { double(:read_string => "") }
|
63
|
-
context "when header returns not :abort" do
|
64
|
-
it "returns number bigger than 0" do
|
65
|
-
expect(header_write_callback.call(stream, 1, 1, nil) > 0).to be(true)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
context "when header returns :abort" do
|
70
|
-
before do
|
71
|
-
easy.on_headers.clear
|
72
|
-
easy.on_headers { :abort }
|
73
|
-
end
|
74
|
-
let(:header_write_callback) { easy.instance_variable_get(:@header_write_callback) }
|
75
|
-
|
76
|
-
it "returns -1 to indicate abort to libcurl" do
|
77
|
-
expect(header_write_callback.call(stream, 1, 1, nil)).to eq(-1)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe Ethon::Easy::DebugInfo do
|
5
|
-
let(:easy) { Ethon::Easy.new }
|
6
|
-
|
7
|
-
before do
|
8
|
-
easy.url = "http://localhost:3001/"
|
9
|
-
easy.perform
|
10
|
-
end
|
11
|
-
|
12
|
-
describe "#debug_info" do
|
13
|
-
context "when verbose is not set to true" do
|
14
|
-
it "does not save any debug info after a request" do
|
15
|
-
expect(easy.debug_info.to_a.length).to eq(0)
|
16
|
-
expect(easy.debug_info.to_h.values.flatten.length).to eq(0)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
context "when verbose is set to true" do
|
21
|
-
before do
|
22
|
-
easy.verbose = true
|
23
|
-
easy.perform
|
24
|
-
end
|
25
|
-
|
26
|
-
after do
|
27
|
-
easy.verbose = false
|
28
|
-
easy.reset
|
29
|
-
end
|
30
|
-
|
31
|
-
it "saves debug info after a request" do
|
32
|
-
expect(easy.debug_info.to_a.length).to be > 0
|
33
|
-
end
|
34
|
-
|
35
|
-
it "saves request headers" do
|
36
|
-
expect(easy.debug_info.header_out.join).to include('GET / HTTP/1.1')
|
37
|
-
end
|
38
|
-
|
39
|
-
it "saves response headers" do
|
40
|
-
expect(easy.debug_info.header_in.length).to be > 0
|
41
|
-
expect(easy.response_headers).to include(easy.debug_info.header_in.join)
|
42
|
-
end
|
43
|
-
|
44
|
-
it "saves incoming data" do
|
45
|
-
expect(easy.debug_info.data_in.length).to be > 0
|
46
|
-
expect(easy.response_body).to include(easy.debug_info.data_in.join)
|
47
|
-
end
|
48
|
-
|
49
|
-
it "saves debug text" do
|
50
|
-
expect(easy.debug_info.text.length).to be > 0
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe Ethon::Easy::Informations do
|
5
|
-
|
6
|
-
describe "#supports_asynch_dns?" do
|
7
|
-
it "returns boolean" do
|
8
|
-
expect([true, false].include? Ethon::Easy.supports_asynch_dns?).to be_truthy
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
describe "#supports_zlib?" do
|
13
|
-
it "returns boolean" do
|
14
|
-
expect([true, false].include? Ethon::Easy.supports_zlib?).to be_truthy
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
describe "#supports_timeout_ms?" do
|
19
|
-
it "returns boolean" do
|
20
|
-
expect([true, false].include? Ethon::Easy.supports_timeout_ms?).to be_truthy
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
@@ -1,104 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe Ethon::Easy::Form do
|
5
|
-
let(:hash) { {} }
|
6
|
-
let!(:easy) { Ethon::Easy.new }
|
7
|
-
let(:form) { Ethon::Easy::Form.new(easy, hash) }
|
8
|
-
|
9
|
-
describe ".new" do
|
10
|
-
it "assigns attribute to @params" do
|
11
|
-
expect(form.instance_variable_get(:@params)).to eq(hash)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
describe "#first" do
|
16
|
-
it "returns a pointer" do
|
17
|
-
expect(form.first).to be_a(FFI::Pointer)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
describe "#last" do
|
22
|
-
it "returns a pointer" do
|
23
|
-
expect(form.first).to be_a(FFI::Pointer)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
describe "#multipart?" do
|
28
|
-
before { form.instance_variable_set(:@query_pairs, pairs) }
|
29
|
-
|
30
|
-
context "when query_pairs contains string values" do
|
31
|
-
let(:pairs) { [['a', '1'], ['b', '2']] }
|
32
|
-
|
33
|
-
it "returns false" do
|
34
|
-
expect(form.multipart?).to be_falsey
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
context "when query_pairs contains file" do
|
39
|
-
let(:pairs) { [['a', '1'], ['b', ['path', 'encoding', 'abs_path']]] }
|
40
|
-
|
41
|
-
it "returns true" do
|
42
|
-
expect(form.multipart?).to be_truthy
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
context "when options contains multipart=true" do
|
47
|
-
before { form.instance_variable_set(:@multipart, true) }
|
48
|
-
let(:pairs) { [['a', '1'], ['b', '2']] }
|
49
|
-
|
50
|
-
it "returns true" do
|
51
|
-
expect(form.multipart?).to be_truthy
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
describe "#materialize" do
|
57
|
-
before { form.instance_variable_set(:@query_pairs, pairs) }
|
58
|
-
|
59
|
-
context "when query_pairs contains string values" do
|
60
|
-
let(:pairs) { [['a', '1']] }
|
61
|
-
|
62
|
-
it "adds params to form" do
|
63
|
-
expect(Ethon::Curl).to receive(:formadd)
|
64
|
-
form.materialize
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
context "when query_pairs contains nil" do
|
69
|
-
let(:pairs) { [['a', nil]] }
|
70
|
-
|
71
|
-
it "adds params to form" do
|
72
|
-
expect(Ethon::Curl).to receive(:formadd)
|
73
|
-
form.materialize
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
context "when query_pairs contains file" do
|
78
|
-
let(:pairs) { [['a', ["file", "type", "path/file"]]] }
|
79
|
-
|
80
|
-
it "adds file to form" do
|
81
|
-
expect(Ethon::Curl).to receive(:formadd)
|
82
|
-
form.materialize
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
context "when query_pairs contains file and string values" do
|
87
|
-
let(:pairs) { [['a', ["file", "type", "path/file"]], ['b', '1']] }
|
88
|
-
|
89
|
-
it "adds file to form" do
|
90
|
-
expect(Ethon::Curl).to receive(:formadd).twice
|
91
|
-
form.materialize
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
context "when query_pairs contains file, string and int values" do
|
96
|
-
let(:pairs) { [['a', ["file", "type", "path/file"]], ['b', '1'], ['c', 1]] }
|
97
|
-
|
98
|
-
it "adds file to form" do
|
99
|
-
expect(Ethon::Curl).to receive(:formadd).exactly(3).times
|
100
|
-
form.materialize
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
@@ -1,79 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe Ethon::Easy::Header do
|
5
|
-
let(:easy) { Ethon::Easy.new }
|
6
|
-
|
7
|
-
describe "#headers=" do
|
8
|
-
let(:headers) { { 'User-Agent' => 'Ethon' } }
|
9
|
-
|
10
|
-
it "sets header" do
|
11
|
-
expect_any_instance_of(Ethon::Easy).to receive(:set_callbacks)
|
12
|
-
expect(Ethon::Curl).to receive(:set_option)
|
13
|
-
easy.headers = headers
|
14
|
-
end
|
15
|
-
|
16
|
-
context "when requesting" do
|
17
|
-
before do
|
18
|
-
easy.headers = headers
|
19
|
-
easy.url = "http://localhost:3001"
|
20
|
-
easy.perform
|
21
|
-
end
|
22
|
-
|
23
|
-
it "sends" do
|
24
|
-
expect(easy.response_body).to include('"HTTP_USER_AGENT":"Ethon"')
|
25
|
-
end
|
26
|
-
|
27
|
-
context "when header value contains null byte" do
|
28
|
-
let(:headers) { { 'User-Agent' => "Ethon\0" } }
|
29
|
-
|
30
|
-
it "escapes" do
|
31
|
-
expect(easy.response_body).to include('"HTTP_USER_AGENT":"Ethon\\\\0"')
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
context "when header value has leading whitespace" do
|
36
|
-
let(:headers) { { 'User-Agent' => " Ethon" } }
|
37
|
-
|
38
|
-
it "removes" do
|
39
|
-
expect(easy.response_body).to include('"HTTP_USER_AGENT":"Ethon"')
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
context "when header value has traiing whitespace" do
|
44
|
-
let(:headers) { { 'User-Agent' => "Ethon " } }
|
45
|
-
|
46
|
-
it "removes" do
|
47
|
-
expect(easy.response_body).to include('"HTTP_USER_AGENT":"Ethon"')
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
describe "#compose_header" do
|
54
|
-
it "has space in between" do
|
55
|
-
expect(easy.compose_header('a', 'b')).to eq('a: b')
|
56
|
-
end
|
57
|
-
|
58
|
-
context "when value is a symbol" do
|
59
|
-
it "works" do
|
60
|
-
expect{ easy.compose_header('a', :b) }.to_not raise_error
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
describe "#header_list" do
|
66
|
-
context "when no set_headers" do
|
67
|
-
it "returns nil" do
|
68
|
-
expect(easy.header_list).to eq(nil)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
context "when set_headers" do
|
73
|
-
it "returns pointer to header list" do
|
74
|
-
easy.headers = {'User-Agent' => 'Custom'}
|
75
|
-
expect(easy.header_list).to be_a(FFI::Pointer)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|