AnyEvent::FCP - freenet client protocol 2.0
use AnyEvent::FCP; my $fcp = new AnyEvent::FCP; # transactions return condvars my $lp_cv = $fcp->list_peers; my $pr_cv = $fcp->list_persistent_requests; my $peers = $lp_cv->recv; my $reqs = $pr_cv->recv;
This module implements the freenet client protocol version 2.0, as used by freenet 0.7. See Net::FCP for the earlier freenet 0.5 version.
See http://wiki.freenetproject.org/FreenetFCPSpec2Point0 for a description of what the messages do.
The module uses AnyEvent to find a suitable event module.
Only very little is implemented, ask if you need more, and look at the example program later in this section.
This example fetches the download list and sets the priority of all files with "a" in their name to "emergency":
use AnyEvent::FCP; my $fcp = new AnyEvent::FCP; $fcp->watch_global (1, 0); my $req = $fcp->list_persistent_requests;
TODO for my $req (values %$req) { if ($req->{filename} =~ /a/) { $fcp->modify_persistent_request (1, $req->{identifier}, undef, 0); } }
Nothing much can be "imported" from this module right now.
Create a new FCP connection to the given host and port (default 127.0.0.1:9481, or the environment variables FREDHOST and FREDPORT).
FREDHOST
FREDPORT
If no name was specified, then AnyEvent::FCP will generate a (hopefully) unique client name for you.
name
The following methods implement various requests. Most of them map directory to the FCP message of the same name. The added benefit of these over sending requests yourself is that they handle the necessary serialisation, protocol quirks, and replies.
All of them exist in two versions, the variant shown in this manpage, and a variant with an extra _ at the end, and an extra $cb argument. The version as shown is synchronous - it will wait for any replies, and either return the reply, or croak with an error. The underscore variant returns immediately and invokes one or more callbacks or condvars later.
_
$cb
For example, the call
$info = $fcp->get_plugin_info ($name, $detailed);
Also comes in this underscore variant:
$fcp->get_plugin_info_ ($name, $detailed, $cb);
You can thinbk of the underscore as a kind of continuation indicator - the normal function waits and returns with the data, the _ indicates that you pass the continuation yourself, and the continuation will be invoked with the results.
This callback/continuation argument ($cb) can come in three forms itself:
This code reference will be invoked with the result on success. On an error, it will die (in the event loop) with a backtrace of the call site.
This is a popular choice, but it makes handling errors hard - make sure you never generate protocol errors!
AnyEvent->condvar
When a condvar is passed, it is sent ($cv->send ($results)) the results when the request has finished. Should an error occur, the error will instead result in $cv->croak ($error).
$cv->send ($results)
$cv->croak ($error)
This is also a popular choice.
[$success, $failure]
The $success callback will be invoked with the results, while the $failure callback will be invoked on any errors.
$success
$failure
undef
This is the same thing as specifying sub { } as callback, i.e. on success, the results are ignored, while on failure, you the module dies with a backtrace.
sub { }
This is good for quick scripts, or when you really aren't interested in the results.
Update either the client_token or priority_class of a request identified by $global and $identifier, depending on which of $client_token and $priority_class are not undef.
client_token
priority_class
$global
$identifier
$client_token
$priority_class
%kv can contain (http://wiki.freenetproject.org/FCP2p0ClientGet).
ignore_ds, ds_only, verbosity, max_size, max_temp_size, max_retries, priority_class, persistence, client_token, global, return_type, binary_blob, allowed_mime_types, filename, temp_filename
Remove the request with the given isdentifier. Returns true if successful, false on error.
The DDA test in FCP is probably the single most broken protocol - only one directory test can be outstanding at any time, and some guessing and heuristics are involved in mangling the paths.
This function combines TestDDARequest and TestDDAResponse in one request, handling file reading and writing as well, and tries very hard to do the right thing.
TestDDARequest
TestDDAResponse
Both $local_directory and $remote_directory must specify the same directory - $local_directory is the directory path on the client (where AnyEvent::FCP runs) and $remote_directory is the directory path on the server (where the freenet node runs). When both are running on the same node, the paths are generally identical.
$local_directory
$remote_directory
$want_read and $want_write should be set to a true value when you want to read (get) files or write (put) files, respectively.
$want_read
$want_write
On error, an exception is thrown. Otherwise, $can_read and $can_write indicate whether you can reaqd or write to freenet via the directory.
$can_read
$can_write
The AnyEvent::FCP class keeps a request cache, where it caches all information from requests.
AnyEvent::FCP
For these messages, it will store a copy of the key-value pairs, together with a type slot, in $fcp->{req}{$identifier}:
type
$fcp->{req}{$identifier}
persistent_get persistent_put persistent_put_dir
This message updates the stored data:
persistent_request_modified
This message will remove this entry:
persistent_request_removed
These messages get merged into the cache entry, under their type, i.e. a simple_progress message will be stored in $fcp->{req}{$identifier}{simple_progress}:
simple_progress
$fcp->{req}{$identifier}{simple_progress}
simple_progress # get/put uri_generated # put generated_metadata # put started_compression # put finished_compression # put put_failed # put put_fetchable # put put_successful # put sending_to_network # get compatibility_mode # get expected_hashes # get expected_mime # get expected_data_length # get get_failed # get data_found # get enter_finite_cooldown # get
In addition, an event (basically a fake message) of type request_changed is generated on every change, which will be called as $cb->($fcp, $kv, $type), where $type is the type of the original message triggering the change,
request_changed
$cb->($fcp, $kv, $type)
$type
To fill this cache with the global queue and keep it updated, call watch_global to subscribe to updates, followed by list_persistent_requests_sync.
watch_global
list_persistent_requests_sync
$fcp->watch_global_sync_; # do not wait $fcp->list_persistent_requests; # wait
To get a better idea of what is stored in the cache, here is an example of what might be stored in $fcp->{req}{"Frost-gpl.txt"}:
$fcp->{req}{"Frost-gpl.txt"}
{ identifier => "Frost-gpl.txt", uri => 'CHK@Fnx5kzdrfE,EImdzaVyEWl,AAIC--8/gpl.txt', binary_blob => "false", global => "true", max_retries => -1, max_size => 9223372036854775807, persistence => "forever", priority_class => 3, real_time => "false", return_type => "direct", started => "true", type => "persistent_get", verbosity => 2147483647, sending_to_network => { identifier => "Frost-gpl.txt", global => "true", }, compatibility_mode => { identifier => "Frost-gpl.txt", definitive => "true", dont_compress => "false", global => "true", max => "COMPAT_1255", min => "COMPAT_1255", }, expected_hashes => { identifier => "Frost-gpl.txt", global => "true", hashes => { ed2k => "d83596f5ee3b7...", md5 => "e0894e4a2a6...", sha1 => "...", sha256 => "...", sha512 => "...", tth => "...", }, }, expected_mime => { identifier => "Frost-gpl.txt", global => "true", metadata => { content_type => "application/rar" }, }, expected_data_length => { identifier => "Frost-gpl.txt", data_length => 37576, global => "true", }, simple_progress => { identifier => "Frost-gpl.txt", failed => 0, fatally_failed => 0, finalized_total => "true", global => "true", last_progress => 1438639282628, required => 372, succeeded => 102, total => 747, }, data_found => { identifier => "Frost-gpl.txt", completion_time => 1438663354026, data_length => 37576, global => "true", metadata => { content_type => "image/jpeg" }, startup_time => 1438657196167, }, }
use AnyEvent::FCP; my $fcp = new AnyEvent::FCP; # let us look at the global request list $fcp->watch_global_ (1); # list them, synchronously my $req = $fcp->list_persistent_requests; # go through all requests TODO for my $req (values %$req) { # skip jobs not directly-to-disk next unless $req->{return_type} eq "disk"; # skip jobs not issued by FProxy next unless $req->{identifier} =~ /^FProxy:/; if ($req->{data_found}) { # file has been successfully downloaded ... move the file away (left as exercise) # remove the request $fcp->remove_request (1, $req->{identifier}); } elsif ($req->{get_failed}) { # request has failed if ($req->{get_failed}{code} == 11) { # too many path components, should restart } else { # other failure } } else { # modify priorities randomly, to improve download rates $fcp->modify_persistent_request (1, $req->{identifier}, undef, int 6 - 5 * (rand) ** 1.7) if 0.1 > rand; } } # see if the dummy plugin is loaded, to ensure all previous requests have finished. $fcp->get_plugin_info_sync ("dummy");
http://wiki.freenetproject.org/FreenetFCPSpec2Point0, Net::FCP.
Marc Lehmann <schmorp@schmorp.de> http://home.schmorp.de/
To install AnyEvent::FCP, copy and paste the appropriate command in to your terminal.
cpanm
cpanm AnyEvent::FCP
CPAN shell
perl -MCPAN -e shell install AnyEvent::FCP
For more information on module installation, please visit the detailed CPAN module installation guide.