File Coverage

blib/lib/Plack/Middleware/Proxy/Connect.pm
Criterion Covered Total %
statement 15 52 28.8
branch 0 18 0.0
condition n/a
subroutine 5 7 71.4
pod 1 1 100.0
total 21 78 26.9


line stmt bran cond sub pod time code
1             package Plack::Middleware::Proxy::Connect;
2 1     1   141372 use strict;
  1         3  
  1         34  
3 1     1   6 use warnings;
  1         3  
  1         27  
4 1     1   5 use parent 'Plack::Middleware';
  1         2  
  1         6  
5              
6 1     1   5011 use AnyEvent::Socket;
  1         49427  
  1         138  
7 1     1   1272 use AnyEvent::Handle;
  1         7855  
  1         543  
8              
9             our $VERSION = '0.01';
10              
11             sub call {
12 0     0 1   my($self, $env) = @_;
13 0 0         return $self->app->( $env ) unless $env->{ REQUEST_METHOD } eq 'CONNECT';
14              
15 0 0         my $client_fh = $env->{'psgix.io'}
16             or return [ 501, [], ['Not implemented CONNECT method']];
17 0           my ( $host, $port ) =
18             ( $env->{REQUEST_URI} =~ m{^(?:.+\@)?(.+?)(?::(\d+))?$} );
19              
20             sub {
21 0     0     my $respond = shift;
22              
23             # Run the loop by myself when psgi.nonblocking is turend off.
24 0 0         my $cv = $env->{'psgi.nonblocking'} ? undef : AE::cv;
25              
26             tcp_connect $host, $port, sub {
27 0           my ( $origin_fh ) = @_;
28 0 0         unless( $origin_fh ){
29 0           $respond->( [ 502, [], ['Bad Gateway'] ] );
30 0 0         $cv->send if $cv;
31 0           return;
32             }
33              
34 0           my $writer = $respond->( [ 200, [] ] );
35              
36 0           my $client_hdl = AnyEvent::Handle->new( fh => $client_fh );
37 0           my $origin_hdl = AnyEvent::Handle->new( fh => $origin_fh );
38              
39             # Join 2 handles by a tunnel
40             $client_hdl->on_read(sub {
41 0           my $hdl = shift;
42 0           my $rbuf = delete $hdl->{rbuf};
43 0           $origin_hdl->push_write( $rbuf );
44 0           } );
45             $client_hdl->on_error( sub {
46 0           my ( $hdl, $fatal, $message ) = @_;
47 0 0         $! and warn "error($fatal): $message\n";
48 0           $origin_hdl->push_shutdown;
49             # Finish this request.
50 0           $writer->close;
51 0 0         $cv->send if $cv;
52             # Use $client_hdl to keep the handle by a cyclical reference.
53 0           $client_hdl->destroy;
54 0           } );
55              
56             $origin_hdl->on_read(sub {
57 0           my $hdl = shift;
58 0           my $rbuf = delete $hdl->{rbuf};
59 0           $client_hdl->push_write( $rbuf );
60 0           } );
61             $origin_hdl->on_error( sub {
62 0           my ( $hdl, $fatal, $message ) = @_;
63 0 0         $! and warn "error($fatal): $message\n";
64 0           $client_hdl->push_shutdown;
65             # Use $origin_hdl to keep the handle by a cyclical reference.
66 0           $origin_hdl->destroy;
67 0           } );
68 0           };
69              
70 0 0         $cv->recv if $cv;
71 0           };
72             }
73              
74             1;
75              
76             __END__