1 package HTTP::Request::AsCGI;
6 use base 'Class::Accessor::Fast';
9 use HTTP::Response qw[];
11 use IO::File qw[SEEK_SET];
14 __PACKAGE__->mk_accessors( qw[ is_setup
29 our $VERSION = 0.6_01;
32 my $class = ref $_[0] ? ref shift : shift;
39 $params = { request => shift, environment => { @_ } };
42 return bless( {}, $class )->initialize($params);
46 my ( $self, $params ) = @_;
48 if ( exists $params->{request} ) {
49 $self->request( $params->{request} );
52 croak("Mandatory parameter 'request' is missing.");
55 if ( exists $params->{environment} ) {
56 $self->environment( { %{ $params->{environment} } } );
59 $self->environment( {} );
62 if ( exists $params->{stdin} ) {
63 $self->stdin( $params->{stdin} );
66 $self->stdin( IO::File->new_tmpfile );
69 if ( exists $params->{stdout} ) {
70 $self->stdout( $params->{stdout} );
73 $self->stdout( IO::File->new_tmpfile );
76 if ( exists $params->{stderr} ) {
77 $self->stderr( $params->{stderr} );
80 if ( exists $params->{dup} ) {
81 $self->should_dup( $params->{dup} ? 1 : 0 );
87 if ( exists $params->{restore} ) {
88 $self->should_restore( $params->{restore} ? 1 : 0 );
91 $self->should_restore(1);
94 if ( exists $params->{rewind} ) {
95 $self->should_rewind( $params->{rewind} ? 1 : 0 );
98 $self->should_rewind(1);
101 if ( exists $params->{content} ) {
102 $self->should_setup_content( $params->{content} ? 1 : 0 );
105 $self->should_setup_content(1);
113 *enviroment = \&environment;
115 sub has_stdin { return defined $_[0]->stdin }
116 sub has_stdout { return defined $_[0]->stdout }
117 sub has_stderr { return defined $_[0]->stderr }
122 my $environment = $self->environment;
123 my $request = $self->request;
125 my $host = $request->header('Host');
126 my $uri = $request->uri->clone;
128 $uri->scheme('http') unless $uri->scheme;
129 $uri->host('localhost') unless $uri->host;
130 $uri->port(80) unless $uri->port;
131 $uri->host_port($host) unless !$host || ( $host eq $uri->host_port );
133 $uri = $uri->canonical;
136 GATEWAY_INTERFACE => 'CGI/1.1',
137 HTTP_HOST => $uri->host_port,
138 HTTPS => ( $uri->scheme eq 'https' ) ? 'ON' : 'OFF', # not in RFC 3875
139 PATH_INFO => $uri->path,
140 QUERY_STRING => $uri->query || '',
142 SERVER_NAME => $uri->host,
143 SERVER_PORT => $uri->port,
144 SERVER_PROTOCOL => $request->protocol || 'HTTP/1.1',
145 SERVER_SOFTWARE => "HTTP-Request-AsCGI/$VERSION",
146 REMOTE_ADDR => '127.0.0.1',
147 REMOTE_HOST => 'localhost',
148 REMOTE_PORT => int( rand(64000) + 1000 ), # not in RFC 3875
149 REQUEST_URI => $uri->path_query, # not in RFC 3875
150 REQUEST_METHOD => $request->method
153 foreach my $key ( keys %cgi ) {
155 unless ( exists $environment->{ $key } ) {
156 $environment->{ $key } = $cgi{ $key };
160 foreach my $field ( $self->request->headers->header_field_names ) {
162 my $key = uc("HTTP_$field");
164 $key =~ s/^HTTP_// if $field =~ /^Content-(Length|Type)$/;
166 unless ( exists $environment->{ $key } ) {
167 $environment->{ $key } = $self->request->headers->header($field);
171 unless ( $environment->{SCRIPT_NAME} eq '/' && $environment->{PATH_INFO} ) {
172 $environment->{PATH_INFO} =~ s/^\Q$environment->{SCRIPT_NAME}\E/\//;
173 $environment->{PATH_INFO} =~ s/^\/+/\//;
176 $self->is_prepared(1);
182 if ( $self->is_setup ) {
183 croak("An attempt was made to setup environment variables and STD handles which has already been setup.");
186 $self->setup_content;
190 $self->setup_environment;
192 if ( $INC{'CGI.pm'} ) {
193 CGI::initialize_globals();
202 my ( $self, $handle ) = @_;
204 my $content = $self->request->content_ref;
206 if ( ref($content) eq 'SCALAR' ) {
208 if ( defined($$content) && length($$content) ) {
210 print( { $self->stdin } $$content )
211 or croak("Couldn't write request content SCALAR to stdin handle: '$!'");
213 if ( $self->should_rewind ) {
215 seek( $self->stdin, 0, SEEK_SET )
216 or croak("Couldn't rewind stdin handle: '$!'");
220 elsif ( ref($content) eq 'CODE' ) {
224 my $chunk = &$content();
226 if ( defined($chunk) && length($chunk) ) {
228 print( { $self->stdin } $chunk )
229 or croak("Couldn't write request content callback to stdin handle: '$!'");
236 if ( $self->should_rewind ) {
238 seek( $self->stdin, 0, SEEK_SET )
239 or croak("Couldn't rewind stdin handle: '$!'");
243 croak("Couldn't write request content to stdin handle: 'Unknown request content $content'");
250 if ( $self->should_setup_content && $self->has_stdin ) {
251 $self->write_content($self->stdin);
258 if ( $self->has_stdin ) {
260 if ( $self->should_dup ) {
262 if ( $self->should_restore ) {
264 open( my $stdin, '<&STDIN' )
265 or croak("Couldn't dup STDIN: '$!'");
267 $self->{restore}->{stdin} = $stdin;
270 open( STDIN, '<&' . fileno($self->stdin) )
271 or croak("Couldn't dup stdin handle to STDIN: '$!'");
275 my $stdin = Symbol::qualify_to_ref('STDIN');
277 if ( $self->should_restore ) {
279 $self->{restore}->{stdin} = *$stdin;
280 $self->{restore}->{stdin_ref} = \*$stdin;
283 *$stdin = $self->stdin;
286 binmode( $self->stdin );
294 if ( $self->has_stdout ) {
296 if ( $self->should_dup ) {
298 if ( $self->should_restore ) {
300 open( my $stdout, '>&STDOUT' )
301 or croak("Couldn't dup STDOUT: '$!'");
303 $self->{restore}->{stdout} = $stdout;
306 open( STDOUT, '>&' . fileno($self->stdout) )
307 or croak("Couldn't dup stdout handle to STDOUT: '$!'");
311 my $stdout = Symbol::qualify_to_ref('STDOUT');
313 if ( $self->should_restore ) {
315 $self->{restore}->{stdout} = *$stdout;
316 $self->{restore}->{stdout_ref} = \*$stdout;
319 *$stdout = $self->stdout;
322 binmode( $self->stdout );
330 if ( $self->has_stderr ) {
332 if ( $self->should_dup ) {
334 if ( $self->should_restore ) {
336 open( my $stderr, '>&STDERR' )
337 or croak("Couldn't dup STDERR: '$!'");
339 $self->{restore}->{stderr} = $stderr;
342 open( STDERR, '>&' . fileno($self->stderr) )
343 or croak("Couldn't dup stdout handle to STDOUT: '$!'");
347 my $stderr = Symbol::qualify_to_ref('STDERR');
349 if ( $self->should_restore ) {
351 $self->{restore}->{stderr} = *$stderr;
352 $self->{restore}->{stderr_ref} = \*$stderr;
355 *$stderr = $self->stderr;
358 binmode( $self->stderr );
363 sub setup_environment {
366 no warnings 'uninitialized';
368 if ( $self->should_restore ) {
369 $self->{restore}->{environment} = { %ENV };
372 %ENV = %{ $self->environment };
375 my $HTTP_Token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
376 my $HTTP_Version = qr/HTTP\/[0-9]+\.[0-9]+/;
380 my %params = ( headers_only => 0, sync => 0, @_ );
382 return undef unless $self->has_stdout;
384 if ( $self->should_rewind ) {
386 seek( $self->stdout, 0, SEEK_SET )
387 or croak("Couldn't seek stdout handle: '$!'");
391 my $response = HTTP::Response->new( 200, 'OK' );
392 $response->protocol('HTTP/1.1');
394 while ( my $line = readline($self->stdout) ) {
396 if ( !$message && $line =~ /^\x0d?\x0a$/ ) {
403 last if $message =~ /\x0d?\x0a\x0d?\x0a$/;
407 $response->code(500);
408 $response->message('Internal Server Error');
409 $response->date( time() );
410 $response->content( $response->error_as_HTML );
411 $response->content_type('text/html');
412 $response->content_length( length $response->content );
417 if ( $message =~ s/^($HTTP_Version)[\x09\x20]+(\d\d\d)[\x09\x20]+([\x20-\xFF]*)\x0D?\x0A//o ) {
418 $response->protocol($1);
420 $response->message($3);
423 $message =~ s/\x0D?\x0A[\x09\x20]+/\x20/gs;
425 foreach ( split /\x0D?\x0A/, $message ) {
429 if ( /^($HTTP_Token+)[\x09\x20]*:[\x09\x20]*([\x20-\xFF]+)$/o ) {
430 $response->headers->push_header( $1 => $2 );
433 # XXX what should we do on bad headers?
437 my $status = $response->header('Status');
439 if ( $status && $status =~ /^(\d\d\d)[\x09\x20]+([\x20-\xFF]+)$/ ) {
441 $response->message($2);
444 if ( !$response->date ) {
445 $response->date(time());
448 if ( $params{headers_only} ) {
450 if ( $params{sync} ) {
452 my $position = tell( $self->stdout )
453 or croak("Couldn't get file position from stdout handle: '$!'");
455 sysseek( $self->stdout, $position, SEEK_SET )
456 or croak("Couldn't seek stdout handle: '$!'");
463 my $content_length = 0;
467 my $r = read( $self->stdout, $content, 65536, $content_length );
475 $content_length += $r;
479 croak("Couldn't read response content from stdin handle: '$!'");
483 if ( $content_length ) {
485 $response->content_ref(\$content);
487 if ( !$response->content_length ) {
488 $response->content_length($content_length);
498 if ( !$self->should_restore ) {
499 croak("An attempt was made to restore environment variables and STD handles which has not been saved.");
502 if ( !$self->is_setup ) {
503 croak("An attempt was made to restore environment variables and STD handles which has not been setup.");
506 if ( $self->is_restored ) {
507 croak("An attempt was made to restore environment variables and STD handles which has already been restored.");
510 $self->restore_environment;
511 $self->restore_stdin;
512 $self->restore_stdout;
513 $self->restore_stderr;
515 $self->{restore} = {};
517 $self->is_restored(1);
522 sub restore_environment {
525 no warnings 'uninitialized';
527 %ENV = %{ $self->{restore}->{environment} };
533 if ( $self->has_stdin ) {
535 my $stdin = $self->{restore}->{stdin};
537 if ( $self->should_dup ) {
539 STDIN->fdopen( fileno($stdin), '<' )
540 or croak("Couldn't restore STDIN: '$!'");
544 my $stdin_ref = $self->{restore}->{stdin_ref};
545 *$stdin_ref = $stdin;
548 if ( $self->should_rewind ) {
550 seek( $self->stdin, 0, SEEK_SET )
551 or croak("Couldn't rewind stdin handle: '$!'");
559 if ( $self->has_stdout ) {
561 my $stdout = $self->{restore}->{stdout};
563 if ( $self->should_dup ) {
566 or croak("Couldn't flush STDOUT: '$!'");
568 STDOUT->fdopen( fileno($stdout), '>' )
569 or croak("Couldn't restore STDOUT: '$!'");
573 my $stdout_ref = $self->{restore}->{stdout_ref};
574 *$stdout_ref = $stdout;
577 if ( $self->should_rewind ) {
579 seek( $self->stdout, 0, SEEK_SET )
580 or croak("Couldn't rewind stdout handle: '$!'");
588 if ( $self->has_stderr ) {
590 my $stderr = $self->{restore}->{stderr};
592 if ( $self->should_dup ) {
595 or croak("Couldn't flush STDERR: '$!'");
597 STDERR->fdopen( fileno($stderr), '>' )
598 or croak("Couldn't restore STDERR: '$!'");
602 my $stderr_ref = $self->{restore}->{stderr_ref};
603 *$stderr_ref = $stderr;
606 if ( $self->should_rewind ) {
608 seek( $self->stderr, 0, SEEK_SET )
609 or croak("Couldn't rewind stderr handle: '$!'");
617 if ( $self->should_restore && $self->is_setup && !$self->is_restored ) {
628 HTTP::Request::AsCGI - Setup a CGI environment from a HTTP::Request
634 use HTTP::Request::AsCGI;
636 my $request = HTTP::Request->new( GET => 'http://www.host.com/' );
640 my $c = HTTP::Request::AsCGI->new($request)->setup;
644 $q->start_html('Hello World'),
645 $q->h1('Hello World'),
648 $stdout = $c->stdout;
650 # environment and descriptors is automatically restored
651 # when $c is destructed.
654 while ( my $line = $stdout->getline ) {
660 Provides a convinient way of setting up an CGI environment from a HTTP::Request.
670 HTTP::Request->new( $request, %environment );
672 HTTP::Request->new( request => $request, environment => \%environment );
678 request => HTTP::Request->new( GET => 'http://www.host.com/' )
682 Filehandle to be used as C<STDIN>, defaults to a temporary file. If value is
683 C<undef>, C<STDIN> will be left as is.
685 stdin => IO::File->new_tmpfile
686 stdin => IO::String->new
692 Filehandle to be used as C<STDOUT>, defaults to a temporary file. If value is
693 C<undef>, C<STDOUT> will be left as is.
695 stdout => IO::File->new_tmpfile
696 stdout => IO::String->new
702 Filehandle to be used as C<STDERR>, defaults to C<undef>. If value is C<undef>,
703 C<STDERR> will be left as is.
705 stderr => IO::File->new_tmpfile
706 stderr => IO::String->new
713 environment => { PATH => '/bin:/usr/bin' }
739 Returns a hashref containing the environment that will be used in setup.
740 Changing the hashref after setup has been called will have no effect.
744 Setups the environment and descriptors.
748 Restores the environment and descriptors. Can only be called after setup.
752 Returns the request given to constructor.
756 Returns a HTTP::Response. Can only be called after restore.
760 Accessor for handle that will be used for STDIN, must be a real seekable
761 handle with an file descriptor. Defaults to a temporary IO::File instance.
765 Accessor for handle that will be used for STDOUT, must be a real seekable
766 handle with an file descriptor. Defaults to a temporary IO::File instance.
770 Accessor for handle that will be used for STDERR, must be a real seekable
771 handle with an file descriptor.
779 =item examples directory in this distribution.
781 =item L<WWW::Mechanize::CGI>
783 =item L<Test::WWW::Mechanize::CGI>
789 Thomas L. Shinnick for his valuable win32 testing.
793 Christian Hansen, C<ch@ngmedia.com>
797 This library is free software. You can redistribute it and/or modify
798 it under the same terms as perl itself.