initial import of CGI wrapping code
Matt S. Trout [Mon, 9 Apr 2007 14:11:49 +0000 (14:11 +0000)]
lib/CatalystX/Controller/WrapCGI.pm [new file with mode: 0644]

diff --git a/lib/CatalystX/Controller/WrapCGI.pm b/lib/CatalystX/Controller/WrapCGI.pm
new file mode 100644 (file)
index 0000000..afcef2a
--- /dev/null
@@ -0,0 +1,99 @@
+package CatalystX::Controller::WrapCGI;
+
+# AUTHOR: Matt S Trout, mst@shadowcatsystems.co.uk
+# Original development sponsored by http://www.altinity.com/
+
+use strict;
+use warnings;
+use base 'Catalyst::Controller';
+
+use HTTP::Request::AsCGI;
+use HTTP::Request;
+use URI::Escape;
+
+# Hack-around because Catalyst::Engine::HTTP goes and changes
+# them to be the remote socket, and FCGI.pm does even dumber things.
+
+open(*REAL_STDIN, "<&=".fileno(*STDIN));
+open(*REAL_STDOUT, ">>&=".fileno(*STDOUT));
+
+sub cgi_to_response {
+  my ($self, $c, $script) = @_;
+  my $res = $self->wrap_cgi($c, $script);
+
+  # if the CGI doesn't set the response code but sets location they were
+  # probably trying to redirect so set 302 for them
+
+  if (length($res->headers->header('Location')) && $res->code == 200) {
+    $c->res->status(302);
+  } else { 
+    $c->res->status($res->code);
+  }
+  $c->res->body($res->content);
+  $c->res->headers($res->headers);
+}
+
+sub wrap_cgi {
+  my ($self, $c, $call) = @_;
+  my $req = HTTP::Request->new(
+    map { $c->req->$_ } qw/method uri headers/
+  );
+  my $body = $c->req->body;
+  my $body_content = '';
+
+  $req->content_type($c->req->content_type); # set this now so we can override
+
+  if ($body) { # Slurp from body filehandle
+    local $/; $body_content = <$body>;
+  } else {
+    my $body_params = $c->req->body_parameters;
+    if (keys %$body_params) {
+      my @parts;
+      foreach my $key (keys %$body_params) {
+        my $raw = $body_params->{$key};
+        foreach my $value (ref $raw ? @$raw : ($raw)) {
+          push(@parts, join('=', map { uri_escape($_) } ($key, $value)));
+        }
+      }
+      $body_content = join('&', @parts);
+      $req->content_type('application/x-www-form-urlencoded');
+    }
+  }
+
+  #warn "Body type: ".$req->content_type;
+  #warn "Body: ${body_content}";
+      
+  $req->content($body_content);
+  $req->content_length(length($body_content));
+  my $user = (($c->can('user_exists') && $c->user_exists)
+               ? $c->user_object->username
+                : '');
+  my $env = HTTP::Request::AsCGI->new(
+              $req,
+              REMOTE_USER => $user,
+              PERL5LIB => $ENV{PERL5LIB}  # propagate custom perl lib paths
+            );
+
+  {
+    local *STDIN = \*REAL_STDIN;   # restore the real ones so the filenos
+    local *STDOUT = \*REAL_STDOUT; # are 0 and 1 for the env setup
+
+    my $old = select(REAL_STDOUT); # in case somebody just calls 'print'
+
+    my $saved_error;
+
+    $env->setup;
+    eval { $call->() };
+    $saved_error = $@;
+    $env->restore;
+
+    select($old);
+
+    warn "CGI invoke failed: $saved_error" if $saved_error;
+
+  }
+
+  return $env->response;
+}
+
+1;