Invoking a CGI method in external program trashes form params

Invoking a CGI method in external program trashes form params

am 20.09.2007 04:08:54 von usenet

Greetings. Kindly consider this simplified CGI script which
illustrates my question:

#!/usr/bin/perl
use strict; use warnings;
use CGI qw{ :standard };

system('/var/www/cgi-bin/do_nothing.pl');

print(
header(),
start_html(),
start_form(),
h2(param('xxx')),
textfield({-name => 'xxx'}),
submit(),
end_form(),
end_html()
);
__END__

As you can see, the CGI first runs an external perl program. OK,
fine. Here is the external program:

#!/usr/bin/perl
use strict; use warnings;
use CGI qw{ header };

open (my $file, '>', '/tmp/junk.xxx') or die "oops - $!\n";

my $test_mode = 0;

print $file $test_mode ? header()
: "Content-Type: text/html;
charset=ISO-8859-1\n\n",
__END__

This external program simply prints an HTML content declaration to a
junk file. But it has the option to use CGI.pm's header() method, or
to just print hardcoded text. Either way, the resulting junk file is
identical (same md5sum).

When I run the CGI (in a browser), I should be able to type something
in the textbox and it will be shown to me when I click submit. And if
I run everything as posted, that's exactly what happens.

But if I change $test_mode to non-zero then the value of the CGI
parameter 'xxx' is no longer carried. I can type something in the
textfield and hit submit, but I won't see the value of the param.
Changing $test_mode back to zero causes the form to again work as
expected.

Somehow invoking the CGI.pm header method() in the external program is
causing my CGI form parameters to be lost between form invocations!

That's strange! Does anyone have any idea why?

Thanks!

--
David Filmer (http://DavidFilmer.com)

Re: Invoking a CGI method in external program trashes form params

am 20.09.2007 04:24:09 von Ben Morrow

Quoth usenet@DavidFilmer.com:
> Greetings. Kindly consider this simplified CGI script which
> illustrates my question:
>
> #!/usr/bin/perl
> use strict; use warnings;
> use CGI qw{ :standard };
>
> system('/var/www/cgi-bin/do_nothing.pl');
>
> print(
> header(),
> start_html(),
> start_form(),
> h2(param('xxx')),
> textfield({-name => 'xxx'}),
> submit(),
> end_form(),
> end_html()
> );
> __END__
>
> As you can see, the CGI first runs an external perl program. OK,
> fine. Here is the external program:
>
> #!/usr/bin/perl
> use strict; use warnings;
> use CGI qw{ header };
>
> open (my $file, '>', '/tmp/junk.xxx') or die "oops - $!\n";
>
> my $test_mode = 0;
>
> print $file $test_mode ? header()
> : "Content-Type: text/html;
> charset=ISO-8859-1\n\n",
> __END__
>
> This external program simply prints an HTML content declaration to a
> junk file. But it has the option to use CGI.pm's header() method, or
> to just print hardcoded text. Either way, the resulting junk file is
> identical (same md5sum).
>
> When I run the CGI (in a browser), I should be able to type something
> in the textbox and it will be shown to me when I click submit. And if
> I run everything as posted, that's exactly what happens.
>
> But if I change $test_mode to non-zero then the value of the CGI
> parameter 'xxx' is no longer carried. I can type something in the
> textfield and hit submit, but I won't see the value of the param.
> Changing $test_mode back to zero causes the form to again work as
> expected.
>
> Somehow invoking the CGI.pm header method() in the external program is
> causing my CGI form parameters to be lost between form invocations!
>
> That's strange! Does anyone have any idea why?

You are using POST, yes? POST parameters are passed on STDIN; when a CGI
object is created (with the function interface, the first time you call
a CGI.pm function) it reads the whole of STDIN and extracts the
information. The second script shares its STDIN with the first; the
first call to a CGI function occurs in the second. This call reads the
whole of STDIN, so when you get back to the first script there's nothing
left there to read, and the CGI object in that script ends up empty.

There are many solutions:

Explicitly create a CGI object, or perhaps just call 'header()' and
throw it away, before calling the second script.

'do' the second script instead of executing it with system: you will
lose some separation, but save a fork, and the two will share the
same implicit default CGI object.

Execute the second script with STDIN redirected from /dev/null (or
File::Spec->devnull for portability).

Refactor both scripts (I presume they're not really this trivial) so
that you don't need to invoke the second at all; put the important
stuff in modules, and call the right bits at the right times.

Ben

Re: Invoking a CGI method in external program trashes form params

am 20.09.2007 06:00:23 von usenet

On Sep 19, 7:24 pm, Ben Morrow wrote:
> The second script shares its STDIN with the first; the
> first call to a CGI function occurs in the second. This call reads the
> whole of STDIN, so when you get back to the first script there's nothing
> left there to read, and the CGI object in that script ends up empty.

Thanks, Ben, for your great explanation and plethora of solutions!
This was exactly what I needed to know to solve this problem!

FWIW, in the "real" application, I'm using HTML::Template, but I build
the template with Perl using the CGI module (because I hate to write
raw HTML). As I'm developing the program, the template-generating
program is changing constantly, so I want to rebuild the template each
time the CGI is invoked.

Thanks again, Ben!

--
David Filmer (http://DavidFilmer.com)