1 |
#!/opt/bin/perl |
2 |
|
3 |
use common::sense; |
4 |
use Compress::LZF; |
5 |
use EV; |
6 |
use AnyEvent::HTTP; |
7 |
use BDB; |
8 |
use Coro; |
9 |
use Coro::BDB; |
10 |
use AnyEvent::DNS; |
11 |
use Set::IntSpan; |
12 |
use CBOR::XS; |
13 |
use Compress::Zlib (); |
14 |
use Compress::Raw::Zlib (); |
15 |
use AnyEvent::TLS; |
16 |
|
17 |
BDB::min_parallel 8; |
18 |
|
19 |
our @TPB = qw(thepiratebay.la); |
20 |
our $TPB = $TPB[0]; |
21 |
|
22 |
our $TODAY = int time / 86400; |
23 |
|
24 |
#our $TPB_SWITCHER = AE::timer 1, 0.02, sub { |
25 |
# state $i; |
26 |
# $TPB = $TPB[++$i % @TPB]; |
27 |
#}; |
28 |
|
29 |
############################################################################# |
30 |
|
31 |
$AnyEvent::HTTP::MAX_PER_HOST = |
32 |
$AnyEvent::HTTP::MAX_PERSISTENT_PER_HOST = 400; |
33 |
|
34 |
our %stat; |
35 |
|
36 |
our $statprint = AE::timer 1, 1, sub { |
37 |
syswrite STDOUT, "\r" . (join " ", map "$_=$stat{$_}", sort keys %stat) . " " |
38 |
if $ENV{VERBOSE}; |
39 |
}; |
40 |
|
41 |
our @DICTIONARY = (do { |
42 |
open my $fh, "<:perlio", "dictionary0" |
43 |
or die "dictionary0: $!"; |
44 |
local $/; |
45 |
<$fh> |
46 |
}); |
47 |
|
48 |
############################################################################# |
49 |
|
50 |
our $db_env = db_env_create; |
51 |
|
52 |
$db_env->set_flags (BDB::AUTO_COMMIT | BDB::TXN_WRITE_NOSYNC, 1); |
53 |
$db_env->log_set_config (BDB::LOG_AUTO_REMOVE); |
54 |
$db_env->set_cachesize (0, 8 * 1024 * 1024); |
55 |
$db_env->set_lk_max_lockers (20480); |
56 |
$db_env->set_lk_max_locks (20480); |
57 |
$db_env->set_lk_max_objects (20480); |
58 |
|
59 |
mkdir "db", 0777; |
60 |
db_env_open |
61 |
$db_env, |
62 |
"db", |
63 |
BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL |
64 |
| BDB::INIT_TXN | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE, |
65 |
0666; |
66 |
|
67 |
our %DB; |
68 |
|
69 |
sub table($) { |
70 |
my $db = $DB{$_[0]} = db_create $db_env; |
71 |
db_open $db, undef, $_[0], undef, BDB::BTREE, BDB::AUTO_COMMIT | BDB::CREATE, 0666; |
72 |
die if $!; |
73 |
$db |
74 |
} |
75 |
|
76 |
our $db_state = table "state"; # global state |
77 |
our $db_info = table "info"; # per torrent state |
78 |
|
79 |
our $TXN; |
80 |
|
81 |
sub get($$) { |
82 |
db_get $_[0], $TXN, $_[1], my $data, 0; |
83 |
$data # will be undef on error |
84 |
} |
85 |
|
86 |
sub put($$$) { |
87 |
db_put $_[0], $TXN, $_[1], $_[2], 0; |
88 |
} |
89 |
|
90 |
sub del($$) { |
91 |
db_del $_[0], $TXN, $_[1], 0, sub { }; |
92 |
} |
93 |
|
94 |
sub sget($) { |
95 |
get $db_state, $_[0] |
96 |
} |
97 |
|
98 |
sub sput($$) { |
99 |
put $db_state, $_[0], $_[1]; |
100 |
} |
101 |
|
102 |
sub iget($) { |
103 |
my $data = get $db_info, $_[0]; |
104 |
length $data ? decode_cbor $data : [] |
105 |
} |
106 |
|
107 |
sub iput($$) { |
108 |
put $db_info, $_[0], encode_cbor $_[1]; |
109 |
} |
110 |
|
111 |
our $syncer = AE::timer 5, 5, sub { |
112 |
db_env_txn_checkpoint $db_env, 0, 0, 0, sub { }; |
113 |
}; |
114 |
|
115 |
sub dbsync() { |
116 |
db_env_txn_checkpoint $db_env; |
117 |
} |
118 |
|
119 |
sub dbflush { |
120 |
return if $::dbflush_called++; |
121 |
db_env_txn_checkpoint $db_env, sub { }; |
122 |
BDB::set_sync_prepare undef; |
123 |
db_close $_ for values %DB; |
124 |
db_env_txn_checkpoint $db_env; |
125 |
db_env_close $db_env; |
126 |
} |
127 |
|
128 |
END { |
129 |
dbflush; |
130 |
} |
131 |
|
132 |
sub quit { |
133 |
EV::unloop; |
134 |
#exit 1; |
135 |
}; |
136 |
|
137 |
our $sigint = AE::signal INT => \&quit; |
138 |
our $sigterm = AE::signal TERM => \&quit; |
139 |
|
140 |
sub dic_compress($$) { |
141 |
my $d = new Compress::Raw::Zlib::Deflate |
142 |
-Level => 9, |
143 |
-WindowBits => -15, |
144 |
-Dictionary => $DICTIONARY[$_[0]], |
145 |
-AppendOutput => 1, |
146 |
; |
147 |
|
148 |
my $o; |
149 |
|
150 |
$d->deflate ($_[1], $o); |
151 |
$d->flush ($o); |
152 |
|
153 |
$o |
154 |
} |
155 |
|
156 |
sub dic_decompress($$) { |
157 |
my $i = new Compress::Raw::Zlib::Inflate |
158 |
-WindowBits => -15, |
159 |
-Dictionary => $DICTIONARY[$_[0]], |
160 |
-AppendOutput => 1, |
161 |
-ConsumeInput => 0, |
162 |
; |
163 |
|
164 |
$i->inflate ($_[1], my $o, 1); |
165 |
|
166 |
$o |
167 |
} |
168 |
|
169 |
############################################################################# |
170 |
|
171 |
############################################################################# |
172 |
|
173 |
sub GET { |
174 |
my ($data, $hdr); |
175 |
|
176 |
for (1..10) { |
177 |
http_get $_[0], |
178 |
proxy => undef, |
179 |
recurse => 0, |
180 |
headers => { |
181 |
# "user-agent" => "Mozilla/5.0 (X11; Linux x86_64; rv:20.0) Gecko/20100101 Firefox/20.0 Iceweasel/20.0", |
182 |
"referer" => undef, |
183 |
}, |
184 |
Coro::rouse_cb; |
185 |
($data, $hdr) = Coro::rouse_wait; |
186 |
|
187 |
$hdr->{Status} = 580 |
188 |
if $data =~ /^(?:Upgrading software|Could not connect to caching server)/; |
189 |
|
190 |
$hdr->{Status} = 581 |
191 |
unless length $data; |
192 |
|
193 |
$data = Compress::Zlib::memGunzip $data |
194 |
if $hdr->{"content-encoding"} eq "gzip"; |
195 |
|
196 |
last |
197 |
if $hdr->{Status} < 500; |
198 |
|
199 |
Coro::AnyEvent::sleep 0.5; |
200 |
++$stat{"retry-$hdr->{Status}"}; |
201 |
} |
202 |
|
203 |
++$stat{$hdr->{Status}}; |
204 |
($data, $hdr) |
205 |
} |
206 |
|
207 |
1 |
208 |
|