MISC – Jun 5th, 2014

Today I wrote a script to check urls availability using Perl.

(LWP::Simple::head($_) ? “OK” : “!!!***BAD***!!!”);

The $_ will be an url like ‘http://abc.com.cn’ and it will append ‘OK’ if it is available otherwise ‘BAD’.

However for some urls, LWP::Simple::head will be hanging. With the help of http://perldoc.perl.org/functions/alarm.html, by changing the code as below, it will avoid hanging after hanging for 2 seconds.

alarm( 2 );
eval
{
$str = "$_ " . (LWP::Simple::head($_) ? "OK" : "!!!***BAD***!!!");
};

### I found that LWP::Simple::head return false if the link it accesses redirects to another link.
The following command will return OK for this scenario. Note, in Perl, 0 means false and all other non-zero mean true.

$str = "$_ " . ((system("wget -q --spider -t 2 $_")) ? "!!!***BAD***!!!" : "OK");

Quote in DB String – Perl DBI

Today I fixed a bug in one of our legacy perl tool.

The tool worked well until I got the complaint from user this morning that it is not able to update data. After debugging I found it is due to quote string in the text.

E.g

$sth = $dbh->prepare(“UPDATE ABC_REQUEST set COMMENTS=$paramComm‘ where REQUEST_SID=$paramID”);

if the $paramComm contains ‘ (quote) string then it fails. For example, below update statement will fail.

UPDATE ABC_REQUEST set COMMENTS=’This is required for bug xxx in ‘I am a quote’ here ‘ where REQUEST_SID=xxx

Solution:

Change

From-

$sth = $dbh->prepare("UPDATE ABC_REQUEST set COMMENTS='$paramComm' where REQUEST_SID=$paramID");

To-


$paramComm = $dbh->quote($paramComm);
$sth = $dbh->prepare("UPDATE ABC_REQUEST set COMMENTS=$paramComm where REQUEST_SID=$paramID");

Note: by using quote(), you don’t need to quote your comments with ” any more.

More details, http://search.cpan.org/~timb/DBI-1.631/DBI.pm#quote

Camel – A pure Perl web server

To refresh my Perl skills, I wrote a web server in Perl. I called it as ‘Camel Server’. Camel Server has following features:

  • Support HTTP GET/POST/HEAD methods.
  • Support multi-processes.
  • Support any static text pages.
  • Support PERL files execution.
  • Support default pages.
  • Support Form & CGI parameters.
  • Support XML configuration.
  • Support Authentication & Session management page per page.
  • Certified by Perl Testing framework (of course it is not bug-free!)

You can refer to its README file for more details. https://github.com/luohuahuang/Camel/blob/master/README.md

In this page, I will introduce Camel’s filesystem and recipes.

Filesystem:

bin – it contains script to start Camel Server.

conf – it contains .xml configuration files to configure authentication and server setting.

lib – it is the core component of Camel.

camelauth.pm – for authentication

camelcgi.pm – for CGI & parameters pass

camelhttp.pm – main module. Server will be started here and listen/receive requests

camellogger.pm – utility to log logs

camelutils.pm – utility subroutines

camelxml.pm – XML engine. It takes care of XML interactive.

t – it contains testing cases

temp – it is a temporary working directory. Camel requires temp directory to handle .pl execution.

webapps – the root directory of apps.

firstapp – it is a demo apps.

manager – it is the administrator apps shipped with Camel Server. You can not delete it. Currently it has only one page named as login_portal.pl. It is the core .pl page to implement Authentication. In future releasing, I will add more utility pages under manager directory.

logs – it contains the logs file logged by camellogger.pm

Recipes:

The followings are my posts about Camel Server. You might find some useful recipes.

Auth mechanism in Camel

HTTP is a stateless protocol which means each time a client retrieves a Web page, the client opens a separate connection to the web server.

Still there are following three ways to maintain session between weh client and web server. I will have a brief introduction to these ways to maintain session. And then I will introduce the implementation in my light weight pure Web server – Camel.

