Hypersonic

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN


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 2.169 seconds using v1.01-cache-2.11-cpan-524268b4103 )