1 | package Mojo::UserAgent;
|
---|
2 | use Mojo::Base 'Mojo::EventEmitter';
|
---|
3 |
|
---|
4 | # "Fry: Since when is the Internet about robbing people of their privacy?
|
---|
5 | # Bender: August 6, 1991."
|
---|
6 | use Mojo::IOLoop;
|
---|
7 | use Mojo::IOLoop::Stream::HTTPClient;
|
---|
8 | use Mojo::IOLoop::Stream::WebSocketClient;
|
---|
9 | use Mojo::Promise;
|
---|
10 | use Mojo::Util 'monkey_patch';
|
---|
11 | use Mojo::UserAgent::CookieJar;
|
---|
12 | use Mojo::UserAgent::Proxy;
|
---|
13 | use Mojo::UserAgent::Server;
|
---|
14 | use Mojo::UserAgent::Transactor;
|
---|
15 | use Scalar::Util 'weaken';
|
---|
16 |
|
---|
17 | use constant DEBUG => $ENV{MOJO_CLIENT_DEBUG} || 0;
|
---|
18 |
|
---|
19 | has ca => sub { $ENV{MOJO_CA_FILE} };
|
---|
20 | has cert => sub { $ENV{MOJO_CERT_FILE} };
|
---|
21 | has connect_timeout => sub { $ENV{MOJO_CONNECT_TIMEOUT} || 10 };
|
---|
22 | has cookie_jar => sub { Mojo::UserAgent::CookieJar->new };
|
---|
23 | has inactivity_timeout => sub { $ENV{MOJO_INACTIVITY_TIMEOUT} // 20 };
|
---|
24 | has insecure => sub { $ENV{MOJO_INSECURE} };
|
---|
25 | has [qw(local_address max_response_size)];
|
---|
26 | has ioloop => sub { Mojo::IOLoop->new };
|
---|
27 | has key => sub { $ENV{MOJO_KEY_FILE} };
|
---|
28 | has max_connections => 5;
|
---|
29 | has max_redirects => sub { $ENV{MOJO_MAX_REDIRECTS} || 0 };
|
---|
30 | has proxy => sub { Mojo::UserAgent::Proxy->new };
|
---|
31 | has request_timeout => sub { $ENV{MOJO_REQUEST_TIMEOUT} // 0 };
|
---|
32 | has server => sub { Mojo::UserAgent::Server->new(ioloop => shift->ioloop) };
|
---|
33 | has transactor => sub { Mojo::UserAgent::Transactor->new };
|
---|
34 |
|
---|
35 | # Common HTTP methods
|
---|
36 | for my $name (qw(DELETE GET HEAD OPTIONS PATCH POST PUT)) {
|
---|
37 | monkey_patch __PACKAGE__, lc $name, sub {
|
---|
38 | my ($self, $cb) = (shift, ref $_[-1] eq 'CODE' ? pop : undef);
|
---|
39 | return $self->start($self->build_tx($name, @_), $cb);
|
---|
40 | };
|
---|
41 | monkey_patch __PACKAGE__, lc($name) . '_p', sub {
|
---|
42 | my $self = shift;
|
---|
43 | return $self->start_p($self->build_tx($name, @_));
|
---|
44 | };
|
---|
45 | }
|
---|
46 |
|
---|
47 | sub DESTROY { Mojo::Util::_global_destruction() or shift->_cleanup }
|
---|
48 |
|
---|
49 | sub build_tx { shift->transactor->tx(@_) }
|
---|
50 | sub build_websocket_tx { shift->transactor->websocket(@_) }
|
---|
51 |
|
---|
52 | sub start {
|
---|
53 | my ($self, $tx, $cb) = @_;
|
---|
54 |
|
---|
55 | # Fork-safety
|
---|
56 | $self->_cleanup->server->restart unless ($self->{pid} //= $$) eq $$;
|
---|
57 |
|
---|
58 | # Non-blocking
|
---|
59 | if ($cb) {
|
---|
60 | warn "-- Non-blocking request (@{[_url($tx)]})\n" if DEBUG;
|
---|
61 | return $self->_start(Mojo::IOLoop->singleton, $tx, $cb);
|
---|
62 | }
|
---|
63 |
|
---|
64 | # Blocking
|
---|
65 | warn "-- Blocking request (@{[_url($tx)]})\n" if DEBUG;
|
---|
66 | $self->_start($self->ioloop, $tx => sub { shift->ioloop->stop; $tx = shift });
|
---|
67 | $self->ioloop->start;
|
---|
68 |
|
---|
69 | return $tx;
|
---|
70 | }
|
---|
71 |
|
---|
72 | sub start_p {
|
---|
73 | my ($self, $tx) = @_;
|
---|
74 |
|
---|
75 | my $promise = Mojo::Promise->new;
|
---|
76 | $self->start(
|
---|
77 | $tx => sub {
|
---|
78 | my ($self, $tx) = @_;
|
---|
79 | my $err = $tx->error;
|
---|
80 | return $promise->reject($err->{message}) if $err && !$err->{code};
|
---|
81 | return $promise->reject('WebSocket handshake failed')
|
---|
82 | if $tx->req->is_handshake && !$tx->is_websocket;
|
---|
83 | $promise->resolve($tx);
|
---|
84 | }
|
---|
85 | );
|
---|
86 |
|
---|
87 | return $promise;
|
---|
88 | }
|
---|
89 |
|
---|
90 | sub websocket {
|
---|
91 | my ($self, $cb) = (shift, pop);
|
---|
92 | $self->start($self->build_websocket_tx(@_), $cb);
|
---|
93 | }
|
---|
94 |
|
---|
95 | sub websocket_p {
|
---|
96 | my $self = shift;
|
---|
97 | return $self->start_p($self->build_websocket_tx(@_));
|
---|
98 | }
|
---|
99 |
|
---|
100 | sub _cleanup {
|
---|
101 | my $self = shift;
|
---|
102 | delete $self->{pid};
|
---|
103 | $self->_error($_, 'Premature connection close')
|
---|
104 | for keys %{$self->{connections} || {}};
|
---|
105 | return $self;
|
---|
106 | }
|
---|
107 |
|
---|
108 | sub _connect {
|
---|
109 | my ($self, $loop, $peer, $tx, $handle, $cb) = @_;
|
---|
110 |
|
---|
111 | my $t = $self->transactor;
|
---|
112 | my ($proto, $host, $port) = $peer ? $t->peer($tx) : $t->endpoint($tx);
|
---|
113 |
|
---|
114 | my %options = (
|
---|
115 | timeout => $self->connect_timeout,
|
---|
116 | stream_class => 'Mojo::IOLoop::Stream::HTTPClient'
|
---|
117 | );
|
---|
118 | if ($proto eq 'http+unix') { $options{path} = $host }
|
---|
119 | else { @options{qw(address port)} = ($host, $port) }
|
---|
120 | if (my $local = $self->local_address) { $options{local_address} = $local }
|
---|
121 | $options{handle} = $handle if $handle;
|
---|
122 |
|
---|
123 | # SOCKS
|
---|
124 | if ($proto eq 'socks') {
|
---|
125 | @options{qw(socks_address socks_port)} = @options{qw(address port)};
|
---|
126 | ($proto, @options{qw(address port)}) = $t->endpoint($tx);
|
---|
127 | my $userinfo = $tx->req->via_proxy(0)->proxy->userinfo;
|
---|
128 | @options{qw(socks_user socks_pass)} = split ':', $userinfo if $userinfo;
|
---|
129 | }
|
---|
130 |
|
---|
131 | # TLS
|
---|
132 | if ($options{tls} = $proto eq 'https') {
|
---|
133 | map { $options{"tls_$_"} = $self->$_ } qw(ca cert key);
|
---|
134 | $options{tls_verify} = 0x00 if $self->insecure;
|
---|
135 | }
|
---|
136 |
|
---|
137 | weaken $self;
|
---|
138 | my $id;
|
---|
139 | return $id = $loop->client(
|
---|
140 | %options => sub {
|
---|
141 | my ($loop, $err, $stream) = @_;
|
---|
142 |
|
---|
143 | # Connection error
|
---|
144 | return unless $self;
|
---|
145 | return $self->_error($id, $err) if $err;
|
---|
146 |
|
---|
147 | # Connection established
|
---|
148 | $stream->on(timeout => sub { $self->_error($id, 'Inactivity timeout') });
|
---|
149 | $stream->on(close => sub { $self && $self->_finish($id, 1) });
|
---|
150 | $stream->on(error => sub { $self && $self->_error($id, pop) });
|
---|
151 | $stream->on(upgrade => sub { $self->_upgrade($id, pop) });
|
---|
152 | $self->$cb($id);
|
---|
153 | }
|
---|
154 | );
|
---|
155 | }
|
---|
156 |
|
---|
157 | sub _connect_proxy {
|
---|
158 | my ($self, $loop, $old, $cb) = @_;
|
---|
159 |
|
---|
160 | # Start CONNECT request
|
---|
161 | return undef unless my $new = $self->transactor->proxy_connect($old);
|
---|
162 | return $self->_start(
|
---|
163 | ($loop, $new) => sub {
|
---|
164 | my ($self, $tx) = @_;
|
---|
165 |
|
---|
166 | # CONNECT failed
|
---|
167 | $old->previous($tx)->req->via_proxy(0);
|
---|
168 | my $id = $tx->connection;
|
---|
169 | if ($tx->error || !$tx->res->is_success || !$tx->keep_alive) {
|
---|
170 | $old->res->error({message => 'Proxy connection failed'});
|
---|
171 | $self->_remove($id) if $id;
|
---|
172 | return $self->$cb($old);
|
---|
173 | }
|
---|
174 |
|
---|
175 | # Start real transaction without TLS upgrade
|
---|
176 | return $self->_start($loop, $old->connection($id), $cb)
|
---|
177 | unless $tx->req->url->protocol eq 'https';
|
---|
178 |
|
---|
179 | # TLS upgrade before starting the real transaction
|
---|
180 | my $handle = $loop->stream($id)->steal_handle;
|
---|
181 | $self->_remove($id);
|
---|
182 | $id = $self->_connect($loop, 0, $old, $handle,
|
---|
183 | sub { shift->_start($loop, $old->connection($id), $cb) });
|
---|
184 | $self->{connections}{$id} = {cb => $cb, ioloop => $loop, tx => $old};
|
---|
185 | }
|
---|
186 | );
|
---|
187 | }
|
---|
188 |
|
---|
189 | sub _connected {
|
---|
190 | my ($self, $id) = @_;
|
---|
191 |
|
---|
192 | my $c = $self->{connections}{$id};
|
---|
193 | my $stream = $c->{ioloop}->stream($id)->timeout($self->inactivity_timeout)
|
---|
194 | ->request_timeout($self->request_timeout);
|
---|
195 | my $tx = $c->{tx}->connection($id);
|
---|
196 |
|
---|
197 | weaken $self;
|
---|
198 | $tx->on(finish => sub { $self->_finish($id) });
|
---|
199 | $stream->process($tx);
|
---|
200 | }
|
---|
201 |
|
---|
202 | sub _connection {
|
---|
203 | my ($self, $loop, $tx, $cb) = @_;
|
---|
204 |
|
---|
205 | # Reuse connection
|
---|
206 | my ($proto, $host, $port) = $self->transactor->endpoint($tx);
|
---|
207 | my $id = $tx->connection || $self->_dequeue($loop, "$proto:$host:$port", 1);
|
---|
208 | if ($id) {
|
---|
209 | warn "-- Reusing connection $id ($proto://$host:$port)\n" if DEBUG;
|
---|
210 | @{$self->{connections}{$id}}{qw(cb tx)} = ($cb, $tx);
|
---|
211 | $tx->kept_alive(1) unless $tx->connection;
|
---|
212 | $self->_connected($id);
|
---|
213 | return $id;
|
---|
214 | }
|
---|
215 |
|
---|
216 | # CONNECT request to proxy required
|
---|
217 | if (my $id = $self->_connect_proxy($loop, $tx, $cb)) { return $id }
|
---|
218 |
|
---|
219 | # New connection
|
---|
220 | $tx->res->error({message => "Unsupported protocol: $proto"})
|
---|
221 | and return $loop->next_tick(sub { $self->$cb($tx) })
|
---|
222 | unless $proto eq 'http' || $proto eq 'https' || $proto eq 'http+unix';
|
---|
223 | $id = $self->_connect($loop, 1, $tx, undef, \&_connected);
|
---|
224 | warn "-- Connect $id ($proto://$host:$port)\n" if DEBUG;
|
---|
225 | $self->{connections}{$id} = {cb => $cb, ioloop => $loop, tx => $tx};
|
---|
226 |
|
---|
227 | return $id;
|
---|
228 | }
|
---|
229 |
|
---|
230 | sub _dequeue {
|
---|
231 | my ($self, $loop, $name, $test) = @_;
|
---|
232 |
|
---|
233 | my $old = $self->{queue}{$loop} ||= [];
|
---|
234 | my ($found, @new);
|
---|
235 | for my $queued (@$old) {
|
---|
236 | push @new, $queued and next if $found || !grep { $_ eq $name } @$queued;
|
---|
237 |
|
---|
238 | # Search for id/name and sort out corrupted connections if necessary
|
---|
239 | next unless my $stream = $loop->stream($queued->[1]);
|
---|
240 | $test && $stream->is_readable ? $stream->close : ($found = $queued->[1]);
|
---|
241 | }
|
---|
242 | @$old = @new;
|
---|
243 |
|
---|
244 | return $found;
|
---|
245 | }
|
---|
246 |
|
---|
247 | sub _error {
|
---|
248 | my ($self, $id, $err) = @_;
|
---|
249 | my $tx = $self->{connections}{$id}{tx};
|
---|
250 | $tx->closed->res->finish->error({message => $err}) if $tx;
|
---|
251 | $self->_finish($id, 1);
|
---|
252 | }
|
---|
253 |
|
---|
254 | sub _finish {
|
---|
255 | my ($self, $id, $close) = @_;
|
---|
256 |
|
---|
257 | return unless my $c = $self->{connections}{$id};
|
---|
258 | return $self->_reuse($id, $close) unless my $tx = $c->{tx};
|
---|
259 |
|
---|
260 | $self->cookie_jar->collect($tx);
|
---|
261 |
|
---|
262 | $self->_reuse($id, $close) unless uc $tx->req->method eq 'CONNECT';
|
---|
263 | $c->{cb}($self, $tx) unless $self->_redirect($c, $tx);
|
---|
264 | }
|
---|
265 |
|
---|
266 | sub _redirect {
|
---|
267 | my ($self, $c, $old) = @_;
|
---|
268 | return undef unless my $new = $self->transactor->redirect($old);
|
---|
269 | return undef unless @{$old->redirects} < $self->max_redirects;
|
---|
270 | return $self->_start($c->{ioloop}, $new, delete $c->{cb});
|
---|
271 | }
|
---|
272 |
|
---|
273 | sub _remove {
|
---|
274 | my ($self, $id) = @_;
|
---|
275 | my $c = delete $self->{connections}{$id};
|
---|
276 | $self->_dequeue($c->{ioloop}, $id);
|
---|
277 | $c->{ioloop}->remove($id);
|
---|
278 | }
|
---|
279 |
|
---|
280 | sub _reuse {
|
---|
281 | my ($self, $id, $close) = @_;
|
---|
282 |
|
---|
283 | # Connection close
|
---|
284 | my $c = $self->{connections}{$id};
|
---|
285 | my $tx = delete $c->{tx};
|
---|
286 | my $max = $self->max_connections;
|
---|
287 | return $self->_remove($id) if $close || !$tx || !$max;
|
---|
288 |
|
---|
289 | # Keep connection alive
|
---|
290 | my $queue = $self->{queue}{$c->{ioloop}} ||= [];
|
---|
291 | $self->_remove(shift(@$queue)->[1]) while @$queue && @$queue >= $max;
|
---|
292 | push @$queue, [join(':', $self->transactor->endpoint($tx)), $id];
|
---|
293 | }
|
---|
294 |
|
---|
295 | sub _start {
|
---|
296 | my ($self, $loop, $tx, $cb) = @_;
|
---|
297 |
|
---|
298 | # Application serve
|
---|
299 | my $url = $tx->req->url;
|
---|
300 | unless ($url->is_abs) {
|
---|
301 | my $base
|
---|
302 | = $loop == $self->ioloop ? $self->server->url : $self->server->nb_url;
|
---|
303 | $url->scheme($base->scheme)->host($base->host)->port($base->port);
|
---|
304 | }
|
---|
305 |
|
---|
306 | $_->prepare($tx) for $self->proxy, $self->cookie_jar;
|
---|
307 | my $max = $self->max_response_size;
|
---|
308 | $tx->res->max_message_size($max) if defined $max;
|
---|
309 |
|
---|
310 | $self->emit(start => $tx);
|
---|
311 | return $self->_connection($loop, $tx, $cb);
|
---|
312 | }
|
---|
313 |
|
---|
314 | sub _upgrade {
|
---|
315 | my ($self, $id, $ws) = @_;
|
---|
316 |
|
---|
317 | my $c = delete $self->{connections}{$id};
|
---|
318 | my $loop = $c->{ioloop};
|
---|
319 | my $timeout = $loop->stream($id)->timeout;
|
---|
320 | my $stream = $loop->transition($id, 'Mojo::IOLoop::Stream::WebSocketClient');
|
---|
321 | $stream->timeout($timeout);
|
---|
322 |
|
---|
323 | weaken $self;
|
---|
324 | $stream->on(timeout => sub { $self->_error($id, 'Inactivity timeout') });
|
---|
325 | $stream->on(close => sub { $self && $self->_remove($id) });
|
---|
326 |
|
---|
327 | $self->cookie_jar->collect($ws);
|
---|
328 |
|
---|
329 | $c->{cb}($self, $c->{tx} = $ws);
|
---|
330 | $self->{connections}{$id} = $c;
|
---|
331 | $stream->process($ws);
|
---|
332 | }
|
---|
333 |
|
---|
334 | sub _url { shift->req->url->to_abs }
|
---|
335 |
|
---|
336 | 1;
|
---|
337 |
|
---|
338 | =encoding utf8
|
---|
339 |
|
---|
340 | =head1 NAME
|
---|
341 |
|
---|
342 | Mojo::UserAgent - Non-blocking I/O HTTP and WebSocket user agent
|
---|
343 |
|
---|
344 | =head1 SYNOPSIS
|
---|
345 |
|
---|
346 | use Mojo::UserAgent;
|
---|
347 |
|
---|
348 | # Fine grained response handling (dies on connection errors)
|
---|
349 | my $ua = Mojo::UserAgent->new;
|
---|
350 | my $res = $ua->get('mojolicious.org/perldoc')->result;
|
---|
351 | if ($res->is_success) { say $res->body }
|
---|
352 | elsif ($res->is_error) { say $res->message }
|
---|
353 | elsif ($res->code == 301) { say $res->headers->location }
|
---|
354 | else { say 'Whatever...' }
|
---|
355 |
|
---|
356 | # Say hello to the Unicode snowman and include an Accept header
|
---|
357 | say $ua->get('www.â.net?hello=there' => {Accept => '*/*'})->result->body;
|
---|
358 |
|
---|
359 | # Extract data from HTML and XML resources with CSS selectors
|
---|
360 | say $ua->get('www.perl.org')->result->dom->at('title')->text;
|
---|
361 |
|
---|
362 | # Scrape the latest headlines from a news site
|
---|
363 | say $ua->get('blogs.perl.org')
|
---|
364 | ->result->dom->find('h2 > a')->map('text')->join("\n");
|
---|
365 |
|
---|
366 | # IPv6 PUT request with Content-Type header and content
|
---|
367 | my $tx = $ua->put('[::1]:3000' => {'Content-Type' => 'text/plain'} => 'Hi!');
|
---|
368 |
|
---|
369 | # Quick JSON API request with Basic authentication
|
---|
370 | my $value = $ua->get('https://sri:[email protected]/test.json')->result->json;
|
---|
371 |
|
---|
372 | # JSON POST (application/json) with TLS certificate authentication
|
---|
373 | my $tx = $ua->cert('tls.crt')->key('tls.key')
|
---|
374 | ->post('https://example.com' => json => {top => 'secret'});
|
---|
375 |
|
---|
376 | # Search DuckDuckGo anonymously through Tor
|
---|
377 | $ua->proxy->http('socks://127.0.0.1:9050');
|
---|
378 | say $ua->get('api.3g2upl4pq6kufc4m.onion/?q=mojolicious&format=json')
|
---|
379 | ->result->json('/Abstract');
|
---|
380 |
|
---|
381 | # GET request via UNIX domain socket "/tmp/myapp.sock" (percent encoded slash)
|
---|
382 | say $ua->get('http+unix://%2Ftmp%2Fmyapp.sock/perldoc')->result->body;
|
---|
383 |
|
---|
384 | # Follow redirects to download Mojolicious from GitHub
|
---|
385 | $ua->max_redirects(5)
|
---|
386 | ->get('https://www.github.com/kraih/mojo/tarball/master')
|
---|
387 | ->result->content->asset->move_to('/home/sri/mojo.tar.gz');
|
---|
388 |
|
---|
389 | # Form POST (application/x-www-form-urlencoded) with manual exception handling
|
---|
390 | my $tx = $ua->post('https://metacpan.org/search' => form => {q => 'mojo'});
|
---|
391 | if (my $res = $tx->success) { say $res->body }
|
---|
392 | else {
|
---|
393 | my $err = $tx->error;
|
---|
394 | die "$err->{code} response: $err->{message}" if $err->{code};
|
---|
395 | die "Connection error: $err->{message}";
|
---|
396 | }
|
---|
397 |
|
---|
398 | # Non-blocking request
|
---|
399 | $ua->get('mojolicious.org' => sub {
|
---|
400 | my ($ua, $tx) = @_;
|
---|
401 | say $tx->result->dom->at('title')->text;
|
---|
402 | });
|
---|
403 | Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
|
---|
404 |
|
---|
405 | # Concurrent non-blocking requests (synchronized with promises)
|
---|
406 | my $mojo = $ua->get_p('mojolicious.org');
|
---|
407 | my $cpan = $ua->get_p('cpan.org');
|
---|
408 | Mojo::Promise->all($mojo, $cpan)->then(sub {
|
---|
409 | my ($mojo, $cpan) = @_;
|
---|
410 | say $mojo->[0]->result->dom->at('title')->text;
|
---|
411 | say $cpan->[0]->result->dom->at('title')->text;
|
---|
412 | })->wait;
|
---|
413 |
|
---|
414 | # WebSocket connection sending and receiving JSON via UNIX domain socket
|
---|
415 | $ua->websocket('ws+unix://%2Ftmp%2Fmyapp.sock/echo.json' => sub {
|
---|
416 | my ($ua, $tx) = @_;
|
---|
417 | say 'WebSocket handshake failed!' and return unless $tx->is_websocket;
|
---|
418 | $tx->on(json => sub {
|
---|
419 | my ($tx, $hash) = @_;
|
---|
420 | say "WebSocket message via JSON: $hash->{msg}";
|
---|
421 | $tx->finish;
|
---|
422 | });
|
---|
423 | $tx->send({json => {msg => 'Hello World!'}});
|
---|
424 | });
|
---|
425 | Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
|
---|
426 |
|
---|
427 | =head1 DESCRIPTION
|
---|
428 |
|
---|
429 | L<Mojo::UserAgent> is a full featured non-blocking I/O HTTP and WebSocket user
|
---|
430 | agent, with IPv6, TLS, SNI, IDNA, HTTP/SOCKS5 proxy, UNIX domain socket, Comet
|
---|
431 | (long polling), Promises/A+, keep-alive, connection pooling, timeout, cookie,
|
---|
432 | multipart, gzip compression and multiple event loop support.
|
---|
433 |
|
---|
434 | All connections will be reset automatically if a new process has been forked,
|
---|
435 | this allows multiple processes to share the same L<Mojo::UserAgent> object
|
---|
436 | safely.
|
---|
437 |
|
---|
438 | For better scalability (epoll, kqueue) and to provide non-blocking name
|
---|
439 | resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
|
---|
440 | L<Net::DNS::Native> (0.15+), L<IO::Socket::Socks> (0.64+) and
|
---|
441 | L<IO::Socket::SSL> (2.009+) will be used automatically if possible. Individual
|
---|
442 | features can also be disabled with the C<MOJO_NO_NNR>, C<MOJO_NO_SOCKS> and
|
---|
443 | C<MOJO_NO_TLS> environment variables.
|
---|
444 |
|
---|
445 | See L<Mojolicious::Guides::Cookbook/"USER AGENT"> for more.
|
---|
446 |
|
---|
447 | =head1 EVENTS
|
---|
448 |
|
---|
449 | L<Mojo::UserAgent> inherits all events from L<Mojo::EventEmitter> and can emit
|
---|
450 | the following new ones.
|
---|
451 |
|
---|
452 | =head2 start
|
---|
453 |
|
---|
454 | $ua->on(start => sub {
|
---|
455 | my ($ua, $tx) = @_;
|
---|
456 | ...
|
---|
457 | });
|
---|
458 |
|
---|
459 | Emitted whenever a new transaction is about to start, this includes
|
---|
460 | automatically prepared proxy C<CONNECT> requests and followed redirects.
|
---|
461 |
|
---|
462 | $ua->on(start => sub {
|
---|
463 | my ($ua, $tx) = @_;
|
---|
464 | $tx->req->headers->header('X-Bender' => 'Bite my shiny metal ass!');
|
---|
465 | });
|
---|
466 |
|
---|
467 | =head1 ATTRIBUTES
|
---|
468 |
|
---|
469 | L<Mojo::UserAgent> implements the following attributes.
|
---|
470 |
|
---|
471 | =head2 ca
|
---|
472 |
|
---|
473 | my $ca = $ua->ca;
|
---|
474 | $ua = $ua->ca('/etc/tls/ca.crt');
|
---|
475 |
|
---|
476 | Path to TLS certificate authority file used to verify the peer certificate,
|
---|
477 | defaults to the value of the C<MOJO_CA_FILE> environment variable.
|
---|
478 |
|
---|
479 | # Show certificate authorities for debugging
|
---|
480 | IO::Socket::SSL::set_defaults(
|
---|
481 | SSL_verify_callback => sub { say "Authority: $_[2]" and return $_[0] });
|
---|
482 |
|
---|
483 | =head2 cert
|
---|
484 |
|
---|
485 | my $cert = $ua->cert;
|
---|
486 | $ua = $ua->cert('/etc/tls/client.crt');
|
---|
487 |
|
---|
488 | Path to TLS certificate file, defaults to the value of the C<MOJO_CERT_FILE>
|
---|
489 | environment variable.
|
---|
490 |
|
---|
491 | =head2 connect_timeout
|
---|
492 |
|
---|
493 | my $timeout = $ua->connect_timeout;
|
---|
494 | $ua = $ua->connect_timeout(5);
|
---|
495 |
|
---|
496 | Maximum amount of time in seconds establishing a connection may take before
|
---|
497 | getting canceled, defaults to the value of the C<MOJO_CONNECT_TIMEOUT>
|
---|
498 | environment variable or C<10>.
|
---|
499 |
|
---|
500 | =head2 cookie_jar
|
---|
501 |
|
---|
502 | my $cookie_jar = $ua->cookie_jar;
|
---|
503 | $ua = $ua->cookie_jar(Mojo::UserAgent::CookieJar->new);
|
---|
504 |
|
---|
505 | Cookie jar to use for requests performed by this user agent, defaults to a
|
---|
506 | L<Mojo::UserAgent::CookieJar> object.
|
---|
507 |
|
---|
508 | # Ignore all cookies
|
---|
509 | $ua->cookie_jar->ignore(sub { 1 });
|
---|
510 |
|
---|
511 | # Ignore cookies for public suffixes
|
---|
512 | my $ps = IO::Socket::SSL::PublicSuffix->default;
|
---|
513 | $ua->cookie_jar->ignore(sub {
|
---|
514 | my $cookie = shift;
|
---|
515 | return undef unless my $domain = $cookie->domain;
|
---|
516 | return ($ps->public_suffix($domain))[0] eq '';
|
---|
517 | });
|
---|
518 |
|
---|
519 | # Add custom cookie to the jar
|
---|
520 | $ua->cookie_jar->add(
|
---|
521 | Mojo::Cookie::Response->new(
|
---|
522 | name => 'foo',
|
---|
523 | value => 'bar',
|
---|
524 | domain => 'mojolicious.org',
|
---|
525 | path => '/perldoc'
|
---|
526 | )
|
---|
527 | );
|
---|
528 |
|
---|
529 | =head2 inactivity_timeout
|
---|
530 |
|
---|
531 | my $timeout = $ua->inactivity_timeout;
|
---|
532 | $ua = $ua->inactivity_timeout(15);
|
---|
533 |
|
---|
534 | Maximum amount of time in seconds a connection can be inactive before getting
|
---|
535 | closed, defaults to the value of the C<MOJO_INACTIVITY_TIMEOUT> environment
|
---|
536 | variable or C<20>. Setting the value to C<0> will allow connections to be
|
---|
537 | inactive indefinitely.
|
---|
538 |
|
---|
539 | =head2 insecure
|
---|
540 |
|
---|
541 | my $bool = $ua->insecure;
|
---|
542 | $ua = $ua->insecure($bool);
|
---|
543 |
|
---|
544 | Do not require a valid TLS certificate to access HTTPS/WSS sites, defaults to
|
---|
545 | the value of the C<MOJO_INSECURE> environment variable.
|
---|
546 |
|
---|
547 | # Disable TLS certificate verification for testing
|
---|
548 | say $ua->insecure(1)->get('https://127.0.0.1:3000')->result->code;
|
---|
549 |
|
---|
550 | =head2 ioloop
|
---|
551 |
|
---|
552 | my $loop = $ua->ioloop;
|
---|
553 | $ua = $ua->ioloop(Mojo::IOLoop->new);
|
---|
554 |
|
---|
555 | Event loop object to use for blocking I/O operations, defaults to a
|
---|
556 | L<Mojo::IOLoop> object.
|
---|
557 |
|
---|
558 | =head2 key
|
---|
559 |
|
---|
560 | my $key = $ua->key;
|
---|
561 | $ua = $ua->key('/etc/tls/client.crt');
|
---|
562 |
|
---|
563 | Path to TLS key file, defaults to the value of the C<MOJO_KEY_FILE> environment
|
---|
564 | variable.
|
---|
565 |
|
---|
566 | =head2 local_address
|
---|
567 |
|
---|
568 | my $address = $ua->local_address;
|
---|
569 | $ua = $ua->local_address('127.0.0.1');
|
---|
570 |
|
---|
571 | Local address to bind to.
|
---|
572 |
|
---|
573 | =head2 max_connections
|
---|
574 |
|
---|
575 | my $max = $ua->max_connections;
|
---|
576 | $ua = $ua->max_connections(5);
|
---|
577 |
|
---|
578 | Maximum number of keep-alive connections that the user agent will retain before
|
---|
579 | it starts closing the oldest ones, defaults to C<5>. Setting the value to C<0>
|
---|
580 | will prevent any connections from being kept alive.
|
---|
581 |
|
---|
582 | =head2 max_redirects
|
---|
583 |
|
---|
584 | my $max = $ua->max_redirects;
|
---|
585 | $ua = $ua->max_redirects(3);
|
---|
586 |
|
---|
587 | Maximum number of redirects the user agent will follow before it fails,
|
---|
588 | defaults to the value of the C<MOJO_MAX_REDIRECTS> environment variable or
|
---|
589 | C<0>.
|
---|
590 |
|
---|
591 | =head2 max_response_size
|
---|
592 |
|
---|
593 | my $max = $ua->max_response_size;
|
---|
594 | $ua = $ua->max_response_size(16777216);
|
---|
595 |
|
---|
596 | Maximum response size in bytes, defaults to the value of
|
---|
597 | L<Mojo::Message::Response/"max_message_size">. Setting the value to C<0> will
|
---|
598 | allow responses of indefinite size. Note that increasing this value can also
|
---|
599 | drastically increase memory usage, should you for example attempt to parse an
|
---|
600 | excessively large response body with the methods L<Mojo::Message/"dom"> or
|
---|
601 | L<Mojo::Message/"json">.
|
---|
602 |
|
---|
603 | =head2 proxy
|
---|
604 |
|
---|
605 | my $proxy = $ua->proxy;
|
---|
606 | $ua = $ua->proxy(Mojo::UserAgent::Proxy->new);
|
---|
607 |
|
---|
608 | Proxy manager, defaults to a L<Mojo::UserAgent::Proxy> object.
|
---|
609 |
|
---|
610 | # Detect proxy servers from environment
|
---|
611 | $ua->proxy->detect;
|
---|
612 |
|
---|
613 | # Manually configure HTTP proxy (using CONNECT for HTTPS/WebSockets)
|
---|
614 | $ua->proxy->http('http://127.0.0.1:8080')->https('http://127.0.0.1:8080');
|
---|
615 |
|
---|
616 | # Manually configure Tor (SOCKS5)
|
---|
617 | $ua->proxy->http('socks://127.0.0.1:9050')->https('socks://127.0.0.1:9050');
|
---|
618 |
|
---|
619 | # Manually configure UNIX domain socket (using CONNECT for HTTPS/WebSockets)
|
---|
620 | $ua->proxy->http('http+unix://%2Ftmp%2Fproxy.sock')
|
---|
621 | ->https('http+unix://%2Ftmp%2Fproxy.sock');
|
---|
622 |
|
---|
623 | =head2 request_timeout
|
---|
624 |
|
---|
625 | my $timeout = $ua->request_timeout;
|
---|
626 | $ua = $ua->request_timeout(5);
|
---|
627 |
|
---|
628 | Maximum amount of time in seconds sending the request and receiving a whole
|
---|
629 | response may take before getting canceled, defaults to the value of the
|
---|
630 | C<MOJO_REQUEST_TIMEOUT> environment variable or C<0>. This does not include
|
---|
631 | L</"connect_timeout">. Setting the value to C<0> will allow the user agent to
|
---|
632 | wait indefinitely. The timeout will reset for every followed redirect.
|
---|
633 |
|
---|
634 | # Allow 3 seconds to establish connection and 5 seconds to process request
|
---|
635 | $ua->max_redirects(0)->connect_timeout(3)->request_timeout(5);
|
---|
636 |
|
---|
637 | =head2 server
|
---|
638 |
|
---|
639 | my $server = $ua->server;
|
---|
640 | $ua = $ua->server(Mojo::UserAgent::Server->new);
|
---|
641 |
|
---|
642 | Application server relative URLs will be processed with, defaults to a
|
---|
643 | L<Mojo::UserAgent::Server> object.
|
---|
644 |
|
---|
645 | # Mock web service
|
---|
646 | $ua->server->app(Mojolicious->new);
|
---|
647 | $ua->server->app->routes->get('/time' => sub {
|
---|
648 | my $c = shift;
|
---|
649 | $c->render(json => {now => time});
|
---|
650 | });
|
---|
651 | my $time = $ua->get('/time')->result->json->{now};
|
---|
652 |
|
---|
653 | # Change log level
|
---|
654 | $ua->server->app->log->level('fatal');
|
---|
655 |
|
---|
656 | # Port currently used for processing relative URLs blocking
|
---|
657 | say $ua->server->url->port;
|
---|
658 |
|
---|
659 | # Port currently used for processing relative URLs non-blocking
|
---|
660 | say $ua->server->nb_url->port;
|
---|
661 |
|
---|
662 | =head2 transactor
|
---|
663 |
|
---|
664 | my $t = $ua->transactor;
|
---|
665 | $ua = $ua->transactor(Mojo::UserAgent::Transactor->new);
|
---|
666 |
|
---|
667 | Transaction builder, defaults to a L<Mojo::UserAgent::Transactor> object.
|
---|
668 |
|
---|
669 | # Change name of user agent
|
---|
670 | $ua->transactor->name('MyUA 1.0');
|
---|
671 |
|
---|
672 | =head1 METHODS
|
---|
673 |
|
---|
674 | L<Mojo::UserAgent> inherits all methods from L<Mojo::EventEmitter> and
|
---|
675 | implements the following new ones.
|
---|
676 |
|
---|
677 | =head2 build_tx
|
---|
678 |
|
---|
679 | my $tx = $ua->build_tx(GET => 'example.com');
|
---|
680 | my $tx = $ua->build_tx(
|
---|
681 | PUT => 'http://example.com' => {Accept => '*/*'} => 'Content!');
|
---|
682 | my $tx = $ua->build_tx(
|
---|
683 | PUT => 'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
|
---|
684 | my $tx = $ua->build_tx(
|
---|
685 | PUT => 'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
|
---|
686 |
|
---|
687 | Generate L<Mojo::Transaction::HTTP> object with
|
---|
688 | L<Mojo::UserAgent::Transactor/"tx">.
|
---|
689 |
|
---|
690 | # Request with custom cookie
|
---|
691 | my $tx = $ua->build_tx(GET => 'https://example.com/account');
|
---|
692 | $tx->req->cookies({name => 'user', value => 'sri'});
|
---|
693 | $tx = $ua->start($tx);
|
---|
694 |
|
---|
695 | # Deactivate gzip compression
|
---|
696 | my $tx = $ua->build_tx(GET => 'example.com');
|
---|
697 | $tx->req->headers->remove('Accept-Encoding');
|
---|
698 | $tx = $ua->start($tx);
|
---|
699 |
|
---|
700 | # Interrupt response by raising an error
|
---|
701 | my $tx = $ua->build_tx(GET => 'http://example.com');
|
---|
702 | $tx->res->on(progress => sub {
|
---|
703 | my $res = shift;
|
---|
704 | return unless my $server = $res->headers->server;
|
---|
705 | $res->error({message => 'Oh noes, it is IIS!'}) if $server =~ /IIS/;
|
---|
706 | });
|
---|
707 | $tx = $ua->start($tx);
|
---|
708 |
|
---|
709 | =head2 build_websocket_tx
|
---|
710 |
|
---|
711 | my $tx = $ua->build_websocket_tx('ws://example.com');
|
---|
712 | my $tx = $ua->build_websocket_tx(
|
---|
713 | 'ws://example.com' => {DNT => 1} => ['v1.proto']);
|
---|
714 |
|
---|
715 | Generate L<Mojo::Transaction::HTTP> object with
|
---|
716 | L<Mojo::UserAgent::Transactor/"websocket">.
|
---|
717 |
|
---|
718 | # Custom WebSocket handshake with cookie
|
---|
719 | my $tx = $ua->build_websocket_tx('wss://example.com/echo');
|
---|
720 | $tx->req->cookies({name => 'user', value => 'sri'});
|
---|
721 | $ua->start($tx => sub {
|
---|
722 | my ($ua, $tx) = @_;
|
---|
723 | say 'WebSocket handshake failed!' and return unless $tx->is_websocket;
|
---|
724 | $tx->on(message => sub {
|
---|
725 | my ($tx, $msg) = @_;
|
---|
726 | say "WebSocket message: $msg";
|
---|
727 | $tx->finish;
|
---|
728 | });
|
---|
729 | $tx->send('Hi!');
|
---|
730 | });
|
---|
731 | Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
|
---|
732 |
|
---|
733 | =head2 delete
|
---|
734 |
|
---|
735 | my $tx = $ua->delete('example.com');
|
---|
736 | my $tx = $ua->delete('http://example.com' => {Accept => '*/*'} => 'Content!');
|
---|
737 | my $tx = $ua->delete(
|
---|
738 | 'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
|
---|
739 | my $tx = $ua->delete(
|
---|
740 | 'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
|
---|
741 |
|
---|
742 | Perform blocking C<DELETE> request and return resulting
|
---|
743 | L<Mojo::Transaction::HTTP> object, takes the same arguments as
|
---|
744 | L<Mojo::UserAgent::Transactor/"tx"> (except for the C<DELETE> method, which is
|
---|
745 | implied). You can also append a callback to perform requests non-blocking.
|
---|
746 |
|
---|
747 | $ua->delete('http://example.com' => json => {a => 'b'} => sub {
|
---|
748 | my ($ua, $tx) = @_;
|
---|
749 | say $tx->result->body;
|
---|
750 | });
|
---|
751 | Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
|
---|
752 |
|
---|
753 | =head2 delete_p
|
---|
754 |
|
---|
755 | my $promise = $ua->delete_p('http://example.com');
|
---|
756 |
|
---|
757 | Same as L</"delete">, but performs all requests non-blocking and returns a
|
---|
758 | L<Mojo::Promise> object instead of accepting a callback.
|
---|
759 |
|
---|
760 | $ua->delete_p('http://example.com' => json => {a => 'b'})->then(sub {
|
---|
761 | my $tx = shift;
|
---|
762 | say $tx->result->body;
|
---|
763 | })->catch(sub {
|
---|
764 | my $err = shift;
|
---|
765 | warn "Connection error: $err";
|
---|
766 | })->wait;
|
---|
767 |
|
---|
768 | =head2 get
|
---|
769 |
|
---|
770 | my $tx = $ua->get('example.com');
|
---|
771 | my $tx = $ua->get('http://example.com' => {Accept => '*/*'} => 'Content!');
|
---|
772 | my $tx = $ua->get(
|
---|
773 | 'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
|
---|
774 | my $tx = $ua->get(
|
---|
775 | 'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
|
---|
776 |
|
---|
777 | Perform blocking C<GET> request and return resulting L<Mojo::Transaction::HTTP>
|
---|
778 | object, takes the same arguments as L<Mojo::UserAgent::Transactor/"tx"> (except
|
---|
779 | for the C<GET> method, which is implied). You can also append a callback to
|
---|
780 | perform requests non-blocking.
|
---|
781 |
|
---|
782 | $ua->get('http://example.com' => json => {a => 'b'} => sub {
|
---|
783 | my ($ua, $tx) = @_;
|
---|
784 | say $tx->result->body;
|
---|
785 | });
|
---|
786 | Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
|
---|
787 |
|
---|
788 | =head2 get_p
|
---|
789 |
|
---|
790 | my $promise = $ua->get_p('http://example.com');
|
---|
791 |
|
---|
792 | Same as L</"get">, but performs all requests non-blocking and returns a
|
---|
793 | L<Mojo::Promise> object instead of accepting a callback.
|
---|
794 |
|
---|
795 | $ua->get_p('http://example.com' => json => {a => 'b'})->then(sub {
|
---|
796 | my $tx = shift;
|
---|
797 | say $tx->result->body;
|
---|
798 | })->catch(sub {
|
---|
799 | my $err = shift;
|
---|
800 | warn "Connection error: $err";
|
---|
801 | })->wait;
|
---|
802 |
|
---|
803 | =head2 head
|
---|
804 |
|
---|
805 | my $tx = $ua->head('example.com');
|
---|
806 | my $tx = $ua->head('http://example.com' => {Accept => '*/*'} => 'Content!');
|
---|
807 | my $tx = $ua->head(
|
---|
808 | 'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
|
---|
809 | my $tx = $ua->head(
|
---|
810 | 'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
|
---|
811 |
|
---|
812 | Perform blocking C<HEAD> request and return resulting
|
---|
813 | L<Mojo::Transaction::HTTP> object, takes the same arguments as
|
---|
814 | L<Mojo::UserAgent::Transactor/"tx"> (except for the C<HEAD> method, which is
|
---|
815 | implied). You can also append a callback to perform requests non-blocking.
|
---|
816 |
|
---|
817 | $ua->head('http://example.com' => json => {a => 'b'} => sub {
|
---|
818 | my ($ua, $tx) = @_;
|
---|
819 | say $tx->result->body;
|
---|
820 | });
|
---|
821 | Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
|
---|
822 |
|
---|
823 | =head2 head_p
|
---|
824 |
|
---|
825 | my $promise = $ua->head_p('http://example.com');
|
---|
826 |
|
---|
827 | Same as L</"head">, but performs all requests non-blocking and returns a
|
---|
828 | L<Mojo::Promise> object instead of accepting a callback.
|
---|
829 |
|
---|
830 | $ua->head_p('http://example.com' => json => {a => 'b'})->then(sub {
|
---|
831 | my $tx = shift;
|
---|
832 | say $tx->result->body;
|
---|
833 | })->catch(sub {
|
---|
834 | my $err = shift;
|
---|
835 | warn "Connection error: $err";
|
---|
836 | })->wait;
|
---|
837 |
|
---|
838 | =head2 options
|
---|
839 |
|
---|
840 | my $tx = $ua->options('example.com');
|
---|
841 | my $tx = $ua->options('http://example.com' => {Accept => '*/*'} => 'Content!');
|
---|
842 | my $tx = $ua->options(
|
---|
843 | 'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
|
---|
844 | my $tx = $ua->options(
|
---|
845 | 'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
|
---|
846 |
|
---|
847 | Perform blocking C<OPTIONS> request and return resulting
|
---|
848 | L<Mojo::Transaction::HTTP> object, takes the same arguments as
|
---|
849 | L<Mojo::UserAgent::Transactor/"tx"> (except for the C<OPTIONS> method, which is
|
---|
850 | implied). You can also append a callback to perform requests non-blocking.
|
---|
851 |
|
---|
852 | $ua->options('http://example.com' => json => {a => 'b'} => sub {
|
---|
853 | my ($ua, $tx) = @_;
|
---|
854 | say $tx->result->body;
|
---|
855 | });
|
---|
856 | Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
|
---|
857 |
|
---|
858 | =head2 options_p
|
---|
859 |
|
---|
860 | my $promise = $ua->options_p('http://example.com');
|
---|
861 |
|
---|
862 | Same as L</"options">, but performs all requests non-blocking and returns a
|
---|
863 | L<Mojo::Promise> object instead of accepting a callback.
|
---|
864 |
|
---|
865 | $ua->options_p('http://example.com' => json => {a => 'b'})->then(sub {
|
---|
866 | my $tx = shift;
|
---|
867 | say $tx->result->body;
|
---|
868 | })->catch(sub {
|
---|
869 | my $err = shift;
|
---|
870 | warn "Connection error: $err";
|
---|
871 | })->wait;
|
---|
872 |
|
---|
873 | =head2 patch
|
---|
874 |
|
---|
875 | my $tx = $ua->patch('example.com');
|
---|
876 | my $tx = $ua->patch('http://example.com' => {Accept => '*/*'} => 'Content!');
|
---|
877 | my $tx = $ua->patch(
|
---|
878 | 'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
|
---|
879 | my $tx = $ua->patch(
|
---|
880 | 'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
|
---|
881 |
|
---|
882 | Perform blocking C<PATCH> request and return resulting
|
---|
883 | L<Mojo::Transaction::HTTP> object, takes the same arguments as
|
---|
884 | L<Mojo::UserAgent::Transactor/"tx"> (except for the C<PATCH> method, which is
|
---|
885 | implied). You can also append a callback to perform requests non-blocking.
|
---|
886 |
|
---|
887 | $ua->patch('http://example.com' => json => {a => 'b'} => sub {
|
---|
888 | my ($ua, $tx) = @_;
|
---|
889 | say $tx->result->body;
|
---|
890 | });
|
---|
891 | Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
|
---|
892 |
|
---|
893 | =head2 patch_p
|
---|
894 |
|
---|
895 | my $promise = $ua->patch_p('http://example.com');
|
---|
896 |
|
---|
897 | Same as L</"patch">, but performs all requests non-blocking and returns a
|
---|
898 | L<Mojo::Promise> object instead of accepting a callback.
|
---|
899 |
|
---|
900 | $ua->patch_p('http://example.com' => json => {a => 'b'})->then(sub {
|
---|
901 | my $tx = shift;
|
---|
902 | say $tx->result->body;
|
---|
903 | })->catch(sub {
|
---|
904 | my $err = shift;
|
---|
905 | warn "Connection error: $err";
|
---|
906 | })->wait;
|
---|
907 |
|
---|
908 | =head2 post
|
---|
909 |
|
---|
910 | my $tx = $ua->post('example.com');
|
---|
911 | my $tx = $ua->post('http://example.com' => {Accept => '*/*'} => 'Content!');
|
---|
912 | my $tx = $ua->post(
|
---|
913 | 'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
|
---|
914 | my $tx = $ua->post(
|
---|
915 | 'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
|
---|
916 |
|
---|
917 | Perform blocking C<POST> request and return resulting
|
---|
918 | L<Mojo::Transaction::HTTP> object, takes the same arguments as
|
---|
919 | L<Mojo::UserAgent::Transactor/"tx"> (except for the C<POST> method, which is
|
---|
920 | implied). You can also append a callback to perform requests non-blocking.
|
---|
921 |
|
---|
922 | $ua->post('http://example.com' => json => {a => 'b'} => sub {
|
---|
923 | my ($ua, $tx) = @_;
|
---|
924 | say $tx->result->body;
|
---|
925 | });
|
---|
926 | Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
|
---|
927 |
|
---|
928 | =head2 post_p
|
---|
929 |
|
---|
930 | my $promise = $ua->post_p('http://example.com');
|
---|
931 |
|
---|
932 | Same as L</"post">, but performs all requests non-blocking and returns a
|
---|
933 | L<Mojo::Promise> object instead of accepting a callback.
|
---|
934 |
|
---|
935 | $ua->post_p('http://example.com' => json => {a => 'b'})->then(sub {
|
---|
936 | my $tx = shift;
|
---|
937 | say $tx->result->body;
|
---|
938 | })->catch(sub {
|
---|
939 | my $err = shift;
|
---|
940 | warn "Connection error: $err";
|
---|
941 | })->wait;
|
---|
942 |
|
---|
943 | =head2 put
|
---|
944 |
|
---|
945 | my $tx = $ua->put('example.com');
|
---|
946 | my $tx = $ua->put('http://example.com' => {Accept => '*/*'} => 'Content!');
|
---|
947 | my $tx = $ua->put(
|
---|
948 | 'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
|
---|
949 | my $tx = $ua->put(
|
---|
950 | 'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
|
---|
951 |
|
---|
952 | Perform blocking C<PUT> request and return resulting L<Mojo::Transaction::HTTP>
|
---|
953 | object, takes the same arguments as L<Mojo::UserAgent::Transactor/"tx"> (except
|
---|
954 | for the C<PUT> method, which is implied). You can also append a callback to
|
---|
955 | perform requests non-blocking.
|
---|
956 |
|
---|
957 | $ua->put('http://example.com' => json => {a => 'b'} => sub {
|
---|
958 | my ($ua, $tx) = @_;
|
---|
959 | say $tx->result->body;
|
---|
960 | });
|
---|
961 | Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
|
---|
962 |
|
---|
963 | =head2 put_p
|
---|
964 |
|
---|
965 | my $promise = $ua->put_p('http://example.com');
|
---|
966 |
|
---|
967 | Same as L</"put">, but performs all requests non-blocking and returns a
|
---|
968 | L<Mojo::Promise> object instead of accepting a callback.
|
---|
969 |
|
---|
970 | $ua->put_p('http://example.com' => json => {a => 'b'})->then(sub {
|
---|
971 | my $tx = shift;
|
---|
972 | say $tx->result->body;
|
---|
973 | })->catch(sub {
|
---|
974 | my $err = shift;
|
---|
975 | warn "Connection error: $err";
|
---|
976 | })->wait;
|
---|
977 |
|
---|
978 | =head2 start
|
---|
979 |
|
---|
980 | my $tx = $ua->start(Mojo::Transaction::HTTP->new);
|
---|
981 |
|
---|
982 | Perform blocking request for a custom L<Mojo::Transaction::HTTP> object, which
|
---|
983 | can be prepared manually or with L</"build_tx">. You can also append a callback
|
---|
984 | to perform requests non-blocking.
|
---|
985 |
|
---|
986 | my $tx = $ua->build_tx(GET => 'http://example.com');
|
---|
987 | $ua->start($tx => sub {
|
---|
988 | my ($ua, $tx) = @_;
|
---|
989 | say $tx->result->body;
|
---|
990 | });
|
---|
991 | Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
|
---|
992 |
|
---|
993 | =head2 start_p
|
---|
994 |
|
---|
995 | my $promise = $ua->start_p(Mojo::Transaction::HTTP->new);
|
---|
996 |
|
---|
997 | Same as L</"start">, but performs all requests non-blocking and returns a
|
---|
998 | L<Mojo::Promise> object instead of accepting a callback.
|
---|
999 |
|
---|
1000 | my $tx = $ua->build_tx(GET => 'http://example.com');
|
---|
1001 | $ua->start_p($tx)->then(sub {
|
---|
1002 | my $tx = shift;
|
---|
1003 | say $tx->result->body;
|
---|
1004 | })->catch(sub {
|
---|
1005 | my $err = shift;
|
---|
1006 | warn "Connection error: $err";
|
---|
1007 | })->wait;
|
---|
1008 |
|
---|
1009 | =head2 websocket
|
---|
1010 |
|
---|
1011 | $ua->websocket('ws://example.com' => sub {...});
|
---|
1012 | $ua->websocket(
|
---|
1013 | 'ws://example.com' => {DNT => 1} => ['v1.proto'] => sub {...});
|
---|
1014 |
|
---|
1015 | Open a non-blocking WebSocket connection with transparent handshake, takes the
|
---|
1016 | same arguments as L<Mojo::UserAgent::Transactor/"websocket">. The callback will
|
---|
1017 | receive either a L<Mojo::Transaction::WebSocket> or L<Mojo::Transaction::HTTP>
|
---|
1018 | object, depending on if the handshake was successful.
|
---|
1019 |
|
---|
1020 | $ua->websocket('wss://example.com/echo' => ['v1.proto'] => sub {
|
---|
1021 | my ($ua, $tx) = @_;
|
---|
1022 | say 'WebSocket handshake failed!' and return unless $tx->is_websocket;
|
---|
1023 | say 'Subprotocol negotiation failed!' and return unless $tx->protocol;
|
---|
1024 | $tx->on(finish => sub {
|
---|
1025 | my ($tx, $code, $reason) = @_;
|
---|
1026 | say "WebSocket closed with status $code.";
|
---|
1027 | });
|
---|
1028 | $tx->on(message => sub {
|
---|
1029 | my ($tx, $msg) = @_;
|
---|
1030 | say "WebSocket message: $msg";
|
---|
1031 | $tx->finish;
|
---|
1032 | });
|
---|
1033 | $tx->send('Hi!');
|
---|
1034 | });
|
---|
1035 | Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
|
---|
1036 |
|
---|
1037 | You can activate C<permessage-deflate> compression by setting the
|
---|
1038 | C<Sec-WebSocket-Extensions> header, this can result in much better performance,
|
---|
1039 | but also increases memory usage by up to 300KiB per connection.
|
---|
1040 |
|
---|
1041 | $ua->websocket('ws://example.com/foo' => {
|
---|
1042 | 'Sec-WebSocket-Extensions' => 'permessage-deflate'
|
---|
1043 | } => sub {...});
|
---|
1044 |
|
---|
1045 | =head2 websocket_p
|
---|
1046 |
|
---|
1047 | my $promise = $ua->websocket_p('ws://example.com');
|
---|
1048 |
|
---|
1049 | Same as L</"websocket">, but returns a L<Mojo::Promise> object instead of
|
---|
1050 | accepting a callback.
|
---|
1051 |
|
---|
1052 | $ua->websocket_p('wss://example.com/echo')->then(sub {
|
---|
1053 | my $tx = shift;
|
---|
1054 | my $promise = Mojo::Promise->new;
|
---|
1055 | $tx->on(finish => sub { $promise->resolve });
|
---|
1056 | $tx->on(message => sub {
|
---|
1057 | my ($tx, $msg) = @_;
|
---|
1058 | say "WebSocket message: $msg";
|
---|
1059 | $tx->finish;
|
---|
1060 | });
|
---|
1061 | $tx->send('Hi!');
|
---|
1062 | return $promise;
|
---|
1063 | })->catch(sub {
|
---|
1064 | my $err = shift;
|
---|
1065 | warn "WebSocket error: $err";
|
---|
1066 | })->wait;
|
---|
1067 |
|
---|
1068 | =head1 DEBUGGING
|
---|
1069 |
|
---|
1070 | You can set the C<MOJO_CLIENT_DEBUG> environment variable to get some advanced
|
---|
1071 | diagnostics information printed to C<STDERR>.
|
---|
1072 |
|
---|
1073 | MOJO_CLIENT_DEBUG=1
|
---|
1074 |
|
---|
1075 | =head1 SEE ALSO
|
---|
1076 |
|
---|
1077 | L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
|
---|
1078 |
|
---|
1079 | =cut
|
---|