Yes, It seems my problem described incorrectly. I tried not to go into details.
Ok, here it is...
I am trying to develop module IO::Socket::Socks::Wrapper, just for fun and skills. And recently found that not all modules could be wrapped correctly with current version.
For example:
use IO::Socket::Socks::Wrapper (
Net::HTTPS => {
ProxyAddr => 'localhost',
ProxyPort => 1080,
SocksDebug => 1,
SocksVersion => 5
}
);
use Net::HTTPS;
my $s = Net::HTTPS->new(Host => "encrypted.google.com") || die $@;
$s->write_request(GET => "/", 'User-Agent' => "Mozilla/5.0");
my($code, $mess, %h) = $s->read_response_headers;
while(1) {
my $buf;
my $n = $s->read_entity_body($buf, 1024);
die "read failed: $!" unless defined $n;
last unless $n;
print $buf;
}
This doesn't work because Net::HTTPS inherits from Net::SSL and then only Net::SSL inherits from IO::Socket::INET. So, when we exports our connect function to Net::HTTPS it will be called instead of Net::SSL::connect. But Net::SSL::connect has some stuff for creating encription. So creating of the socket will fail.
But, this will work with Net::HTTP, because it directly inherits from IO::Socket::INET.
I need to solve this problem. And it seems advice about local() from Javafan helped me. Here below is what I got and it seems it works. However I need more tests. If someone can tell me less ugly way to do it, you are welcome.
package IO::Socket::Socks::Wrapper;
use strict;
use Socket;
use base 'Exporter';
our $VERSION = 0.01;
our @EXPORT_OK = 'connect';
sub import
{
my $pkg = shift;
while(my ($module, $cfg) = splice @_, 0, 2) {
unless(defined $cfg) {
$cfg = $module;
$module = undef;
}
if($module) {
# override connect() in the package
eval "require $module"
or die $@;
if($module->isa('IO::Socket::INET')) {
*connect = sub(*$) {
local(*IO::Socket::INET::connect) = sub(*$) {
_connect(@_, $cfg);
};
my $self = shift;
my $ref = ref($self);
no strict 'refs';
foreach my $parent (@{$ref.'::ISA'}) {
if($parent->isa('IO::Socket::INET')) {
bless $self, $parent;
$self->connect(@_);
bless $self, $ref;
return $self;
}
}
}
}
else {
*connect = sub(*$) {
_connect(@_, $cfg);
}
}
$pkg->export($module, 'connect');
}
else {
# override connect() globally
*connect = sub(*$) {
_connect(@_, $cfg);
};
$pkg->export('CORE::GLOBAL', 'connect');
}
}
}
sub _connect
{
my ($socket, $name, $cfg) = @_;
my $ref = ref($socket);
return CORE::connect( $socket, $name )
if (($ref && $socket->isa('IO::Socket::Socks')) || !$cfg);
my ($port, $host) = sockaddr_in($name);
$host = inet_ntoa($host);
# global overriding will not work with `use' pragma
require IO::Socket::Socks;
IO::Socket::Socks->new_from_socket(
$socket,
ConnectAddr => $host,
ConnectPort => $port,
%$cfg
) or return;
bless $socket, $ref
if $ref && $ref ne 'GLOB';
}
1;
|