2

I'm running a perl script via mod_perl with Apache. I am trying to parse parameters in a restful fashion (aka GET www.domain.com/rest.pl/Object/ID).

If I specify the ID like this: GET www.domain.com/rest.pl/Object/1234 I will get object 1234 back as a result as expected.

However, if I specify an incorrect hacked url like this GET www.domain.com/rest.pl/Object/123 I will also get back object 1234.

I am pretty sure that the issue in this question is happening so I packaged up my method and invoked it from my core script out of the package. Even after that I am still seeing the threads returning seemingly cached data.

One of the recommendations in the aforementioned article is to replace my with our. My impression from reading up on our vs my was that our is global and my is local. My assumption is the the local variable gets reinitialized each time. That said, like in my code example below I am resetting the variables each time with new values.

Apache Perl Configuration is set up like this:

PerlModule ModPerl::Registry

<Directory /var/www/html/perl/>
   SetHandler perl-script
   PerlHandler ModPerl::Registry
   Options +ExecCGI
</Directory>

Here is the perl script getting invoked directly:

#!/usr/bin/perl -w
use strict;
use warnings;
use Rest;

Rest::init();

Here is my package Rest. This file contains various functions for handling the rest requests.

#!/usr/bin/perl -w

package Rest;

use strict;
# Other useful packages declared here.

our @EXPORT = qw( init );

our $q = CGI->new;

sub GET($$) {
    my ($path, $code) = @_;
    return unless $q->request_method eq 'GET' or $q->request_method eq 'HEAD';
    return unless $q->path_info =~ $path;
    $code->();
    exit;
}

sub init()
{
eval {
        GET qr{^/ZonesByCustomer/(.+)$} => sub {
            Rest::Zone->ZonesByCustomer($1);
        }
    }
}

# packages must return 1
1;

As a side note, I don't completely understand how this eval statement works, or what string is getting parsed by the qr command. This script was largely taken from this example for setting up a rest based web service with perl. I suspect it is getting pulled from that magical $_ variable, but I'm not sure if it is, or what it is getting populated with (clearly the query string or url, but it only seems to be part of it?).

Here is my package Rest::Zone, which will contain the meat of the functional actions. I'm leaving out the specifics of how I'm manipulating the data because it is working and leaving this as an abstract stub. The main issue seems to be either with the parameter getting passed in to this function, or the output of the function.

#!/usr/bin/perl -w

package Rest::Zone;

sub ZonesByCustomer($)
{
    my ($className, $ObjectID) = @_;

    my $q = CGI->new;
    print $q->header(-status=>200, -type=>'text/xml',expires => 'now', -Cache_control => 'no-cache', -Pragma => 'no-cache');
    my $objectXML = "<xml><object>" . $ObjectID . "</object></xml>";
    print $objectXML;
}

# packages must return 1
1;

What is going on here? Why do I keep getting stale or cached values?

Community
  • 1
  • 1
