From: Christian Hansen Date: Wed, 8 Nov 2006 02:45:36 +0000 (+0000) Subject: First stab at cleaning up H::R::AsCGI X-Git-Tag: v1.0~32 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FHTTP-Request-AsCGI.git;a=commitdiff_plain;h=26e3d92b31d2230622552f8f951d46c73607d72c First stab at cleaning up H::R::AsCGI --- diff --git a/Changes b/Changes index c2a08d5..190bb94 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,9 @@ This file documents the revision history for Perl extension HTTP::Request::AsCGI. +0.6 2006-11-08 00:00:00 2005 + - Support for perl 5.6 + - Support for in memory handles + 0.5 2006-01-20 00:00:00 2005 - Fixed bug where content was overridden on 500 responses. @@ -17,4 +21,3 @@ This file documents the revision history for Perl extension HTTP::Request::AsCGI 0.1 2005-10-21 00:00:00 2005 - First release. - diff --git a/lib/HTTP/Request/AsCGI.pm b/lib/HTTP/Request/AsCGI.pm index 380551f..2148005 100644 --- a/lib/HTTP/Request/AsCGI.pm +++ b/lib/HTTP/Request/AsCGI.pm @@ -5,30 +5,106 @@ use warnings; use bytes; use base 'Class::Accessor::Fast'; -use Carp; -use HTTP::Response; -use IO::Handle; -use IO::File; +use Carp qw[croak]; +use HTTP::Response qw[]; +use IO::Handle qw[]; +use IO::File qw[SEEK_SET]; +use Symbol qw[]; -__PACKAGE__->mk_accessors(qw[ enviroment request stdin stdout stderr ]); +__PACKAGE__->mk_accessors(qw[environment request is_restored is_setuped is_prepared should_dup should_restore should_rewind stdin stdout stderr]); -our $VERSION = 0.5; +our $VERSION = 0.6_01; sub new { - my $class = shift; - my $request = shift; + my $class = ref $_[0] ? ref shift : shift; + my $params = {}; - unless ( @_ % 2 == 0 && eval { $request->isa('HTTP::Request') } ) { - croak(qq/usage: $class->new( \$request [, key => value] )/); + if ( @_ % 2 == 0 ) { + $params = { @_ }; + } + else { + $params = { request => shift, environment => { @_ } }; + } + + return bless( {}, $class )->initialize($params); +} + +sub initialize { + my ( $self, $params ) = @_; + + if ( exists $params->{request} ) { + $self->request( $params->{request} ); + } + else { + croak("Mandatory parameter 'request' is missing."); } - my $self = $class->SUPER::new( { restored => 0, setuped => 0 } ); - $self->request($request); - $self->stdin( IO::File->new_tmpfile ); - $self->stdout( IO::File->new_tmpfile ); + if ( exists $params->{environment} ) { + $self->environment( $params->{environment} ); + } + else { + $self->environment( {} ); + } + + if ( exists $params->{stdin} ) { + $self->stdin( $params->{stdin} ); + } + else { + $self->stdin( IO::File->new_tmpfile ); + } + + if ( exists $params->{stdout} ) { + $self->stdout( $params->{stdout} ); + } + else { + $self->stdout( IO::File->new_tmpfile ); + } + + if ( exists $params->{stderr} ) { + $self->stderr( $params->{stderr} ); + } + + if ( exists $params->{dup} ) { + $self->should_dup( $params->{dup} ? 1 : 0 ); + } + else { + $self->should_dup(1); + } + + if ( exists $params->{restore} ) { + $self->should_restore( $params->{restore} ? 1 : 0 ); + } + else { + $self->should_restore(1); + } + + if ( exists $params->{rewind} ) { + $self->should_rewind( $params->{rewind} ? 1 : 0 ); + } + else { + $self->should_rewind(1); + } + + $self->prepare; + + return $self; +} + +*enviroment = \&environment; + +sub has_stdin { return defined $_[0]->stdin } +sub has_stdout { return defined $_[0]->stdout } +sub has_stderr { return defined $_[0]->stderr } + +sub prepare { + my $self = shift; + + my $environment = $self->environment; + my $request = $self->request; my $host = $request->header('Host'); my $uri = $request->uri->clone; + $uri->scheme('http') unless $uri->scheme; $uri->host('localhost') unless $uri->host; $uri->port(80) unless $uri->port; @@ -36,7 +112,7 @@ sub new { $uri = $uri->canonical; - my $enviroment = { + my %cgi = ( GATEWAY_INTERFACE => 'CGI/1.1', HTTP_HOST => $uri->host_port, HTTPS => ( $uri->scheme eq 'https' ) ? 'ON' : 'OFF', # not in RFC 3875 @@ -51,91 +127,180 @@ sub new { REMOTE_HOST => 'localhost', REMOTE_PORT => int( rand(64000) + 1000 ), # not in RFC 3875 REQUEST_URI => $uri->path_query, # not in RFC 3875 - REQUEST_METHOD => $request->method, - @_ - }; + REQUEST_METHOD => $request->method + ); - foreach my $field ( $request->headers->header_field_names ) { + foreach my $key ( keys %cgi ) { + + unless ( exists $environment->{ $key } ) { + $environment->{ $key } = $cgi{ $key }; + } + } + + foreach my $field ( $self->request->headers->header_field_names ) { my $key = uc("HTTP_$field"); $key =~ tr/-/_/; $key =~ s/^HTTP_// if $field =~ /^Content-(Length|Type)$/; - unless ( exists $enviroment->{$key} ) { - $enviroment->{$key} = $request->headers->header($field); + unless ( exists $environment->{ $key } ) { + $environment->{ $key } = $self->request->headers->header($field); } } - unless ( $enviroment->{SCRIPT_NAME} eq '/' && $enviroment->{PATH_INFO} ) { - $enviroment->{PATH_INFO} =~ s/^\Q$enviroment->{SCRIPT_NAME}\E/\//; - $enviroment->{PATH_INFO} =~ s/^\/+/\//; + unless ( $environment->{SCRIPT_NAME} eq '/' && $environment->{PATH_INFO} ) { + $environment->{PATH_INFO} =~ s/^\Q$environment->{SCRIPT_NAME}\E/\//; + $environment->{PATH_INFO} =~ s/^\/+/\//; + } + + $self->is_prepared(1); +} + +sub setup { + my $self = shift; + + $self->setup_stdin; + $self->setup_stdout; + $self->setup_stderr; + $self->setup_environment; + + if ( $INC{'CGI.pm'} ) { + CGI::initialize_globals(); } - $self->enviroment($enviroment); + $self->is_setuped(1); return $self; } -sub setup { +sub setup_environment { my $self = shift; - $self->{restore}->{enviroment} = {%ENV}; + no warnings 'uninitialized'; + + if ( $self->should_restore ) { + $self->{restore}->{environment} = { %ENV }; + } + + %ENV = %{ $self->environment }; +} + +sub setup_stdin { + my $self = shift; + + if ( $self->has_stdin ) { + + binmode( $self->stdin ); + + if ( $self->request->content_length ) { - binmode( $self->stdin ); + syswrite( $self->stdin, $self->request->content ) + or croak("Couldn't write request content to stdin handle: '$!'"); - if ( $self->request->content_length ) { + sysseek( $self->stdin, 0, SEEK_SET ) + or croak("Couldn't seek stdin handle: '$!'"); + } + + if ( $self->should_dup ) { + + if ( $self->should_restore ) { + + open( my $stdin, '<&STDIN' ) + or croak("Couldn't dup STDIN: '$!'"); + + $self->{restore}->{stdin} = $stdin; + } + + STDIN->fdopen( $self->stdin, '<' ) + or croak("Couldn't redirect STDIN: '$!'"); + } + else { + + my $stdin = Symbol::qualify_to_ref('STDIN'); + + if ( $self->should_restore ) { + + $self->{restore}->{stdin} = *$stdin; + $self->{restore}->{stdin_ref} = \*$stdin; + } - syswrite( $self->stdin, $self->request->content ) - or croak("Can't write request content to stdin handle: $!"); + *{ $stdin } = $self->stdin; + } - sysseek( $self->stdin, 0, SEEK_SET ) - or croak("Can't seek stdin handle: $!"); + binmode( STDIN ); } +} + +sub setup_stdout { + my $self = shift; + + if ( $self->has_stdout ) { - open( $self->{restore}->{stdin}, '<&', STDIN->fileno ) - or croak("Can't dup stdin: $!"); + if ( $self->should_dup ) { - open( STDIN, '<&=', $self->stdin->fileno ) - or croak("Can't open stdin: $!"); + if ( $self->should_restore ) { + + open( my $stdout, '>&STDOUT' ) + or croak("Couldn't dup STDOUT: '$!'"); + + $self->{restore}->{stdout} = $stdout; + } + + STDOUT->fdopen( $self->stdout, '>' ) + or croak("Couldn't redirect STDOUT: '$!'"); + } + else { - binmode( STDIN ); + my $stdout = Symbol::qualify_to_ref('STDOUT'); - if ( $self->stdout ) { + if ( $self->should_restore ) { - open( $self->{restore}->{stdout}, '>&', STDOUT->fileno ) - or croak("Can't dup stdout: $!"); + $self->{restore}->{stdout} = *$stdout; + $self->{restore}->{stdout_ref} = \*$stdout; + } - open( STDOUT, '>&=', $self->stdout->fileno ) - or croak("Can't open stdout: $!"); + *{ $stdout } = $self->stdout; + } binmode( $self->stdout ); binmode( STDOUT); } +} - if ( $self->stderr ) { +sub setup_stderr { + my $self = shift; - open( $self->{restore}->{stderr}, '>&', STDERR->fileno ) - or croak("Can't dup stderr: $!"); + if ( $self->has_stderr ) { - open( STDERR, '>&=', $self->stderr->fileno ) - or croak("Can't open stderr: $!"); + if ( $self->should_dup ) { - binmode( $self->stderr ); - binmode( STDERR ); - } + if ( $self->should_restore ) { - { - no warnings 'uninitialized'; - %ENV = %{ $self->enviroment }; - } + open( my $stderr, '>&STDERR' ) + or croak("Couldn't dup STDERR: '$!'"); - if ( $INC{'CGI.pm'} ) { - CGI::initialize_globals(); - } + $self->{restore}->{stderr} = $stderr; + } - $self->{setuped}++; + STDERR->fdopen( $self->stderr, '>' ) + or croak("Couldn't redirect STDERR: '$!'"); + } + else { - return $self; + my $stderr = Symbol::qualify_to_ref('STDERR'); + + if ( $self->should_restore ) { + + $self->{restore}->{stderr} = *$stderr; + $self->{restore}->{stderr_ref} = \*$stderr; + } + + *{ $stderr } = $self->stderr; + } + + binmode( $self->stderr ); + binmode( STDERR ); + } } sub response { @@ -144,14 +309,14 @@ sub response { return undef unless $self->stdout; seek( $self->stdout, 0, SEEK_SET ) - or croak("Can't seek stdout handle: $!"); + or croak("Couldn't seek stdout handle: '$!'"); my $headers; while ( my $line = $self->stdout->getline ) { $headers .= $line; last if $headers =~ /\x0d?\x0a\x0d?\x0a$/; } - + unless ( defined $headers ) { $headers = "HTTP/1.1 500 Internal Server Error\x0d\x0a"; } @@ -178,7 +343,7 @@ sub response { $response->code($code); $response->message($message); } - + my $length = ( stat( $self->stdout ) )[7] - tell( $self->stdout ); if ( $response->code == 500 && !$length ) { @@ -222,49 +387,125 @@ sub response { sub restore { my $self = shift; - { - no warnings 'uninitialized'; - %ENV = %{ $self->{restore}->{enviroment} }; + if ( $self->should_restore ) { + + $self->restore_environment; + $self->restore_stdin; + $self->restore_stdout; + $self->restore_stderr; + + $self->{restore} = {}; + + $self->is_restored(1); } - open( STDIN, '<&', $self->{restore}->{stdin} ) - or croak("Can't restore stdin: $!"); + return $self; +} + +sub restore_environment { + my $self = shift; + + no warnings 'uninitialized'; - sysseek( $self->stdin, 0, SEEK_SET ) - or croak("Can't seek stdin: $!"); + %ENV = %{ $self->{restore}->{environment} }; +} + +sub restore_stdin { + my $self = shift; - if ( $self->{restore}->{stdout} ) { + if ( $self->has_stdin ) { - STDOUT->flush - or croak("Can't flush stdout: $!"); + my $stdin = $self->{restore}->{stdin}; - open( STDOUT, '>&', $self->{restore}->{stdout} ) - or croak("Can't restore stdout: $!"); + if ( $self->should_dup ) { + + STDIN->fdopen( $stdin, '<' ) + or croak("Couldn't restore STDIN: '$!'"); + } + else { + + my $stdin_ref = $self->{restore}->{stdin_ref}; + + *{ $stdin_ref } = $stdin; + } - sysseek( $self->stdout, 0, SEEK_SET ) - or croak("Can't seek stdout: $!"); + if ( $self->should_rewind ) { + + seek( $self->stdin, 0, SEEK_SET ) + or croak("Couldn't seek stdin handle: '$!'"); + } } +} + +sub restore_stdout { + my $self = shift; + + if ( $self->has_stdout ) { + + my $stdout = $self->{restore}->{stdout}; + + if ( $self->should_dup ) { + + STDOUT->flush + or croak("Couldn't flush STDOUT: '$!'"); + + STDOUT->fdopen( $stdout, '>' ) + or croak("Couldn't restore STDOUT: '$!'"); + } + else { - if ( $self->{restore}->{stderr} ) { + my $stdout_ref = $self->{restore}->{stdout_ref}; - STDERR->flush - or croak("Can't flush stderr: $!"); + *{ $stdout_ref } = $stdout; + } - open( STDERR, '>&', $self->{restore}->{stderr} ) - or croak("Can't restore stderr: $!"); + if ( $self->should_rewind ) { - sysseek( $self->stderr, 0, SEEK_SET ) - or croak("Can't seek stderr: $!"); + seek( $self->stdout, 0, SEEK_SET ) + or croak("Couldn't seek stdout handle: '$!'"); + } } +} - $self->{restored}++; +sub restore_stderr { + my $self = shift; - return $self; + if ( $self->has_stderr ) { + + my $stderr = $self->{restore}->{stderr}; + + if ( $self->should_dup ) { + + STDERR->flush + or croak("Couldn't flush STDERR: '$!'"); + + STDERR->fdopen( $stderr, '>' ) + or croak("Couldn't restore STDERR: '$!'"); + } + else { + + my $stderr_ref = $self->{restore}->{stderr_ref}; + + *{ $stderr_ref } = $stderr; + } + + if ( $self->should_rewind ) { + + seek( $self->stderr, 0, SEEK_SET ) + or croak("Couldn't seek stderr handle: '$!'"); + } + } } sub DESTROY { my $self = shift; - $self->restore if $self->{setuped} && !$self->{restored}; + + if ( $self->should_restore ) { + + if ( $self->is_setuped && !$self->is_restored ) { + $self->restore; + } + } } 1; @@ -273,52 +514,52 @@ __END__ =head1 NAME -HTTP::Request::AsCGI - Setup a CGI enviroment from a HTTP::Request +HTTP::Request::AsCGI - Setup a CGI environment 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 + + # environment and descriptors will automatically be restored # when $c is destructed. } - + while ( my $line = $stdout->getline ) { print $line; } - + =head1 DESCRIPTION -Provides a convinient way of setting up an CGI enviroment from a HTTP::Request. +Provides a convinient way of setting up an CGI environment from a HTTP::Request. =head1 METHODS -=over 4 +=over 4 =item new ( $request [, key => value ] ) Contructor, first argument must be a instance of HTTP::Request followed by optional pairs of environment key and value. -=item enviroment +=item environment -Returns a hashref containing the environment that will be used in setup. +Returns a hashref containing the environment that will be used in setup. Changing the hashref after setup has been called will have no effect. =item setup @@ -327,7 +568,7 @@ Setups the environment and descriptors. =item restore -Restores the enviroment and descriptors. Can only be called after setup. +Restores the environment and descriptors. Can only be called after setup. =item request @@ -376,7 +617,7 @@ Christian Hansen, C =head1 LICENSE -This library is free software. You can redistribute it and/or modify +This library is free software. You can redistribute it and/or modify it under the same terms as perl itself. =cut