Cookies:

A web server can assign a set of key/value to each web client and the server can use these values to track session. For example, in Perl,

set cookie with Perl CGI module:

print "Set-Cookie: user=luohua; domain=abc.def.com; " .
"path=/cgi-bin/report.pl; expires=Mon, 12-Aug-2013 12:00:00 GMT\n";
print "Set-Cookie: passwd=xxxxxxxxxxx; domain=abc.def.com; " .
"path=/cgi-bin/report.pl; expires=Mon, 12-Aug-2013 12:00:00 GMT\n\n";

Check cookie with Perl CGI module:

@cookie = split(/; /, $ENV{'HTTP_COOKIE'});
foreach(@cookie)
{
($cookiename,$cookievalue) = split(/=/,$_) ;

}

The good point is, cookie is very convenient to maintain sessions through the site. However the weak point is that, some of the browsers/clients don’t support cookies or allow cookies.

Hidden Form Fields:

A web server can send a hidden HTML form with its required value:


<p><input type="hidden" name="username" value="Luohua"></p>
 <p><input type="hidden" name="password" value="xxxxxxxxx"></p>

These two entries mean that, when the form is submitted, the specified names and values are automatically included in the GET or POST method.

For example,

POST /firstapp/test.pl HTTP/1.1
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: en-us,en;q=0.5
Host: localhost:8080
Referer: http://localhost:8080/firstapp/login.html
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:7.0.1) Gecko/20100101 Firefox/7.0.1
Content-Length: 37
Content-Type: application/x-www-form-urlencoded

username=luohua&password=xxxxxx&Submit=submit

The good point is, you can use Hidden Form anywhere. However the weak point is that, you have to do Form Authentication page by page.

URL Rewriting:

Also you can append your session data on the end of each URL. For example,

http://luohuahuang.com/intro.html;sessionid=123456789

Session Object:

In JEE, JSP & Servelet use session object to maintain session status. For example, in Tomcat, Catalina use Session Manager to maintain session objects. Servelet instance can getSession() from HttpServletRequest interface. In the front tier, developer can just easily use  tags like <%@ page session=”1234567 %> in their jsp pages to get/set values.

Now come back to Camel’s session mechanism.

In my design, basically I use the way of Hidden Form Fields.

1. In the auth.xml, it will define the role/account and pages privileges.


<?xml version='1.0' encoding='utf-8'?>
 <!-- Camel Web Server -->
 <!-- The contents of this file will be loaded for each web application -->
 <Auth>
 <role rolename="administrator"/>
 <role rolename="update"/>
 <role rolename="view"/>
 <user username="admin" password="admin123" roles="administrator,view,update"/>
 <user username="luhuang" password="luhuang123" roles="view, update"/>
 <user username="homer" password="simpsons" roles="view"/>
 <user username="update1" password="update123"/>
 <user username="view1" password="view123"/>
 <user username="view2" password="view123"/>

<pagecontrol>
 <page path="/firstapp/auth_admin.pl">
 <roles>administrator</roles>
 <users>luhuang</users>
 </page>
 <page path="/firstapp/auth_update.pl">
 <roles>update</roles>
 <users>luhuang</users>
 </page>
 <page path="/firstapp/auth_view.pl">
 <roles>update</roles>
 <users>view1,view2</users>
 </page>
 <page path="/firstapp/welcome.html">
 <roles>update</roles>
 <users>view1,view2</users>
 </page>
 </pagecontrol>
 </Auth>

When a request comes, it will check the page privilege.

If the page is a public page (not indicated in the auth.xml), then authentication pass.

If the page is under prvs control and the user has logged in, then authentication pass.

If the page is under prvs control and the user has not yet log in, then redirect to login portal.

If username or password is wrong, then authentication fail.

If user has no privs to access the page, then authentication fail.

The main logic:

