Name

SPVM::Mojo::UserAgent - Context-aware HTTP and WebSocket user agent

Description

Mojo::UserAgent class in SPVM is a full featured HTTP and WebSocket user agent. Every I/O operation is context-aware and can be canceled via Go::Context.

Since this class is designed to be goroutine-safe, you can perform multiple HTTP requests in parallel by creating goroutines. This is efficient for fetching data from multiple APIs simultaneously.

Usage

use Mojo::UserAgent;
use Go::Context;
use Go;

# Note: This version is not compatible with previous versions.
# The first argument of I/O methods (get, post, put, patch, delete, head, options, start) 
# now requires a Go::Context object.

my $ua  = Mojo::UserAgent->new;
my $ctx = Go::Context->background;

# --- Single request example ---
my $res = $ua->get($ctx, "docs.mojolicious.org")->result;
say $res->body if $res->is_success;

# --- Parallel access example ---
# Fetch multiple URLs in parallel using goroutines.
# Note: SPVM does not support closures, so variables must be passed as arguments.
my $urls = ["https://google.com", "https://github.com", "https://metacpan.org"];
for my $url (@$urls) {
  Go->go([$ctx : Go::Context, $ua : Mojo::UserAgent, $url : string], method : void () {
    my $tx = $ua->get($ctx, $url);
    if (my $res = $tx->result) {
      say "URL: $url, Code: " . $res->code;
    }
  });
}

# Wait for all goroutines to finish
Go->gosched;

# --- Request with timeout example ---
{
  my $timeout_ctx_derived = Go::Context->with_timeout($ctx, 0.5);
  my $timeout_ctx = $timeout_ctx_derived->ctx;
  my $tx = $ua->get($timeout_ctx, "https://example.com");
}

Class Methods

new

static method new : Mojo::UserAgent ();

Create a new Mojo::UserAgent object, and return it.

Instance Methods

build_tx

method build_tx : Mojo::Transaction::HTTP ($method : string, $url : string|Mojo::URL, $args : object...);

Generate Mojo::Transaction::HTTP object with Mojo::UserAgent::Transactor#tx.

Examples:

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 => "*/*"} => [(object)form => {a => "b"}]);
my $tx = $ua->build_tx(PUT => "http://example.com" => {Accept => "*/*"} => [(object)json => {a => "b"}]);

# Request with custom cookie
my $tx = $ua->build_tx(GET => "https://example.com/account");
$tx->req->cookies({name => "user", value => "sri"});
$tx = $ua->start($ctx, $tx);

# Deactivate gzip compression
my $tx = $ua->build_tx(GET => "example.com");
$tx->req->headers->remove("Accept-Encoding");
$tx = $ua->start($ctx, $tx);

# Interrupt response by raising an error
my $tx = $ua->build_tx(GET => "http://example.com");
$tx->res->on(progress => method ($res : Mojo::Message::Response) {
  unless (my $server = $res->headers->server) {
    return;
  }
  
  if (Re->m($server, "IIS")) {
    die "Oh noes, it is IIS!";
  }
});
$tx = $ua->start($ctx, $tx);

build_websocket_tx

Not yet implemented.

delete

method delete : Mojo::Transaction::HTTP ($ctx : Go::Context, $url : string|Mojo::URL, $args : object...);

Perform blocking DELETE request and return resulting Mojo::Transaction::HTTP object, takes the same arguments as "tx" in Mojo::UserAgent::Transactor (except for the DELETE method, which is implied).

Examples:

my $tx = $ua->delete($ctx, "example.com");
my $tx = $ua->delete($ctx, "http://example.com" => {Accept => "*/*"} => "Content!");
my $tx = $ua->delete($ctx, "http://example.com" => {Accept => "*/*"} => [(object)form => {a => "b"}]);
my $tx = $ua->delete($ctx, "http://example.com" => {Accept => "*/*"} => [(object)json => {a => "b"}]);

get

method get : Mojo::Transaction::HTTP ($ctx : Go::Context, $url : string|Mojo::URL, $args : object...);

Perform blocking GET request and return resulting Mojo::Transaction::HTTP object, takes the same arguments as "tx" in Mojo::UserAgent::Transactor (except for the GET method, which is implied).

Examples:

my $tx = $ua->get($ctx, "example.com");
my $tx = $ua->get($ctx, "http://example.com" => {Accept => "*/*"} => "Content!");
my $tx = $ua->get($ctx, "http://example.com" => {Accept => "*/*"} => [(object)form => {a => "b"}]);
my $tx = $ua->get($ctx, "http://example.com" => {Accept => "*/*"} => [(object)json => {a => "b"}]);

