How do you print an environment variable in Perl/Plack - perl

I'm trying to switch out 'Ben' with an environment variable using Perl/Plack. If the environment variable is set (e.g. 'Dave'), then it will print out "Powered by Dave". But it if is not set, it will default to 'Ben'.
Currently, the following program works when using plackup -r
#!/usr/bin/env plackup
my $app = sub {
my $env = shift;
return [
200,
['Content-Type', 'text/plain'],
['Powered by Ben'],
];
};
Currently, my cpanfile contains the following
requires 'Plack' => '1.0028';
requires 'CGI::Emulate::PSGI' => '0.15';
requires 'CGI::Compile' => '0.16';

By Environment Varaible do you mean, the operating system's environment or the plack request enviroment and thus an HTTP request parameter? (they're completely different).
If the former, you can access it via the %ENV hash.
If the latter, you should read the documentation for Plack::Request.
(note: I've never used plack; I'm only going by the docs)

Related

Insecure dependency with Inline::Python

What could explain this compile-time error message when running Inline::Python in -T mode?
Insecure dependency in open while running with -T switch at /usr/local/lib/perl/5.14.2/Inline/Python.pm line 193.
Line 193 is where Inline::Python opens $o->{API}{location}, which I take to be the "Inline DIRECTORY".
I have, of course, used the required options:
use constant _INLINE_DIR_ => '/var/myapp/inline';
use Inline Config => UNTAINT => 1,
NO_UNTAINT_WARN => 1,
DIRECTORY => _INLINE_DIR_;
I have made sure that /var/myapp/inline and everything inside it is writable by everyone, obviously including both root and the user that the application is setuid'ed to at run-time.
The very same script works without problem on my computer, whether I start it as root or not, running Inline 0.50 Inline::Python 0.43, but gives me this error when I try running it on a server that uses the same version of Inline::Python and either version 0.49 or 0.55 of Inline.
Since this is different in different environments, my bet is that somehow there's an environment variable that either Inline or Inline::Python is reading before it does the step requested by the UNTAINT config parameter.
(Contrary to the comment, I don't think that file permissions could cause this message, only insecure dependencies on command-line parameters or environment variables)
Given that, I'd start your script by forcibly clearing the environment and then adding in only those environmental variables you know you need:
%ENV = ();
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin'; # Or whatever's appropriate
$ENV{'PYTHONPATH'} = '/usr/local/lib/python'; # Optional, if appropriate
# ... etc ...

Change Environment Variables During Perl Unit Testing

I am trying to write some unit tests for this perl module function, but am having some issues with environment variables. I'll list the files first, then explain the issue in greater detail.
processBuildSubs.pm
package processBuildSubs;
use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Request::Common;
use HTTP::Status;
# Declare environment variables used in this package that are needed
use constant URI_BASE => $ENV {"URI_BASE"};
use constant URI_RESOURCE => $ENV {"URI_RESOURCE"};
# Shell Environment Related Constants visible.
# Make visible.
our $URI_BASE = URI_BASE;
our $URI_RESOURCE = URI_RESOURCE;
sub populatePartitions
{
# Define locals
my $url;
my $ua = new LWP::UserAgent;
$url = "$URI_BASE"."$URI_RESOURCE"."/some/path";
# Make a request to the $url
$res = $ua->request (GET $url);
if ($res->code() != HTTP::Status->RC_OK() )
{
# The request didn't return 200 OK so it's in here now.
}
else
{
# The request returned 200 OK, so now it's here.
}
}
I want to be able to unit test both the if path and the else path, however, it would be best for me if I don't need to change the processBuildSubs.pm code at all. It's an external file that I don't currently have control over. I am just tasked in unit testing it (although I do understand it could be tested more efficiently if we could also change the source code).
So in order to test both paths, we need the environment variables URI_BASE and URI_RESOURCE to be set accordingly, so that the request fails once, and succeeds another time. (I am interested in learning how to stub out this call at a future time, but that's reserved for another question.)
Here's my test file:
processBuildSubs.t
use strict;
use Test::More qw(no_plan);
BEGIN { use_ok('processBuildSubs') };
# Test 1 of populatePartitions() function
my $processBuildProdsCall = processBuildSubs::populatePartitions();
is( $populatePartitionsCall, 0, "populatePartitions() Test for 0 Val Passed" );
# Test 2 of populatePartitions() function
# I need to change some environment variables that processBuildSubs depends on here.
my $processBuildProdsCall = processBuildSubs::populatePartitions();
is( $populatePartitionsCall, 0, "populatePartitions() Test for 0 Val Passed" );
The best attempt we have right now at changing the environment variables is using an external shell script like so (But it would be ideal to change them between the my calls in the file above):
run_tests.sh
#!/bin/bash
# Run the tests once
perl ./BuildProcess.pl
perl ./Build testcover # Ultimately calls the processBuildSubs.t test file
# Now export some variables so the other test passes.
export URI_BASE="https://some-alias/"
export URI_RESOURCE="some-resource"
# Then run the test suite again with the env set so the else condition passes.
perl ./BuildProcess.pl
perl ./Build testcover
As you can see, this would be a bad way of doing things, as we run the entire test suite with different environments each time. Ideally we'd like to setup our environment in the processBuildSubs.t file if possible, between tests.
Please let me know if I can provide any further information.
Are you averse to having separate scripts for separate test environments?
# processBuildSubs.t
BEGIN {
#ENV{"URI_BASE","URI_RESOURCE"} = ("https://some-alias/","some-resource");
}
use Test::More;
... tests go here ...
# processBuildSubs-env2.t
BEGIN {
#ENV{"URI_BASE","URI_RESOURCE"} = ("https://another-alias/","another-resource");
}
use Test::More;
... tests go here ...
By setting %ENV in a BEGIN block, before any other modules are loaded, you make the different environment variables available to your other modules at compile-time.

Perl Rover v3 pass environment variable to in the Rulesets

I am using perl Rover module version 3 to login to the Linux/Unix server and run the script. In the ruleset if I add the full path name it copies the script to the remote server, not able to substitute the environment variable.
eg.
This works:
copy:{
put_file "/home/u1/find.sh" "/tmp/"
};
This didn't work:
copy:{
put_file "$HOME/find.sh" "/tmp/"
};
used $ENV{'HOME'}, this also didn't work.
How can I pass the environment variable?
Rover module document.
http://rover.sourceforge.net/QuickStart/Rover-QuickStart-3.html#ss3.2
http://rover.sourceforge.net/
After reviewing the source code for rover, which I never used, I determined it was not possible from the existing code.
I created a new extension for you, that has that functionality, it supports the ~ and ${HOME} syntax, (which are bash extensions and not part of the OS directly, that is why perl does not support them).
code is here:
https://github.com/h4ck3rm1k3/perl-rover/commit/2c78aefb97e819956bb665b04056763f8df1b242
I have had a hard time testing it because I never used rover before, and rover does not seem to support scp.(I read it is supported,but could not test it yet.) Anyway, let me know if you like it. I will put more work into it if reasonably requested.
Update
Here is my example ruleset :
example ruleset
[rulesets]
test:
{
put_file_from_home put_file "~/find2.sh" "/tmp/"
put_file_from_home put_file "${HOME}/find3.sh" "/tmp/"
}, ;
example output
Here is the example output, I cannot get rover to work. See the test case below.
Test output
perl -I lib t/example2.t
Local was ~/find2.sh and home was /home/mdupont at lib/Rover/CoreExtension.pm line 19.
Local now /home/mdupont/find2.sh at lib/Rover/CoreExtension.pm line 22.
Local was ${HOME}/find3.sh and home was /home/mdupont at lib/Rover/CoreExtension.pm line 19.
Local now /home/mdupont/find3.sh at lib//Rover/CoreExtension.pm line 22.
new config option for the new sshport option
[hosts]
someexample:{
os linux
username myusername
description 'myhost'
sshport 12345
ftp_method_used sftp
};
update2
Dont use quotes around the name, use a comma between the args,
To git#github.com:h4ck3rm1k3/perl-rover.git
2207417..7637741 CoreExtension -> CoreExtension
[rulesets]
test: { put_file_from_home ~/find2.sh,/tmp/ }, ;
[hosts]
localhost:{
os linux
username mdupont
description 'localhost'
ftp_methods sftp
ftp_method_used sftp };
mike
Old question but new answer, since your using Rover v3 you can just extend the Rover::Core modules by overloading it.
Add this to your ~/.rover/contrib directory:
CoreVariables.pm:
package CoreVariables;
use strict;
use Exporter;
our #ISA = qw( Exporter );
our #EXPORT = qw( put_file );
sub put_file {
my ($self, $host, $command) = #_;
$command =~ s/(\$[\w{}]+)/$1/eeg;
return Rover::Core::put_file($self, $host, $command);
}
And add the following to your ~/.rover/config [modules] section (must be after Rover::Core):
CoreVariables:{
};
And then you can store environment variables in your rover config when using put_file. Add other routines if you wish, this only extends put_file.
And since this is such an easy task I will add it to the requested feature list and include it in the next release (I am the Rover author).
The better place to ask Rover questions is on the sourceforge website of course: http://sourceforge.net/projects/rover/

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.

How to access a simple SOAP Service in Perl

I am currently dabbling around with perl and SOAP, using SOAP::Lite.
I have a simple SOAP server that appears to run fine:
#!perl -w
use SOAP::Transport::HTTP;
use Demo;
# don't want to die on 'Broken pipe' or Ctrl-C
$SIG{PIPE} = $SIG{INT} = 'IGNORE';
my $daemon = SOAP::Transport::HTTP::Daemon
-> new (LocalPort => 801)
-> dispatch_to('/home/soaplite/modules')
;
print "Contact to SOAP server at ", $daemon->url, "\n";
$daemon->handle;
It includes a small class called Demo, which simply retrieves the systems total memory:
Demo.py
#!/usr/bin/perl
use Sys::MemInfo qw(totalmem freemem totalswap);
print "total memory: ".(&totalmem / 1024)."\n";
I have an example of a SOAP client below written in PERL, although I am unsure how to communicate with the server (since the tutorial I am following here goes of on a tangent e.g. retrieve the result of the Demo.py class from the client:
#!perl -w
use SOAP::Lite;
# Frontier http://www.userland.com/
$s = SOAP::Lite
-> uri('/examples')
-> on_action(sub { sprintf '"%s"', shift })
-> proxy('http://superhonker.userland.com/')
;
print $s->getStateName(SOAP::Data->name(statenum => 25))->result;
Any help would be greatly appreciated :)
For the server script, the dispatch_to method takes the path to the package to load, and the name of the package itself. If you pass a third parameter, it will limit the names of the methods made visible by the server. (e.g. 2 methods named memory and time, passing Demo::time as the 3rd param will make memory invisible to the client service.)
File server.pl
my $daemon = SOAP::Transport::HTTP::Daemon
-> new (LocalPort => 801)
-> dispatch_to('/home/soaplite/modules', 'Demo')
;
Your Demo package should be a package with methods that return the values. I couldn't get Sys::MemInfo compiled on my system, so I just used localtime instead. I'm not sure why you named your package Demo.py, but Perl packages must have the extension pm, otherwise they won't be properly loaded.
File Demo.pm
#!/usr/bin/perl
package Demo;
#use Sys::MemInfo qw(totalmem freemem totalswap);
sub memory {
#print "total memory: ".(&totalmem / 1024)."\n";
return "Can't load Sys::MemInfo, sorry";
}
sub time {
my $time = localtime;
return $time;
}
1;
For the client code, there's 2 important pieces that must be properly specified to work, the proxy and the uri. The proxy is the url path to the soap web service. Since you are running the server script as a daemon process, your path is just the web site's url. My computer doesn't have a url, so I used http://localhost:801/. The 801 is the port you specified above. If you were running as a cgi script inside of a different web server (such as Apache), then you would need to specify the cgi script to call (e.g. http://localhost/cgi-bin/server.pl, changing the package in server.pl to SOAP::Transport::HTTP::CGI.
uri is probably the most confusing, but it's the namespace of the xml files returned by the web service. Turn on +trace => 'debug' to see the xml file returned by the web service. The uri should just be the name of the server. Even if you switch ports or to a cgi dispatch method, this uri stays the same.
File test.pl
#!perl -w
use SOAP::Lite +trace => 'debug';
# Frontier http://www.userland.com/
$s = SOAP::Lite->new(proxy => 'http://superhonker.userland.com:801/',
uri => 'http://superhonker.userland.com/');
#might be http://www.userland.com/
#but I could not test sub-domains
print $s->time()->result;
I'll recycle these two answers for tips:
Client of web service in Perl
Remote function call using SOAP::Lite