Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Net::HTTP#write_timeout method (Ruby 2.6.0) #647

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ rvm:
- 2.3.8
- 2.4.5
- 2.5.3
- 2.6.0
- 2.6.1
bundler_args: --without development
before_install: gem install bundler -v '< 2'
13 changes: 13 additions & 0 deletions lib/httparty.rb
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,19 @@ def read_timeout(t)
default_options[:read_timeout] = t
end

# Allows setting a default write_timeout for all HTTP calls in seconds
# Supported by Ruby > 2.6.0
#
# class Foo
# include HTTParty
# write_timeout 10
# end
def write_timeout(t)
raise ArgumentError, 'write_timeout must be an integer or float' unless t && (t.is_a?(Integer) || t.is_a?(Float))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could extract it to method and refactor other timeout methods as well, please?
Something like validate_timeout_argument(timeout) or check_timeout_argument(timeout)

default_options[:write_timeout] = t
end


# Set an output stream for debugging, defaults to $stderr.
# The output stream is passed on to Net::HTTP#set_debug_output.
#
Expand Down
15 changes: 15 additions & 0 deletions lib/httparty/connection_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ module HTTParty
# * :+timeout+: timeout in seconds
# * :+open_timeout+: http connection open_timeout in seconds, overrides timeout if set
# * :+read_timeout+: http connection read_timeout in seconds, overrides timeout if set
# * :+write_timeout+: http connection write_timeout in seconds, overrides timeout if set (Ruby >= 2.6.0 required)
# * :+debug_output+: see HTTParty::ClassMethods.debug_output.
# * :+cert_store+: contains certificate data. see method 'attach_ssl_certificates'
# * :+pem+: contains pem client certificate data. see method 'attach_ssl_certificates'
Expand Down Expand Up @@ -116,6 +117,12 @@ def connection
if options[:timeout] && (options[:timeout].is_a?(Integer) || options[:timeout].is_a?(Float))
http.open_timeout = options[:timeout]
http.read_timeout = options[:timeout]

if RUBY_VERSION >= "2.6.0"
http.write_timeout = options[:timeout]
else
Kernel.warn("Warning: option :write_timeout requires Ruby version 2.6.0 or later")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a timeout for everything, we should set write_timeout if we can, but there is no need to add warning here.

end
end

if options[:read_timeout] && (options[:read_timeout].is_a?(Integer) || options[:read_timeout].is_a?(Float))
Expand All @@ -126,6 +133,14 @@ def connection
http.open_timeout = options[:open_timeout]
end

if options[:write_timeout] && (options[:write_timeout].is_a?(Integer) || options[:write_timeout].is_a?(Float))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a lot of duplication here. Would be better to extract it to a private method add_timeout?(options[:write_timeout])

if RUBY_VERSION >= "2.6.0"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a lot of duplication as well. Probably a would be better, to create a wrapper method like so (just an idea, not sure about the naming)

for_versions_above('2.6', option: :write_timeout) do
  http.write_timeout = options[:write_timeout]
end

http.write_timeout = options[:write_timeout]
else
Kernel.warn("Warning: option :write_timeout requires Ruby version 2.6.0 or later")
end
end

if options[:debug_output]
http.set_debug_output(options[:debug_output])
end
Expand Down
130 changes: 128 additions & 2 deletions spec/httparty/connection_adapter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
)
expect(http).not_to receive(:open_timeout=)
expect(http).not_to receive(:read_timeout=)
expect(http).not_to receive(:write_timeout=)
allow(Net::HTTP).to receive_messages(new: http)

adapter.connection
Expand All @@ -150,6 +151,13 @@
subject { super().read_timeout }
it { is_expected.to eq(5) }
end

if RUBY_VERSION >= '2.6.0'
describe '#write_timeout' do
subject { super().write_timeout }
it { is_expected.to eq(5) }
end
end
end

context "and timeout is a string" do
Expand All @@ -164,6 +172,7 @@
)
expect(http).not_to receive(:open_timeout=)
expect(http).not_to receive(:read_timeout=)
expect(http).not_to receive(:write_timeout=)
allow(Net::HTTP).to receive_messages(new: http)

adapter.connection
Expand Down Expand Up @@ -191,6 +200,19 @@
allow(Net::HTTP).to receive_messages(new: http)
adapter.connection
end

