Hypersonic
view release on metacpan or search on metacpan
0.08 2026-01-28
- Add Hypersonic::UA HTTP client with JIT-compiled XS
- Optional feature compilation (async, parallel, tls, http2, compression)
- Connection pooling with keep-alive support
- Async request support (currently only kqueue/epoll supported)
- Benchmark static get against python, rust and go.
0.07 2026-01-28
- Adds c_helpers for reusable c functions
- Adds need_xs_builder as an endpoint feature...
- Refactor Future::Pool so you can have more than one
- Centralise the caching
- Be a little more JIT but some improvements can still be made..
0.06 2026-01-28
- Async/Future Enhancements in preparation to remove the next bottle
0.05 2026-01-28
- Fix G_LIST -> G_ARRAY for Perl 5.32+ compatibility (CPAN testers fix)
lib/Hypersonic.pm view on Meta::CPAN
# Route registration methods
sub get { shift->_add_route('GET', @_) }
sub post { shift->_add_route('POST', @_) }
sub put { shift->_add_route('PUT', @_) }
sub del { shift->_add_route('DELETE', @_) }
sub patch { shift->_add_route('PATCH', @_) }
sub head { shift->_add_route('HEAD', @_) }
sub options { shift->_add_route('OPTIONS', @_) }
# Health check endpoint - built-in route for load balancer / k8s probes
sub health_check {
my ($self, $path, $handler) = @_;
$path //= '/health';
# Default handler returns JSON string (JIT compiled as constant)
$handler //= sub {
return '{"status":"ok"}';
};
return $self->get($path => $handler);
}
# Readiness check endpoint - separate from health for k8s
sub ready_check {
my ($self, $path, $handler) = @_;
$path //= '/ready';
$handler //= sub {
return '{"ready":true}';
};
return $self->get($path => $handler);
}
t/2100-ua-integration.t view on Meta::CPAN
# Start a test server
my $PORT = 31000 + ($$ % 1000);
my $server_pid;
my $server_log;
sub start_test_server {
($server_pid, $server_log) = spawn_server(sub {
require Hypersonic;
my $server = Hypersonic->new(cache_dir => "_test_ua_server_$$");
# Simple GET endpoints
$server->get('/hello' => sub { 'Hello, World!' });
$server->get('/json' => sub { '{"status":"ok","message":"Hello"}' });
# POST endpoint
$server->post('/post' => sub { 'POST received' });
# PUT endpoint
$server->put('/put' => sub { 'PUT received' });
# DELETE endpoint
$server->del('/delete' => sub { 'DELETE received' });
$server->compile();
$server->run(port => $PORT, workers => 1);
});
wait_for_port($PORT, { pid => $server_pid, log => $server_log, tries => 50 })
or die "Server failed to start";
return 1;
}
t/2101-ua-async-integration.t view on Meta::CPAN
my $PORT = 32000 + ($$ % 1000);
my $server_pid;
my $server_log;
sub start_test_server {
($server_pid, $server_log) = spawn_server(sub {
require Hypersonic;
my $server = Hypersonic->new(cache_dir => "_test_async_server_$$");
# Fast endpoint
$server->get('/fast' => sub { 'fast' });
# Slow endpoint - must be dynamic to actually execute sleep at runtime
# Note: Routes with :param are automatically dynamic, access via $req->param('name')
$server->get('/slow/:ms' => sub {
my ($req) = @_;
my $ms = $req->param('ms') // 100;
select(undef, undef, undef, $ms / 1000);
return "waited $ms ms";
});
# Counter endpoint for testing multiple requests
# Access param via $req->param('n') or shorthand $req->n
$server->get('/count/:n' => sub {
my ($req) = @_;
return $req->param('n');
});
$server->compile();
$server->run(port => $PORT, workers => 1);
});
t/2101-ua-async-integration.t view on Meta::CPAN
is(res_body($results[$i]), $i + 1, "Request $i body correct");
}
note("5 sequential requests in ${elapsed}s");
};
subtest 'Mixed GET and POST' => sub {
# GET
my $get_res = $ua->get("http://127.0.0.1:$PORT/fast");
is(res_body($get_res), 'fast', 'GET completed');
# Another GET to dynamic endpoint
my $count_res = $ua->get("http://127.0.0.1:$PORT/count/42");
is(res_body($count_res), '42', 'Dynamic GET completed');
};
subtest 'Slow endpoint' => sub {
my $start = time();
my $res = $ua->get("http://127.0.0.1:$PORT/slow/100");
my $elapsed = time() - $start;
is(res_status($res), 200, 'Got response from slow endpoint');
like(res_body($res), qr/waited 100 ms/, 'Body indicates wait time');
ok($elapsed >= 0.05, "Request took ${elapsed}s (expected >= 0.05s)");
};
# Cleanup
do { local $@; eval { require File::Path; File::Path::remove_tree($_, { safe => 1, error => \my $e }) for grep { -e $_ } glob(qq(_test_async_client_*)); }; };
done_testing();
( run in 0.647 second using v1.01-cache-2.11-cpan-524268b4103 )