sub authAgent{
	my ($username, $password, $page) = @_;
	if (camelxml::isPageAuth($page)){
		if($username){
			my $pwd = camelxml::getPasswordByUsername($username);
			if ($pwd){
				if ($pwd eq $password) {
					if (camelxml::getAuthByUsernameAndPage($username, $page)){
						return AUTH;
					} else {
						return NOPRVS;
					}
				} else {
					return WRGACNT;
				}
			} else {
				return WRGACNT;
			}

		} else {
			return UNAUTH;
		}
	} else {
		return PUB;
	}
}

In the login portal, it will inherit the original url and redirect back if the authentication passes.

use strict;
use camelcgi;

my $cgiinst = camelcgi->new(PARAMS => $ARGV[0]);

print <<END_HTML;
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Camel Server - Login Form</title>
<style type="text/css">
img {border: none;}
</style>
</head>
<body>
<p>*** Camel Server - Applications Login Portal ***</p>
<p>You are going to $cgiinst->{originaluri}. $cgiinst->{message}</p>
<form action="$cgiinst->{originaluri}" method="post">
<p>User name: &nbsp;&nbsp;
<input type="text" name="username" value="apps"/></p>
<p>Password: &nbsp;&nbsp;&nbsp;&nbsp;
<input type="password" name="password" /></p>
<p><input type="submit" name="Submit" value="Submit" /></p>
</form>
</body>
</html>
END_HTML

In the form, it will use Camel’s CGI to pass its original url automatically. More details about Camel CGI: Tricky way to instantiate a class in Perl

At last, it will pass the values in the way of Hidden form:


<form action="/firstapp/auth_update.pl" method="post">
 <p>Result: $test </p>
 <p><input type="text" name="test" value=""></p>
 <p><input type="hidden" name="username" value="$cgiinst->{username}"></p>
 <p><input type="hidden" name="password" value="$cgiinst->{password}"></p>
 <p><input type="submit" name="Submit" value="Submit" /></p>
 </form>

In this way, Camel can track client’s session information.

Let me conclude Camel’s session process here:

1. Define the account/roles and pages prvs in auth.xml. Camel session mechanism will help you track prvs.

2. If you want to pass username/password to other pages, then just inject below two lines in your form:


<p><input type="hidden" name="username" value="$cgiinst->{username}"></p>
 <p><input type="hidden" name="password" value="$cgiinst->{password}"></p>

# So light weight! 🙂

Perl Testing

Testing Driven Development is a very good development practice, so is it in Perl!

Perl test framework has a similar convention as other test frameworks in other languages, like JUnit for Java.

Perl has a simple way to note which tests pass (OK) and which fail (not OK). E.g,

Let’s see an example,

ok 1 # it means your first test pass

not ok 3 – Hmmm, it fail #it means your third test fail

It provides a set of asserts. E.g,

ok(getname(), ‘luohua’, ‘yes, my name is Luohua’);

is(getname(), ‘luohua’, ‘yes, my name is Luohua’);

like(getname(), qr/luo.*/’, ‘yes, my name is Luohua’);

Well, let me introduce its test moduel, Test::More http://search.cpan.org/~mschwern/Test-Simple-0.98/lib/Test/More.pm

For example, I want to test my camellogger.pm which has getLogLevel(), createLogFile(), getFilename(), and logger() method. I will write the test file as below,

t/camellogger.t

use strict;
use Test::More ;
use camellogger;

#getLogLevel() should return 2,

ok (camellogger::getLogLevel() eq 2, ‘camellogger can retrieve log level properly’);

#createLogFile() should return the new created file name. E.g, camel.20130808155158.log
like (camellogger::createLogFile(), qr/camel\.\d{14}\.log/, ‘camellogger can create a log file properly’);

#getLogFilename() should return the new created file name. E.g, camel.20130808155158.log
like (camellogger::getLogFilename(), qr/camel\.\d{14}\.log/, ‘camellogger can create a log file properly’);
# logger should return 0 otherwise should fail.
is(camellogger::logger(0, ‘$0 – I am Camel’), 0, ‘camellogger can log an event properly’);
done_testing();

