root/lang/perl/HTTPEx-Adaptor-ModPerl/trunk/lib/HTTPEx/Adaptor/ModPerl.pm @ 10381

Revision 10381, 8.6 kB (checked in by yappo, 5 years ago)

HTTP::Engine の context は使い回すようにした。
あと HTTP::Engine::Plugin::Interface::Adaptor から呼ばれるメソッドは
$self, $plugin, $context の順で引数渡されるので、ちょっと弄った

  • Property svn:keywords set to Id
Line 
1# $Id$
2
3package HTTPEx::Adaptor::ModPerl;
4use strict;
5use warnings;
6use base qw(Class::Accessor::Fast Class::Data::ConfigHash);
7use File::Spec;
8use HTTP::Engine;
9use URI;
10use URI::http;
11use URI::https;
12
13use constant MP2 => (
14    exists $ENV{MOD_PERL_API_VERSION} and
15           $ENV{MOD_PERL_API_VERSION} >= 2
16);
17
18our $VERSION = '0.00001';
19
20__PACKAGE__->mk_accessors($_) for qw(apache return);
21HTTP::Engine->load_plugins(qw/ Interface::Adaptor /);
22
23my $httpe;
24sub setup
25{
26    my $class = shift;
27    $httpe = HTTP::Engine->new(
28        handle_request => $class->config->{handler},
29        config         => $class->config->{config},
30    );
31}
32
33sub handler : method
34{
35    my ($class, $r) = @_;
36
37    my $self = $class->new({ apache => $r });
38    $httpe->set_adaptee($self);
39    $httpe->handle_request;
40
41    return ok_constant();
42}
43
44#use Apache2::Const -compile => qw(OK);
45#sub ok_constant { Apache2::Const::OK() }
46
47sub prepare_cookie { }
48sub prepare_body { }
49sub prepare_body_parameters { }
50sub prepare_parameters { }
51sub prepare_uploads { }
52sub finalize_cookies { }
53sub finalize_output_headers { }
54sub finalize_output_body { }
55
56sub prepare_request {
57    my ( $self, $adaptor, $c ) = @_;
58    $self->return( undef );
59}
60
61sub prepare_connection {
62    my ( $self, $adaptor, $c ) = @_;
63
64    $c->request->address( $self->apache->connection->remote_ip );
65
66    PROXY_CHECK:
67    {
68        my $headers = $self->apache->headers_in;
69        unless ( $c->config->{using_frontend_proxy} ) {
70            last PROXY_CHECK if $c->request->address ne '127.0.0.1';
71            last PROXY_CHECK if $c->config->{ignore_frontend_proxy};
72        }       
73        last PROXY_CHECK unless $headers->{'X-Forwarded-For'};
74
75        # If we are running as a backend server, the user will always appear
76        # as 127.0.0.1. Select the most recent upstream IP (last in the list)
77        my ($ip) = $headers->{'X-Forwarded-For'} =~ /([^,\s]+)$/;
78        $c->request->address( $ip );
79    }
80
81    $c->request->hostname( $self->apache->connection->remote_host );
82    $c->request->protocol( $self->apache->protocol );
83    $c->request->user( $self->apache->user );
84
85    # when config options are set, check them here first
86    if ($INC{'Apache2/ModSSL.pm'}) {
87        $c->request->secure(1) if $self->apache->connection->is_https;
88    } else {
89        my $https = $self->apache->subprocess_env('HTTPS');
90        $c->request->secure(1) if defined $https and uc $https eq 'ON';
91    }
92
93}
94
95sub prepare_query_parameters {
96    my ( $self, $adaptor, $c ) = @_;
97   
98    if ( my $query_string = $self->apache->args ) {
99# XXX - 後で
100#        $self->SUPER::prepare_query_parameters( $c, $query_string );
101    }
102}
103
104sub prepare_headers {
105    my ( $self, $adaptor, $c ) = @_;
106
107    $c->request->method( $self->apache->method );
108
109    if ( my %headers = %{ $self->apache->headers_in } ) {
110        $c->request->header( %headers );
111    }
112}
113
114sub prepare_path {
115    my ( $self, $adaptor, $c ) = @_;
116
117    my $scheme = $c->request->secure ? 'https' : 'http';
118    my $host   = $self->apache->hostname || 'localhost';
119    my $port   = $self->apache->get_server_port;
120
121    # If we are running as a backend proxy, get the true hostname
122    PROXY_CHECK:
123    {
124        unless ( $c->config->{using_frontend_proxy} ) {
125            last PROXY_CHECK if $host !~ /localhost|127.0.0.1/;
126            last PROXY_CHECK if $c->config->{ignore_frontend_proxy};
127        }
128        last PROXY_CHECK unless $c->request->header( 'X-Forwarded-Host' );
129       
130        $host = $c->request->header( 'X-Forwarded-Host' );
131
132        if ( $host =~ /^(.+):(\d+)$/ ) {
133            $host = $1;
134            $port = $2;
135        } else {
136            # backend could be on any port, so
137            # assume frontend is on the default port
138            $port = $c->request->secure ? 443 : 80;
139        }
140    }
141
142    my $base_path = '';
143
144    # Are we running in a non-root Location block?
145    my $location = $self->apache->location;
146    if ( $location && $location ne '/' ) {
147        $base_path = $location;
148    }
149   
150    # Using URI directly is way too slow, so we construct the URLs manually
151    my $uri_class = "URI::$scheme";
152   
153    if ( $port !~ /^(?:80|443)$/ && $host !~ /:/ ) {
154        $host .= ":$port";
155    }
156   
157    # We want the path before Apache escapes it.  Under mod_perl2 this is available
158    # with the unparsed_uri method.  Under mod_perl 1 we must parse it out of the
159    # request line.
160    my ($path, $qs);
161   
162    if ( MP2 ) {
163        ($path, $qs) = split /\?/, $self->apache->unparsed_uri, 2;
164    }
165    else {
166        my (undef, $path_query) = split / /, $self->apache->the_request, 3;
167        ($path, $qs)            = split /\?/, $path_query, 2;
168    }
169   
170    # Don't check for LocationMatch blocks if requested
171    # http://rt.cpan.org/Ticket/Display.html?id=26921
172    if ( $self->apache->dir_config('CatalystDisableLocationMatch') ) {
173        $base_path = '';
174    }
175       
176    # Check if $base_path appears to be a regex (contains invalid characters),
177    # meaning we're in a LocationMatch block
178    elsif ( $base_path =~ m/[^$URI::uric]/o ) {
179        # Find out what part of the URI path matches the LocationMatch regex,
180        # that will become our base
181        my $match = qr/($base_path)/;
182        my ($base_match) = $path =~ $match;
183       
184        $base_path = $base_match || '';
185    }
186
187    # Strip leading slash
188    $path =~ s{^/+}{};
189   
190    # base must end in a slash
191    $base_path .= '/' unless $base_path =~ m{/$};
192
193    # Are we an Apache::Registry script? Why anyone would ever want to run
194    # this way is beyond me, but we'll support it!
195    # XXX: This needs a test
196    if ( defined $ENV{SCRIPT_NAME} && $self->apache->filename && -f $self->apache->filename && -x _ ) {
197        $base_path .= $ENV{SCRIPT_NAME};
198    }
199   
200    # If the path is contained within the base, we need to make the path
201    # match base.  This handles the case where the app is running at /deep/path
202    # but a request to /deep/path fails where /deep/path/ does not.
203    if ( $base_path ne '/' && $base_path ne $path && $base_path =~ m{/$path} ) {
204        $path = $base_path;
205        $path =~ s{^/+}{};
206    }
207   
208    my $query = $qs ? '?' . $qs : '';
209    my $uri   = $scheme . '://' . $host . '/' . $path . $query;
210
211    $c->request->uri( bless \$uri, $uri_class );
212   
213    my $base_uri = $scheme . '://' . $host . $base_path;
214
215    $c->request->base( bless \$base_uri, $uri_class );
216}
217
218sub read_chunk {
219    my $self = shift;
220    my $adaptor = shift;
221    my $c = shift;
222   
223    $self->apache->read( @_ );
224}
225
226sub finalize_body {
227    my ( $self, $adaptor, $c ) = @_;
228   
229    # XXX - 後で
230#    $self->SUPER::finalize_body($c);
231   
232    # Data sent using $self->apache->print is buffered, so we need
233    # to flush it after we are done writing.
234    $self->apache->rflush;
235}
236
237sub finalize_headers {
238    my ( $self, $adaptor, $c ) = @_;
239
240    for my $name ( $c->response->headers->header_field_names ) {
241        next if $name =~ /^Content-(Length|Type)$/i;
242        my @values = $c->response->header($name);
243        # allow X headers to persist on error
244        if ( $name =~ /^X-/i ) {
245            $self->apache->err_headers_out->add( $name => $_ ) for @values;
246        }
247        else {
248            $self->apache->headers_out->add( $name => $_ ) for @values;
249        }
250    }
251
252    # persist cookies on error responses
253    if ( $c->response->header('Set-Cookie') && $c->response->status >= 400 ) {
254        for my $cookie ( $c->response->header('Set-Cookie') ) {
255            $self->apache->err_headers_out->add( 'Set-Cookie' => $cookie );
256        }
257    }
258
259    # The trick with Apache is to set the status code in $apache->status but
260    # always return the OK constant back to Apache from the handler.
261    $self->apache->status( $c->response->status );
262    $c->response->status( $self->return || $self->ok_constant );
263
264    my $type = $c->response->header('Content-Type') || 'text/html';
265    $self->apache->content_type( $type );
266
267    if ( my $length = $c->response->content_length ) {
268        $self->apache->set_content_length( $length );
269    }
270
271    # This handles the case where Apache2 will remove the Content-Length
272    # header on a HEAD request.
273    # http://perl.apache.org/docs/2.0/user/handlers/http.html
274    if ( $self->apache->header_only ) {
275        $self->apache->rflush;
276    }
277
278    return 0;
279}
280
281sub write {
282    my ( $self, $adaptor, $c, $buffer ) = @_;
283
284    if ( ! $self->apache->connection->aborted && defined $buffer) {
285        return $self->apache->print( $buffer );
286    }
287    return;
288}
289
2901;
291
292__END__
293
294=head1 NAME
295
296HTTPEx::Adaptor::ModPerl - ModPerl Adaptor For HTTP::Engine
297
298=head1 SYNOPSIS
299
300  package MyApp;
301  use strict;
302  use base qw(HTTPEx::Adaptor::ModPerl);
303
304  __PACKAGE__->config(
305    handler => \&handler,
306    config  => $config,
307  );
308  __PACKAGE__->setup;
309
310=cut
Note: See TracBrowser for help on using the browser.