Ben Liyanage
  • 4,993
  • 1
  • 21
  • 23
  • I'd like to know more, but if you change it to a POST request, does it still cache? – vol7ron Jun 15 '12 at 20:44
  • 1
    Please provide a [full code example](http://sscce.org) and your mod_perl configuration, so that we may actually [reproduce the problem](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html#showmehow). – You make at least one fundamental caching mistake (`Pragma` is a **request** directive), so it appears you just cargo-culted code from somewhere without understanding what it does and how it works. Check your assumptions with [REDbot](http://redbot.org/). – daxim Jun 15 '12 at 20:50
  • Where in your code are you taking the pathinfo and putting it into the body? Your code sample outputs `"1234"` statically. Did you mean `"" . $q->path_info . ""`? – Dondi Michael Stroma Jun 15 '12 at 21:52
  • @daxim, I just updated the code examples to be more complete. I do not believe the page is actually caching, I believe it is very similar to the [stack overflow question](http://stackoverflow.com/questions/3169146/perl-cgi-script-returns-different-results-depending-on-run) that I referenced. What am I looking for for the mod_perl configuration? Is this an apache conf file? Or does it get its own separate conf file? – Ben Liyanage Jun 15 '12 at 23:09
  • @DondiMichaelStroma I updated the code example to be more thorough in showing were the path info was coming from. – Ben Liyanage Jun 15 '12 at 23:10
  • Are you using ModPerl::Registry (Apache::Registry in v1)? If not, what are you using in your Apache config for set-handler/perlhandler/perlresponsehandler (yes, it is in the apache conf file). You might want to take a look at Router::Simple for routing your url's. The above codepath is convoluted at best. – Dondi Michael Stroma Jun 16 '12 at 02:58
  • That code cannot work. The variable `$q` is not declared in the sub `GET`. You would have noticed that yourself if you'd add `use strict; use warnings;` to the package Rest. – daxim Jun 16 '12 at 14:19
  • @DondiMichaelStroma I have updated the question with Apache configuartion information. I'll look into Router::Simple. Ultimately I am not looking at spending too much time on this (aka learning frameworks and what not), as at some point in the near future the vendor for the software I am integrating with will be providing their own REST based web service and I will be ditching this fast (~1 month for beta, probably 1 year before I'd use it/have bandwidth to use it). I do agree this is a bit convoluted--I am a bit out of my element with this Perl and trying to refactor it a bit now. – Ben Liyanage Jun 18 '12 at 15:07
  • Another question: how does GET get variable $q? Where is the $q used in sub GET declared and initialized? Dancer is very easy, the syntax is almost identical to what you have so far, the only thing you have to do is install it :) – Dondi Michael Stroma Jun 18 '12 at 20:38
  • @DondiMichaelStroma added $q declaration, and some other package formating as well. I just noticed that I am exporting `init` in the Rest package, but not any of the methods in the other package. I'm not sure if that's an issue or not (though probably not related to this at all). – Ben Liyanage Jun 19 '12 at 15:14
  • @DondiMichaelStroma Actually, that could be it--I am/was declaring the $q with `our` aka global, so presumably it was remembering past requests. I've updated my code to use `my` and will see if that resolves the issue. – Ben Liyanage Jun 19 '12 at 15:20
  • That looks to be the problem but changing it from our to my is not going to work. It's in the main body of a package, and a package is only required once, so it's only going to get executed one time. – Dondi Michael Stroma Jun 19 '12 at 15:44

2 Answers2

3

Try this one:

my $q = CGI->new; 
print $q->header(-status => 200,
                 -type => 'text/xml',
                 -Cache_control => 'no-cache, no-store, must-revalidate'); 

my $ID = $q->path_info();
$ID =~ s/^.*\/(.*)$/$1/;

my $objectXML = "<xml><object>$ID</object></xml>"; 

print $objectXML; 
Ωmega
  • 42,614
  • 34
  • 134
  • 203
  • 1
    Agree, that should work. I think Ben should throw his code away and use a framework such as (from simple to complex) Dancer, Mojolicious, or Catalyst, or use Router::Simple. – Dondi Michael Stroma Jun 16 '12 at 03:34
3

Don't use a global CGI object.

package Rest;

...

#our $q = CGI->new; 
# Remove that, it is only going to be executed once when the pacakge is loaded (use'd)

sub GET($$) {
    my ($path, $code) = @_;
    my $q = CGI->new; # Add this line (you can call CGI->new multiple times)
    return unless $q->request_method eq 'GET' or $q->request_method eq 'HEAD';
    return unless $q->path_info =~ $path;
    $code->();
    exit;
}

You COULD leave it as an our global and refresh it with each request. Since init() is called for each request, put it there:

package Rest;
our $q;
...
sub GET ....
...
sub init
{
  $q = CGI->new;
  eval ...
  ...
}
Dondi Michael Stroma
  • 4,668
  • 18
  • 21
  • I haven't forgotten about this, I am just observing over the next couple days to make sure the strange behavior seems to have stopped. – Ben Liyanage Jun 20 '12 at 22:04