Output:

ok 1 – camellogger can retrieve log level properly
ok 2 – camellogger can create a log file properly
ok 3 – camellogger can create a log file properly
ok 4 – camellogger can log an event properly
1..4

Here I will change qr/camel\.\d{14}\.log/ to qr/Alibaba\.\d{14}\.log/. See the result again,

Output 2

ok 1 – camellogger can retrieve log level properly
not ok 2 – camellogger can create a log file properly
# Failed test ‘camellogger can create a log file properly’
# at /home/luhuang/workspace/Camel/t/camellogger.t line 6.
# ‘../logs/camel.20130808223917.log’
# doesn’t match ‘(?^:Alibaba\.\d{14}\.log)’
not ok 3 – camellogger can create a log file properly
# Failed test ‘camellogger can create a log file properly’
# at /home/luhuang/workspace/Camel/t/camellogger.t line 7.
# ‘../logs/camel.20130808223917.log’
# doesn’t match ‘(?^:Alibaba\.\d{14}\.log)’
ok 4 – camellogger can log an event properly
1..4
# Looks like you failed 2 tests of 4.

So it is cool and easy to use, isn’t it? Embrace it NOW, and it will make your Perl life more beautiful!

Parsing XML in Perl

Today I started to design the config engine to remove the hardcode from my Camel Web Server (You can refer to my previous posts for more details about this light weighted pure Perl Web container). Basically my target is that all of the configuration files should come from the config engine which work upon context.xml file.

In a short, Perl provides three ways to interactive with XML files.

Method 1,

Parsing XML into Dta Structures. In this way, Perl process XML data with a combination of hashes and arrays. Say, you can use $data->(author}{book}{ISBN} to access the content of ISBN.

In method 1, you will need to use XML::Simple;

Method 2,

Parsing XML into a DOM Tree.

DOM is abbr. of Document Object Model. Each element is a node in the tree, with which you can do operations like find its children nodes. E.g, $dom->getElementsByTagName(“book”);

In method2, you will need to use XML::LibXML;

Method 3,

Parsing XML into SAX Events.

Simple API for XML (SAX) is faster and uses less memory than parsers that build a DOM tree.

In method 3, you will need to use XML::SAX::ParserFactory.

In my case, I just used the Method 1 as my configuration file is very simple. Below is the receipt.

XML file:

<?xml version=’1.0′ encoding=’utf-8′?>
<!– Camel Web Server –>
<!– The contents of this file will be loaded for each web application –>
<Context>
<!– Server Host –>
<ServerHost>localhost</ServerHost>
<!–Define a non-SSL HTTP/1.1 port –>
<PortNumber>8080</PortNumber>
<!– root dir for all of the web application –>
<BaseDir>../webapps</BaseDir>
<!– log dir –>
<LogDir>../logs</LogDir>
<!– temp dir –>
<TemporaryDir>../temp</TemporaryDir>
<!– debug level –>
<DebugLevel>2</DebugLevel>
<WelcomeList>
<WeclomeFile>index.html</WeclomeFile>
<WeclomeFile>index.htm</WeclomeFile>
<WeclomeFile>index.pl</WeclomeFile>
</WelcomeList>
</Context>

.pl file:

use strict;
use warnings;
use XML::Simple;

my $ctxfile = “../conf/context.xml”;

my $parser = XML::Simple->new();
my $data = $parser->XMLin($ctxfile);
debug();
sub debug{
print $data->{ServerHost} . “\n”;
print $data->{PortNumber} . “\n”;
print $data->{BaseDir} . “\n”;
print $data->{TemporaryDir} . “\n”;
print $data->{LogDir} . “\n”;
print $data->{DebugLevel} . “\n”;
print $data->{WelcomeList}->{WeclomeFile}->[0] . “\n”;
print $data->{WelcomeList}->{WeclomeFile}->[1] . “\n”;
print $data->{WelcomeList}->{WeclomeFile}->[2] . “\n”;
}

