Security Advisories (10)
CPANSA-Mojolicious-2022-03 (2022-12-10)

Mojo::DOM did not correctly parse <script> tags.

CPANSA-Mojolicious-2021-02 (2021-06-01)

Small sessions could be used as part of a brute-force attack to decode the session secret.

CVE-2021-47208 (2021-03-16)

A bug in format detection can potentially be exploited for a DoS attack.

CVE-2018-25100 (2018-02-13)

Mojo::UserAgent::CookieJar leaks old cookies because of the missing host_only flag on empty domain.

CPANSA-Mojolicious-2015-01 (2015-02-02)

Directory traversal on Windows

CPANSA-Mojolicious-2018-03 (2018-05-19)

Mojo::UserAgent was not checking peer SSL certificates by default.

CVE-2020-36829 (2020-11-10)

Mojo::Util secure_compare can leak the string length. By immediately returning when the two strings are not the same length, the function allows an attacker to guess the length of the secret string using timing attacks.

CPANSA-Mojolicious-2018-02 (2018-05-11)

GET requests with embedded backslashes can be used to access local files on Windows hosts

CPANSA-Mojolicious-2014-01 (2014-10-07)

Context sensitivity of method param could lead to parameter injection attacks.

CVE-2024-58134 (2025-05-03)

Mojolicious versions from 0.999922 for Perl uses a hard coded string, or the application's class name, as a HMAC session secret by default. These predictable default secrets can be exploited to forge session cookies. An attacker who knows or guesses the secret could compute valid HMAC signatures for the session cookie, allowing them to tamper with or hijack another user's session.

NAME

Mojo::UserAgent - Non-blocking I/O HTTP and WebSocket user agent

SYNOPSIS

use Mojo::UserAgent;
my $ua = Mojo::UserAgent->new;

# Say hello to the Unicode snowman with "Do Not Track" header
say $ua->get('www.☃.net?hello=there' => {DNT => 1})->res->body;

# Form POST with exception handling
my $tx = $ua->post_form('search.cpan.org/search' => {q => 'mojo'});
if (my $res = $tx->success) { say $res->body }
else {
  my ($message, $code) = $tx->error;
  say $code ? "$code response: $message" : "Connection error: $message";
}

# Quick JSON API request with Basic authentication
say $ua->get('https://sri:s3cret@search.twitter.com/search.json?q=perl')
  ->res->json('/results/0/text');

# Extract data from HTML and XML resources
say $ua->get('mojolicio.us')->res->dom->html->head->title->text;

# Scrape the latest headlines from a news site
say $ua->max_redirects(5)->get('www.reddit.com/r/perl/')
  ->res->dom('p.title > a.title')->pluck('text')->shuffle;

# IPv6 PUT request with content
my $tx
  = $ua->put('[::1]:3000' => {'Content-Type' => 'text/plain'} => 'Hello!');

# Grab the latest Mojolicious release :)
$ua->max_redirects(5)->get('latest.mojolicio.us')
  ->res->content->asset->move_to('/Users/sri/mojo.tar.gz');

# TLS certificate authentication and JSON POST
my $tx = $ua->cert('tls.crt')->key('tls.key')
  ->post_json('https://mojolicio.us' => {top => 'secret'});

# Custom JSON PUT request
my $tx = $ua->build_json_tx('http://mojolicious/foo' => {hi => 'there'});
$tx->req->method('PUT');
say $ua->start($tx)->res->body;

# Blocking parallel requests (does not work inside a running event loop)
my $delay = Mojo::IOLoop->delay;
for my $url ('mojolicio.us', 'cpan.org') {
  $delay->begin;
  $ua->get($url => sub {
    my ($ua, $tx) = @_;
    $delay->end($tx->res->dom->at('title')->text);
  });
}
my @titles = $delay->wait;

# Non-blocking parallel requests (does work inside a running event loop)
my $delay = Mojo::IOLoop->delay(sub {
  my ($delay, @titles) = @_;
  ...
});
for my $url ('mojolicio.us', 'cpan.org') {
  $delay->begin;
  $ua->get($url => sub {
    my ($ua, $tx) = @_;
    $delay->end($tx->res->dom->at('title')->text);
  });
}
$delay->wait unless Mojo::IOLoop->is_running;

