Fixed SCRIPT_NAME bug thanks to shenme
[catagits/HTTP-Request-AsCGI.git] / lib / HTTP / Request / AsCGI.pm
index 9d062a2..70cf9c8 100644 (file)
@@ -5,8 +5,7 @@ use warnings;
 use base 'Class::Accessor::Fast';
 
 use Carp;
-use IO::Handle;
-use File::Temp;
+use IO::File;
 
 __PACKAGE__->mk_accessors( qw[ enviroment request stdin stdout stderr ] );
 
@@ -19,16 +18,17 @@ sub new {
     my $self = {
         request  => $request,
         restored => 0,
-        stdin    => File::Temp->new,
-        stdout   => File::Temp->new,
-        stderr   => File::Temp->new
+        setuped  => 0,
+        stdin    => IO::File->new_tmpfile,
+        stdout   => IO::File->new_tmpfile,
+        stderr   => IO::File->new_tmpfile
     };
 
     $self->{enviroment} = {
         GATEWAY_INTERFACE => 'CGI/1.1',
         HTTP_HOST         => $request->uri->host_port,
         QUERY_STRING      => $request->uri->query || '',
-        SCRIPT_NAME       => $request->uri->path || '/',
+        SCRIPT_NAME       => '/',
         SERVER_NAME       => $request->uri->host,
         SERVER_PORT       => $request->uri->port,
         SERVER_PROTOCOL   => $request->protocol || 'HTTP/1.1',
@@ -44,7 +44,7 @@ sub new {
     foreach my $field ( $request->headers->header_field_names ) {
 
         my $key = uc($field);
-        $key =~ tr/_/-/;
+        $key =~ tr/-/_/;
         $key = 'HTTP_' . $key unless $field =~ /^Content-(Length|Type)$/;
 
         unless ( exists $self->{enviroment}->{$key} ) {
@@ -76,14 +76,17 @@ sub setup {
 
     if ( $self->request->content_length ) {
 
-        $self->stdin->write( $self->request->content )
-          or croak("Can't write content: $!");
+        $self->stdin->syswrite( $self->request->content )
+          or croak("Can't write request content to stdin handle: $!");
 
-        seek( $self->stdin, 0, 0 )
-          or croak("Can't seek stdin: $!");
+        $self->stdin->sysseek( 0, SEEK_SET )
+          or croak("Can't seek stdin handle: $!");
     }
 
-    %ENV = %{ $self->enviroment };
+    {
+        no warnings 'uninitialized';
+        %ENV = %{ $self->enviroment };
+    }
 
     open( STDIN, '<&=', $self->stdin->fileno )
       or croak("Can't open stdin: $!");
@@ -93,10 +96,67 @@ sub setup {
 
     open( STDERR, '>&=', $self->stderr->fileno )
       or croak("Can't open stderr: $!");
+      
+    $self->{setuped}++;
 
     return $self;
 }
 
+sub response {
+    my ( $self, $callback ) = @_;
+
+    return undef unless $self->{setuped};
+    return undef unless $self->{restored};
+
+    require HTTP::Response;
+
+    my $message  = undef;
+    my $position = $self->stdin->tell;
+
+    $self->stdin->sysseek( 0, SEEK_SET )
+      or croak("Can't seek stdin handle: $!");
+
+    while ( my $line = $self->stdout->getline ) {
+        $message .= $line;
+        last if $line =~ /^\x0d?\x0a$/;
+    }
+
+    unless ( $message =~ /^HTTP/ ) {
+        $message = "HTTP/1.1 200\x0d\x0a" . $message;
+    }
+
+    my $response = HTTP::Response->parse($message);
+
+    if ( my $code = $response->header('Status') ) {
+        $response->code($code);
+    }
+
+    $response->protocol( $self->request->protocol );
+    $response->headers->date( time() );
+
+    if ( $callback ) {
+        $response->content( sub {
+            if ( $self->stdout->read( my $buffer, 4096 ) ) {
+                return $buffer;
+            }
+            return undef;
+        });        
+    }
+    else {
+        my $length = 0;
+        while ( $self->stdout->read( my $buffer, 4096 ) ) {
+            $length += length($buffer);
+            $response->add_content($buffer);
+        }
+        $response->content_length($length) unless $response->content_length;
+    }
+
+    $self->stdin->sysseek( $position, SEEK_SET )
+      or croak("Can't seek stdin handle: $!");
+
+    return $response;
+}
+
 sub restore {
     my $self = shift;
 
@@ -110,13 +170,26 @@ sub restore {
 
     open( STDERR, '>&', $self->{restore}->{stderr} )
       or croak("Can't restore stderr: $!");
-      
+
+    $self->stdin->sysseek( 0, SEEK_SET )
+      or croak("Can't seek stdin: $!");
+
+    if ( $self->stdout->fileno != STDOUT->fileno ) {
+        $self->stdout->sysseek( 0, SEEK_SET )
+          or croak("Can't seek stdout: $!");
+    }
+
+    if ( $self->stderr->fileno != STDERR->fileno ) {
+        $self->stderr->sysseek( 0, SEEK_SET )
+          or croak("Can't seek stderr: $!");
+    }
+
     $self->{restored}++;
 }
 
 sub DESTROY {
     my $self = shift;
-    $self->restore unless $self->{restored};
+    $self->restore if $self->{setuped} && !$self->{restored};
 }
 
 1;
@@ -125,10 +198,35 @@ __END__
 
 =head1 NAME
 
-HTTP::Request::AsCGI - Create a CGI enviroment from a HTTP::Request
+HTTP::Request::AsCGI - Setup a CGI enviroment from a HTTP::Request
 
 =head1 SYNOPSIS
 
+    use CGI;
+    use HTTP::Request;
+    use HTTP::Request::AsCGI;
+    
+    my $request = HTTP::Request->new( GET => 'http://www.host.com/' );
+    my $stdout;
+    
+    {
+        my $c = HTTP::Request::AsCGI->new($request)->setup;
+        my $q = CGI->new;
+        
+        print $q->header,
+              $q->start_html('Hello World'),
+              $q->h1('Hello World'),
+              $q->end_html;
+        
+        $stdout = $c->stdout;
+        
+        # enviroment and descriptors will automatically be restored when $c is destructed.
+    }
+    
+    while ( my $line = $stdout->getline ) {
+        print $line;
+    }
+    
 =head1 DESCRIPTION
 
 =head1 METHODS
@@ -137,12 +235,16 @@ HTTP::Request::AsCGI - Create a CGI enviroment from a HTTP::Request
 
 =item new
 
+=item enviroment
+
 =item setup
 
 =item restore
 
 =item request
 
+=item response
+
 =item stdin
 
 =item stdout