# It is just in a very hard-coded way to demo how to parse XML with Perl.

Actually you can use below snippet to parse,


sub readConfig{
 my %config;
 my @element = qw(ServerHost PortNumber BaseDir TemporaryDir LogDir DebugLevel);
 foreach my $key (@element){
 $config{$key} = $data->{$key};
 }
 my $count = 1;
 for (@{ $data->{WelcomeList}->{WeclomeFile} }) {
 my $id = "WelcomeFile" . $count++;
 $config{$id} = $_;
 }
 return %config;
 }

#It is a bit elegant, but we can improve it further like reading the element name from its tag.

Form content enctype

On my previous post, Tricky way to instantiate a class in Perl I shared how to instantiate a class in a tricky way. On that post, while I did the instantiation of my CGI class it was supposed that the Form was posted with its default content enctype – ‘application/x-www-form-urlencoded’.

More details about Form content enctype:

http://www.w3.org/TR/html4/interact/forms.html

enctype = content-type [CI]This attribute specifies the content type used to submit the form to the server (when the value of method is “post”). The default value for this attribute is “application/x-www-form-urlencoded”. The value”multipart/form-data” should be used in combination with the INPUT element, type=”file”.

 By default, Form is posted with application/x-www-form-urlencoded type and its content will be as below,

Content-Type: application/x-www-form-urlencoded

userid=user2&dbid=db1&Submit=submit

# Here it is easily to strip its key/value

 However for type multipart/form-data, its content will be,

Content-Type: multipart/form-data; boundary=—————————11619042012130902275583102315 —————————–11619042012130902275583102315 Content-Disposition: form-data; name=”dbsid” fx2 —————————–11619042012130902275583102315 Content-Disposition: form-data; name=”appsschema” APPSSHEMA —————————–11619042012130902275583102315 Content-Disposition: form-data; name=”appspass” APPSPASSWORD —————————–11619042012130902275583102315 Content-Disposition: form-data; name=”systemschema” SYSTEMSCHEMA —————————–11619042012130902275583102315 Content-Disposition: form-data; name=”systempass” MANAGER —————————–11619042012130902275583102315 Content-Disposition: form-data; name=”Submit” Submit—————————–11619042012130902275583102315–

# It is a bit difficult to strip key/value from it. To support that I added a method parseContent() to retrieve the key/value pairs in the same format as ‘application/x-www-form-urlencoded’ – key=value&key1=value1&key2=value2

# check whether form type is “multipart/form-data”

# if yes, it will retrieve it in format of ‘key=value&key1=value1’.

sub parseContent{
my $paramstr = shift;
my $pos = index($paramstr, "name=");
my $replaced = substr($paramstr, 0, $pos);
my @array = split(/$replaced/, $paramstr);
my $s = "";
my $count = 0;
my $sclr = scalar(@array) - 1;
foreach my $line (@array){
if(($count &gt; 0) and ($count &lt; $sclr)){
$line =~ s/name=//;
$line =~ s/\s+/=/;
$line =~ s/^\s*|\s*$//g;
chomp($line);
$s .= $line . "\&amp;";
}
$count++;
}
$s = substr($s,0,length($s)-1);
return $s;
}

sub getParams{
my $s = shift;
#$s = "hahaid=haha1&amp;abcid=abc2&amp;Submit=submit";
chomp($s);
if ($s =~ m/form-data/){
$s = parseContent($s);
}
my %params = {};
my @pairs = split(/\&amp;/,$s);
foreach (@pairs){
my ($key, $value) = split(/=/,$_);
$params{$key} = $value;
}
return %params;
}

Tricky way to instantiate a class in Perl

Today I started to design a CGI engine for my light weighted Perl Web container and I wanted to share my solution about how to instantiate the CGI class here.

Let’s see how a form works on Perl CGI:

1. User submit a form request,

</pre>
<form action="/firstapp/test.pl" method="post"><select name="userid">
<option value="user1">1</option><option value="user2">2</option>
</select><select name="dbid">
<option value="db1">1</option>
<option value="db2">2</option></select>
<input type="submit" name="Submit" value="submit" />

</form>
<pre>

2. The form request comes to server side in below format,

POST /firstapp/test.pl HTTP/1.1
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: en-us,en;q=0.5
Host: localhost:8080
Referer: http://localhost:8080/firstapp/login.html
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:7.0.1) Gecko/20100101 Firefox/7.0.1
Content-Length: 37
Content-Type: application/x-www-form-urlencoded

userid=user2&dbid=db1&Submit=submit

3. On Server side, CGI engine injects parameters submitted via form ‘hahaid=haha2&abcid=abc1&Submit=submit’ to the action page ‘/firstapp/test.pl’,

4. Developer can access those paramters using method my  param(‘$key’); E.g,

use CGI qw(:standard);

my $userid  = param('userid'); # Here it will return value "user2".

In my CGI implementation, I design it as below process,

Step 1, 2 are the same as above as both are client operation.

Starting from Step 3,

On Server side, my CGI engine class,

sub new{
my $classname = shift;
my $self = {};
bless($self, $classname);
$self->_init(@_);
return $self;
}

sub _init {
my $self = shift;
if (@_) {
my %extra = @_;
my $paramstr = $extra{"PARAMS"};# PARAMS is a keyword here
if (defined $paramstr) {
my %params = getParams($paramstr);
@$self{keys %params} = values %params;
}
}
}

sub getParams{
my $s = shift;
chomp($s);
my %params = {};
my @pairs = split(/\&/,$s);
foreach (@pairs){
my ($key, $value) = split(/=/,$_);
$params{$key} = $value;
}
return %params;
}

Step 4. Developer can access those paramters with below steps:

1. import my CGI,

use camelcgi;

2. Instantiate it,

my $cgiinst = camelcgi-&gt;new(PARAMS =&gt; $ARGV[0]); 
# In my HTTP Daemon, it will pass the request content to it 
# as $ARG[0] automatically.

# E.g, $ARGV[0]=”userid=user2&dbid=db1&Submit=submit”

3. Retrieve those parameters with,

$cgiinst-&gt;{userid} ;

Let’s summarize the snippet here,


use camelcgi;

my $cgiinst = camelcgi-&gt;new(PARAMS =&gt; $ARGV[0]);

$cgiinst-&gt;{userid} ;

It is tricky, right? 🙂

HTTP:Daemon in Perl

Yesterday I tried socket in Perl in post Socket in Perl. Today I will share my experience in HTTP:Daemon.

Basically HTTP package implements the necessary elements defined in http://tools.ietf.org/pdf/rfc2616.pdf (Hypertext Transfer Protocol — HTTP/1.1). For example, on my post Socket in Perl yesterday, the connection is established using socket and it doesn’t support cache and this brings unnecessary network load.  However from http://tools.ietf.org/pdf/rfc2616.pdf, it defines the GET method as below. (I tested HTTP:Daemon seems it is implemented well to support cache).

9.3 GET
The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI.
If the Request-URI refers to a data-producing process, it is the produced data which shall be returned as the entity
in the response and not the source text of the process, unless that text happens to be the output of the process.
The semantics of the GET method change to a “conditional GET” if the request message includes an IfModified-Since, If-Unmodified-Since, If-Match, If-None-Match, or If-Range header field. A
conditional GET method requests that the entity be transferred only under the circumstances described by the
conditional header field(s). The conditional GET method is intended to reduce unnecessary network usage by
allowing cached entities to be refreshed without requiring multiple requests or transferring data already held by the
client

API provided by HTTP:Daemon from http://search.cpan.org/~gaas/libwww-perl-5.837/lib/HTTP/Daemon.pm

Additionally, I introduced the fork in this Daemon script.

The logic in the script:

1. Accept a http request connection.

2. Fork a process to take care of the request.

3. Return to 1.

camelhttp.pm:

package camelhttp;

use strict;
use HTTP::Daemon;
use HTTP::Status;
use Carp;
use FileHandle;
use camellogger;
use camelutils;

my $portno = "8080";
my $base_dir = "../webapps";
my $SERVER = HTTP::Daemon->new(LocalPort => $portno, LocalAddr => 'localhost')
 or die;
loader();
camelHttpCore();

END{
_destroy();
}

sub camelHttpCore{

# autoreaping of zombies
$SIG{CHLD} = 'IGNORE';
while (1)
{
while (my $con = $SERVER->accept){
logger(0, "$0 - Camel is forking a worker");
next if my $pid = fork; #parent
die logger(0, "$0 - Camel can't fork a worker : $!") unless defined $pid;
while (my $req = $con->get_request){

my $path = $base_dir . $req->uri->path;
if ($req->method eq 'GET'){
if (_handleGet($path) eq "YES"){
$con->send_file_response("$path");
} else {
$con->send_error(RC_NOT_FOUND);
}

} elsif ($req->method eq 'POST') {
$con->send_error(RC_FORBIDDEN);
}
}
close($con);
exit;
} continue {
# nothing here so far.
}
}
}

sub _handleGet{
my ($path) = @_;
if ($fshash{"$path"}){
return "YES";
} else {
return "NO";
}
}

sub _destroy {
logger(0, "$0 - Camel is destroying itself");
close ($SERVER) if (defined $SERVER);
}

 1;

Socket in Perl

Today I started to wrote a light weighted Web container in Perl (I named it as Camel – hats off to Tomcat). Basically my target is, it can take care of all of the static requests (html, txt, etc) in async, and it can support Perl CGI. It should have an elegant architecture (basically I will steal the filesystem architecture from Tomcat). Below is the snippet which can provide HTTP service for static requests in sync mode.

I think it is a tiniest web container with Perl’s Socket support.
#Pls. comment out the logger() from below snippet if you want to run it from your side.

package camel;

use strict;
use Socket;
use Carp;
use FileHandle;
use camellogger;
use camelutils;

my $portno = "8080";
my $base_dir = "../webapps";

logger(0, "$0 - Loading Camel Core");
logger(2, "$0 - Making, Setting and binding socket");
socket(SERVER, PF_INET, SOCK_STREAM, getprotobyname('tcp')) 
or die "can't make the socket : $! \n";
setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, 1) 
or die "can't set socket options : $! \n";
my $my_addr = sockaddr_in($portno, INADDR_ANY);
bind(SERVER, $my_addr) 
or die "can't bind to port $portno : $! \n";
logger(0, "$0 - Camel is listening on port $portno");
listen(SERVER, SOMAXCONN) 
or die "can't listen on port $portno : $! \n";

while (1)
{
while (my $newcon = accept(CLIENT, SERVER)){
CLIENT-&gt;autoflush(1);
my ($portno, $ipaddr) = sockaddr_in($newcon);
my $hostname = gethostbyaddr($ipaddr, AF_INET);

logger(0, "$0 - Camel is connecting with $hostname");
my $getinfo = &lt;CLIENT&gt; ;
print "getinfo: $getinfo \n";
my $rsppage = $base_dir . "/" . dispatch($getinfo);

open IN, "&lt; $rsppage" 
or warn logger(0, "$0 - Camel can't talk with $hostname : $!");
while (&lt;IN&gt;)
{
print CLIENT $_ . "\n";
}
close(CLIENT);
close IN;
logger(0, "$0 - Goodbye with $hostname");
}
}

sub dispatch(){
my ($info) = @_;
my ($s, $url) = split(" ",$info);
$url =~ s/^\s*|\s*$//g;
$url = substr($url, 1);
if ($url eq "") {
$url = "login.html";
}
return $url;
}

camel1