Async-Redis
view release on metacpan or search on metacpan
examples/pagi-chat/lib/ChatApp/HTTP.pm view on Meta::CPAN
ico => 'image/x-icon',
);
sub handler {
return async sub {
my ($scope, $receive, $send) = @_;
my $path = $scope->{path} // '/';
my $method = $scope->{method} // 'GET';
if ($path =~ m{^/api/}) {
return await _handle_api($scope, $receive, $send, $path, $method);
}
return await _serve_static($scope, $receive, $send, $path);
};
}
async sub _handle_api {
my ($scope, $receive, $send, $path, $method) = @_;
my $result = await _do_api($path, $method)->catch(sub {
my ($err) = @_;
warn "API error: $err";
return Future->done({ status => 500, data => { error => 'Internal error' } });
});
my $body = $JSON->encode($result->{data});
await $send->({
type => 'http.response.start',
status => $result->{status},
headers => [
['content-type', 'application/json; charset=utf-8'],
['content-length', length($body)],
],
});
await $send->({
type => 'http.response.body',
body => $body,
});
}
async sub _do_api {
my ($path, $method) = @_;
if ($path eq '/api/rooms' && $method eq 'GET') {
my $rooms = await get_all_rooms();
return {
status => 200,
data => [
map { { name => $_, users => scalar(keys %{$rooms->{$_}{users}}) } }
sort keys %$rooms
],
};
}
if ($path =~ m{^/api/room/([^/]+)/history$} && $method eq 'GET') {
my $room_name = $1;
my $room = await get_room($room_name);
return { status => 404, data => { error => 'Room not found' } } unless $room;
return { status => 200, data => await get_room_messages($room_name, 100) };
}
if ($path =~ m{^/api/room/([^/]+)/users$} && $method eq 'GET') {
my $room_name = $1;
my $room = await get_room($room_name);
return { status => 404, data => { error => 'Room not found' } } unless $room;
return { status => 200, data => await get_room_users($room_name) };
}
if ($path eq '/api/stats' && $method eq 'GET') {
return { status => 200, data => await get_stats() };
}
return { status => 404, data => { error => 'Not found' } };
}
async sub _serve_static {
my ($scope, $receive, $send, $path) = @_;
$path = '/index.html' if $path eq '/';
$path =~ s/\.\.//g;
$path =~ s|//+|/|g;
my $file_path = File::Spec->catfile($PUBLIC_DIR, $path);
print STDERR "[HTTP] Serving: $file_path (exists: " . (-f $file_path ? 'yes' : 'no') . ")\n";
unless (-f $file_path && -r $file_path) {
return await _send_404($send);
}
my ($ext) = $file_path =~ /\.(\w+)$/;
my $content_type = $MIME_TYPES{lc($ext // '')} // 'application/octet-stream';
my $content;
{
open my $fh, '<:raw', $file_path or return await _send_500($send);
local $/;
$content = <$fh>;
close $fh;
}
await $send->({
type => 'http.response.start',
status => 200,
headers => [
['content-type', $content_type],
['content-length', length($content)],
],
});
await $send->({
type => 'http.response.body',
body => $content,
});
}
async sub _send_404 {
my ($send) = @_;
my $body = '{"error":"Not found"}';
await $send->({
type => 'http.response.start',
status => 404,
headers => [['content-type', 'application/json']],
});
await $send->({ type => 'http.response.body', body => $body });
}
async sub _send_500 {
my ($send) = @_;
my $body = '{"error":"Internal server error"}';
await $send->({
type => 'http.response.start',
status => 500,
headers => [['content-type', 'application/json']],
});
await $send->({ type => 'http.response.body', body => $body });
}
1;
( run in 0.953 second using v1.01-cache-2.11-cpan-39bf76dae61 )