Created
July 28, 2014 22:03
-
-
Save anonymous/7cd45b5399922daf695e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/lib/Mojo/IOLoop.pm b/lib/Mojo/IOLoop.pm | |
index 9aafea0..18e7726 100644 | |
--- a/lib/Mojo/IOLoop.pm | |
+++ b/lib/Mojo/IOLoop.pm | |
@@ -320,11 +320,12 @@ right in, to make writing test servers as easy as possible | |
convenience the C<PIPE> signal will be set to C<IGNORE> when L<Mojo::IOLoop> | |
is loaded. | |
-For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS | |
-support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+) and | |
-L<IO::Socket::SSL> (1.84+) will be used automatically if they are installed. | |
-Individual features can also be disabled with the C<MOJO_NO_IPV6> and | |
-C<MOJO_NO_TLS> environment variables. | |
+For better scalability (epoll, kqueue) and to provide IPv6, SOCKS as well as | |
+TLS support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+), | |
+L<IO::Socket::Socks> (0.63+) and L<IO::Socket::SSL> (1.84+) will be used | |
+automatically if they are installed. Individual features can also be disabled | |
+with the C<MOJO_NO_IPV6>, C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment | |
+variables. | |
See L<Mojolicious::Guides::Cookbook/"REAL-TIME WEB"> for more. | |
diff --git a/lib/Mojo/IOLoop/Client.pm b/lib/Mojo/IOLoop/Client.pm | |
index 01f79d1..7cac13b 100644 | |
--- a/lib/Mojo/IOLoop/Client.pm | |
+++ b/lib/Mojo/IOLoop/Client.pm | |
@@ -19,6 +19,13 @@ use constant TLS => $ENV{MOJO_NO_TLS} | |
use constant TLS_READ => TLS ? IO::Socket::SSL::SSL_WANT_READ() : 0; | |
use constant TLS_WRITE => TLS ? IO::Socket::SSL::SSL_WANT_WRITE() : 0; | |
+# SOCKS support requires IO::Socket::Socks | |
+use constant SOCKS => $ENV{MOJO_NO_SOCKS} | |
+ ? 0 | |
+ : eval 'use IO::Socket::Socks 0.63 (); 1'; | |
+use constant SOCKS_READ => SOCKS ? IO::Socket::Socks::SOCKS_WANT_READ() : 0; | |
+use constant SOCKS_WRITE => SOCKS ? IO::Socket::Socks::SOCKS_WANT_WRITE() : 0; | |
+ | |
has reactor => sub { Mojo::IOLoop->singleton->reactor }; | |
sub DESTROY { shift->_cleanup } | |
@@ -42,12 +49,13 @@ sub _connect { | |
my $handle; | |
my $reactor = $self->reactor; | |
- my $address = $args->{address} ||= 'localhost'; | |
+ my $address = $args->{socks_address} || ($args->{address} ||= 'localhost'); | |
+ my $port = $args->{socks_port} || $args->{port} || ($args->{tls} ? 443 : 80); | |
unless ($handle = $self->{handle} = $args->{handle}) { | |
my %options = ( | |
Blocking => 0, | |
PeerAddr => $address eq 'localhost' ? '127.0.0.1' : $address, | |
- PeerPort => $args->{port} || ($args->{tls} ? 443 : 80) | |
+ PeerPort => $port | |
); | |
$options{LocalAddr} = $args->{local_address} if $args->{local_address}; | |
$options{PeerAddr} =~ s/[\[\]]//g if $options{PeerAddr}; | |
@@ -63,7 +71,37 @@ sub _connect { | |
# Wait for handle to become writable | |
weaken $self; | |
- $reactor->io($handle => sub { $self->_try($args) })->watch($handle, 0, 1); | |
+ $reactor->io($handle => sub { $self->_ready($args) })->watch($handle, 0, 1); | |
+} | |
+ | |
+sub _ready { | |
+ my ($self, $args) = @_; | |
+ | |
+ # Retry or handle exceptions | |
+ my $handle = $self->{handle}; | |
+ return $! == EINPROGRESS ? undef : $self->emit(error => $!) | |
+ if $handle->isa('IO::Socket::IP') && !$handle->connect; | |
+ return $self->emit(error => $! = $handle->sockopt(SO_ERROR)) | |
+ unless $handle->connected; | |
+ | |
+ # Disable Nagle's algorithm | |
+ setsockopt $handle, IPPROTO_TCP, TCP_NODELAY, 1; | |
+ | |
+ $self->_try_socks($args); | |
+} | |
+ | |
+sub _socks { | |
+ my ($self, $args) = @_; | |
+ | |
+ # Connected | |
+ my $handle = $self->{handle}; | |
+ return $self->_try_tls($args) if $handle->ready; | |
+ | |
+ # Switch between reading and writing | |
+ my $err = $IO::Socket::Socks::SOCKS_ERROR; | |
+ if ($err == SOCKS_READ) { $self->reactor->watch($handle, 1, 0) } | |
+ elsif ($err == SOCKS_WRITE) { $self->reactor->watch($handle, 1, 1) } | |
+ else { $self->emit(error => $err) } | |
} | |
sub _tls { | |
@@ -80,19 +118,28 @@ sub _tls { | |
elsif ($err == TLS_WRITE) { $self->reactor->watch($handle, 1, 1) } | |
} | |
-sub _try { | |
+sub _try_socks { | |
my ($self, $args) = @_; | |
- # Retry or handle exceptions | |
my $handle = $self->{handle}; | |
- return $! == EINPROGRESS ? undef : $self->emit(error => $!) | |
- if $handle->isa('IO::Socket::IP') && !$handle->connect; | |
- return $self->emit(error => $! = $handle->sockopt(SO_ERROR)) | |
- unless $handle->connected; | |
+ return $self->_try_tls($args) unless $args->{socks_address}; | |
+ return $self->emit( | |
+ error => 'IO::Socket::Socks 0.63 required for SOCKS support') | |
+ unless SOCKS; | |
- # Disable Nagle's algorithm | |
- setsockopt $handle, IPPROTO_TCP, TCP_NODELAY, 1; | |
+ my %options | |
+ = (ConnectAddr => $args->{address}, ConnectPort => $args->{port}); | |
+ my $reactor = $self->reactor; | |
+ $reactor->remove($handle); | |
+ return $self->emit(error => 'SOCKS upgrade failed') | |
+ unless IO::Socket::Socks->start_SOCKS($handle, %options); | |
+ $reactor->io($handle => sub { $self->_socks($args) })->watch($handle, 0, 1); | |
+} | |
+ | |
+sub _try_tls { | |
+ my ($self, $args) = @_; | |
+ my $handle = $self->{handle}; | |
return $self->_cleanup->emit_safe(connect => $handle) | |
if !$args->{tls} || $handle->isa('IO::Socket::SSL'); | |
return $self->emit(error => 'IO::Socket::SSL 1.84 required for TLS support') | |
@@ -115,7 +162,7 @@ sub _try { | |
my $reactor = $self->reactor; | |
$reactor->remove($handle); | |
return $self->emit(error => 'TLS upgrade failed') | |
- unless $handle = IO::Socket::SSL->start_SSL($handle, %options); | |
+ unless IO::Socket::SSL->start_SSL($handle, %options); | |
$reactor->io($handle => sub { $self->_tls })->watch($handle, 0, 1); | |
} | |
@@ -225,6 +272,18 @@ Local address to bind to. | |
Port to connect to, defaults to C<80> or C<443> with C<tls> option. | |
+=item socks_address | |
+ | |
+ socks_address => '127.0.0.1' | |
+ | |
+Address or host name of SOCKS proxy server to use for connection. | |
+ | |
+=item socks_port | |
+ | |
+ socks_port => 9050 | |
+ | |
+Port of SOCKS proxy server to use for connection. | |
+ | |
=item timeout | |
timeout => 15 | |
diff --git a/lib/Mojo/Message/Request.pm b/lib/Mojo/Message/Request.pm | |
index 264fc0d..5ba8583 100644 | |
--- a/lib/Mojo/Message/Request.pm | |
+++ b/lib/Mojo/Message/Request.pm | |
@@ -101,15 +101,15 @@ sub get_start_line_chunk { | |
# CONNECT | |
my $method = uc $self->method; | |
+ my $proxy = $self->proxy; | |
if ($method eq 'CONNECT') { | |
my $port = $url->port || ($url->protocol eq 'https' ? '443' : '80'); | |
$path = $url->ihost . ":$port"; | |
} | |
# Proxy | |
- elsif ($self->proxy) { | |
- $path = $url->clone->userinfo(undef) | |
- unless $self->is_handshake || $url->protocol eq 'https'; | |
+ elsif ($proxy && $proxy->protocol ne 'socks' && !$self->is_handshake) { | |
+ $path = $url->clone->userinfo(undef) unless $url->protocol eq 'https'; | |
} | |
$self->{start_buffer} = "$method $path HTTP/@{[$self->version]}\x0d\x0a"; | |
diff --git a/lib/Mojo/Server/Daemon.pm b/lib/Mojo/Server/Daemon.pm | |
index 471f7f4..b05a477 100644 | |
--- a/lib/Mojo/Server/Daemon.pm | |
+++ b/lib/Mojo/Server/Daemon.pm | |
@@ -265,11 +265,12 @@ HTTP and WebSocket server, with IPv6, TLS, Comet (long pol | |
connection pooling, timeout, cookie, multipart and multiple event loop | |
support. | |
-For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS | |
-support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+) and | |
-L<IO::Socket::SSL> (1.84+) will be used automatically by L<Mojo::IOLoop> if | |
-they are installed. Individual features can also be disabled with the | |
-C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables. | |
+For better scalability (epoll, kqueue) and to provide IPv6, SOCKS as well as | |
+TLS support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+), | |
+L<IO::Socket::Socks> (0.63+) and L<IO::Socket::SSL> (1.84+) will be used | |
+automatically if they are installed. Individual features can also be disabled | |
+with the C<MOJO_NO_IPV6>, C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment | |
+variables. | |
See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more. | |
diff --git a/lib/Mojo/Server/Hypnotoad.pm b/lib/Mojo/Server/Hypnotoad.pm | |
index df71b51..a5100ae 100644 | |
--- a/lib/Mojo/Server/Hypnotoad.pm | |
+++ b/lib/Mojo/Server/Hypnotoad.pm | |
@@ -172,11 +172,12 @@ You can run the same command again for automatic hot deplo | |
This second invocation will load the application again, detect the process id | |
file with it, and send a L</"USR2"> signal to the already running server. | |
-For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS | |
-support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+) and | |
-L<IO::Socket::SSL> (1.84+) will be used automatically by L<Mojo::IOLoop> if | |
-they are installed. Individual features can also be disabled with the | |
-C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables. | |
+For better scalability (epoll, kqueue) and to provide IPv6, SOCKS as well as | |
+TLS support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+), | |
+L<IO::Socket::Socks> (0.63+) and L<IO::Socket::SSL> (1.84+) will be used | |
+automatically if they are installed. Individual features can also be disabled | |
+with the C<MOJO_NO_IPV6>, C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment | |
+variables. | |
See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more. | |
diff --git a/lib/Mojo/Server/Morbo.pm b/lib/Mojo/Server/Morbo.pm | |
index 6bffa34..2badabf 100644 | |
--- a/lib/Mojo/Server/Morbo.pm | |
+++ b/lib/Mojo/Server/Morbo.pm | |
@@ -130,11 +130,12 @@ To start applications with it you can use the L<morbo> scr | |
$ morbo myapp.pl | |
Server available at http://127.0.0.1:3000. | |
-For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS | |
-support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+) and | |
-L<IO::Socket::SSL> (1.84+) will be used automatically by L<Mojo::IOLoop> if | |
-they are installed. Individual features can also be disabled with the | |
-C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables. | |
+For better scalability (epoll, kqueue) and to provide IPv6, SOCKS as well as | |
+TLS support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+), | |
+L<IO::Socket::Socks> (0.63+) and L<IO::Socket::SSL> (1.84+) will be used | |
+automatically if they are installed. Individual features can also be disabled | |
+with the C<MOJO_NO_IPV6>, C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment | |
+variables. | |
See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more. | |
diff --git a/lib/Mojo/Server/Prefork.pm b/lib/Mojo/Server/Prefork.pm | |
index 36638a2..9333e43 100644 | |
--- a/lib/Mojo/Server/Prefork.pm | |
+++ b/lib/Mojo/Server/Prefork.pm | |
@@ -261,11 +261,12 @@ keep-alive, connection pooling, timeout, cookie, multipart | |
loop support. Note that the server uses signals for process management, so you | |
should avoid modifying signal handlers in your applications. | |
-For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS | |
-support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+) and | |
-L<IO::Socket::SSL> (1.84+) will be used automatically by L<Mojo::IOLoop> if | |
-they are installed. Individual features can also be disabled with the | |
-C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables. | |
+For better scalability (epoll, kqueue) and to provide IPv6, SOCKS as well as | |
+TLS support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+), | |
+L<IO::Socket::Socks> (0.63+) and L<IO::Socket::SSL> (1.84+) will be used | |
+automatically if they are installed. Individual features can also be disabled | |
+with the C<MOJO_NO_IPV6>, C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment | |
+variables. | |
See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more. | |
diff --git a/lib/Mojo/UserAgent.pm b/lib/Mojo/UserAgent.pm | |
index 290b28a..f7768f9 100644 | |
--- a/lib/Mojo/UserAgent.pm | |
+++ b/lib/Mojo/UserAgent.pm | |
@@ -85,21 +85,24 @@ sub _cleanup { | |
} | |
sub _connect { | |
- my ($self, $nb, $proto, $host, $port, $handle, $cb) = @_; | |
+ my ($self, $nb, $tx, $proto, $host, $port, $handle, $cb) = @_; | |
+ | |
+ my $options | |
+ = {address => $host, port => $port, timeout => $self->connect_timeout}; | |
+ if (my $local = $self->local_address) { $options->{local_address} = $local } | |
+ $options->{handle} = $handle if $handle; | |
+ if ($proto eq 'socks') { | |
+ @$options{qw(socks_address socks_port)} = @$options{qw(address port)}; | |
+ ($proto, @$options{qw(address port)}) = $self->transactor->endpoint($tx); | |
+ } | |
+ if ($options->{tls} = $proto eq 'https') { | |
+ $options->{"tls_$_"} = $self->$_ for qw(ca cert key); | |
+ } | |
weaken $self; | |
my $id; | |
return $id = $self->_loop($nb)->client( | |
- address => $host, | |
- handle => $handle, | |
- local_address => $self->local_address, | |
- port => $port, | |
- timeout => $self->connect_timeout, | |
- tls => $proto eq 'https', | |
- tls_ca => $self->ca, | |
- tls_cert => $self->cert, | |
- tls_key => $self->key, | |
- sub { | |
+ $options => sub { | |
my ($loop, $err, $stream) = @_; | |
# Connection error | |
@@ -143,7 +146,7 @@ sub _connect_proxy { | |
my $handle = $loop->stream($id)->steal_handle; | |
my $c = delete $self->{connections}{$id}; | |
$loop->remove($id); | |
- $id = $self->_connect($nb, $self->transactor->endpoint($old), | |
+ $id = $self->_connect($nb, $old, $self->transactor->endpoint($old), | |
$handle, sub { shift->_start($nb, $old->connection($id), $cb) }); | |
$self->{connections}{$id} = $c; | |
} | |
@@ -191,7 +194,7 @@ sub _connection { | |
# Connect | |
warn "-- Connect ($proto:$host:$port)\n" if DEBUG; | |
($proto, $host, $port) = $self->transactor->peer($tx); | |
- $id = $self->_connect(($nb, $proto, $host, $port, $id) => \&_connected); | |
+ $id = $self->_connect(($nb, $tx, $proto, $host, $port, $id) => \&_connected); | |
$self->{connections}{$id} = {cb => $cb, nb => $nb, tx => $tx}; | |
return $id; | |
@@ -424,19 +427,20 @@ Mojo::UserAgent - Non-blocking I/O HTTP and WebSocket user | |
=head1 DESCRIPTION | |
L<Mojo::UserAgent> is a full featured non-blocking I/O HTTP and WebSocket user | |
-agent, with IPv6, TLS, SNI, IDNA, Comet (long polling), keep-alive, connection | |
-pooling, timeout, cookie, multipart, proxy, gzip compression and multiple | |
-event loop support. | |
+agent, with IPv6, SOCKS, TLS, SNI, IDNA, Comet (long polling), keep-alive, | |
+connection pooling, timeout, cookie, multipart, proxy, gzip compression and | |
+multiple event loop support. | |
All connections will be reset automatically if a new process has been forked, | |
this allows multiple processes to share the same L<Mojo::UserAgent> object | |
safely. | |
-For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS | |
-support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+) and | |
-L<IO::Socket::SSL> (1.84+) will be used automatically by L<Mojo::IOLoop> if | |
-they are installed. Individual features can also be disabled with the | |
-C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables. | |
+For better scalability (epoll, kqueue) and to provide IPv6, SOCKS as well as | |
+TLS support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+), | |
+L<IO::Socket::Socks> (0.63+) and L<IO::Socket::SSL> (1.84+) will be used | |
+automatically if they are installed. Individual features can also be disabled | |
+with the C<MOJO_NO_IPV6>, C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment | |
+variables. | |
See L<Mojolicious::Guides::Cookbook/"USER AGENT"> for more. | |
@@ -580,6 +584,9 @@ Proxy manager, defaults to a L<Mojo::UserAgent::Proxy> objec | |
# Detect proxy servers from environment | |
$ua->proxy->detect; | |
+ # Manually configure Tor | |
+ $ua->proxy->http('socks://127.0.0.1:9050')->https('socks://127.0.0.1:9050'); | |
+ | |
=head2 request_timeout | |
my $timeout = $ua->request_timeout; | |
diff --git a/lib/Mojo/UserAgent/Transactor.pm b/lib/Mojo/UserAgent/Transactor.pm | |
index 15740c4..2a90369 100644 | |
--- a/lib/Mojo/UserAgent/Transactor.pm | |
+++ b/lib/Mojo/UserAgent/Transactor.pm | |
@@ -50,6 +50,7 @@ sub proxy_connect { | |
# No proxy | |
return undef unless my $proxy = $req->proxy; | |
+ return undef if $proxy->protocol eq 'socks'; | |
# WebSocket and/or HTTPS | |
my $url = $req->url; | |
diff --git a/lib/Mojolicious/Command/version.pm b/lib/Mojolicious/Command/versio | |
index 56c1612..ec54e97 100644 | |
--- a/lib/Mojolicious/Command/version.pm | |
+++ b/lib/Mojolicious/Command/version.pm | |
@@ -1,7 +1,7 @@ | |
package Mojolicious::Command::version; | |
use Mojo::Base 'Mojolicious::Command'; | |
-use Mojo::IOLoop::Server; | |
+use Mojo::IOLoop::Client; | |
use Mojo::UserAgent; | |
use Mojolicious; | |
@@ -11,11 +11,11 @@ has usage => sub { shift->extract_usage }; | |
sub run { | |
my $self = shift; | |
- my $ev = eval 'use Mojo::Reactor::EV; 1' ? $EV::VERSION : 'not installed'; | |
- my $ipv6 | |
- = Mojo::IOLoop::Server::IPV6 ? $IO::Socket::IP::VERSION : 'not installed'; | |
- my $tls | |
- = Mojo::IOLoop::Server::TLS ? $IO::Socket::SSL::VERSION : 'not installed'; | |
+ my $ev = eval 'use Mojo::Reactor::EV; 1' ? $EV::VERSION : 'not installed'; | |
+ my $class = 'Mojo::IOLoop::Client'; | |
+ my $ipv6 = $class->IPV6 ? $IO::Socket::IP::VERSION : 'not installed'; | |
+ my $socks = $class->SOCKS ? $IO::Socket::Socks::VERSION : 'not installed'; | |
+ my $tls = $class->TLS ? $IO::Socket::SSL::VERSION : 'not installed'; | |
print <<EOF; | |
CORE | |
@@ -23,9 +23,10 @@ CORE | |
Mojolicious ($Mojolicious::VERSION, $Mojolicious::CODENAME) | |
OPTIONAL | |
- EV 4.0+ ($ev) | |
- IO::Socket::IP 0.20+ ($ipv6) | |
- IO::Socket::SSL 1.84+ ($tls) | |
+ EV 4.0+ ($ev) | |
+ IO::Socket::IP 0.20+ ($ipv6) | |
+ IO::Socket::Socks 0.63+ ($ipv6) | |
+ IO::Socket::SSL 1.84+ ($tls) | |
EOF | |
diff --git a/t/mojo/transactor.t b/t/mojo/transactor.t | |
index 9710c45..197d516 100644 | |
--- a/t/mojo/transactor.t | |
+++ b/t/mojo/transactor.t | |
@@ -494,6 +494,9 @@ is $tx->req->headers->host, 'mojolicio.us', 'right "Host" he | |
is $t->proxy_connect($tx), undef, 'already a CONNECT request'; | |
$tx->req->method('Connect'); | |
is $t->proxy_connect($tx), undef, 'already a CONNECT request'; | |
+$tx = $t->tx(GET => 'http://mojolicio.us'); | |
+$tx->req->proxy('socks://127.0.0.1:3000'); | |
+is $t->proxy_connect($tx), undef, 'using a SOCKS proxy'; | |
# Simple 301 redirect | |
$tx = $t->tx( | |
diff --git a/t/mojo/user_agent.t b/t/mojo/user_agent.t | |
index a0dce4c..1b44c97 100644 | |
--- a/t/mojo/user_agent.t | |
+++ b/t/mojo/user_agent.t | |
@@ -1,7 +1,7 @@ | |
use Mojo::Base -strict; | |
BEGIN { | |
- $ENV{MOJO_NO_IPV6} = $ENV{MOJO_NO_TLS} = 1; | |
+ $ENV{MOJO_NO_IPV6} = $ENV{MOJO_NO_SOCKS} = $ENV{MOJO_NO_TLS} = 1; | |
$ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll'; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment