Catalyst application under Apache2 mod_perl won't render Mason templates - perl

my application root is /home/user/apps/learningcatalyst/CGAddressBook
in that folder I have a /mason folder where mason components are stored
my View is located in /lib/CGAddressBook/View where I have a few files called login_form and addressbook.
Couldn't render component "login_form" - error was "could not find component for initial path '/login_form' (component roots are: '/home/user/apps/learningcatalyst/CGAddressBook/lib/CGAddressBook/View')
is the error I get when trying to reach the site through Apache. My apache conf file is
PerlSwitches -I/home/user/apps/learningcatalyst/CGAddressBook/lib
PerlModule CGAddressBook
<Location /user_catalyst/>
SetHandler modperl
PerlResponseHandler CGAddressBook
</Location>
DocumentRoot /home/user/apps/learningcatalyst/CGAddressBook/root
<Location /user_catalyst/static>
SetHandler default-handler
</Location>
All folders are readable by user, its group, and other. The mason folder, where Mason is set to write its cache, is set to read, write, and execute by user, group, and other. Its owner is user.
I have restarted Apache to no avail, I have changed folder owners (and recursively) of both the /View and /mason.
The application works just fine when running script/cgaddressbook_server.pl -r -p3001 and I go to my site on port 3001.

The path to comp_root is meant to be the path to your template "components". You appear to be pointing it at the same directory as the "View" package. You "might" actually have the components there but that is not really a good practice.
The default location without specifying this should be the "root/comps" folder ( or something like that ) in your Catalyst project structure. One of my own samples is like this:
package SnakierTen::Web::View::HTML;
use Modern::Perl;
use Moose;
use MooseX::NonMoose;
extends 'Catalyst::View::Mason2';
around BUILDARGS => sub {
my ( $orig, $class, $c, #args ) = #_;
$class->config(
comp_root => $c->path_to( 'root' ),
data_dir => $c->path_to( 'data' ),
plugins => [
'TidyObjectFiles',
'HTMLFilters',
],
);
$class->$orig( $c, #args );
};
__PACKAGE__->meta->make_immutable;
no Moose;
1;
Aside from that, direct support of Mod Perl handlers is being deprecated in Catalyst core.
The favored method is to start the application under it's own PSGI compliant server and use a "front end" web server to "proxy" the requests to this application server.
Where you must run the application under a mod_perl environment, it is still recommended to run under a PSGI handler. There are some notes here that are a part of the documentation yet to be built on this. You can use the methods there to guide you in doing this.

Related

Mojolicious source IP and user agent

I have the following Mojolicious app which serving files from specific folder
use Mojolicious::Lite;
use Mojo::Log;
my $app = app;
my $static = $app->static;
push #{$static->paths} => 'c:\test';
$app->start
when I run:
perl mojo_server.pl daemon -m development
I get this:
[2021-05-18 19:46:39.22370] [29388] [info] Listening at "http://*:3000"
Server available at http://127.0.0.1:3000
and when I access a file from the web browser I am able to access the file, but there are no information on the STDERR after "Server available at http://127.0.0.1:3000"
like what is the source address and the page requested and the user agent. How can I turn mojolicious to show those info on debug mode, if this possible?
You can use after_static from HOOKS in Mojolicious:
use Mojolicious::Lite -signatures;
use Mojo::Log;
my $log = Mojo::Log->new;
hook after_static => sub ($c){
$log->debug('original_remote_address : ' => $c->tx->original_remote_address);
$log->debug('user_agent : ' => $c->tx->req->content->headers->user_agent );
$log->debug('url : ' => $c->tx->req->url->to_abs);
};
app->start;
The after_static event is triggered when a static response has been generated.
Update: If you need a more general solution which is not specifically targeted at serving static files, take a look at the AccessLog plugin.
use Mojolicious::Lite -signatures;
use Mojo::Log;
use Mojolicious::Plugin::AccessLog;
my $log = Mojo::Log->new;
plugin 'AccessLog';
app->start;

Bugzilla: Code on start

I tried to install Bugzilla on my Raspberry. Everything is greater than the minimum system requirements and I installed perl lib to apache too, but I got this when I wanted to "run" it.
#!/usr/bin/perl -T
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.
use 5.10.1;
use strict;
use warnings;
use lib qw(. lib);
use Bugzilla;
use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::Update;
# Check whether or not the user is logged in
my $user = Bugzilla->login(LOGIN_OPTIONAL);
my $cgi = Bugzilla->cgi;
my $template = Bugzilla->template;
my $vars = {};
# And log out the user if requested. We do this first so that nothing
# else accidentally relies on the current login.
if ($cgi->param('logout')) {
Bugzilla->logout();
$user = Bugzilla->user;
$vars->{'message'} = "logged_out";
# Make sure that templates or other code doesn't get confused about this.
$cgi->delete('logout');
}
# Return the appropriate HTTP response headers.
print $cgi->header();
if ($user->in_group('admin')) {
# If 'urlbase' is not set, display the Welcome page.
unless (Bugzilla->params->{'urlbase'}) {
$template->process('welcome-admin.html.tmpl')
|| ThrowTemplateError($template->error());
exit;
}
# Inform the administrator about new releases, if any.
$vars->{'release'} = Bugzilla::Update::get_notifications();
}
if ($user->id) {
my $dbh = Bugzilla->dbh;
$vars->{assignee_count} =
$dbh->selectrow_array("SELECT COUNT(*) FROM bugs WHERE assigned_to = ?
AND resolution = ''", undef, $user->id);
$vars->{reporter_count} =
$dbh->selectrow_array("SELECT COUNT(*) FROM bugs WHERE reporter = ?
AND resolution = ''", undef, $user->id);
$vars->{requestee_count} =
$dbh->selectrow_array('SELECT COUNT(DISTINCT bug_id) FROM flags
WHERE requestee_id = ?', undef, $user->id);
}
# Generate and return the UI (HTML page) from the appropriate template.
$template->process("index.html.tmpl", $vars)
|| ThrowTemplateError($template->error());
What I missed? Or should I use an another issue tracker? (MantisBT)
Assuming that by "run it" you mean "Visited an HTTP URL pointing to your Raspbery Pi on the network in a web browser" and that by "this" you mean "The source code of the CGI program was rendered in the browser" then:
You haven't configured Apache to support CGI for whereever you installed Bugzilla.
The Apache manual page covers how to do this in detail.
You'll need to start by loading the module:
LoadModule cgid_module modules/mod_cgid.so
and enabling CGI for the location you put Bugzilla:
<Directory "/path/to/bugzilla/">
Options +ExecCGI
AddHandler cgi-script .cgi
</Directory>
This is helped me
nano /etc/apache2/sites-available/bugzilla.conf
Paste in the following and save:
ServerName localhost
<Directory /var/www/html/bugzilla>
AddHandler cgi-script .cgi
Options +ExecCGI
DirectoryIndex index.cgi index.html
AllowOverride All
</Directory>
$ a2ensite bugzilla
$ a2enmod cgi headers expires
$ service apache2 restart
Referral URL:
https://bugzilla.readthedocs.io/en/latest/installing/quick-start.html#configure-apache

PHP Built-in Webserver and Relative Paths

TL;DR
Does PHP 5.4 built-in webserver have any bug or restriction about relative paths? Or does it need to be properly (and additionally) configured?
When I used to programming actively I had a system working under URI routing using these lines in a .htaccess file:
RewriteEngine On
RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php [L]
The FrontController received the Request, find the proper route from given URI in a SQLITE database and the Dispatcher call the the Action Controller.
It worked very nicely with Apache. Today, several months later I decided to run my Test Application with PHP 5.4 built-in webserver.
First thing I noticed, obviously, .htaccess don't work so I used code file instead:
<?php
if( preg_match( '/\.(?:png|jpg|jpeg|gif)$/', $_SERVER["REQUEST_URI"] ) ) {
return false;
}
include __DIR__ . '/index.php';
And started the webserver like this:
php.exe -c "php.ini" -S "localhost:8080" "path\to\testfolder\routing.php"
So far, so good. Everything my application need to bootstrap could be accomplished by modifying the include_path like this:
set_include_path(
'.' . PATH_SEPARATOR . realpath( '../common/next' )
);
Being next the core folder of all modules inside a folder for with everything common to all applications I have. And it doesn't need any further explanation for this purpose.
None of the AutoLoader techniques I've ever saw was able to autoload themselves, so the only class manually required is my Autoloader. But after running the Test Application I received an error because my AutoLoader could not be found. o.O
I always was very suspicious about realpath() so I decided to change it with the full and absolute path of this next directory and it worked. It shouldn't be needed to do as I did, but it worked.
My autoloader was loaded and successfully registered by spl_autoload_register(). For the reference, this is the autoloading function (only the Closure, of course):
function( $classname ) {
$classname = stream_resolve_include_path(
str_replace( '\\', DIRECTORY_SEPARATOR, $classname ) . '.php'
);
if( $classname !== FALSE ) {
include $classname;
}
};
However, resources located whithin index.php path, like the MVC classes, could not be found. So I did something else I also should not be doing and added the working directory to the include_path. And again, manually, without rely on realpath():
set_include_path(
'.' . PATH_SEPARATOR . 'path/to/common/next'
. PATH_SEPARATOR . 'path/to/htdocs/testfolder/'
);
And it worked again... Almost! >.<
The most of Applications I can create with this system works quite well with my Standard Router, based on SQLITE databases. And to make things even easier this Router looks for a predefined SQLITE file within the working directory.
Of course, I also provide a way to change this default entry just in case and because of this I check if this file exist and trigger an error if it doesn't.
And this is the specific error I'm seeing. The checking routine is like this:
if( ! file_exists( $this -> options -> dbPath ) ) {
throw RouterException::connectionFailure(
'Routes Database File %s doesn\'t exist in Data Directory',
array( $this -> options -> dbPath )
);
}
The dbPath entry, if not changed, uses a constant value Data/Routes.sqlite, relatively to working directory.
If, again, again, I set the absolute path manually, everything (really) works, the the Request flow reached the Action Controllers successfully.
What's going on?
This a bug in PHP's built-in web server that is still not fixed, as of PHP version 5.6.30.
In short, the web server does not redirect to www.foo.com/bar/ if www.foo./bar was requested and happens to be a directory. The client being server www.foo.com/bar, assumes it is a file (because of the missing slash at the end), so all subsequent relative links will be fetched relative to www.foo.com/instead of www.foo.com/bar/.
A bug ticket was opened back in 2013 but was mistakenly set to a status of "Not a Bug".
I'm experiencing a similar issue in 2017, so I left a comment on the bug ticket.
Edit : Just noticed that #jens-a-koch opened the ticket I linked to. I was not awar of his comment on the original question.

How to pass environment variable to an AutoLoaded mod_perl handler, to be used at module load time?

I have a HTTP Request Handler for mod_perl which needs to read an environment variable, from %ENV, at module load time. The environment variable is passed from the Apache config into mod_perl using the PerlSetEnv directive.
This worked fine, until we changed the Apache configuration to AutoLoad the handler at startup time, for performance reasons. When the module is AutoLoaded like this, thePerlSetEnv does not take effect at module load time, and the variable we need is only available from %ENV at request time inside the handler method.
Is there a way to continue using AutoLoad, but still set an environment variable in the Apache config which is available in Perl's %ENV at module load time?
Minimal example:
Here's a stripped down test-case to illustrate the problem.
The Apache config without autoload enabled:
PerlSwitches -I/home/day/modperl
<Location /perl>
SetHandler modperl
PerlSetEnv TEST_PERLSETENV 'Does it work?'
PerlResponseHandler ModPerl::Test
Allow from all
</Location>
Contents of /home/day/modperl/ModPerl/Test.pm:
package ModPerl::Test;
use strict;
use warnings;
use Apache2::RequestRec ();
use Apache2::RequestIO ();
use Apache2::Const qw(OK);
my %ENV_AT_MODULE_LOAD = %ENV; # Take a copy
sub handler {
my $r = shift;
$r->content_type('text/plain');
$r->print("ENV:\n");
foreach my $key (sort keys %ENV) {
$r->print(" $key: $ENV{$key}\n");
}
$r->print("ENV_AT_MODULE_LOAD:\n");
foreach my $key (sort keys %ENV_AT_MODULE_LOAD) {
$r->print(" $key: $ENV_AT_MODULE_LOAD{$key}\n");
}
return OK;
}
1;
When localhost/perl is viewed in the browser, I see this:
ENV:
MOD_PERL: mod_perl/2.0.5
MOD_PERL_API_VERSION: 2
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
TEST_PERLSETENV: Does it work?
ENV_AT_MODULE_LOAD:
MOD_PERL: mod_perl/2.0.5
MOD_PERL_API_VERSION: 2
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
TEST_PERLSETENV: Does it work?
Hooray! TEST_PERLSETENV is available at module load time, as we want.
But when we change the Apache config to enable Autoload (by using + in the PerlResponseHandler like so):
PerlResponseHandler +ModPerl::Test
I get the following output instead:
ENV:
MOD_PERL: mod_perl/2.0.5
MOD_PERL_API_VERSION: 2
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
TEST_PERLSETENV: Does it work?
ENV_AT_MODULE_LOAD:
MOD_PERL: mod_perl/2.0.5
MOD_PERL_API_VERSION: 2
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Boo! TEST_PERLSETENV is no longer available at module load time :( How can I get it back while keeping the AutoLoad behaviour?
Argh, 30 seconds after posting this question, I found the answer. Thank you rubber duck.
Move the PerlSetEnv to before the <Location> block which contains the PerlResponseHandler directive, and it works again!
i.e. like this:
PerlSwitches -I/home/dbarr/modperl
PerlSetEnv TEST_PERLSETENV 'Does it work?'
<Location /perl>
SetHandler modperl
PerlResponseHandler +ModPerl::Test
Allow from all
</Location>

Is there a simple way to setup a local, self-contained web server for perl testing?

I'm building a perl application to archive simple web pages (i.e. static pages with no query strings involved). I'd like to write tests to verify the functionality of the module that will be accessing the remote files. To make the tests self-reliant, I'm looking for a simple, self-contained web server that the test scripts can use locally.
Below is an example which outlines what I'm trying to do. I've cut it down to a minimum with the following directory structure:
./MirrorPage.pm
./t/001_get_url.t
./t/test-docroot/test-1.json
Contents of "./MirrorPage.pm":
package MirrorPage;
use Moose;
use LWP::Simple;
use namespace::autoclean;
sub get_url {
my ($self, $url_to_get) = #_;
### grab the contents of the url
my $url_data = get($url_to_get);
### return the contents.
return $url_data;
}
__PACKAGE__->meta->make_immutable;
1;
Contents of "./t/001_get_url.t":
#!/usr/bin/perl
use Modern::Perl;
use Test::More;
use MirrorPage;
### Start test www server on port 8123 here ###
my $t = new_ok('MirrorPage', undef, 'Create MirrorPage');
is(
$t->get_url("http://localhost:8123/test-1.json"),
'{ testkey: "testvalue" }',
"Verify the data."
);
### Kill test www server here ###
done_testing();
Contents of "./t/test-docroot/test-1.json":
{ testkey: "testvalue" }
The goal is to start and kill a self-contained web server at the corresponding comment locations in "./t/001_get_url.t". The web server needs to serve the contents of the "./t/test-docroot" directory as its document root.
Given all that: What is the best/simplest way to setup a self-contained web server to provide static files for testing in perl?
I would mock the HTTP call near the top of your .t file (if you're only wanting to test MirrorPage.pm):
my $mock = new Test::MockObject();
$mock->fake_module( 'LWP::Simple', get => sub { return '{ testkey: "testvalue" }' } );
LWP can fetch files, so you can rewrite $url_to_get from http://... to file://....
Perhaps:
At the top, fork and do a simple static file server using HTTP::Server::Simple::Static, then at the bottom terminate the child process.
I can highly recommend Mojolicious as a great way to produce a test server for a client. Since Mojolicious simply maps a URL into a subroutine call, it's very easy to have very fine control over what the server does, and therefore you can easily test things like "does my client fail properly if the server returns a bad response/bad content/times out". And since it is very simple to set up and tear down a server, a little cleverness with fork() makes it possible to have the test and the server setup live in the same test file.
Here's what I've come up with using Net::HTTPServer. Based on the idea that "It’s OK to Ask and Answer Your Own Questions", I'm posting it here for comment/consideration. What I've done is the following:
First, create a new module at: "./t/TestServer.pm". The contents of this file are:
package TestServer;
use Moose;
use Net::HTTPServer;
use namespace::autoclean;
has 'server' => (
is => "rw",
isa => "Net::HTTPServer",
default => sub {
Net::HTTPServer->new (
port => 8123,
docroot => "t/test-docroot"
)
},
);
sub BUILD {
my $self = shift;
### Spin up the server.
$self->server->Start();
$self->server->Process();
}
### Close up the Moose package.
__PACKAGE__->meta->make_immutable;
1;
Then, update the test "./t/001_get_url.t" file to use it via a fork:
#!/usr/bin/perl
use Modern::Perl;
use Test::More;
use MirrorPage;
### Fork for the server
my $pid = fork();
### Parent process. Holds the tests.
if($pid) {
### Make sure the server has a moment to startup
sleep(2);
my $t = new_ok('MirrorPage', undef, 'Create MirrorPage');
is(
$t->get_url("http://localhost:8123/test-1.json"),
'{ testkey: "testvalue" }',
"Verify the data."
);
}
### Child process. Holds the server.
elsif(defined($pid)) {
use lib "t/";
use TestServer;
my $svr = TestServer->new();
exit; # Should never get here.
}
### Error out if necessary.
else {
die "Can not fork child process.";
}
### Kill the server fork.
kill 1, $pid;
done_testing();
This is working well for me.
Although it's OS specific, I'm sure Apache is your answer.