PAGI
view release on metacpan or search on metacpan
lib/PAGI/Cookbook.pod view on Meta::CPAN
['content-type', 'application/octet-stream'],
['content-disposition', qq{attachment; filename="$filename"}],
],
});
await $send->({
type => 'http.response.body',
file => $filepath,
});
});
my $app = builder {
enable 'XSendfile',
type => 'X-Accel-Redirect',
mapping => { '/var/www/files/' => '/protected/' };
$router->to_app;
};
=head3 Apache Configuration (mod_xsendfile)
Enable mod_xsendfile and whitelist your file directory:
# Apache config
XSendFile On
XSendFilePath /var/www/files
# Application
my $app = builder {
enable 'XSendfile', type => 'X-Sendfile';
$my_app;
};
=head3 Filehandle Support
The middleware also works with filehandle responses, but only if the
filehandle object has a C<path()> method. L<IO::File::WithPath> from CPAN
is an easy drop-in for this:
use IO::File::WithPath;
# This works - IO::File::WithPath provides the path method
my $fh = IO::File::WithPath->new('/path/to/file.bin', 'r');
await $send->({ type => 'http.response.body', fh => $fh });
# This does NOT trigger X-Sendfile (no path method)
open my $plain_fh, '<', '/path/to/file.bin';
await $send->({ type => 'http.response.body', fh => $plain_fh });
For plain filehandles, either use C<file =E<gt> $path> directly, or use
L<IO::File::WithPath>. See L<PAGI::Middleware::XSendfile> for details.
=head1 TESTING
Use L<PAGI::Test::Client> to test apps directly without a running server:
use strict;
use warnings;
use Test2::V0;
use PAGI::Test::Client;
# Load your app
my $app = require './app.pl';
my $client = PAGI::Test::Client->new(app => $app);
subtest 'GET /' => sub {
my $res = $client->get('/');
is $res->status, 200, 'status is 200';
like $res->text, qr/Hello/, 'body contains Hello';
};
subtest 'POST /api/users' => sub {
my $res = $client->post('/api/users',
json => { name => 'Alice' },
);
is $res->status, 201, 'status is 201';
is $res->json->{id}, 1, 'returns user id';
};
subtest 'form submission' => sub {
my $res = $client->post('/login',
form => { user => 'admin', pass => 'secret' },
);
is $res->status, 302, 'redirects after login';
# Session cookies persist across requests
my $dashboard = $client->get('/dashboard');
is $dashboard->status, 200, 'authenticated access';
};
subtest 'custom headers' => sub {
my $res = $client->get('/api/data',
headers => { Authorization => 'Bearer token123' },
);
is $res->status, 200;
};
done_testing;
See L<PAGI::Test::Client> for the full API including WebSocket and SSE testing.
=head1 SEE ALSO
=over 4
=item * L<PAGI::Tutorial> - Learn PAGI from the ground up
=item * L<PAGI::App::Router> - Functional routing
=item * L<PAGI::Endpoint::Router> - Class-based routing
=item * L<PAGI::Middleware::Session> - Session management
=item * L<PAGI::App::WebSocket::Chat> - Multi-room chat application
=item * L<PAGI::Middleware::XSendfile> - Delegate file serving to reverse proxy
=item * L<PAGI::Test::Client> - Test apps without a running server
=back
=head1 AUTHOR
( run in 2.423 seconds using v1.01-cache-2.11-cpan-5837b0d9d2c )