名前

Mojo::UserAgent - ノンブロッキングI/OのHTTPとWebSocketのユーザーエージェント

使い方

use Mojo::UserAgent;

# 良い粒度のレスポンスの処理(接続エラーの場合は、例外を発生)
my $ua  = Mojo::UserAgent->new;
my $res = $ua->get('mojolicious.org/perldoc')->result;
if    ($res->is_success)  { say $res->body }
elsif ($res->is_error)    { say $res->message }
elsif ($res->code == 301) { say $res->headers->location }
else                      { say 'Whatever...' }

# Acceptヘッダをつけてユニコードのスノーマンにハローを言う
say $ua->get('www.☃.net?hello=there' => {Accept => '*/*'})->result->body;

# HTMLとXMLのリソースからCSSセレクタを使ってデータを抽出する
say $ua->get('www.perl.org')->result->dom->at('title')->text;

# 新しいサイトから最近のヘッドラインをスクレイピングする
say $ua->get('blogs.perl.org')
  ->result->dom->find('h2 > a')->map('text')->join("\n");

# コンテンツのついたIPv6のPUTリクエスト
my $tx = $ua->put('[::1]:3000' => {'Content-Type' => 'text/plain'} => 'Hi!');

# ベーシック認証によるすばやいJSON APIリクエスト
my $value = $ua->get('https://sri:t3st@example.com/test.json')->result->json;

# TLS証明書認証のついたJSON POST (application/json)
my $tx = $ua->cert('tls.crt')->key('tls.key')
  ->post('https://example.com' => json => {top => 'secret'});

# POSTフォーム (application/x-www-form-urlencoded)
my $tx = $ua->post('https://metacpan.org/search' => form => {q => 'mojo'});

# DuckDuckGoのanonymously through Torを検索
$ua->proxy->http('socks://127.0.0.1:9050');
say $ua->get('api.3g2upl4pq6kufc4m.onion/?q=mojolicious&format=json')
  ->result->json('/Abstract');

# UNIXドメインソケットを通したGETリクエスト"/tmp/myapp.sock" (スラッシュをパーセントエンコード)
say $ua->get('http+unix://%2Ftmp%2Fmyapp.sock/perldoc')->result->body;

# GitHubからMojoliciousをダウンロードするためにリダイレクトを設定
$ua->max_redirects(5)
  ->get('https://www.github.com/mojolicious/mojo/tarball/master')
  ->result->save_to('/home/sri/mojo.tar.gz');