method head : Mojo::Transaction::HTTP ($ctx : Go::Context, $url : string|Mojo::URL, $args : object...);

Perform blocking HEAD request and return resulting Mojo::Transaction::HTTP object, takes the same arguments as "tx" in Mojo::UserAgent::Transactor (except for the HEAD method, which is implied).

Examples:

my $tx = $ua->head($ctx, "example.com");
my $tx = $ua->head($ctx, "http://example.com" => {Accept => "*/*"} => "Content!");
my $tx = $ua->head($ctx, "http://example.com" => {Accept => "*/*"} => [(object)form => {a => "b"}]);
my $tx = $ua->head($ctx, "http://example.com" => {Accept => "*/*"} => [(object)json => {a => "b"}]);

options

method options : Mojo::Transaction::HTTP ($ctx : Go::Context, $url : string|Mojo::URL, $args : object...);

Perform blocking OPTIONS request and return resulting Mojo::Transaction::HTTP object, takes the same arguments as "tx" in Mojo::UserAgent::Transactor (except for the OPTIONS method, which is implied).

Examples:

my $tx = $ua->options($ctx, "example.com");
my $tx = $ua->options($ctx, "http://example.com" => {Accept => "*/*"} => "Content!");
my $tx = $ua->options($ctx, "http://example.com" => {Accept => "*/*"} => [(object)form => {a => "b"}]);
my $tx = $ua->options($ctx, "http://example.com" => {Accept => "*/*"} => [(object)json => {a => "b"}]);

patch

method patch : Mojo::Transaction::HTTP ($ctx : Go::Context, $url : string|, $args : object...);

Perform blocking PATCH request and return resulting Mojo::Transaction::HTTP object, takes the same arguments as "tx" in Mojo::UserAgent::Transactor (except for the PATCH method, which is implied).

Examples:

my $tx = $ua->patch($ctx, "example.com");
my $tx = $ua->patch($ctx, "http://example.com" => {Accept => "*/*"} => "Content!");
my $tx = $ua->patch($ctx, "http://example.com" => {Accept => "*/*"} => [(object)form => {a => "b"}]);
my $tx = $ua->patch($ctx, "http://example.com" => {Accept => "*/*"} => [(object)json => {a => "b"}]);

post

method post : Mojo::Transaction::HTTP ($ctx : Go::Context, $url : string|Mojo::URL, $args : object...);

Perform blocking POST request and return resulting Mojo::Transaction::HTTP object, takes the same arguments as "tx" in Mojo::UserAgent::Transactor (except for the POST method, which is implied).

Examples:

my $tx = $ua->post($ctx, "example.com");
my $tx = $ua->post($ctx, "http://example.com" => {Accept => "*/*"} => "Content!");
my $tx = $ua->post($ctx, "http://example.com" => {Accept => "*/*"} => [(object)form => {a => "b"}]);
my $tx = $ua->post($ctx, "http://example.com" => {Accept => "*/*"} => [(object)json => {a => "b"}]);

put

method put : Mojo::Transaction::HTTP ($ctx : Go::Context, $url : string|Mojo::URL, $args : object...);

Perform blocking PUT request and return resulting Mojo::Transaction::HTTP object, takes the same arguments as "tx" in Mojo::UserAgent::Transactor (except for the PUT method, which is implied).

Examples:

my $tx = $ua->put($ctx, "example.com");
my $tx = $ua->put($ctx, "http://example.com" => {Accept => "*/*"} => "Content!");
my $tx = $ua->put($ctx, "http://example.com" => {Accept => "*/*"} => [(object)form => {a => "b"}]);
my $tx = $ua->put($ctx, "http://example.com" => {Accept => "*/*"} => [(object)json => {a => "b"}]);

start

method start : Mojo::Transaction::HTTP ($ctx : Go::Context, $tx : Mojo::Transaction::HTTP);

Perform blocking request for a custom Mojo::Transaction::HTTP object, which can be prepared manually or with "build_tx".

Examples:

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

websocket

Not yet implemented.

See Also

Copyright & License

Copyright (c) 2025 Yuki Kimoto

MIT License

1 POD Error

The following errors were encountered while parsing the POD:

Around line 166:

Nested L<> are illegal. Pretending inner one is X<...> so can continue looking for other errors.