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