# ノンブロッキングリクエスト
$ua->get('mojolicious.org' => sub {
  my ($ua, $tx) = @_;
  say $tx->result->dom->at('title')->text;
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

# ノンブロッキングな並列リクエスト(プロミスで同期)
my $mojo = $ua->get_p('mojolicious.org');
my $cpan = $ua->get_p('cpan.org');
Mojo::Promise->all($mojo, $cpan)->then(sub {
  my ($mojo, $cpan) = @_;
  say $mojo->[0]->result->dom->at('title')->text;
  say $cpan->[0]->result->dom->at('title')->text;
})->wait;

# UNIXドメインソケットを通してJSONメッセージを受信・送信するためのノンブロッキングなWebSocket接続
$ua->websocket('ws+unix://%2Ftmp%2Fmyapp.sock/echo.json' => sub {
  my ($ua, $tx) = @_;
  say 'WebSocket handshake failed!' and return unless $tx->is_websocket;
  $tx->on(json => sub {
    my ($tx, $hash) = @_;
    say "WebSocket message via JSON: $hash->{msg}";
    $tx->finish;
  });
  $tx->send({json => {msg => 'Hello World!'}});
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

説明

Mojo::UserAgent は完全な特徴を備えたノンブロッキングI/OのHTTPと WebSocketのユーザーエージェントです。 IPv6, TLS, SNI, IDNA, HTTP/SOCKS5プロキシ、Unixドメインソケット Comet (ロングポーリング), キープアライブ, コネクションポーリング, タイムアウト, クッキー、マルチパート、プロシキ、gzip圧縮、複数のイベントループをサポートしています。

すべての接続は、新しいプロセスがフォークされれば、自動的にリセットされます。 これは複数のプロセスが、同じMojo::UserAgentオブジェクトを安全に共有する ことを許可します。

よりよいスケーラビリティ(epoll, kqueue)、ノンブロッキングの名前解決、 SOCK5、TLSサポートも以下のモジュールによってサポートされます。 オプションのモジュールとしてEV(4.0+), Net::DNS::Native (0.15+) IO::Socket::SSL(2.009+),IO::Socket::Socks (0.64+)、IO::Socket::SSL (1.94+)が 透過的にサポートされ、インストールされていれば利用されます。 個々の機能はMOJO_NO_NDNMOJO_NO_IPV6MOJO_NO_TLS環境変数で無効にすることもできます。

イベント

Mojo::UserAgentMojo::EventEmitterを継承しており、次のイベントが発生します。

prepare

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

新しいトランザクションが準備されているときはいつでも、相対URLが書き換えられ、クッキーが追加される前に発行されます。 これは自動的に準備されたプロキシのCONNECTリクエストとそれに続くリダイレクトを含みます

$ua->on(prepare => sub {
  my ($ua, $tx) = @_;
  $tx->req->url(Mojo::URL->new('/mock-mojolicious'))
    if $tx->req->url->host eq 'mojolicious.org';
});

start

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

新しいトランザクションがまさに開始しようとしているときにいつでも発生します。 これは自動的に準備されるプロキシのCONNECTリクエストとレスポンス、それに続くリダイレクトも含みます。

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

属性

Mojo::UserAgent は以下の属性を実装しています。

ca

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

TLS認証局ファイルへのパス。 デフォルトはMOJO_CA_FILE環境変数の値。 ホスト名の検証も有効にします。

# デバッグで認証局を表示する
IO::Socket::SSL::set_ctx_defaults(
  SSL_verify_callback => sub { say "Authority: $_[2]" and return $_[0] });

cert

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

TLS証明書ファイルへのパス。デフォルトは MOJO_CERT_FILE の値です。

connect_timeout

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

接続が確立されるまでの最大秒数。デフォルトはMOJO_CONNECT_TIMEOUT環境変数 の値か10です。

cookie_jar

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

このユーザーエージェントのリクエストのためのクッキージャー。 デフォルトは Mojo::CookieJar オブジェクトです。

# すべてのクッキーを無効にする
$ua->cookie_jar->ignore(sub { 1 });

# パブリックなサフィックスのためにクッキーを無視する
my $ps = IO::Socket::SSL::PublicSuffix->default;
$ua->cookie_jar->ignore(sub {
  my $cookie = shift;
  return undef unless my $domain = $cookie->domain;
  return ($ps->public_suffix($domain))[0] eq '';
});

# ジャーにカスタムクッキーを追加する
$ua->cookie_jar->add(
  Mojo::Cookie::Response->new(
    name   => 'foo',
    value  => 'bar',
    domain => 'mojolicio.us',
    path   => '/perldoc'
  )
);

inactivity_timeout

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

接続がドロップする前に非アクティブでいる最大の秒数。 デフォルトはMOJO_INACTIVITY_TIMEOUT環境変数の値か20です。 この値を0に設定すると接続を永久に非アクティブにしたままにすることができます。

insecure

my $bool = $ua->insecure;
$ua      = $ua->insecure($bool);

Do not require a valid TLS certificate to access HTTPS/WSS sites, defaults to the value of the MOJO_INSECURE environment variable.

HTTPS / WSSサイトにアクセスするために有効なTLS証明書を必要としません。デフォルトは MOJO_INSECURE環境変数の値です。

# テスト用にTLS証明書の検証を無効にする
say $ua->insecure(1)->get('https://127.0.0.1:3000')->result->code;

ioloop

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

I/O 処理のために利用されるループオブジェクト。デフォルトは Mojo::IOLoop オブジェクトが利用されます。

key

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

TLS キーファイルへのパス。デフォルトは MOJO_KEY_FILE環境変数の値です。

local_address

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

バインドされているローカルアドレス。

max_connections

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

ユーザーエージェントが、最も古いキャッシュされた接続を閉じ始めるまでに 保有するキープアライブ接続の最大数。デフォルトは 5。この値を0に設定するとすべての接続がケプトアライブになることを防ぎます。

max_redirects

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

ユーザーエージェントが、失敗する前までに辿るリダイレクトの最大数。 デフォルトは MOJO_MAX_REDIRECTS の値、または 0 です。

max_response_size

my $max = $ua->max_response_size;
$ua     = $ua->max_response_size(16777216);

最大応答サイズ(バイト)。デフォルトはMojo::Message::Responseの"max_message_size"の値です。 この値を0に設定すると、無限のサイズのレスポンスを許可します。 この値を増加させると、劇的にメモリの使用量が増えることに注意してください。たとえば、とても大きなサイズのレスポンスボディを、Mojo::Messageの"dom"や"json"で解析しようとした場合などです。

proxy

my $proxy = $ua->proxy;
$ua       = $ua->proxy(Mojo::UserAgent::Proxy->new);

プロキシマネージャー。デフォルトはMojo::UserAgent::Proxyオブジェクト。

# 環境からプロキシサーバーを検知します。
$ua->proxy->detect;

# 手動でHTTPプロキシを設定(HTTPS/WebSocketsのためにCONNECTを使う)
$ua->proxy->http('http://127.0.0.1:8080')->https('http://127.0.0.1:8080');

# Tor(SOCKS5)のための手動での設定
$ua->proxy->http('socks://127.0.0.1:9050')->https('socks://127.0.0.1:9050');

# 手動でUNIXドメインソケットを設定する(HTTPS/WebSocketsのためにCONNECTを使います)
$ua->proxy->http('http+unix://%2Ftmp%2Fproxy.sock')
  ->https('http+unix://%2Ftmp%2Fproxy.sock');

request_timeout

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

「接続が確立、リクエストが送信、完全なレスポンスを受けとる」ことがキャンセルされるまでの 最大秒数。デフォルトは、MOJO_REQUEST_TIMEOUT環境変数の値か0。 この値を0に設定すると、ユーザーエージェントが無限に待つことを許可します。 タイムアウトはすべての続くリダイレクトにおいてリセットされます。

# 全体の制限は5秒、接続には3秒が費やされるかもしれない。
$ua->max_redirects(0)->connect_timeout(3)->request_timeout(5);

server

my $server = $ua->server;
$ua        = $ua->server(Mojo::UserAgent::Server->new);

アプリケーションサーバーの相対URLが一緒に処理されます。 デフォルトはMojo::UserAgent::Serverです。

# Webサービスをモックする
$ua->server->app(Mojolicious->new);
$ua->server->app->routes->get('/time' => sub {
  my $c = shift;
  $c->render(json => {now => time});
});
my $time = $ua->get('/time')->result->json->{now};

# ログレベルの変更
$ua->server->app->log->level('fatal');

# 相対URLのブロッキングを処理するために現在利用されているポート
say $ua->server->url->port;

# 相対URLのノンブロッキングを処理するために現在利用されているポート
say $ua->server->nb_url->port;

transactor

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

トランザクションビルダー。 デフォルトはMojo::UserAgent::Transactorオブジェクトです。

# ユーザーエージェントの名前を変更
$ua->transactor->name('MyUA 1.0');

# 圧縮を無効にする
$ua->transactor->compressed(0);

メソッド

Mojo::UserAgentMojo::EventEmitter のすべてのメソッドを継承しており、 以下の新しいメソッドを実装しています。

build_tx

my $tx = $ua->build_tx(GET => 'example.com');
my $tx = $ua->build_tx(
  PUT => 'http://example.com' => {Accept => '*/*'} => 'Content!');
my $tx = $ua->build_tx(
  PUT => 'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
my $tx = $ua->build_tx(
  PUT => 'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});

Mojo::UserAgent::Transactortxメソッドで Mojo::Transaction::HTTPオブジェクトを生成します。

# カスタムクッキーを持ったリクエスト
my $tx = $ua->build_tx(GET => 'https://example.com/account');
$tx->req->cookies({name => 'user', value => 'sri'});
$tx = $ua->start($tx);

# gzip圧縮を非アクティブにする
my $tx = $ua->build_tx(GET => 'example.com');
$tx->req->headers->remove('Accept-Encoding');
$tx = $ua->start($tx);

# ひとつのエラーを発生することによって、レスポンスを中断する
my $tx = $ua->build_tx(GET => 'example.com');
$tx->res->on(progress => sub {
  my $res = shift;
  return unless my $server = $res->headers->server;
  $res->error({message => 'Oh noes, it is IIS!'}) if $server =~ /IIS/;
});
$tx = $ua->start($tx);

build_websocket_tx

my $tx = $ua->build_websocket_tx('ws://example.com');
my $tx = $ua->build_websocket_tx(
  'ws://example.com' => {DNT => 1} => ['v1.proto']);

Mojo::UserAgent::Transactorwebsocketメソッドで Mojo::Transaction::HTTPオブジェクトを生成します。

# クッキーを持ったWebSocketハンドシェイク
my $tx = $ua->build_websocket_tx('wss://example.com/echo');
$tx->req->cookies({name => 'user', value => 'sri'});
$ua->start($tx => sub {
  my ($ua, $tx) = @_;
  say 'WebSocket handshake failed!' and return unless $tx->is_websocket;
  $tx->on(message => sub {
    my ($tx, $msg) = @_;
    say "WebSocket message: $msg";
    $tx->finish;
  });
  $tx->send('Hi!');
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

delete

my $tx = $ua->delete('example.com');
my $tx = $ua->delete('http://example.com' => {Accept => '*/*'} => 'Hi!');
my $tx = $ua->delete(
  'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
my $tx = $ua->delete(
  'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});

ブロッキングするDELETEリクエストを実行し、結果であるMojo::Transaction::HTTPオブジェクト を返却します。 (メソッドを除いて)Mojo::UserAgent::Transactortxとまったく同じ引数を受け取ります。 ノンブロッキングのリクエストを実行するため、コールバックを追加することも できます。

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

delete_p

my $promise = $ua->delete_p('http://example.com');

Same as "delete", but performs all requests non-blocking and returns a Mojo::Promise object instead of accepting a callback.

"delete"と同じですが、すべてのリクエストをノンブロッキングで実行し、 コールバックを受け付ける代わりに、Mojo::Promiseオブジェクトを返します。

$ua->delete_p('http://example.com' => json => {a => 'b'})->then(sub {
  my $tx = shift;
  say $tx->result->body;
})->catch(sub {
  my $err = shift;
  warn "Connection error: $err";
})->wait;

get

my $tx = $ua->get('example.com');
my $tx = $ua->get('http://example.com' => {Accept => '*/*'} => 'Hi!');
my $tx = $ua->get(
  'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
my $tx = $ua->get(
  'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});

ブロッキングするHTTP GETリクエストを実行し、結果であるMojo::Transaction::HTTPオブジェクト を返却します。 (メソッドを除いて)Mojo::UserAgent::Transactortxとまったく同じ引数を受け取ります。 ノンブロッキングのリクエストを実行するため、コールバックを追加することも できます。

$ua->get('http://example.com' => json => {a => 'b'} => sub {
  my ($ua, $tx) = @_;
  say $tx->result->body;
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

get_p

my $promise = $ua->get_p('http://example.com');

"get"と同じですが、すべてのリクエストをノンブロッキングで実行し、 コールバックを受け付ける代わりに、Mojo::Promiseオブジェクトを返します。

$ua->get_p('http://example.com' => json => {a => 'b'})->then(sub {
  my $tx = shift;
  say $tx->result->body;
})->catch(sub {
  my $err = shift;
  warn "Connection error: $err";
})->wait;

head

my $tx = $ua->head('example.com');
my $tx = $ua->head('http://example.com' => {Accept => '*/*'} => 'Hi!');
my $tx = $ua->head(
  'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
my $tx = $ua->head(
  'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});

ブロッキングするHTTP HEADリクエストを実行し、結果であるMojo::Transaction::HTTPオブジェクト を返却します。 (メソッドを除いて)Mojo::UserAgent::Transactortxとまったく同じ引数を受け取ります。 ノンブロッキングのリクエストを実行するため、コールバックを追加することも できます。

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

head_p

my $promise = $ua->head_p('http://example.com');

"head"と同じですが、すべてのリクエストをノンブロッキングで実行し、 コールバックを受け付ける代わりに、Mojo::Promiseオブジェクトを返します。

$ua->head_p('http://example.com' => json => {a => 'b'})->then(sub {
  my $tx = shift;
  say $tx->result->body;
})->catch(sub {
  my $err = shift;
  warn "Connection error: $err";
})->wait;

options

my $tx = $ua->options('example.com');
my $tx = $ua->options('http://example.com' => {Accept => '*/*'} => 'Hi!');
my $tx = $ua->options(
  'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
my $tx = $ua->options(
  'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});

ブロッキングするOPTIONSリクエストを実行し、結果であるMojo::Transaction::HTTPオブジェクト を返却します。 (メソッドを除いて)Mojo::UserAgent::Transactortxとまったく同じ引数を受け取ります。 ノンブロッキングのリクエストを実行するため、コールバックを追加することも できます。

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

options_p

my $promise = $ua->options_p('http://example.com');

"options"と同じですが、すべてのリクエストをノンブロッキングで実行し、 コールバックを受け付ける代わりに、Mojo::Promiseオブジェクトを返します。

$ua->options_p('http://example.com' => json => {a => 'b'})->then(sub {
  my $tx = shift;
  say $tx->result->body;
})->catch(sub {
  my $err = shift;
  warn "Connection error: $err";
})->wait;

patch

my $tx = $ua->patch('example.com');
my $tx = $ua->patch('http://example.com' => {Accept => '*/*'} => 'Hi!');
my $tx = $ua->patch(
  'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
my $tx = $ua->patch(
  'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});

ブロッキングするPATCHリクエストを実行し、結果であるMojo::Transaction::HTTPオブジェクト を返却します。 (メソッドを除いて)Mojo::UserAgent::Transactortxとまったく同じ引数を受け取ります。 ノンブロッキングのリクエストを実行するため、コールバックを追加することも できます。

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

patch_p

my $promise = $ua->patch_p('http://example.com');

"patch"と同じですが、すべてのリクエストをノンブロッキングで実行し、 コールバックを受け付ける代わりに、Mojo::Promiseオブジェクトを返します。

$ua->patch_p('http://example.com' => json => {a => 'b'})->then(sub {
  my $tx = shift;
  say $tx->result->body;
})->catch(sub {
  my $err = shift;
  warn "Connection error: $err";
})->wait;

post

my $tx = $ua->post('example.com');
my $tx = $ua->post('http://example.com' => {Accept => '*/*'} => 'Hi!');
my $tx = $ua->post(
  'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
my $tx = $ua->post(
  'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});

ブロッキングするPOSTリクエストを実行し、結果であるMojo::Transaction::HTTPオブジェクト を返却します。 (メソッドを除いて)Mojo::UserAgent::Transactortxとまったく同じ引数を受け取ります。 ノンブロッキングのリクエストを実行するため、コールバックを追加することも できます。

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

post_p

my $promise = $ua->post_p('http://example.com');

"post"と同じですが、すべてのリクエストをノンブロッキングで実行し、 コールバックを受け付ける代わりに、Mojo::Promiseオブジェクトを返します。

$ua->post_p('http://example.com' => json => {a => 'b'})->then(sub {
  my $tx = shift;
  say $tx->result->body;
})->catch(sub {
  my $err = shift;
  warn "Connection error: $err";
})->wait;

put

 my $tx = $ua->put('example.com');
my $tx = $ua->put('http://example.com' => {Accept => '*/*'} => 'Hi!');
my $tx = $ua->put(
  'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
my $tx = $ua->put(
  'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});

ブロッキングするPUTリクエストを実行し、結果であるMojo::Transaction::HTTPオブジェクト を返却します。 (メソッドを除いて)Mojo::UserAgent::Transactortxとまったく同じ引数を受け取ります。 ノンブロッキングのリクエストを実行するため、コールバックを追加することも できます。

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

put_p

my $promise = $ua->put_p('http://example.com');

"put"と同じですが、すべてのリクエストをノンブロッキングで実行し、 コールバックを受け付ける代わりに、Mojo::Promiseオブジェクトを返します。

$ua->put_p('http://example.com' => json => {a => 'b'})->then(sub {
  my $tx = shift;
  say $tx->result->body;
})->catch(sub {
  my $err = shift;
  warn "Connection error: $err";
})->wait;

start

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

カスタムのMojo::Transaction::HTTPオブジェクトのためにブロッキングするリクエストを処理します。 ノンブロッキングのトランザクションを実行するため、コールバックを追加することも できます。

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

start_p

my $promise = $ua->start_p(Mojo::Transaction::HTTP->new);

"start"と同じですが、すべてのリクエストをノンブロッキングで実行し、 コールバックを受け付ける代わりに、Mojo::Promiseオブジェクトを返します。

my $tx = $ua->build_tx(GET => 'http://example.com');
$ua->start_p($tx)->then(sub {
  my $tx = shift;
  say $tx->result->body;
})->catch(sub {
  my $err = shift;
  warn "Connection error: $err";
})->wait;

websocket

$ua->websocket('ws://example.com' => sub {...});
$ua->websocket(
  'ws://example.com' => {DNT => 1} => ['v1.proto'] => sub {...});

透過的なハンドシェイクでノンブロッキングのWebSocket接続をオープンします。 Mojo::UserAgent::Transactorwebsocketとまったく同じ引数を受け取ります。 コールバックは、ハンドシェイクが成功しかたに依存して、Mojo::Transaction::WebSocketオブジェクトかMojo::Transaction::HTTPオブジェクトを受け取ります。

$ua->websocket('wss://example.com/echo' => ['v1.proto'] => sub {
  my ($ua, $tx) = @_;
  say 'WebSocket handshake failed!' and return unless $tx->is_websocket;
  say 'Subprotocol negotiation failed!' and return unless $tx->protocol;
  $tx->on(finish => sub {
    my ($tx, $code, $reason) = @_;
    say "WebSocket closed with status $code.";
  });
  $tx->on(message => sub {
    my ($tx, $msg) = @_;
    say "WebSocket message: $msg";
    $tx->finish;
  });
  $tx->send('Hi!');
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

Sec-WebSocket-Extensionsを設定することによって、 permessage-deflate圧縮をアクティブにすることができます。 これはよりよりパフォーマンスをもたらすでしょう。 しかし、コネクションあたりメモリ使用率は300KB上昇します。

$ua->websocket('ws://example.com/foo' => {
  'Sec-WebSocket-Extensions' => 'permessage-deflate'
} => sub {...});

websocket_p

my $promise = $ua->websocket_p('ws://example.com');

"websocket"と同じですが、すべてのリクエストをノンブロッキングで実行し、 コールバックを受け付ける代わりに、Mojo::Promiseオブジェクトを返します。

$ua->websocket_p('wss://example.com/echo')->then(sub {
  my $tx = shift;
  my $promise = Mojo::Promise->new;
  $tx->on(finish => sub { $promise->resolve });
  $tx->on(message => sub {
    my ($tx, $msg) = @_;
    say "WebSocket message: $msg";
    $tx->finish;
  });
  $tx->send('Hi!');
  return $promise;
})->catch(sub {
  my $err = shift;
  warn "WebSocket error: $err";
})->wait;

デバッグ

いくらかの発展的な詳細な情報をSTDERRに出力するために、 MOJO_USERAGENT_DEBUG環境変数を設定することができます。

MOJO_USERAGENT_DEBUG=1

参考

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

(Mojolicious 8.12を反映。2019年5月30日更新)