added response method
[catagits/HTTP-Request-AsCGI.git] / lib / HTTP / Request / AsCGI.pm
CommitLineData
b2e1304d 1package HTTP::Request::AsCGI;
2
3use strict;
4use warnings;
5use base 'Class::Accessor::Fast';
6
7use Carp;
bd7813ac 8use IO::File;
b2e1304d 9
10__PACKAGE__->mk_accessors( qw[ enviroment request stdin stdout stderr ] );
11
12our $VERSION = 0.1;
13
14sub new {
15 my $class = shift;
16 my $request = shift;
17
18 my $self = {
19 request => $request,
20 restored => 0,
6f5fb9a7 21 setuped => 0,
bd7813ac 22 stdin => IO::File->new_tmpfile,
23 stdout => IO::File->new_tmpfile,
24 stderr => IO::File->new_tmpfile
b2e1304d 25 };
26
27 $self->{enviroment} = {
28 GATEWAY_INTERFACE => 'CGI/1.1',
29 HTTP_HOST => $request->uri->host_port,
30 QUERY_STRING => $request->uri->query || '',
31 SCRIPT_NAME => $request->uri->path || '/',
32 SERVER_NAME => $request->uri->host,
33 SERVER_PORT => $request->uri->port,
34 SERVER_PROTOCOL => $request->protocol || 'HTTP/1.1',
35 SERVER_SOFTWARE => __PACKAGE__ . "/" . $VERSION,
36 REMOTE_ADDR => '127.0.0.1',
37 REMOTE_HOST => 'localhost',
38 REMOTE_PORT => int( rand(64000) + 1000 ), # not in RFC 3875
39 REQUEST_URI => $request->uri->path || '/', # not in RFC 3875
40 REQUEST_METHOD => $request->method,
41 @_
42 };
43
44 foreach my $field ( $request->headers->header_field_names ) {
45
46 my $key = uc($field);
2aaf55bc 47 $key =~ tr/-/_/;
b2e1304d 48 $key = 'HTTP_' . $key unless $field =~ /^Content-(Length|Type)$/;
49
50 unless ( exists $self->{enviroment}->{$key} ) {
51 $self->{enviroment}->{$key} = $request->headers->header($field);
52 }
53 }
54
55 return $class->SUPER::new($self);
56}
57
58sub setup {
59 my $self = shift;
60
61 open( my $stdin, '>&', STDIN->fileno )
62 or croak("Can't dup stdin: $!");
63
64 open( my $stdout, '>&', STDOUT->fileno )
65 or croak("Can't dup stdout: $!");
66
67 open( my $stderr, '>&', STDERR->fileno )
68 or croak("Can't dup stderr: $!");
69
70 $self->{restore} = {
71 stdin => $stdin,
72 stdout => $stdout,
73 stderr => $stderr,
74 enviroment => {%ENV}
75 };
76
77 if ( $self->request->content_length ) {
78
12852959 79 $self->stdin->syswrite( $self->request->content )
780060e5 80 or croak("Can't write request content to stdin handle: $!");
b2e1304d 81
12852959 82 $self->stdin->sysseek( 0, SEEK_SET )
780060e5 83 or croak("Can't seek stdin handle: $!");
b2e1304d 84 }
85
3cdea3c7 86 {
87 no warnings 'uninitialized';
88 %ENV = %{ $self->enviroment };
89 }
b2e1304d 90
91 open( STDIN, '<&=', $self->stdin->fileno )
92 or croak("Can't open stdin: $!");
93
94 open( STDOUT, '>&=', $self->stdout->fileno )
95 or croak("Can't open stdout: $!");
96
97 open( STDERR, '>&=', $self->stderr->fileno )
98 or croak("Can't open stderr: $!");
6f5fb9a7 99
100 $self->{setuped}++;
b2e1304d 101
102 return $self;
103}
104
780060e5 105sub response {
106 my ( $self, $callback ) = @_;
107
108 return undef unless $self->{setuped};
109 return undef unless $self->{restored};
110
111 require HTTP::Response;
112
113 my $message = undef;
114 my $position = $self->stdin->tell;
115
116 $self->stdin->sysseek( 0, SEEK_SET )
117 or croak("Can't seek stdin handle: $!");
118
119 while ( my $line = $self->stdout->getline ) {
120 $message .= $line;
121 last if $line =~ /^\x0d?\x0a$/;
122 }
123
124 unless ( $message =~ /^HTTP/ ) {
125 $message = "HTTP/1.1 200\x0d\x0a" . $message;
126 }
127
128 my $response = HTTP::Response->parse($message);
129
130 if ( my $code = $response->header('Status') ) {
131 $response->code($code);
132 }
133
134 $response->protocol( $self->request->protocol );
135 $response->headers->date( time() );
136
137 if ( $callback ) {
138 $response->content( sub {
139 if ( $self->stdout->read( my $buffer, 4096 ) ) {
140 return $buffer;
141 }
142 return undef;
143 });
144 }
145 else {
146 my $length = 0;
147 while ( $self->stdout->read( my $buffer, 4096 ) ) {
148 $length += length($buffer);
149 $response->add_content($buffer);
150 }
151 $response->content_length($length) unless $response->content_length;
152 }
153
154 $self->stdin->sysseek( $position, SEEK_SET )
155 or croak("Can't seek stdin handle: $!");
156
157 return $response;
158}
159
b2e1304d 160sub restore {
161 my $self = shift;
162
163 %ENV = %{ $self->{restore}->{enviroment} };
164
165 open( STDIN, '>&', $self->{restore}->{stdin} )
166 or croak("Can't restore stdin: $!");
167
168 open( STDOUT, '>&', $self->{restore}->{stdout} )
169 or croak("Can't restore stdout: $!");
170
171 open( STDERR, '>&', $self->{restore}->{stderr} )
172 or croak("Can't restore stderr: $!");
12852959 173
780060e5 174 $self->stdin->sysseek( 0, SEEK_SET )
175 or croak("Can't seek stdin: $!");
12852959 176
6f5fb9a7 177 if ( $self->stdout->fileno != STDOUT->fileno ) {
178 $self->stdout->sysseek( 0, SEEK_SET )
179 or croak("Can't seek stdout: $!");
180 }
12852959 181
6f5fb9a7 182 if ( $self->stderr->fileno != STDERR->fileno ) {
183 $self->stderr->sysseek( 0, SEEK_SET )
184 or croak("Can't seek stderr: $!");
185 }
12852959 186
b2e1304d 187 $self->{restored}++;
188}
189
190sub DESTROY {
191 my $self = shift;
6f5fb9a7 192 $self->restore if $self->{setuped} && !$self->{restored};
b2e1304d 193}
194
1951;
196
197__END__
198
199=head1 NAME
200
bd7813ac 201HTTP::Request::AsCGI - Setup a CGI enviroment from a HTTP::Request
b2e1304d 202
203=head1 SYNOPSIS
204
bd7813ac 205 use CGI;
206 use HTTP::Request;
207 use HTTP::Request::AsCGI;
208
209 my $request = HTTP::Request->new( GET => 'http://www.host.com/' );
210 my $stdout;
211
212 {
213 my $c = HTTP::Request::AsCGI->new($request)->setup;
214 my $q = CGI->new;
215
216 print $q->header,
217 $q->start_html('Hello World'),
218 $q->h1('Hello World'),
219 $q->end_html;
220
221 $stdout = $c->stdout;
222
223 # enviroment and descriptors will automatically be restored when $c is destructed.
224 }
225
bd7813ac 226 while ( my $line = $stdout->getline ) {
227 print $line;
228 }
229
b2e1304d 230=head1 DESCRIPTION
231
232=head1 METHODS
233
234=over 4
235
236=item new
237
bd7813ac 238=item enviroment
239
b2e1304d 240=item setup
241
242=item restore
243
244=item request
245
780060e5 246=item response
247
b2e1304d 248=item stdin
249
250=item stdout
251
252=item stderr
253
254=back
255
256=head1 BUGS
257
258=head1 AUTHOR
259
260Christian Hansen, C<ch@ngmedia.com>
261
262=head1 LICENSE
263
264This library is free software. You can redistribute it and/or modify
265it under the same terms as perl itself.
266
267=cut