1 package HTTP::Request::AsCGI;
6 use base 'Class::Accessor::Fast';
12 __PACKAGE__->mk_accessors(qw[ enviroment request stdin stdout stderr ]);
24 stdin => IO::File->new_tmpfile,
25 stdout => IO::File->new_tmpfile
28 my $host = $request->header('Host');
29 my $uri = $request->uri->clone;
30 $uri->scheme('http') unless $uri->scheme;
31 $uri->host('localhost') unless $uri->host;
32 $uri->port(80) unless $uri->port;
33 $uri->host_port($host) unless !$host || ( $host eq $uri->host_port );
35 $self->{enviroment} = {
36 GATEWAY_INTERFACE => 'CGI/1.1',
37 HTTP_HOST => $uri->host_port,
38 HTTPS => ( $uri->scheme eq 'https' ) ? 'ON' : 'OFF', # not in RFC 3875
39 PATH_INFO => $uri->path,
40 QUERY_STRING => $uri->query || '',
42 SERVER_NAME => $uri->host,
43 SERVER_PORT => $uri->port,
44 SERVER_PROTOCOL => $request->protocol || 'HTTP/1.1',
45 SERVER_SOFTWARE => "HTTP-Request-AsCGI/$VERSION",
46 REMOTE_ADDR => '127.0.0.1',
47 REMOTE_HOST => 'localhost',
48 REMOTE_PORT => int( rand(64000) + 1000 ), # not in RFC 3875
49 REQUEST_URI => $uri->path_query || '/', # not in RFC 3875
50 REQUEST_METHOD => $request->method,
54 foreach my $field ( $request->headers->header_field_names ) {
58 $key = 'HTTP_' . $key unless $field =~ /^Content-(Length|Type)$/;
60 unless ( exists $self->{enviroment}->{$key} ) {
61 $self->{enviroment}->{$key} = $request->headers->header($field);
65 return $class->SUPER::new($self);
71 $self->{restore}->{enviroment} = {%ENV};
73 open( $self->{restore}->{stdin}, '>&', STDIN->fileno )
74 or croak("Can't dup stdin: $!");
76 open( STDIN, '<&=', $self->stdin->fileno )
77 or croak("Can't open stdin: $!");
79 binmode( $self->stdin );
82 if ( $self->request->content_length ) {
84 syswrite( $self->stdin, $self->request->content )
85 or croak("Can't write request content to stdin handle: $!");
87 sysseek( $self->stdin, 0, SEEK_SET )
88 or croak("Can't seek stdin handle: $!");
91 if ( $self->stdout ) {
93 open( $self->{restore}->{stdout}, '>&', STDOUT->fileno )
94 or croak("Can't dup stdout: $!");
96 open( STDOUT, '>&=', $self->stdout->fileno )
97 or croak("Can't open stdout: $!");
99 binmode( $self->stdout );
103 if ( $self->stderr ) {
105 open( $self->{restore}->{stderr}, '>&', STDERR->fileno )
106 or croak("Can't dup stderr: $!");
108 open( STDERR, '>&=', $self->stderr->fileno )
109 or croak("Can't open stderr: $!");
111 binmode( $self->stderr );
116 no warnings 'uninitialized';
117 %ENV = %{ $self->enviroment };
120 if ( $INC{'CGI.pm'} ) {
121 CGI::initialize_globals();
130 my ( $self, $callback ) = @_;
132 return undef unless $self->{setuped};
133 return undef unless $self->{restored};
134 return undef unless $self->{restore}->{stdout};
136 require HTTP::Response;
138 seek( $self->stdout, 0, SEEK_SET )
139 or croak("Can't seek stdout handle: $!");
142 while ( my $line = $self->stdout->getline ) {
144 last if $message =~ /\x0d?\x0a\x0d?\x0a$/;
147 unless ( $message =~ /^HTTP/ ) {
148 $message = "HTTP/1.1 200 OK\x0d\x0a" . $message;
151 my $response = HTTP::Response->new;
152 my @headers = split( /\x0d?\x0a/, $message );
153 my $status = shift(@headers);
155 unless ( $status =~ s/^(HTTP\/\d\.\d) (\d{3}) (.*)$// ) {
156 croak( "Invalid Status-Line: '$status'" );
159 $response->protocol($1);
161 $response->message($3);
163 my $token = qr/[^][\x00-\x1f\x7f()<>@,;:\\"\/?={} \t]+/;
165 foreach my $header (@headers) {
167 unless( $header =~ s/^($token):[\t ]*// ) {
168 croak( "Invalid header field name : '$header'" );
171 $response->push_header( $1 => $header );
174 if ( my $code = $response->header('Status') ) {
175 $response->code($code);
176 $response->message( HTTP::Status::status_message($code) );
179 $response->headers->date( time() );
182 $response->content( sub {
183 if ( $self->stdout->read( my $buffer, 4096 ) ) {
191 while ( $self->stdout->read( my $buffer, 4096 ) ) {
192 $length += length($buffer);
193 $response->add_content($buffer);
196 if ( $length && !$response->content_length ) {
197 $response->content_length($length);
207 %ENV = %{ $self->{restore}->{enviroment} };
209 open( STDIN, '>&', $self->{restore}->{stdin} )
210 or croak("Can't restore stdin: $!");
212 sysseek( $self->stdin, 0, SEEK_SET )
213 or croak("Can't seek stdin: $!");
215 if ( $self->{restore}->{stdout} ) {
218 or croak("Can't flush stdout: $!");
220 open( STDOUT, '>&', $self->{restore}->{stdout} )
221 or croak("Can't restore stdout: $!");
223 sysseek( $self->stdout, 0, SEEK_SET )
224 or croak("Can't seek stdout: $!");
227 if ( $self->{restore}->{stderr} ) {
230 or croak("Can't flush stderr: $!");
232 open( STDERR, '>&', $self->{restore}->{stderr} )
233 or croak("Can't restore stderr: $!");
235 sysseek( $self->stderr, 0, SEEK_SET )
236 or croak("Can't seek stderr: $!");
246 $self->restore if $self->{setuped} && !$self->{restored};
255 HTTP::Request::AsCGI - Setup a CGI enviroment from a HTTP::Request
261 use HTTP::Request::AsCGI;
263 my $request = HTTP::Request->new( GET => 'http://www.host.com/' );
267 my $c = HTTP::Request::AsCGI->new($request)->setup;
271 $q->start_html('Hello World'),
272 $q->h1('Hello World'),
275 $stdout = $c->stdout;
277 # enviroment and descriptors will automatically be restored when $c is destructed.
280 while ( my $line = $stdout->getline ) {
314 Thomas L. Shinnick for his valuable win32 testing.
318 Christian Hansen, C<ch@ngmedia.com>
322 This library is free software. You can redistribute it and/or modify
323 it under the same terms as perl itself.