如何使用AnyEvent::HTTP通过Socks代理并行发送HTTP GET请求
解决AnyEvent并行HTTP请求通过SOCKS代理的问题
嘿,我之前刚好折腾过类似的场景!AnyEvent自带的HTTP客户端(AnyEvent::HTTP)默认确实只支持HTTP/HTTPS代理,没法直接用SOCKS,但咱们有两种靠谱的办法绕开这个限制,亲测都能正常对接Tor的localhost:9050代理:
方案一:用AnyEvent::Socket::Socks扩展AnyEvent::HTTP
这个方案是给AnyEvent::HTTP加上SOCKS代理的底层支持,核心是用AnyEvent::Socket::Socks模块替换默认的TCP连接逻辑。
首先得安装依赖模块:
cpanm AnyEvent::Socket::Socks
然后是并行请求的示例代码:
use strict; use warnings; use AnyEvent; use AnyEvent::HTTP; use AnyEvent::Socket::Socks; # 配置SOCKS代理信息(Tor默认配置) my $socks_host = 'localhost'; my $socks_port = 9050; # 待请求的URL列表 my @urls = ( 'https://check.torproject.org/api/ip', 'https://example.com', 'https://httpbin.org/ip' ); # 创建事件循环控制器 my $cv = AnyEvent->condvar; foreach my $url (@urls) { $cv->begin; http_request GET => $url, # 自定义连接逻辑:先通过SOCKS代理握手再转发请求 connect => sub { my ($target_host, $target_port, $connect_cb) = @_; # 先连接SOCKS代理服务器 tcp_connect $socks_host, $socks_port, sub { my ($fh) = @_; unless ($fh) { $connect_cb->(); # 连接失败时传递空句柄 return; } # 发起SOCKS5握手,指定目标网站的地址 socks_handshake $fh, { host => $target_host, port => $target_port, version => 5, }, sub { my ($err) = @_; if ($err) { warn "SOCKS握手失败 ($url): $err"; close $fh; $connect_cb->(); } else { $connect_cb->($fh); # 握手成功,传递可用句柄 } }; }; }, sub { my ($body, $hdr) = @_; if ($hdr->{Status} =~ /^2/) { print "请求成功 ($url):\n$body\n\n"; } else { warn "请求失败 ($url): 状态码 $hdr->{Status}, 原因 $hdr->{Reason}\n"; } $cv->end; }; } # 等待所有请求完成 $cv->recv;
这段代码的核心是通过connect参数,把原本直接连接目标网站的逻辑,改成先连接SOCKS代理、完成握手后再转发请求。
方案二:用AnyEvent::Curl::Multi封装curl
如果觉得底层逻辑有点繁琐,咱们可以直接用AnyEvent::Curl::Multi——它封装了curl的多请求功能,而curl本身对SOCKS代理的支持非常完善,这样就能直接复用curl的能力了。
先安装依赖:
cpanm AnyEvent::Curl::Multi
示例代码如下:
use strict; use warnings; use AnyEvent; use AnyEvent::Curl::Multi; # 配置SOCKS5代理(Tor默认地址) my $proxy = 'socks5://localhost:9050'; # 待请求的URL列表 my @urls = ( 'https://check.torproject.org/api/ip', 'https://example.com', 'https://httpbin.org/ip' ); # 创建curl多请求实例 my $curl_multi = AnyEvent::Curl::Multi->new; # 创建事件循环控制器 my $cv = AnyEvent->condvar; foreach my $url (@urls) { $cv->begin; $curl_multi->add( { url => $url, # 设置curl的SOCKS代理选项 opts => { CURLOPT_PROXY => $proxy, CURLOPT_PROXYTYPE => 7, # 数值7对应CURLPROXY_SOCKS5 CURLOPT_FOLLOWLOCATION => 1, # 自动处理跳转 CURLOPT_SSL_VERIFYPEER => 0, # 测试Tor时可临时关闭证书验证(可选) }, cb => sub { my ($response, $stats) = @_; if ($response->{code} =~ /^2/) { print "请求成功 ($url):\n$response->{body}\n\n"; } else { warn "请求失败 ($url): 状态码 $response->{code}, 原因 $response->{message}\n"; } $cv->end; } } ); } # 启动请求并等待全部完成 $curl_multi->start; $cv->recv;
这个方案更省心,curl已经帮我们处理了所有SOCKS的细节,只需要设置对应的选项就行。如果需要带认证的SOCKS5代理,直接把proxy字符串改成socks5://用户名:密码@host:port即可。
验证是否生效
不管用哪种方案,都可以请求https://check.torproject.org/api/ip来验证:返回的IP应该是Tor出口节点的IP,而不是你的真实IP。
内容的提问来源于stack exchange,提问作者Derek
相关产品推荐
相关产品推荐

