Skip to content

Commit 1e3f796

Browse files
authored
Merge pull request #14 from robmueller/implement-keep-alive-timeout
Implement keep_alive_timeout option
2 parents fecc20f + 985adf8 commit 1e3f796

File tree

2 files changed

+39
-1
lines changed

2 files changed

+39
-1
lines changed

lib/HTTP/Tiny.pm

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ This constructor returns a new HTTP::Tiny object. Valid attributes include:
2424
* C<local_address> — The local IP address to bind to
2525
* C<keep_alive> — Whether to reuse the last connection (if for the same
2626
scheme, host and port) (defaults to 1)
27+
* C<keep_alive_timeout> — How many seconds between a request to keep a
28+
keepalive connection available for (defaults to 0, unlimited)
2729
* C<max_redirect> — Maximum number of redirects allowed (defaults to 5)
2830
* C<max_size> — Maximum response size in bytes (only when not using a data
2931
callback). If defined, requests with responses larger than this will return
@@ -65,6 +67,12 @@ attributes are modified via accessor, or if the process ID or thread ID change,
6567
the persistent connection will be dropped. If you want persistent connections
6668
across multiple destinations, use multiple HTTP::Tiny objects.
6769
70+
The C<keep_alive_timeout> parameter allows you to control how long a
71+
keep alive connection will be considered for reuse. By setting this lower
72+
than the server keep alive time, this allows you to avoid race conditions where
73+
the server closes the connection while preparing to write the request on
74+
a reused persistent connection.
75+
6876
See L</TLS/SSL SUPPORT> for more on the C<verify_SSL> and C<SSL_options>
6977
attributes.
7078
@@ -127,6 +135,7 @@ sub new {
127135
max_redirect => 5,
128136
timeout => defined $args{timeout} ? $args{timeout} : 60,
129137
keep_alive => 1,
138+
keep_alive_timeout => 0,
130139
verify_SSL => defined $args{verify_SSL} ? $args{verify_SSL} : _verify_SSL_default(),
131140
no_proxy => $ENV{no_proxy},
132141
};
@@ -694,6 +703,7 @@ sub _request {
694703
&& $response->{protocol} eq 'HTTP/1.1'
695704
&& ($response->{headers}{connection} || '') ne 'close'
696705
) {
706+
$handle->_update_last_used();
697707
$self->{handle} = $handle;
698708
}
699709
else {
@@ -723,9 +733,12 @@ sub _open_handle {
723733
SSL_options => $self->{SSL_options},
724734
verify_SSL => $self->{verify_SSL},
725735
local_address => $self->{local_address},
726-
keep_alive => $self->{keep_alive}
736+
keep_alive => $self->{keep_alive},
737+
keep_alive_timeout => $self->{keep_alive_timeout}
727738
);
728739

740+
require Time::HiRes if $self->{keep_alive_timeout} > 0;
741+
729742
if ($self->{_has_proxy}{$scheme} && ! grep { $host =~ /\Q$_\E$/ } @{$self->{no_proxy}}) {
730743
return $self->_proxy_connect( $request, $handle );
731744
}
@@ -1621,6 +1634,19 @@ sub can_write {
16211634
return $self->_do_timeout('write', @_)
16221635
}
16231636

1637+
sub _has_keep_alive_expired {
1638+
my $self = shift;
1639+
return unless $self->{keep_alive_timeout} > 0;
1640+
my $now = Time::HiRes::time();
1641+
return $now - ($self->{last_used} || $now) > $self->{keep_alive_timeout};
1642+
}
1643+
1644+
sub _update_last_used {
1645+
my $self = shift;
1646+
return unless $self->{keep_alive_timeout} > 0;
1647+
$self->{last_used} = Time::HiRes::time();
1648+
}
1649+
16241650
sub _assert_ssl {
16251651
my($ok, $reason) = HTTP::Tiny->can_ssl();
16261652
die $reason unless $ok;
@@ -1636,6 +1662,7 @@ sub can_reuse {
16361662
|| $host ne $self->{host}
16371663
|| $port ne $self->{port}
16381664
|| $peer ne $self->{peer}
1665+
|| $self->_has_keep_alive_expired()
16391666
|| eval { $self->can_read(0) }
16401667
|| $@ ;
16411668
return 1;

t/170_keepalive.t

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,17 @@ new_ht();
5656
$h->default_headers({ 'X-Foo' => 'Bar' });
5757
test_ht( "Default headers change", 1, 'http://foo.com' );
5858

59+
new_ht();
60+
$h->{handle}->{last_used} = time - 3600;
61+
test_ht( "Unlimited keep_alive_timeout", 1, 'http://foo.com' );
62+
63+
new_ht(keep_alive_timeout => 2);
64+
test_ht( "Less than than keep_alive_timeout", 1, 'http://foo.com' );
65+
66+
new_ht(keep_alive_timeout => 2);
67+
$h->{handle}->{last_used} = time - 3;
68+
test_ht( "Longer than than keep_alive_timeout", 0, 'http://foo.com' );
69+
5970
new_ht();
6071
$h->{handle}->close;
6172
test_ht( "Socket closed", 0, 'http://foo.com' );

0 commit comments

Comments
 (0)