Fixed binmode
[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: $!");
441eeb04 70
71 binmode( $self->stdin, ':raw' );
090cc060 72 binmode( STDIN, ':raw' );
b2e1304d 73
74 if ( $self->request->content_length ) {
75
12852959 76 $self->stdin->syswrite( $self->request->content )
780060e5 77 or croak("Can't write request content to stdin handle: $!");
b2e1304d 78
12852959 79 $self->stdin->sysseek( 0, SEEK_SET )
780060e5 80 or croak("Can't seek stdin handle: $!");
b2e1304d 81 }
82
090cc060 83 if ( $self->stdout ) {
84 open( $self->{restore}->{stdout}, '>&', STDOUT->fileno )
85 or croak("Can't dup stdout: $!");
86
87 open( STDOUT, '>&=', $self->stdout->fileno )
88 or croak("Can't open stdout: $!");
441eeb04 89
90 binmode( $self->stdout, ':raw' );
090cc060 91 binmode( STDOUT, ':raw' );
92 }
93
94 if ( $self->stderr ) {
95 open( $self->{restore}->{stderr}, '>&', STDERR->fileno )
96 or croak("Can't dup stderr: $!");
97
98 open( STDERR, '>&=', $self->stderr->fileno )
99 or croak("Can't open stderr: $!");
441eeb04 100
101 binmode( $self->stderr, ':raw' );
090cc060 102 binmode( STDERR, ':raw' );
103 }
104
3cdea3c7 105 {
106 no warnings 'uninitialized';
107 %ENV = %{ $self->enviroment };
108 }
b2e1304d 109
6f5fb9a7 110 $self->{setuped}++;
b2e1304d 111
112 return $self;
113}
114
780060e5 115sub response {
116 my ( $self, $callback ) = @_;
117
118 return undef unless $self->{setuped};
119 return undef unless $self->{restored};
090cc060 120 return undef unless $self->{restore}->{stdout};
780060e5 121
122 require HTTP::Response;
123
124 my $message = undef;
125 my $position = $self->stdin->tell;
126
090cc060 127 $self->stdout->sysseek( 0, SEEK_SET )
780060e5 128 or croak("Can't seek stdin handle: $!");
129
130 while ( my $line = $self->stdout->getline ) {
131 $message .= $line;
132 last if $line =~ /^\x0d?\x0a$/;
133 }
134
135 unless ( $message =~ /^HTTP/ ) {
136 $message = "HTTP/1.1 200\x0d\x0a" . $message;
137 }
138
139 my $response = HTTP::Response->parse($message);
140
141 if ( my $code = $response->header('Status') ) {
142 $response->code($code);
143 }
144
145 $response->protocol( $self->request->protocol );
146 $response->headers->date( time() );
147
090cc060 148 if ($callback) {
780060e5 149 $response->content( sub {
150 if ( $self->stdout->read( my $buffer, 4096 ) ) {
151 return $buffer;
152 }
153 return undef;
090cc060 154 });
780060e5 155 }
156 else {
157 my $length = 0;
158 while ( $self->stdout->read( my $buffer, 4096 ) ) {
159 $length += length($buffer);
160 $response->add_content($buffer);
161 }
162 $response->content_length($length) unless $response->content_length;
163 }
164
090cc060 165 $self->stdout->sysseek( $position, SEEK_SET )
780060e5 166 or croak("Can't seek stdin handle: $!");
167
168 return $response;
169}
170
b2e1304d 171sub restore {
172 my $self = shift;
173
174 %ENV = %{ $self->{restore}->{enviroment} };
175
176 open( STDIN, '>&', $self->{restore}->{stdin} )
177 or croak("Can't restore stdin: $!");
178
780060e5 179 $self->stdin->sysseek( 0, SEEK_SET )
180 or croak("Can't seek stdin: $!");
12852959 181
090cc060 182 if ( $self->{restore}->{stdout} ) {
183 open( STDOUT, '>&', $self->{restore}->{stdout} )
184 or croak("Can't restore stdout: $!");
185
6f5fb9a7 186 $self->stdout->sysseek( 0, SEEK_SET )
187 or croak("Can't seek stdout: $!");
188 }
12852959 189
090cc060 190 if ( $self->{restore}->{stderr} ) {
191 open( STDERR, '>&', $self->{restore}->{stderr} )
192 or croak("Can't restore stderr: $!");
193
6f5fb9a7 194 $self->stderr->sysseek( 0, SEEK_SET )
195 or croak("Can't seek stderr: $!");
196 }
12852959 197
b2e1304d 198 $self->{restored}++;
090cc060 199
200 return $self;
b2e1304d 201}
202
203sub DESTROY {
204 my $self = shift;
6f5fb9a7 205 $self->restore if $self->{setuped} && !$self->{restored};
b2e1304d 206}
207
2081;
209
210__END__
211
212=head1 NAME
213
bd7813ac 214HTTP::Request::AsCGI - Setup a CGI enviroment from a HTTP::Request
b2e1304d 215
216=head1 SYNOPSIS
217
bd7813ac 218 use CGI;
219 use HTTP::Request;
220 use HTTP::Request::AsCGI;
221
222 my $request = HTTP::Request->new( GET => 'http://www.host.com/' );
223 my $stdout;
224
225 {
226 my $c = HTTP::Request::AsCGI->new($request)->setup;
227 my $q = CGI->new;
228
229 print $q->header,
230 $q->start_html('Hello World'),
231 $q->h1('Hello World'),
232 $q->end_html;
233
234 $stdout = $c->stdout;
235
236 # enviroment and descriptors will automatically be restored when $c is destructed.
237 }
238
bd7813ac 239 while ( my $line = $stdout->getline ) {
240 print $line;
241 }
242
b2e1304d 243=head1 DESCRIPTION
244
245=head1 METHODS
246
247=over 4
248
249=item new
250
bd7813ac 251=item enviroment
252
b2e1304d 253=item setup
254
255=item restore
256
257=item request
258
780060e5 259=item response
260
b2e1304d 261=item stdin
262
263=item stdout
264
265=item stderr
266
267=back
268
269=head1 BUGS
270
271=head1 AUTHOR
272
273Christian Hansen, C<ch@ngmedia.com>
274
275=head1 LICENSE
276
277This library is free software. You can redistribute it and/or modify
278it under the same terms as perl itself.
279
280=cut