views:

434

answers:

3

I'm attempting to run a CGI script in the current environment from another Perl module. All works well using standard systems calls for GET requests. POST is fine too, until the parameter list gets too long, then they get cut off.

Has anyone ran in to this problem, or have any suggestions for other ways to attempt this?

The following are somewhat simplified for clarity. There is more error checking, etc.

For GET requests and POST requests w/o parameters, I do the following:

# $query is a CGI object.
my $perl = $^X;
my $cgi  = $cgi_script_location; # /path/file.cgi
system {$perl} $cgi;
  • Parameters are passed through the QUERY_STRING environment variable.
  • STDOUT is captured by the calling script so whatever the CGI script prints behaves as normal.
  • This part works.

For POST requests with parameters the following works, but seemingly limits my available query length:

# $query is a CGI object.
my $perl = $^X;
my $cgi  = $cgi_script_location; # /path/file.cgi

# Gather parameters into a URL-escaped string suitable 
# to pass to a CGI script ran from the command line.
# Null characters are handled properly.
# e.g., param1=This%20is%20a%20string&param2=42&... etc.
# This works.
my $param_string = $self->get_current_param_string();

# Various ways to do this, but system() doesn't pass any 
# parameters (different question).
# Using qx// and printing the return value works as well.
open(my $cgi_pipe, "|$perl $cgi");
print {$cgi_pipe} $param_string;
close($cgi_pipe);
  • This method works for short parameter lists, but if the entire command gets to be close to 1000 characters, the parameter list is cut short. This is why I attempted to save the parameters to a file; to avoid shell limitations.
  • If I dump the parameter list from the executed CGI script I get something like the following:

param1=blah
... a bunch of other parameters ...
paramN=whatever
p <-- cut off after 'p'. There are more parameters.

Other things I've done that didn't help or work

  • Followed the CGI troubleshooting guide
  • Saved the parameters to a file using CGI->save(), passing that file to the CGI script. Only the first parameter is read using this method.

$> perl index.cgi < temp-param-file

  • Saved $param_string to a file, passing that file to the CGI script just like above. Same limitations as passing the commands through the command line; still gets cut off.
  • Made sure $CGI::POST_MAX is acceptably high (it's -1).
  • Made sure the CGI's command-line processing was working. (:no_debug is not set)
  • Ran the CGI from the command line with the same parameters. This works.

Leads

  • Obviously, this seems like a character limit of the shell Perl is using to execute the command, but it wasn't resolved by passing the parameters through a file.
+1  A: 
atk
The function is `open`, not `fopen`, and you should always use the 3-argument form (e.g. `open my $perl, '|', 'perl' or die`).
Ether
@Ether: Oops! Thanks for the corrections!
atk
I am actually using that form of system to make the call for GET requests, I didn't have the code in front of me, so I missed that. Thanks though, you're right.As I listed in my leads section, I'm aware of a possible character limitation imposed by the shell, thus the reason I attempted to save the parameters to a file and pass that file as the input. I named the file using the user's unique ID plus a large random number by the way, though I'm aware of the slight chance of a race condition. I would probably add something else to mitigate this further if it worked at all.
James van Dyke
As far as the piping goes, is this not the same thing I'm doing with the open ... print .. close commands in the second code block? The parameters get passed this way up until they begin getting cut off. Seems like there could be a STDIN buffer limit that I don't know about.
James van Dyke
@James van Dyke: Regarding piping, I must have missed that in my first read through - I think I first breezed through that block thniking it was the called perl script, rather than the calling script. It does appear to be much the same thing. Have you looked at the called script to ensure it's not doing something funny with the input parameters?
atk
@atk: It's doing whatever the standard CGI module is doing. The CGI works when called directly by the webserver and when I run it at the command line with a huge input of parameters like the CGI documentation itself examples: http://perldoc.perl.org/CGI.html#DEBUGGINGThere seems to be some limitation or special incantation required when piping input to a script in Perl.
James van Dyke
@James van Dyke: Just in case I was unclear: I mean the perl script called by the CGI script - is there anything in that script that could be truncating input? Or do you mean that you found the initial input to the CGI script to already be truncated?
atk
You were clear. The 'final' CGI script is what I was referring too. The input only seemingly gets truncated when called from my other Perl script, but not when called directly from the command line or from the web server.
James van Dyke
A: 

I didn't want to do this, but I've gone with the most direct approach and it works. I'm tricking the environment to think the request method is GET so that the called CGI script will read its input from the QUERY_STRING environment variable it expects. Like so:

$ENV{'QUERY_STRING'} = $long_parameter_string . '&' . $ENV{'QUERY_STRING'};
$ENV{'REQUEST_METHOD'} = 'GET';

system {$perl_exec} $cgi_script;

I'm worried about potential problems this may cause, but I can't think of what this would harm, and it works well so far. But, because I'm worried, I thought I'd ask the horde if they saw any potential problems:

Are there any problems handling a POST request as a GET request on the server

I'll save marking this as the official answer until people have confirmed or at least debated it on the above post.

James van Dyke
I haven't found a problem with this yet, but there is a genuine answer to this problem, which I've posted here.
James van Dyke
A: 

Turns out that the problem is actually related to the difference in Content-Length between the original parameters and the parameter string I cobbled together. I didn't realize that the CGI module was using this value from the original headers as the limit to how much input to read (makes sense!). Apparently the extra escaping I was doing was adding some characters.

My solution's trick is simply to piece together the parameter string I'll be passing and modify the environment variable the CGI module will check to determine the content length to be equal to the .

Here's the final working code:

use CGI::Util qw(escape);

my $params;

foreach my $param (sort $query->param) {
 my $escaped_param  = escape($param);

 foreach my $value ($query->param($param)) {
  $params .= "$escaped_param=" . escape("$value") . "&";
 }
}

foreach (keys %{$query->{'.fieldnames'}}) {
 $params .= ".cgifields=" . escape("$_") . "&";
}

# This is the trick.
$ENV{'CONTENT_LENGTH'} = length($params);

open(my $cgi_pipe, "| $perl $cgi_script") || die("Cannot fork CGI: $!");
local $SIG{PIPE} = sub { warn "spooler pipe broke" };

print {$cgi_pipe} $params;

warn("param chars: " . length($params));

close($cgi_pipe) || warn "Error: CGI exited with value $?";

Thanks for all the help!

James van Dyke