it "should not set the write_timeout" do
http = double(
"http",
:null_object => true,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, use colon notation for symbol keyword arguments

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @TheSmartnik thanks for the comments 👍 I addressed all of them, but this one. I would like to discuss one thing here.

Due to the fact that the doubles contain names with special characters (e.g. :use_ssl= => false) converting them to Ruby 1.9 hash syntax would require using ' to wrap them. Not sure if it is worth doing in this case. I am totally fine with leaving the current syntax.

Besides that, feel free to take a look at the pushed commits. The code looks better now IMO.

:use_ssl= => false,
:use_ssl? => false,
:read_timeout= => 0
)
expect(http).not_to receive(:write_timeout=)
allow(Net::HTTP).to receive_messages(new: http)
adapter.connection
end
end

context "when timeout is set and read_timeout is set to 6 seconds" do
Expand All @@ -201,6 +223,13 @@
it { is_expected.to eq(5) }
end

if RUBY_VERSION >= '2.6.0'
describe '#write_timeout' do
subject { super().write_timeout }
it { is_expected.to eq(5) }
end
end

describe '#read_timeout' do
subject { super().read_timeout }
it { is_expected.to eq(6) }
Expand All @@ -213,10 +242,14 @@
:use_ssl= => false,
:use_ssl? => false,
:read_timeout= => 0,
:open_timeout= => 0
:open_timeout= => 0,
:write_timeout= => 0,
)
expect(http).to receive(:open_timeout=)
expect(http).to receive(:read_timeout=).twice
if RUBY_VERSION >= '2.6.0'
expect(http).to receive(:write_timeout=)
end
allow(Net::HTTP).to receive_messages(new: http)
adapter.connection
end
Expand All @@ -242,6 +275,19 @@
allow(Net::HTTP).to receive_messages(new: http)
adapter.connection
end

it "should not set the write_timeout" do
http = double(
"http",
:null_object => true,
:use_ssl= => false,
:use_ssl? => false,
:open_timeout= => 0
)
expect(http).not_to receive(:write_timeout=)
allow(Net::HTTP).to receive_messages(new: http)
adapter.connection
end
end

context "when timeout is set and open_timeout is set to 7 seconds" do
Expand All @@ -252,6 +298,13 @@
it { is_expected.to eq(7) }
end

if RUBY_VERSION >= '2.6.0'
describe '#write_timeout' do
subject { super().write_timeout }
it { is_expected.to eq(5) }
end
end

describe '#read_timeout' do
subject { super().read_timeout }
it { is_expected.to eq(5) }
Expand All @@ -264,15 +317,88 @@
:use_ssl= => false,
:use_ssl? => false,
:read_timeout= => 0,
:open_timeout= => 0
:open_timeout= => 0,
:write_timeout= => 0,
)
expect(http).to receive(:open_timeout=).twice
expect(http).to receive(:read_timeout=)
if RUBY_VERSION >= '2.6.0'
expect(http).to receive(:write_timeout=)
end
allow(Net::HTTP).to receive_messages(new: http)
adapter.connection
end
end

if RUBY_VERSION >= '2.6.0'
context "when timeout is not set and write_timeout is set to 8 seconds" do
let(:options) { {write_timeout: 8} }

describe '#write_timeout' do
subject { super().write_timeout }
it { is_expected.to eq(8) }
end

it "should not set the open timeout" do
http = double(
"http",
:null_object => true,
:use_ssl= => false,
:use_ssl? => false,
:read_timeout= => 0,
:open_timeout= => 0,
:write_timeout= => 0,

)
expect(http).not_to receive(:open_timeout=)
allow(Net::HTTP).to receive_messages(new: http)
adapter.connection
end

it "should not set the read timeout" do
http = double(
"http",
:null_object => true,
:use_ssl= => false,
:use_ssl? => false,
:read_timeout= => 0,
:open_timeout= => 0,
:write_timeout= => 0,

)
expect(http).not_to receive(:read_timeout=)
allow(Net::HTTP).to receive_messages(new: http)
adapter.connection
end
end

context "when timeout is set and write_timeout is set to 8 seconds" do
let(:options) { {timeout: 2, write_timeout: 8} }

describe '#write_timeout' do
subject { super().write_timeout }
it { is_expected.to eq(8) }
end

it "should override the timeout option" do
http = double(
"http",
:null_object => true,
:use_ssl= => false,
:use_ssl? => false,
:read_timeout= => 0,
:open_timeout= => 0,
:write_timeout= => 0,
)
expect(http).to receive(:read_timeout=)
expect(http).to receive(:open_timeout=)
expect(http).to receive(:write_timeout=).twice
allow(Net::HTTP).to receive_messages(new: http)
adapter.connection
end
end
end

context "when debug_output" do
let(:http) { Net::HTTP.new(uri) }
before do
Expand Down