ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-FCP/FCP.pm
(Generate patch)

Comparing AnyEvent-FCP/FCP.pm (file contents):
Revision 1.2 by root, Sat Jul 25 06:28:49 2009 UTC vs.
Revision 1.6 by root, Mon May 31 06:27:37 2010 UTC

2 2
3AnyEvent::FCP - freenet client protocol 2.0 3AnyEvent::FCP - freenet client protocol 2.0
4 4
5=head1 SYNOPSIS 5=head1 SYNOPSIS
6 6
7 use AnyEvent::FCP; 7 use AnyEvent::FCP;
8 8
9 my $fcp = new AnyEvent::FCP; 9 my $fcp = new AnyEvent::FCP;
10 10
11 my $ni = $fcp->txn_node_info->result; 11# transactions return condvars
12 my $ni = $fcp->node_info; 12 my $lp_cv = $fcp->list_peers;
13 my $pr_cv = $fcp->list_persistent_requests;
14
15 my $peers = $lp_cv->recv;
16 my $reqs = $pr_cv->recv;
13 17
14=head1 DESCRIPTION 18=head1 DESCRIPTION
15 19
16This module implements the freenet client protocol version 2.0, as used by 20This module implements the freenet client protocol version 2.0, as used by
17freenet 0.7. See L<Net::FCP> for the earlier freenet 0.5 version. 21freenet 0.7. See L<Net::FCP> for the earlier freenet 0.5 version.
18 22
19See L<http://wiki.freenetproject.org/FreenetFCPSpec2Point0> for a description 23See L<http://wiki.freenetproject.org/FreenetFCPSpec2Point0> for a
20of what the messages do. 24description of what the messages do.
21 25
22The module uses L<AnyEvent> to find a suitable event module. 26The module uses L<AnyEvent> to find a suitable event module.
23 27
28Only very little is implemented, ask if you need more, and look at the
29example program later in this section.
30
24=head2 IMPORT TAGS 31=head2 IMPORT TAGS
25 32
26Nothing much can be "imported" from this module right now. 33Nothing much can be "imported" from this module right now.
27 34
28=head2 FREENET BASICS 35=head2 THE AnyEvent::FCP CLASS
29
30Ok, this section will not explain any freenet basics to you, just some
31problems I found that you might want to avoid:
32 36
33=over 4 37=over 4
34 38
35=item freenet URIs are _NOT_ URIs
36
37Whenever a "uri" is required by the protocol, freenet expects a kind of
38URI prefixed with the "freenet:" scheme, e.g. "freenet:CHK...". However,
39these are not URIs, as freeent fails to parse them correctly, that is, you
40must unescape an escaped characters ("%2c" => ",") yourself. Maybe in the
41future this library will do it for you, so watch out for this incompatible
42change.
43
44=back
45
46=head2 THE AnyEvent::FCP CLASS
47
48=over 4
49
50=cut 39=cut
51 40
52package AnyEvent::FCP; 41package AnyEvent::FCP;
53 42
54use common::sense; 43use common::sense;
55 44
56use Carp; 45use Carp;
57 46
58our $VERSION = '0.1'; 47our $VERSION = '0.21';
59 48
60use Scalar::Util (); 49use Scalar::Util ();
61 50
62use AnyEvent; 51use AnyEvent;
63use AnyEvent::Handle; 52use AnyEvent::Handle;
64 53
65sub touc($) { 54sub touc($) {
66 local $_ = shift; 55 local $_ = shift;
67 1 while s/((?:^|_)(?:svk|chk|uri|fcp)(?:_|$))/\U$1/; 56 1 while s/((?:^|_)(?:svk|chk|uri|fcp|ds|mime)(?:_|$))/\U$1/;
68 s/(?:^|_)(.)/\U$1/g; 57 s/(?:^|_)(.)/\U$1/g;
69 $_ 58 $_
70} 59}
71 60
72sub tolc($) { 61sub tolc($) {
73 local $_ = shift; 62 local $_ = shift;
74 1 while s/(SVK|CHK|URI|FCP)([^_])/$1\_$2/i; 63 1 while s/(SVK|CHK|URI|FCP|DS|MIME)([^_])/$1\_$2/i;
75 1 while s/([^_])(SVK|CHK|URI|FCP)/$1\_$2/i; 64 1 while s/([^_])(SVK|CHK|URI|FCP|DS|MIME)/$1\_$2/i;
76 s/(?<=[a-z])(?=[A-Z])/_/g; 65 s/(?<=[a-z])(?=[A-Z])/_/g;
77 lc 66 lc
78} 67}
79 68
80=item $fcp = new AnyEvent::FCP [host => $host][, port => $port][, progress => \&cb][, name => $name] 69=item $fcp = new AnyEvent::FCP [host => $host][, port => $port][, progress => \&cb][, name => $name]
81 70
82Create a new FCP connection to the given host and port (default 71Create a new FCP connection to the given host and port (default
83127.0.0.1:9481, or the environment variables C<FREDHOST> and C<FREDPORT>). 72127.0.0.1:9481, or the environment variables C<FREDHOST> and C<FREDPORT>).
84 73
85If no C<name> was specified, then AnyEvent::FCP will generate a (hopefully) 74If no C<name> was specified, then AnyEvent::FCP will generate a
86unique client name for you. 75(hopefully) unique client name for you.
87 76
88#TODO
89#You can install a progress callback that is being called with the AnyEvent::FCP 77You can install a progress callback that is being called with the AnyEvent::FCP
90#object, a txn object, the type of the transaction and the attributes. Use 78object, the type, a hashref with key-value pairs and a reference to any received data,
91#it like this: 79for all unsolicited messages.
92# 80
81Example:
82
93# sub progress_cb { 83 sub progress_cb {
94# my ($self, $txn, $type, $attr) = @_; 84 my ($self, $type, $kv, $rdata) = @_;
95# 85
96# warn "progress<$txn,$type," . (join ":", %$attr) . ">\n"; 86 if ($type eq "simple_progress") {
87 warn "$kv->{identifier} $kv->{succeeded}/$kv->{required}\n";
88 }
97# } 89 }
98 90
99=cut 91=cut
100 92
101sub new { 93sub new {
102 my $class = shift; 94 my $class = shift;
103 my $self = bless { @_ }, $class; 95 my $self = bless { @_ }, $class;
104 96
105 $self->{host} ||= $ENV{FREDHOST} || "127.0.0.1"; 97 $self->{host} ||= $ENV{FREDHOST} || "127.0.0.1";
106 $self->{port} ||= $ENV{FREDPORT} || 9481; 98 $self->{port} ||= $ENV{FREDPORT} || 9481;
107 $self->{name} ||= time.rand.rand.rand; # lame 99 $self->{name} ||= time.rand.rand.rand; # lame
108 $self->{timeout} ||= 600; 100 $self->{timeout} ||= 600;
101 $self->{progress} ||= sub { };
109 102
110 $self->{id} = "a0"; 103 $self->{id} = "a0";
111 104
112 { 105 {
113 Scalar::Util::weaken (my $self = $self); 106 Scalar::Util::weaken (my $self = $self);
130 name => $self->{name}, 123 name => $self->{name},
131 expected_version => "2.0", 124 expected_version => "2.0",
132 ); 125 );
133 126
134 $self 127 $self
135}
136
137sub progress {
138 my ($self, $txn, $type, $attr) = @_;
139
140 $self->{progress}->($self, $txn, $type, $attr)
141 if $self->{progress};
142} 128}
143 129
144sub send_msg { 130sub send_msg {
145 my ($self, $type, %kv) = @_; 131 my ($self, $type, %kv) = @_;
146 132
232 $self->{node_hello} = $kv; 218 $self->{node_hello} = $kv;
233 } elsif (exists $self->{id}{$kv->{identifier}}) { 219 } elsif (exists $self->{id}{$kv->{identifier}}) {
234 $self->{id}{$kv->{identifier}}($self, $type, $kv, $rdata) 220 $self->{id}{$kv->{identifier}}($self, $type, $kv, $rdata)
235 and delete $self->{id}{$kv->{identifier}}; 221 and delete $self->{id}{$kv->{identifier}};
236 } else { 222 } else {
237 # on_warn 223 &{ $self->{progress} };
238 #warn "protocol warning (unexpected $type message)\n";
239 } 224 }
240} 225}
241 226
242sub _txn { 227sub _txn {
243 my ($name, $sub) = @_; 228 my ($name, $sub) = @_;
252 splice @_, 1, 0, (my $cv = AnyEvent->condvar); 237 splice @_, 1, 0, (my $cv = AnyEvent->condvar);
253 &$sub; 238 &$sub;
254 $cv->recv 239 $cv->recv
255 }; 240 };
256} 241}
242
243=item $cv = $fcp->list_peers ([$with_metdata[, $with_volatile]])
244
245=item $peers = $fcp->list_peers_sync ([$with_metdata[, $with_volatile]])
246
247=cut
257 248
258_txn list_peers => sub { 249_txn list_peers => sub {
259 my ($self, $cv, $with_metadata, $with_volatile) = @_; 250 my ($self, $cv, $with_metadata, $with_volatile) = @_;
260 251
261 my @res; 252 my @res;
275 } 266 }
276 }, 267 },
277 ); 268 );
278}; 269};
279 270
271=item $cv = $fcp->list_peer_notes ($node_identifier)
272
273=item $notes = $fcp->list_peer_notes_sync ($node_identifier)
274
275=cut
276
280_txn list_peer_notes => sub { 277_txn list_peer_notes => sub {
281 my ($self, $cv, $node_identifier) = @_; 278 my ($self, $cv, $node_identifier) = @_;
282 279
283 $self->send_msg (list_peer_notes => 280 $self->send_msg (list_peer_notes =>
284 node_identifier => $node_identifier, 281 node_identifier => $node_identifier,
289 1 286 1
290 }, 287 },
291 ); 288 );
292}; 289};
293 290
291=item $cv = $fcp->watch_global ($enabled[, $verbosity_mask])
292
293=item $fcp->watch_global_sync ($enabled[, $verbosity_mask])
294
295=cut
296
294_txn watch_global => sub { 297_txn watch_global => sub {
295 my ($self, $cv, $enabled, $verbosity_mask) = @_; 298 my ($self, $cv, $enabled, $verbosity_mask) = @_;
296 299
297 $self->send_msg (watch_global => 300 $self->send_msg (watch_global =>
298 enabled => $enabled ? "true" : "false", 301 enabled => $enabled ? "true" : "false",
299 defined $verbosity_mask ? (verbosity_mask => $verbosity_mask+0) : (), 302 defined $verbosity_mask ? (verbosity_mask => $verbosity_mask+0) : (),
300 ); 303 );
301 304
302 $cv->(); 305 $cv->();
303}; 306};
307
308=item $cv = $fcp->list_persistent_requests
309
310=item $reqs = $fcp->list_persistent_requests_sync
311
312=cut
304 313
305_txn list_persistent_requests => sub { 314_txn list_persistent_requests => sub {
306 my ($self, $cv) = @_; 315 my ($self, $cv) = @_;
307 316
308 my %res; 317 my %res;
333 0 342 0
334 } 343 }
335 }; 344 };
336}; 345};
337 346
347=item $cv = $fcp->remove_request ($global, $identifier)
348
349=item $status = $fcp->remove_request_sync ($global, $identifier)
350
351=cut
352
338_txn remove_request => sub { 353_txn remove_request => sub {
339 my ($self, $cv, $global, $identifier) = @_; 354 my ($self, $cv, $global, $identifier) = @_;
340 355
341 $self->send_msg (remove_request => 356 $self->send_msg (remove_request =>
342 global => $global ? "true" : "false", 357 global => $global ? "true" : "false",
346 361
347 $cv->($kv); 362 $cv->($kv);
348 1 363 1
349 }, 364 },
350 ); 365 );
351
352 $cv->();
353}; 366};
367
368=item $cv = $fcp->modify_persistent_request ($global, $identifier[, $client_token[, $priority_class]])
369
370=item $sync = $fcp->modify_persistent_request_sync ($global, $identifier[, $client_token[, $priority_class]])
371
372=cut
354 373
355_txn modify_persistent_request => sub { 374_txn modify_persistent_request => sub {
356 my ($self, $cv, $global, $identifier, $client_token, $priority_class) = @_; 375 my ($self, $cv, $global, $identifier, $client_token, $priority_class) = @_;
357 376
358 $self->send_msg (modify_persistent_request => 377 $self->send_msg (modify_persistent_request =>
359 global => $global ? "true" : "false", 378 global => $global ? "true" : "false",
360 identifier => $identifier,
361 defined $client_token ? (client_token => $client_token ) : (), 379 defined $client_token ? (client_token => $client_token ) : (),
362 defined $priority_class ? (priority_class => $priority_class) : (), 380 defined $priority_class ? (priority_class => $priority_class) : (),
381 identifier => $identifier,
363 id_cb => sub { 382 id_cb => sub {
364 my ($self, $type, $kv, $rdata) = @_; 383 my ($self, $type, $kv, $rdata) = @_;
365 384
366 $cv->($kv); 385 $cv->($kv);
367 1 386 1
368 }, 387 },
369 ); 388 );
370
371 $cv->();
372}; 389};
390
391=item $cv = $fcp->get_plugin_info ($name, $detailed)
392
393=item $info = $fcp->get_plugin_info_sync ($name, $detailed)
394
395=cut
373 396
374_txn get_plugin_info => sub { 397_txn get_plugin_info => sub {
375 my ($self, $cv, $name, $detailed) = @_; 398 my ($self, $cv, $name, $detailed) = @_;
376 399
377 $self->send_msg (get_plugin_info => 400 $self->send_msg (get_plugin_info =>
382 405
383 $cv->($kv); 406 $cv->($kv);
384 1 407 1
385 }, 408 },
386 ); 409 );
410};
387 411
388 $cv->(); 412=item $cv = $fcp->client_get ($uri, $identifier, %kv)
413
414=item $status = $fcp->client_get_sync ($uri, $identifier, %kv)
415
416%kv can contain (L<http://wiki.freenetproject.org/FCP2p0ClientGet>).
417
418ignore_ds, ds_only, verbosity, max_size, max_temp_size, max_retries,
419priority_class, persistence, client_token, global, return_type,
420binary_blob, allowed_mime_types, filename, temp_filename
421
422=cut
423
424_txn client_get => sub {
425 my ($self, $cv, $uri, $identifier, %kv) = @_;
426
427 $self->send_msg (client_get =>
428 %kv,
429 uri => $uri,
430 identifier => $identifier,
431 id_cb => sub {
432 my ($self, $type, $kv, $rdata) = @_;
433
434 $cv->($kv);
435 1
436 },
437 );
389}; 438};
390 439
391=back 440=back
441
442=head1 EXAMPLE PROGRAM
443
444 use AnyEvent::FCP;
445
446 my $fcp = new AnyEvent::FCP;
447
448 # let us look at the global request list
449 $fcp->watch_global (1, 0);
450
451 # list them, synchronously
452 my $req = $fcp->list_persistent_requests_sync;
453
454 # go through all requests
455 for my $req (values %$req) {
456 # skip jobs not directly-to-disk
457 next unless $req->{return_type} eq "disk";
458 # skip jobs not issued by FProxy
459 next unless $req->{identifier} =~ /^FProxy:/;
460
461 if ($req->{data_found}) {
462 # file has been successfully downloaded
463
464 ... move the file away
465 (left as exercise)
466
467 # remove the request
468
469 $fcp->remove_request (1, $req->{identifier});
470 } elsif ($req->{get_failed}) {
471 # request has failed
472 if ($req->{get_failed}{code} == 11) {
473 # too many path components, should restart
474 } else {
475 # other failure
476 }
477 } else {
478 # modify priorities randomly, to improve download rates
479 $fcp->modify_persistent_request (1, $req->{identifier}, undef, int 6 - 5 * (rand) ** 1.7)
480 if 0.1 > rand;
481 }
482 }
483
484 # see if the dummy plugin is loaded, to ensure all previous requests have finished.
485 $fcp->get_plugin_info_sync ("dummy");
392 486
393=head1 SEE ALSO 487=head1 SEE ALSO
394 488
395L<http://wiki.freenetproject.org/FreenetFCPSpec2Point0>, L<Net::FCP>. 489L<http://wiki.freenetproject.org/FreenetFCPSpec2Point0>, L<Net::FCP>.
396 490

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines