Combining HTTP:Daemon and CGI

Do you have a question? Post it now! No Registration Necessary.  Now with pictures!

Threaded View

Hi all,

I am working on a script for a simple Browser based GUI.

The idea is to create an HTTP:daemon object on a random local port, spawn a
browser on the local machine pointing to the daemon's URL , send a form to
the browser via CGI and process the user response.

The code works fine up to processing of the form (a POST request). I can see
the form in the browser, and see the request coming back, once the SUBMIT
button is pressed.

I ran into a problem trying to make CGI to process the request within the
daemon  process.
My understanding is that CGI relies on the Web Server to set up  a bunch of
environment variables, invoke the CGI script and feed the POST request to
the script via STDIN.

In order to imitate this setup I populated the environment variables from
the HTTP::Request object, wrote the content field (supposetely containing
the POST data)  into a file,  redirected STDIN from the file and started CGI
(see below). However, the CGI module does not parse the content correctly.

Has anyone tried to make CGI work in t a similar environment?


Here is the code:

use strict;

use CGI ':all';
use HTTP::Daemon;
use Win32::Process;
use Win32;
use File::Temp qw/tempfile/;
use File::Slurp;

my %template = ('title' => Title',
  'fields' => [
         'name' => 'LABEL',
         'type' => '=s',
         'default' => 'V100_005',
         'name' => 'PLATFORM',
         'type' => '=c',
         'default' => ['one', 'two'],
         'name' => 'PRODUCT',
         'type' => '=c',
         'default' => ['one', 'two'],
         'name' => 'Whatever',
         'type' => '=s',
         'default' => 'however',

my %cgi_env = (
       'CONTENT'=> sub ,
       'CONTENT_LENGTH' => \&content_length,
       'CONTENT_TYPE' => sub
           if exists $_[0]->headers->},
        'QUERY_STRING' => sub {$_[0]->uri =~ /\?(.+$)/; $1},
        'REQUEST_METHOD' => sub ,
        'REQUEST_URI' => sub ,

$| = 1;
my $d = HTTP::Daemon->new || die;

# Spawn the browser with out URL
my $proc;
unless (Win32::Process::Create($proc,'c:\WINDOWS\system32\cmd.exe',
          sprintf('cmd /C start %s', $d->url )
    die Win32::FormatMessage( Win32::GetLastError() );

my ($fh, $tmpfile) = tempfile("cgiXXXXXX", UNLINK => 1, DIR =>
close $fh;

while (my $c = $d->accept) {

  my $r = $c->get_request;
  if ($r->method eq 'GET') {
      # Push table out
    generate_form($c, \%template);
  } elsif ($r->method eq 'POST') {
    # Cheat CGI to a temp file
     write_file($tmpfile, $r->content);

     local *STDIN;
     unless (open STDIN, '<', $tmpfile)
      die "cannot redirect stdIN:$!";

     my $cgi = new CGI;
    # can't make it to read content

  } {
    # Process response

  close $c;

sub set_env
    my $r = shift;
    while (my ($k, $v) = each %cgi_env) {
      $ENV = $v->($r);

    # Also set the HTTP_ from headers
    while (my ($k, $v) = each %) {
      my $n = $k;
      $n =~ s/-/_/g;
      $n = 'HTTP_' . uc $n;
      $ENV = $v;

sub content_length
    my $r = shift;
    my $b = $r->headers->;
    $b =~ s!^multipart/form-data; boundary=!!;
    my $bl = length $b;
    my @num = $r->content =~ /$b/g;
    length($r->content) - $#num * $bl;

sub content
    my $r = shift;

sub content_type {
    my $r = shift;
    $r->headers-> if exists $r->headers->;

sub query_string {
    my $r = shift;
    $r->uri =~ /\?(.+$)/;
sub request_method {
    my $r = shift;
sub request_uri {
    my $r = shift;

sub generate_form
    my ($c, $desc) = @_;

    # This will make our life easier
    local *STDOUT;
    unless (open STDOUT, '>&', $c)
 die "cannot redirect stdout:$!";

    # Create table data
    my @tds;
    foreach (@}) {
      my $type = substr($_->, 1, 1);
      my $td;
      if ($type eq 's') { # String
 $td = textfield('-name' => $_->,
   '-size' => 50,
   'maxlength' => 256,
   '-value' => $_->,
      } elsif ($type eq 'm') { # Menu
 $td = popup_menu(-name=> $_->,
    -values => $_->);
      } elsif ($type eq 'c') { # Checkbox
 $td = checkbox_group('-name' => $_->, '-values' =>
      else {
 die sprintf('Invalid type %s for element %s', $type, $_->);
      push(@tds, td([$_-> . ': ', $td]));

    print start_html($desc->), h1(sprintf('Enviroment for Project
%s', $desc->));
    print start_form('-align' => 'center');
    print table(,
  Tr({-align=>'LEFT', -valign=>'TOP'},
      th(['Name', 'Value']),

    print submit(-name=> 'Submit');
    print endform;

    print end_html;
    close STDOUT;

Re: Combining HTTP:Daemon and CGI

Quoted text here. Click to load it

Yep.  Other than multi-part forms, it just worked.  I don't know how (or
if) the environment variables get set up--it happens somewhere behind the

The wrinkles I encountered were:
 need -nph
 no multipart forms => no file uploads
 start_form needs to give an explicit action, or else you get the wrong
 url. Some mysterious deaths, probably interupted system calls I haven't
    down yet.

I didn't try to do a forking server, so I don't know the implications
of it.

If I had it to do over, I'd have changed the whiles to explicit infinite
loops, but I'm too lazy at this point.

use strict;
use warnings;
use HTTP::Daemon;
use HTTP::Status;
use CGI qw( -nph -no_xhtml); # no_xhtml prevents the automatic
                             # use of multipart forms on newer
my $port=9871;
my %h= (
           '/foo.cgi' => \&process ,
           '/test.cgi' => \&test2 ,
my $d = HTTP::Daemon->new(LocalPort => $port, ReuseAddr => 1) or die $!;
#warn $d;
while (my $c = $d->accept or warn "$!,$@:") {
    redo unless $c;
    my $r = $c->get_request or warn "$!, $@, ". ($c->reason()).":";
    redo unless $r;
    #print scalar localtime, "\n";
    #print Dumper($r);
    if (exists $h) {
       my $q = $r->method eq 'GET' ?
             new CGI($r->url->query)  :
             new CGI($r->content);
       my $old = select $c; # Save real STDOUT (for logging)
       #warn "Done";
       select $old;
       #warn "Done2";
    } else {
} continue {

sub process {
  my $q=shift;
  print $q->header();
  print $q->start_html();
  print "<H1>Hi!</H1>There<hr>";
  print $q->Dump();
  print $q->start_form(-action => "/foo.cgi");
  print $q->textfield('sdfsdf');
  print $q->submit();
  print $q->end_form();
  print $q->end_html();


-------------------- http://NewsReader.Com/ --------------------
Usenet Newsgroup Service                        $9.95/Month 30GB

Re: Combining HTTP:Daemon and CGI

xhoster> Yep.  Other than multi-part forms, it just worked.  I don't know how (or
xhoster> if) the environment variables get set up--it happens somewhere behind the
xhoster> scenes.

I have code in CGI::Prototype::Mecha to build a full CGI object
from what WWW::Mechanize would have sent... you might want to look
at that as a model.

print "Just another Perl hacker,"; # the original

Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
Perl/Unix/security consulting, Technical writing, Comedy, etc. etc.
See for onsite and open-enrollment Perl training!

*** Posted via a free Usenet account from ***

Re: Combining HTTP:Daemon and CGI

Quoted text here. Click to load it

Oh, Brother

I finally figured iit out.

CGI would not accept the POST content as call parameter. It expects to read
it from STDIN. This is the funny redirection via a tmp file for.
However, it expects TWO CTRL-M's appended to each input line (at least on
WIndows). The File::Slurp module I used to write the file was out of date in
respect to these stupid CTRL-M's. Once I updated to the latest version of
Fiel::Slurp (9999.12) everything went nice and dandy.

Thank you for your support.

Re: Combining HTTP:Daemon and CGI

Yuri Shtil wrote:

Quoted text here. Click to load it

Thanks for posting your resolution. It would be nice if everybody did

David Filmer ( )

Site Timeline