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_setuped
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 $self->setup_content;
186 $self->setup_environment;
188 if ( $INC{'CGI.pm'} ) {
189 CGI::initialize_globals();
192 $self->is_setuped(1);
200 if ( $self->should_setup_content && $self->has_stdin ) {
202 if ( $self->request->content_length ) {
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 to stdin handle: '$!'");
213 if ( $self->should_rewind ) {
215 seek( $self->stdin, 0, SEEK_SET )
216 or croak("Couldn't seek stdin handle: '$!'");
221 croak("Can't handle request content of '$content'");
230 if ( $self->has_stdin ) {
232 if ( $self->should_dup ) {
234 if ( $self->should_restore ) {
236 open( my $stdin, '<&STDIN' )
237 or croak("Couldn't dup STDIN: '$!'");
239 $self->{restore}->{stdin} = $stdin;
242 STDIN->fdopen( $self->stdin, '<' )
243 or croak("Couldn't dup stdin handle to STDIN: '$!'");
247 my $stdin = Symbol::qualify_to_ref('STDIN');
249 if ( $self->should_restore ) {
251 $self->{restore}->{stdin} = *$stdin;
252 $self->{restore}->{stdin_ref} = \*$stdin;
255 *$stdin = $self->stdin;
258 binmode( $self->stdin );
266 if ( $self->has_stdout ) {
268 if ( $self->should_dup ) {
270 if ( $self->should_restore ) {
272 open( my $stdout, '>&STDOUT' )
273 or croak("Couldn't dup STDOUT: '$!'");
275 $self->{restore}->{stdout} = $stdout;
278 STDOUT->fdopen( $self->stdout, '>' )
279 or croak("Couldn't dup stdout handle to STDOUT: '$!'");
283 my $stdout = Symbol::qualify_to_ref('STDOUT');
285 if ( $self->should_restore ) {
287 $self->{restore}->{stdout} = *$stdout;
288 $self->{restore}->{stdout_ref} = \*$stdout;
291 *$stdout = $self->stdout;
294 binmode( $self->stdout );
302 if ( $self->has_stderr ) {
304 if ( $self->should_dup ) {
306 if ( $self->should_restore ) {
308 open( my $stderr, '>&STDERR' )
309 or croak("Couldn't dup STDERR: '$!'");
311 $self->{restore}->{stderr} = $stderr;
314 STDERR->fdopen( $self->stderr, '>' )
315 or croak("Couldn't dup stderr handle to STDERR: '$!'");
319 my $stderr = Symbol::qualify_to_ref('STDERR');
321 if ( $self->should_restore ) {
323 $self->{restore}->{stderr} = *$stderr;
324 $self->{restore}->{stderr_ref} = \*$stderr;
327 *$stderr = $self->stderr;
330 binmode( $self->stderr );
335 sub setup_environment {
338 no warnings 'uninitialized';
340 if ( $self->should_restore ) {
341 $self->{restore}->{environment} = { %ENV };
344 %ENV = %{ $self->environment };
349 my %params = ( headers_only => 0, sync => 0, @_ );
351 return undef unless $self->has_stdout;
353 if ( $self->should_rewind ) {
355 seek( $self->stdout, 0, SEEK_SET )
356 or croak("Couldn't seek stdout handle: '$!'");
360 my $response = HTTP::Response->new( 200, 'OK' );
361 $response->protocol('HTTP/1.1');
363 while ( my $line = $self->stdout->getline ) {
365 last if $message =~ /\x0d?\x0a\x0d?\x0a$/;
370 $response->code(500);
371 $response->message('Internal Server Error');
372 $response->date( time );
373 $response->content( $response->error_as_HTML );
374 $response->content_type('text/html');
375 $response->content_length( length $response->content );
380 my $Token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
381 my $Version = qr/HTTP\/[0-9]+\.[0-9]+/;
383 if ( $message =~ s/^($Version)[\x09\x20]+(\d{3})[\x09\x20]+([\x20-\xFF]*)\x0D?\x0A//o ) {
385 $response->protocol($1);
387 $response->message($3);
390 $message =~ s/\x0D?\x0A[\x09\x20]+/\x20/gs;
392 foreach ( split /\x0D?\x0A/, $message ) {
394 if ( /^($Token+)[\x09\x20]*:[\x09\x20]*([\x20-\xFF]+)[\x09\x20]*$/o ) {
395 $response->headers->push_header( $1 => $2 );
398 # XXX what should we do on bad headers?
402 my $status = $response->header('Status');
404 if ( $status && $status =~ /^(\d{3})[\x09\x20]+([\x20-\xFF]+)$/ ) {
406 $response->message($2);
409 if ( !$response->date ) {
410 $response->date(time);
413 if ( $params{headers_only} ) {
415 if ( $params{sync} ) {
417 my $position = tell( $self->stdout )
418 or croak("Couldn't get file position from stdout handle: '$!'");
420 sysseek( $self->stdout, $position, SEEK_SET )
421 or croak("Couldn't seek stdout handle: '$!'");
428 my $content_length = 0;
432 my $r = $self->stdout->read( $content, 65536, $content_length );
436 $content_length += $r;
441 croak("Couldn't read response content from stdin handle: '$!'");
445 if ( $content_length ) {
447 $response->content_ref(\$content);
449 if ( !$response->content_length ) {
450 $response->content_length($content_length);
460 if ( $self->should_restore ) {
462 $self->restore_environment;
463 $self->restore_stdin;
464 $self->restore_stdout;
465 $self->restore_stderr;
467 $self->{restore} = {};
469 $self->is_restored(1);
475 sub restore_environment {
478 no warnings 'uninitialized';
480 %ENV = %{ $self->{restore}->{environment} };
486 if ( $self->has_stdin ) {
488 my $stdin = $self->{restore}->{stdin};
490 if ( $self->should_dup ) {
492 STDIN->fdopen( $stdin, '<' )
493 or croak("Couldn't restore STDIN: '$!'");
497 my $stdin_ref = $self->{restore}->{stdin_ref};
498 *$stdin_ref = $stdin;
501 if ( $self->should_rewind ) {
503 seek( $self->stdin, 0, SEEK_SET )
504 or croak("Couldn't seek stdin handle: '$!'");
512 if ( $self->has_stdout ) {
514 my $stdout = $self->{restore}->{stdout};
516 if ( $self->should_dup ) {
519 or croak("Couldn't flush STDOUT: '$!'");
521 STDOUT->fdopen( $stdout, '>' )
522 or croak("Couldn't restore STDOUT: '$!'");
526 my $stdout_ref = $self->{restore}->{stdout_ref};
527 *$stdout_ref = $stdout;
530 if ( $self->should_rewind ) {
532 seek( $self->stdout, 0, SEEK_SET )
533 or croak("Couldn't seek stdout handle: '$!'");
541 if ( $self->has_stderr ) {
543 my $stderr = $self->{restore}->{stderr};
545 if ( $self->should_dup ) {
548 or croak("Couldn't flush STDERR: '$!'");
550 STDERR->fdopen( $stderr, '>' )
551 or croak("Couldn't restore STDERR: '$!'");
555 my $stderr_ref = $self->{restore}->{stderr_ref};
556 *$stderr_ref = $stderr;
559 if ( $self->should_rewind ) {
561 seek( $self->stderr, 0, SEEK_SET )
562 or croak("Couldn't seek stderr handle: '$!'");
570 if ( $self->should_restore && $self->is_setuped && !$self->is_restored ) {
581 HTTP::Request::AsCGI - Setup a CGI environment from a HTTP::Request
587 use HTTP::Request::AsCGI;
589 my $request = HTTP::Request->new( GET => 'http://www.host.com/' );
593 my $c = HTTP::Request::AsCGI->new($request)->setup;
597 $q->start_html('Hello World'),
598 $q->h1('Hello World'),
601 $stdout = $c->stdout;
603 # environment and descriptors will automatically be restored
604 # when $c is destructed.
607 while ( my $line = $stdout->getline ) {
613 Provides a convinient way of setting up an CGI environment from a HTTP::Request.
619 =item new ( $request [, key => value ] )
621 Contructor, first argument must be a instance of HTTP::Request
622 followed by optional pairs of environment key and value.
626 Returns a hashref containing the environment that will be used in setup.
627 Changing the hashref after setup has been called will have no effect.
631 Setups the environment and descriptors.
635 Restores the environment and descriptors. Can only be called after setup.
639 Returns the request given to constructor.
643 Returns a HTTP::Response. Can only be called after restore.
647 Accessor for handle that will be used for STDIN, must be a real seekable
648 handle with an file descriptor. Defaults to a tempoary IO::File instance.
652 Accessor for handle that will be used for STDOUT, must be a real seekable
653 handle with an file descriptor. Defaults to a tempoary IO::File instance.
657 Accessor for handle that will be used for STDERR, must be a real seekable
658 handle with an file descriptor.
666 =item examples directory in this distribution.
668 =item L<WWW::Mechanize::CGI>
670 =item L<Test::WWW::Mechanize::CGI>
676 Thomas L. Shinnick for his valuable win32 testing.
680 Christian Hansen, C<ch@ngmedia.com>
684 This library is free software. You can redistribute it and/or modify
685 it under the same terms as perl itself.