# Non-blocking WebSocket connection
$ua->websocket('ws://websockets.org:8787' => sub {
  my ($ua, $tx) = @_;
  $tx->on(finish  => sub { say 'WebSocket closed.' });
  $tx->on(message => sub {
    my ($tx, $message) = @_;
    say "WebSocket message: $message";
    $tx->finish;
  });
  $tx->send('hi there!');
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

DESCRIPTION

Mojo::UserAgent is a full featured non-blocking I/O HTTP and WebSocket user agent, with IPv6, TLS and libev support.

Optional modules EV (4.0+), IO::Socket::IP (0.16+) and IO::Socket::SSL (1.75+) are supported transparently through Mojo::IOLoop, and used if installed. Individual features can also be disabled with the MOJO_NO_IPV6 and MOJO_NO_TLS environment variables.

See Mojolicious::Guides::Cookbook for more.

EVENTS

Mojo::UserAgent can emit the following events.

error

$ua->on(error => sub {
  my ($ua, $err) = @_;
  ...
});

Emitted if an error happens that can't be associated with a transaction.

$ua->on(error => sub {
  my ($ua, $err) = @_;
  say "This looks bad: $err";
});

start

$ua->on(start => sub {
  my ($ua, $tx) = @_;
  ...
});

Emitted whenever a new transaction is about to start, this includes automatically prepared proxy CONNECT requests and followed redirects.

$ua->on(start => sub {
  my ($ua, $tx) = @_;
  $tx->req->headers->header('X-Bender', 'Bite my shiny metal ass!');
});

ATTRIBUTES

Mojo::UserAgent implements the following attributes.

ca

my $ca = $ua->ca;
$ua    = $ua->ca('/etc/tls/ca.crt');

Path to TLS certificate authority file, defaults to the value of the MOJO_CA_FILE environment variable. Also activates hostname verification.

# Show certificate authorities for debugging
IO::Socket::SSL::set_ctx_defaults(
  SSL_verify_callback => sub { say "Authority: $_[2]" and return $_[0] });

cert

my $cert = $ua->cert;
$ua      = $ua->cert('/etc/tls/client.crt');

Path to TLS certificate file, defaults to the value of the MOJO_CERT_FILE environment variable.

connect_timeout

my $timeout = $ua->connect_timeout;
$ua         = $ua->connect_timeout(5);

Maximum amount of time in seconds establishing a connection may take before getting canceled, defaults to the value of the MOJO_CONNECT_TIMEOUT environment variable or 10.

my $cookie_jar = $ua->cookie_jar;
$ua            = $ua->cookie_jar(Mojo::UserAgent::CookieJar->new);

Cookie jar to use for this user agents requests, defaults to a Mojo::UserAgent::CookieJar object.

# Disable cookie jar
$ua->cookie_jar(0);

http_proxy

my $proxy = $ua->http_proxy;
$ua       = $ua->http_proxy('http://sri:secret@127.0.0.1:8080');

Proxy server to use for HTTP and WebSocket requests.

https_proxy

my $proxy = $ua->https_proxy;
$ua       = $ua->https_proxy('http://sri:secret@127.0.0.1:8080');

Proxy server to use for HTTPS and WebSocket requests.

inactivity_timeout

my $timeout = $ua->inactivity_timeout;
$ua         = $ua->inactivity_timeout(15);

Maximum amount of time in seconds a connection can be inactive before getting closed, defaults to the value of the MOJO_INACTIVITY_TIMEOUT environment variable or 20. Setting the value to 0 will allow connections to be inactive indefinitely.

ioloop

my $loop = $ua->ioloop;
$ua      = $ua->ioloop(Mojo::IOLoop->new);

Loop object to use for blocking I/O operations, defaults to a Mojo::IOLoop object.

key

my $key = $ua->key;
$ua     = $ua->key('/etc/tls/client.crt');

Path to TLS key file, defaults to the value of the MOJO_KEY_FILE environment variable.

local_address

my $address = $ua->local_address;
$ua         = $ua->local_address('127.0.0.1');

Local address to bind to.

max_connections

my $max_connections = $ua->max_connections;
$ua                 = $ua->max_connections(5);

Maximum number of keep alive connections that the user agent will retain before it starts closing the oldest cached ones, defaults to 5.

max_redirects

my $max_redirects = $ua->max_redirects;
$ua               = $ua->max_redirects(3);

Maximum number of redirects the user agent will follow before it fails, defaults to the value of the MOJO_MAX_REDIRECTS environment variable or 0.

name

my $name = $ua->name;
$ua      = $ua->name('Mojolicious');

Value for User-Agent request header, defaults to Mojolicious (Perl).

no_proxy

my $no_proxy = $ua->no_proxy;
$ua          = $ua->no_proxy([qw(localhost intranet.mojolicio.us)]);

Domains that don't require a proxy server to be used.

request_timeout

my $timeout = $ua->request_timeout;
$ua         = $ua->request_timeout(5);

Maximum amount of time in seconds establishing a connection, sending the request and receiving a whole response may take before getting canceled, defaults to the value of the MOJO_REQUEST_TIMEOUT environment variable or 0. Setting the value to 0 will allow the user agent to wait indefinitely. The timeout will reset for every followed redirect.

# Total limit of 5 seconds, of which 3 seconds may be spent connecting
$ua->max_redirects(0)->connect_timeout(3)->request_timeout(5);

transactor

my $t = $ua->transactor;
$ua   = $ua->transactor(Mojo::UserAgent::Transactor->new);

Transaction builder, defaults to a Mojo::UserAgent::Transactor object.

METHODS

Mojo::UserAgent inherits all methods from Mojo::EventEmitter and implements the following new ones.

app

my $app = Mojo::UserAgent->app;
          Mojo::UserAgent->app(MyApp->new);
my $app = $ua->app;
$ua     = $ua->app(MyApp->new);

Application relative URLs will be processed with, instance specific applications override the global default.

# Introspect
say $ua->app->secret;

# Change log level
$ua->app->log->level('fatal');

# Change application behavior
$ua->app->defaults(testing => 'oh yea!');

app_url

my $url = $ua->app_url;
my $url = $ua->app_url('http');
my $url = $ua->app_url('https');

Get absolute Mojo::URL object for app and switch protocol if necessary.

# Port currently used for processing relative URLs
say $ua->app_url->port;

build_form_tx

my $tx = $ua->build_form_tx('http://kraih.com' => {a => 'b'});
my $tx = $ua->build_form_tx('kraih.com', 'UTF-8', {a => 'b'}, {DNT => 1});

Generate Mojo::Transaction::HTTP object with "form" in Mojo::UserAgent::Transactor.

build_json_tx

my $tx = $ua->build_json_tx('http://kraih.com' => {a => 'b'});
my $tx = $ua->build_json_tx('kraih.com' => {a => 'b'} => {DNT => 1});

Generate Mojo::Transaction::HTTP object with "json" in Mojo::UserAgent::Transactor.

build_tx

my $tx = $ua->build_tx(GET => 'kraih.com');
my $tx = $ua->build_tx(PUT => 'http://kraih.com' => {DNT => 1} => 'Hi!');

Generate Mojo::Transaction::HTTP object with "tx" in Mojo::UserAgent::Transactor.

# Request with cookie
my $tx = $ua->build_tx(GET => 'kraih.com');
$tx->req->cookies({name => 'foo', value => 'bar'});
$ua->start($tx);

build_websocket_tx

my $tx = $ua->build_websocket_tx('ws://localhost:3000');
my $tx = $ua->build_websocket_tx('ws://localhost:3000' => {DNT => 1});

Generate Mojo::Transaction::HTTP object with "websocket" in Mojo::UserAgent::Transactor.

delete

my $tx = $ua->delete('kraih.com');
my $tx = $ua->delete('http://kraih.com' => {DNT => 1} => 'Hi!');

Perform blocking HTTP DELETE request and return resulting Mojo::Transaction::HTTP object, takes the exact same arguments as "tx" in Mojo::UserAgent::Transactor (except for the method). You can also append a callback to perform requests non-blocking.

$ua->delete('http://kraih.com' => sub {
  my ($ua, $tx) = @_;
  say $tx->res->body;
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

detect_proxy

$ua = $ua->detect_proxy;

Check environment variables HTTP_PROXY, http_proxy, HTTPS_PROXY, https_proxy, NO_PROXY and no_proxy for proxy information. Automatic proxy detection can be enabled with the MOJO_PROXY environment variable.

get

my $tx = $ua->get('kraih.com');
my $tx = $ua->get('http://kraih.com' => {DNT => 1} => 'Hi!');

Perform blocking HTTP GET request and return resulting Mojo::Transaction::HTTP object, takes the exact same arguments as "tx" in Mojo::UserAgent::Transactor (except for the method). You can also append a callback to perform requests non-blocking.

$ua->get('http://kraih.com' => sub {
  my ($ua, $tx) = @_;
  say $tx->res->body;
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
my $tx = $ua->head('kraih.com');
my $tx = $ua->head('http://kraih.com' => {DNT => 1} => 'Hi!');

Perform blocking HTTP HEAD request and return resulting Mojo::Transaction::HTTP object, takes the exact same arguments as "tx" in Mojo::UserAgent::Transactor (except for the method). You can also append a callback to perform requests non-blocking.

$ua->head('http://kraih.com' => sub {
  my ($ua, $tx) = @_;
  say $tx->res->body;
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

need_proxy

my $success = $ua->need_proxy('intranet.mojolicio.us');

Check if request for domain would use a proxy server.

options

my $tx = $ua->options('kraih.com');
my $tx = $ua->options('http://kraih.com' => {DNT => 1} => 'Hi!');

Perform blocking HTTP OPTIONS request and return resulting Mojo::Transaction::HTTP object, takes the exact same arguments as "tx" in Mojo::UserAgent::Transactor (except for the method). You can also append a callback to perform requests non-blocking.

$ua->options('http://kraih.com' => sub {
  my ($ua, $tx) = @_;
  say $tx->res->body;
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

patch

my $tx = $ua->patch('kraih.com');
my $tx = $ua->patch('http://kraih.com' => {DNT => 1} => 'Hi!');

Perform blocking HTTP PATCH request and return resulting Mojo::Transaction::HTTP object, takes the exact same arguments as "tx" in Mojo::UserAgent::Transactor (except for the method). You can also append a callback to perform requests non-blocking.

$ua->patch('http://kraih.com' => sub {
  my ($ua, $tx) = @_;
  say $tx->res->body;
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

post

my $tx = $ua->post('kraih.com');
my $tx = $ua->post('http://kraih.com' => {DNT => 1} => 'Hi!');

Perform blocking HTTP POST request and return resulting Mojo::Transaction::HTTP object, takes the exact same arguments as "tx" in Mojo::UserAgent::Transactor (except for the method). You can also append a callback to perform requests non-blocking.

$ua->post('http://kraih.com' => sub {
  my ($ua, $tx) = @_;
  say $tx->res->body;
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

post_form

my $tx = $ua->post_form('http://kraih.com' => {a => 'b'});
my $tx = $ua->post_form('kraih.com', 'UTF-8', {a => 'b'}, {DNT => 1});

Perform blocking HTTP POST request with form data and return resulting Mojo::Transaction::HTTP object, takes the exact same arguments as "form" in Mojo::UserAgent::Transactor. You can also append a callback to perform requests non-blocking.

$ua->post_form('http://kraih.com' => {q => 'test'} => sub {
  my ($ua, $tx) = @_;
  say $tx->res->body;
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

post_json

my $tx = $ua->post_json('http://kraih.com' => {a => 'b'});
my $tx = $ua->post_json('kraih.com' => {a => 'b'} => {DNT => 1});

Perform blocking HTTP POST request with JSON data and return resulting Mojo::Transaction::HTTP object, takes the exact same arguments as "json" in Mojo::UserAgent::Transactor. You can also append a callback to perform requests non-blocking.

$ua->post_json('http://kraih.com' => {q => 'test'} => sub {
  my ($ua, $tx) = @_;
  say $tx->res->body;
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

put

my $tx = $ua->put('kraih.com');
my $tx = $ua->put('http://kraih.com' => {DNT => 1} => 'Hi!');

Perform blocking HTTP PUT request and return resulting Mojo::Transaction::HTTP object, takes the exact same arguments as "tx" in Mojo::UserAgent::Transactor (except for the method). You can also append a callback to perform requests non-blocking.

$ua->put('http://kraih.com' => sub {
  my ($ua, $tx) = @_;
  say $tx->res->body;
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

start

my $tx = $ua->start(Mojo::Transaction::HTTP->new);

Perform blocking request. You can also append a callback to perform requests non-blocking.

my $tx = $ua->build_tx(GET => 'http://kraih.com');
$ua->start($tx => sub {
  my ($ua, $tx) = @_;
  say $tx->res->body;
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

websocket

$ua->websocket('ws://localhost:3000' => sub {...});
$ua->websocket('ws://localhost:3000' => {DNT => 1} => sub {...});

Open a non-blocking WebSocket connection with transparent handshake, takes the exact same arguments as "websocket" in Mojo::UserAgent::Transactor.

$ua->websocket('ws://localhost:3000/echo' => sub {
  my ($ua, $tx) = @_;
  $tx->on(message => sub {
    my ($tx, $message) = @_;
    say $message;
  });
  $tx->send('Hi!');
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

DEBUGGING

You can set the MOJO_USERAGENT_DEBUG environment variable to get some advanced diagnostics information printed to STDERR.

MOJO_USERAGENT_DEBUG=1

SEE ALSO

Mojolicious, Mojolicious::Guides, http://mojolicio.us.