NAME
YAHC - Yet another HTTP client
SYNOPSIS
use YAHC qw/yahc_reinit_conn/;
my @hosts = ('www.booking.com', 'www.google.com:80');
my ($yahc, $yahc_storage) = YAHC->new({ host => \@hosts });
$yahc->request({ path => '/', host => 'www.reddit.com' });
$yahc->request({ path => '/', host => sub { 'www.reddit.com' } });
$yahc->request({ path => '/', host => \@hosts });
$yahc->request({ path => '/', callback => sub { ... } });
$yahc->request({ path => '/' });
$yahc->request({
path => '/',
callback => sub {
yahc_reinit_conn($_[0], { host => 'www.newtarget.com' })
if $_[0]->{response}{status} == 301;
}
});
$yahc->run;
DESCRIPTION
YAHC is fast & minimal low-level asynchronous HTTP client intended to be used where you control both the client and the server. Is especially suits cases where set of requests need to be executed against group of machines.
It is NOT
a general HTTP user agent, it doesn't support redirects, proxies and any number of other advanced HTTP features like (in roughly descending order of feature completeness) LWP::UserAgent, WWW::Curl, HTTP::Tiny, HTTP::Lite or Furl. This library is basically one step above manually talking HTTP over sockets.
YAHC supports SSL.
STATE MACHINE
Each YAHC connection goes through following list of states in its lifetime:
+-----------------+
+<<-| INITALIZED <-<<+
v +-----------------+ ^
v | ^
v +-------v---------+ ^
+<<-+ RESOLVE DNS +->>+
v +-----------------+ ^
v | ^
v +-------v---------+ ^
+<<-+ WAIT SYNACK +->>+
v +-----------------+ ^
v | ^
Path in v +-------v---------+ ^ Retry
case of +<<-+ CONNECTED +->>+ logic
failure v +-----------------+ ^ path
v | ^
v +-------v---------+ ^
+<<-+ WRITING +->>+
v +-----------------+ ^
v | ^
v +-------v---------+ ^
+<<-+ READING +->>+
v +-----------------+ ^
v | ^
v +-------v---------+ ^
+>>-> USER ACTION +->>+
+-----------------+
|
+-------v---------+
| COMPLETED |
+-----------------+
There are three main paths:
- 1) Normal execution (central line).
-
In normal situation a connection after being initialized goes through state: - RESOLVE DNS - WAIT SYNACK - wait finishing of handshake - CONNECTED - WRITTING - sending request body - READING - awaiting and reading response - USER ACTION - see below - COMPLETED - all done, this is terminal state * SSL connection has extra state SSL_HANDSHAKE after CONNECTED state. * State 'RESOLVE DNS' is not implemented yet.
- 2) Retry path (right line).
-
In case of IO error during normal execution YAHC retries connection C<retries> times. In practise this means that connection goes back to INITIALIZED state. * It's possible for a connection to go directly to COMPLETED state in case of internal error.
- 3) Failure path (left line).
-
If all retry attempts did not succeeded a connection goes to state 'USER ACTION' (see below).
State 'USER ACTION'
METHODS
new
This method creates YAHC object and accompanying storage object:
my ($yahc, $yahc_storage) = YAHC->new();
This's a radical way of solving all possible memleak because of cyclic references in callbacks. Since all references of callbacks are kept in $yahc_storage object it's fine to use YAHC object inside request callback:
my $yahc->request({
callback => sub {
$yahc->stop; # this is fine!!!
},
});
However, use has to garantee that both $yahc and $yahc_storage objects are kept in the same namespace. So, they will be destroyed at the same time.
This method can be passed with all parameters supported by request
. They will be inhereted by all requests.
request
protocol => "HTTP/1.1", # (or "HTTP/1.0")
scheme => "http" or "https"
host => see below,
port => ...,
method => "GET",
path => "/",
query_string => "",
head => [],
body => "",
# timeouts
connect_timeout => undef,
request_timeout => undef,
drain_timeout => undef,
# callbacks
init_callback => undef,
wait_synack_callback => undef,
connected_callback => undef,
writing_callback => undef,
reading_callback => undef,
callback => undef,
Notice how YAHC does not take a full URI string as input, you have to specify the individual parts of the URL. Users who need to parse an existing URI string to produce a request should use the URI module to do so.
For example, to send a request to http://example.com/flower?color=red
, pass the following parameters:
$yach->request({
host => "example.com",
port => "80",
path => "/flower",
query_string => "color=red"
});
YAHC doesn't escape any values for you, it just passes them through as-is. You can easily produce invalid requests if e.g. any of these strings contain a newline, or aren't otherwise properly escaped.
Notice that you do not need to put the leading "?"
character in the query_string
. You do, however, need to properly uri_escape
the content of query_string
.
The value of head
is an ArrayRef
of key-value pairs instead of a HashRef
, this way you can decide in which order the headers are sent, and you can send the same header name multiple times. For example:
head => [
"Content-Type" => "application/json",
"X-Requested-With" => "YAHC",
]
Will produce these request headers:
Content-Type: application/json
X-Requested-With: YAHC
The value of connect_timeout
, request_timeout
and drain_timeout
is in floating point seconds, and is used as the time limit for connecting to the host (reaching CONNECTED state), full request time (reaching COMPLETED state) and sending request to remote site (reaching READING state) respectively. The default value for all is undef
, meaning no timeout limit. If you don't supply these timeouts and the host really is unreachable or slow, we'll reach the TCP timeout limit before returning some other error to you.
The value of init_callback
, wait_synack_callback
, connected_callback
, writing_callback
, reading_callback
is CodeRef to a subroutine which is called upon reaching corresponding state. Any exception thrown in the subroutine moves connection to COMPLETED state effectivly terminating any ongoing IO.
The value of callback
defines main request callback. TODO
We currently don't support servers returning a http body without an accompanying Content-Length
header; bodies MUST have a Content-Length
or we won't pick them up.
drop
Given connection HashRef or conn_id move connection to COMPLETED state (avoiding 'USER ACTION' state) and drop it from internal pool.
run
Start YAHC's loop. The loop stops when all connection complete.
Note that run
can accept two extra parameters: until_state and list of connections. These two parameters tell YAHC to break the loop once specified connections reach desired state.
For example:
$yahc->run(YAHC::State::READING(), $conn_id);
Will loop until connection '$conn_id' move to state READING meaning that the data has been sent to remote side. In order to gather response one should later call:
$yahc->run(YAHC::State::COMPLETED(), $conn_id);
Leaving list of connection empty makes YAHC waiting for all connection reaching needed until_state.
Note that waiting one particular connection to finish doesn't mean that others are not executed. Instead, all active connections are looped at the same time, but YAHC breaks the loop once waited connection reaches needed state.
run_once
Same as run but with EV::RUN_ONCE set. For more details check https://metacpan.org/pod/EV
run_tick
Same as run but with EV::RUN_NOWAIT set. For more details check https://metacpan.org/pod/EV
is_running
Return true if YAHC is running, false otherwise.
loop
Return underlying EV loop object.
EXPORTED FUNCTIONS
yahc_reinit_conn
TODO
yahc_conn_id
Return id of given connection.
yahc_conn_state
Retrun state of given connection
yahc_conn_target
Return selected host and port for current attempt for given connection. Format "host:port". Default port values are omited.
yahc_conn_url
Same as yahc_conn_target
but return full URL
yahc_conn_errors =head2 yahc_conn_last_error
TODO
yahc_conn_timeline
Return timeline of given connection. See more about timeline in description of new
method.
yahc_conn_request
Return request of given connection. See request
.
yahc_conn_response
Return response of given connection. See request
.
REPOSITORY
https://github.com/ikruglov/YAHC
AUTHORS
Ivan Kruglov <ivan.kruglov@yahoo.com>
COPYRIGHT
Copyright (c) 2013 Ivan Kruglov <ivan.kruglov@yahoo.com>
.
ACKNOWLEDGMENT
This module derived lots of ideas, code and docs from Hijk https://github.com/gugod/Hijk. This module was originally developed for Booking.com.
LICENCE
The MIT License
DISCLAIMER OF WARRANTY
BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.