Possible bug or config problem

In my previous post, I ran through a short tutorial on using nginx as a front-end web proxy for your Catalyst application running on a Starman / PSGI webserver. I just uncovered a problem, and I can’t tell if it’s a configuration problem on my end, or an actual bug…

If I have a GET request, like so : ‘/widgets//view’ or a POST request ‘widgets//update’ for a new `widget`, my double forward slash is being omitted and instead, Catalyst is instead handling my request as ‘/widgets/view’ or ‘widgets/update’ and the response is a 404 page not found error. (Note, for an already existing widget, the double forward slash would be replaced with a primary key identifier of the current `Widget` : /widgets/1542151/view’, etc).

Running Apache + FastCGI, the default catalyst HTTP server, or Starman (via localhost:5000 requests) do not yield this error. Which leads me to believe this problem is stemming from my nginx web proxy. Probably some sort of configuration problem on my part.

To test, I will start an instance of the catalyst web server in place of the starman server (should be an easy change, start the catalyst server and change the port number in my web proxy to 3000 instead of 5000)…

UPDATE :

HERE

Basically, change the proxy_pass from http://localhost:5000/ to http://localhost:5000

HOWTO : nginx, Starman / Plack and Catalyst

In this short tutorial, I will demonstrate how to use nginx as a frontend web proxy and Starman as a backend web server running a Catalyst application.

It assumes you already know about PSGI, Catalyst, nginx and how to install all the required libraries and perl modules.

Requirements :

First, we’ll configure nginx to run as our lightweight http server.  (If you require SSL, please make sure your ./configure command includes the –with-http_ssl_module option and that you have the openssl and pcre libraries already installed)

I’m going to require a ssl connection with a self signed certificate (you can follow the instructions here on how to set that up). I will also be redirecting all http requests to https via the web server (which is the best way to handle this).

In your nginx directory (mine was installed at /usr/local/nginx) edit your conf/nginx.conf file :

... (params)
http {
... (params, default server, etc)
  server {
    listen 80;
    server_name   myapp.example.com;
    location / {
      rewrite         ^/(.*)$ https://myapp.example.com/$1 permanent;
    }
  }

  server {
    listen        443 ssl;
    server_name   myapp.example.com;
    gzip on;
    ssl_certificate /usr/local/nginx/conf/server.crt;
    ssl_certificate_key /usr/local/nginx/conf/server.key;
    #you may need or want to set additional ssl parameters (cipher, etc)

    location / {
      proxy_set_header Host $http_host;
      proxy_set_header X-Forwarded-Host $http_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Port 443; #this is important for Catalyst Apps!
      proxy_pass http://localhost:5000; #changed from http://localhost:5000/ which was causing double forward slash problems in the url
    }

  location /static {
    root /data/users/MyApp/root;
    expires 30d;
  }
}

This assumes that your static content will be servered by nginx and not your Catalyst App, and that when you start your starman server, you’ll be using port 5000.

To get your Catalyst up and running using Starman, install the Plack and Starman via cpanminus as well as the Catalyst::Engine::PSGI module for PSGI integration. Once all of your modules are installed, you’ll need to create a app.psgi file.  You can do so manually, or via the script/app_create.pl command in your Catalyst App directory.

As I found out here from my own answer, when using a front-end web proxy, you’ll need to add a small bit of information to your .psgi file (Catalyst::Engine::PSGI docs), here’s an example of mine :

#!/usr/bin/env perl
use strict;
use warnings;

use Plack::Builder;
use MyApp;

MyApp->setup_engine('PSGI');
my $app = sub { MyApp->run(@_) };

builder {
 enable_if { $_[0]->{REMOTE_ADDR} eq '127.0.0.1' }
        "Plack::Middleware::ReverseProxy";
 $app;
};

If your instance of nginx is running on a different server, you may need to set this configuration variable in your Catalyst App (doesn’t hurt to add it regardless of your situation) :

MyApp->config(using_frontend_proxy => 1)

Start starman (currently defaults to 5 processes and port 5000) :
$ starman script/erp.psgi

Start nginx
$ sudo /usr/local/nginx/sbin/nginx

At this point, all of your http://myapp.example.com requests should be automatically redirected to https://myapp.examples.com. Nginx should be handling all of your static content used by your Catalyst app, and as long as Starman is running in the background on port 5000, your requests and responses should be handled accordingly and in a timely manner! ;-)

In a production production environment, you may want to tweak some of your settings in the nginx.conf file and then create a startup script for both nginx and your Starman server. I also haven’t touched on any of the logs associated with Starman / Plack and your Catalyst app, or how to handle the ip addresses (nginx if forwarding the $remote_addr as `X-Real-IP` in the header) for access / error logs…

Moose

If you’re a perl programmer, than you’ve most likely already heard of  Moose.  If not, there’s a plethora (okay, maybe just a myriad) of blog entries about the popular “postmodern object system” just waiting to be Googled.  Although I first heard of Moose in late 2007 and attended a session on it in early 2008 at the frozen perl conference, I must admit, I haven’t had the chance to write any applications utilizing this technology …. until now.

I’ve started to lay some of the groundwork I think I’ll need to create a replacement custom data import and reporting system for my place of employment.

Some of my most basic wants / needs will be :

  • I’ll want a Config class, which reads in a config file, perhaps a ‘MooseX::Singleton’ or stored via a ‘Registry Pattern’
  • I’ll want a Log4perl class, or better yet, a ‘Moose::Role’ Logger is probably the way to go.  By using a ‘Role’, we can use the ‘with’ keyword and give the classes that need a logger one very easily without writing any additional code.
  • I’ll need some IO classes / modules for data extraction and generic file IO stuff
  • I’ll need some RegExp modules for data validation
  • I’ll have to do quite a bit of database interaction… I might look at using an ORM, perhaps ‘DBIx::Class’, and I’ll want to keep it database independent given that we’re currently using MySQL as our RDBMS but if we want to switch in the future, writing the interface and code in a OO style should make a change as painless as possible

There’s much more that I’ll need when I get a chance to sit down and evaluate all of my requirements and I’ve decided that using Moose for any new perl projects is a no brainer (unless I’m writing a vanilla CGI app or don’t need OO).

My goal here is to report on / update you with my experience working with this technology and hopefully just add to an already rapidly growing community of perl developers using Moose.