Skip to content

Commit c5b9a7f

Browse files
committed
Be stricter of allowed values for timeout
1 parent 4e6d386 commit c5b9a7f

File tree

3 files changed

+77
-10
lines changed

3 files changed

+77
-10
lines changed

lib/http/chainable.rb

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,25 +90,38 @@ def build_request(*args)
9090
# @overload timeout(global_timeout)
9191
# Adds a global timeout to the full request
9292
# @param [Numeric] global_timeout
93+
PER_OPERATION_KEYS = Set.new(%i[read write connect])
9394
def timeout(options)
9495
klass, options = case options
95-
when Numeric then [HTTP::Timeout::Global, {global_timeout: options}]
96+
when Numeric then [HTTP::Timeout::Global, {:global_timeout => options}]
9697
when Hash
97-
options = options.dup
98-
%i[read write connect].each do |k|
99-
next unless options.key? k
98+
options = options.dup
99+
%i[read write connect].each do |k|
100+
next unless options.key? k
100101

101-
options["#{k}_timeout".to_sym] = options.delete k
102-
end
102+
if options.key?("#{k}_timeout".to_sym)
103+
raise ArgumentError, "can't pass both #{k} and #{"#{k}_timeout".to_sym}"
104+
end
103105

104-
[HTTP::Timeout::PerOperation, options.dup]
105-
when :null then [HTTP::Timeout::Null, {}]
106+
options["#{k}_timeout".to_sym] = options.delete k
107+
end
108+
109+
options.each do |key, value|
110+
unless HTTP::Timeout::PerOperation::SETTINGS.member?(key) && value.is_a?(Numeric)
111+
raise ArgumentError, "invalid option #{key.inspect}, must be numeric " \
112+
"`.timeout(connect: x, write: y, read: z)`."
113+
end
114+
end
115+
116+
raise ArgumentError, "at least one option" if options.empty?
117+
118+
[HTTP::Timeout::PerOperation, options.dup]
119+
when :null then [HTTP::Timeout::Null, {}]
106120
else raise ArgumentError, "Use `.timeout(:null)`, " \
107121
"`.timeout(global_timeout_in_seconds)` or " \
108122
"`.timeout(connect: x, write: y, read: z)`."
109123
end
110124

111-
112125
branch default_options.merge(
113126
:timeout_class => klass,
114127
:timeout_options => options

lib/http/timeout/per_operation.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class PerOperation < Null
1111
WRITE_TIMEOUT = 0.25
1212
READ_TIMEOUT = 0.25
1313

14+
SETTINGS = Set.new(%i[read_timeout write_timeout connect_timeout])
1415
def initialize(*args)
1516
super
1617

spec/lib/http_spec.rb

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,49 @@
304304
end
305305

306306
it "sets given timeout options" do
307+
client = HTTP.timeout :read => 125
308+
307309
expect(client.default_options.timeout_options).
308-
to eq :read_timeout => 123
310+
to eq :read_timeout => 125
311+
end
312+
313+
it "sets given timeout options" do
314+
client = HTTP.timeout :read_timeout => 321
315+
316+
expect(client.default_options.timeout_options).
317+
to eq :read_timeout => 321
318+
end
319+
320+
it "sets all available options" do
321+
client = HTTP.timeout :read => 123, :write => 12, :connect => 1
322+
323+
expect(client.default_options.timeout_options).
324+
to eq(:connect_timeout => 1, :write_timeout => 12, :read_timeout => 123)
325+
end
326+
327+
it "raises an error is empty hash is passed" do
328+
expect { HTTP.timeout({}) }
329+
.to raise_error(ArgumentError)
330+
end
331+
332+
it "raises if an invalid key is passed" do
333+
expect { HTTP.timeout({:timeout => 2}) }
334+
.to raise_error(ArgumentError)
335+
end
336+
337+
it "raises if both read and read_timeout is passed" do
338+
expect { HTTP.timeout({:read => 2, :read_timeout => 2}) }
339+
.to raise_error(ArgumentError)
340+
end
341+
342+
it "raises if a string is passed as read timeout" do
343+
expect { HTTP.timeout({:connect => 1, :read => "2"}) }
344+
.to raise_error(ArgumentError)
345+
end
346+
347+
it "don't accept string keys" do
348+
expect { HTTP.timeout({:connect => 1, "read" => 2}) }
349+
.to raise_error(ArgumentError)
309350
end
310351
end
311352

@@ -330,6 +371,18 @@
330371
expect(client.default_options.timeout_options).
331372
to eq :global_timeout => 123
332373
end
374+
375+
it "accepts floats argument" do
376+
client = HTTP.timeout 2.5
377+
378+
expect(client.default_options.timeout_options).
379+
to eq(:global_timeout => 2.5)
380+
end
381+
382+
it "raises expect if a string is passed" do
383+
expect { HTTP.timeout "1" }
384+
.to raise_error(ArgumentError)
385+
end
333386
end
334387
end
335388

0 commit comments

Comments
 (0)