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

Comparing Net-FCP/FCP.pm (file contents):
Revision 1.13 by root, Wed Sep 10 05:06:16 2003 UTC vs.
Revision 1.27 by root, Thu May 13 15:41:01 2004 UTC

35The import tag to use is named C<event=xyz>, e.g. C<event=Event>, 35The import tag to use is named C<event=xyz>, e.g. C<event=Event>,
36C<event=Glib> etc. 36C<event=Glib> etc.
37 37
38You should specify the event module to use only in the main program. 38You should specify the event module to use only in the main program.
39 39
40If no event model has been specified, FCP tries to autodetect it on first
41use (e.g. first transaction), in this order: Coro, Event, Glib, Tk.
42
43=head2 FREENET BASICS
44
45Ok, this section will not explain any freenet basics to you, just some
46problems I found that you might want to avoid:
47
48=over 4
49
50=item freenet URIs are _NOT_ URIs
51
52Whenever a "uri" is required by the protocol, freenet expects a kind of
53URI prefixed with the "freenet:" scheme, e.g. "freenet:CHK...". However,
54these are not URIs, as freeent fails to parse them correctly, that is, you
55must unescape an escaped characters ("%2c" => ",") yourself. Maybe in the
56future this library will do it for you, so watch out for this incompatible
57change.
58
59=item Numbers are in HEX
60
61Virtually every number in the FCP protocol is in hex. Be sure to use
62C<hex()> on all such numbers, as the module (currently) does nothing to
63convert these for you.
64
65=back
66
40=head2 THE Net::FCP CLASS 67=head2 THE Net::FCP CLASS
41 68
42=over 4 69=over 4
43 70
44=cut 71=cut
45 72
46package Net::FCP; 73package Net::FCP;
47 74
48use Carp; 75use Carp;
49 76
50$VERSION = 0.05; 77$VERSION = 0.6;
51 78
52no warnings; 79no warnings;
53 80
54our $EVENT = Net::FCP::Event::Auto::; 81our $EVENT = Net::FCP::Event::Auto::;
55$EVENT = Net::FCP::Event::Event;#d#
56 82
57sub import { 83sub import {
58 shift; 84 shift;
59 85
60 for (@_) { 86 for (@_) {
61 if (/^event=(\w+)$/) { 87 if (/^event=(\w+)$/) {
62 $EVENT = "Net::FCP::Event::$1"; 88 $EVENT = "Net::FCP::Event::$1";
89 eval "require $EVENT";
63 } 90 }
64 } 91 }
65 eval "require $EVENT";
66 die $@ if $@; 92 die $@ if $@;
67} 93}
68 94
69sub touc($) { 95sub touc($) {
70 local $_ = shift; 96 local $_ = shift;
73 $_; 99 $_;
74} 100}
75 101
76sub tolc($) { 102sub tolc($) {
77 local $_ = shift; 103 local $_ = shift;
104 1 while s/(SVK|CHK|URI)([^_])/$1\_$2/i;
105 1 while s/([^_])(SVK|CHK|URI)/$1\_$2/i;
78 s/(?<=[a-z])(?=[A-Z])/_/g; 106 s/(?<=[a-z])(?=[A-Z])/_/g;
79 lc $_; 107 lc $_;
80} 108}
81 109
110# the opposite of hex
111sub xeh($) {
112 sprintf "%x", $_[0];
113}
114
82=item $meta = Net::FCP::parse_metadata $string 115=item $meta = Net::FCP::parse_metadata $string
83 116
84Parse a metadata string and return it. 117Parse a metadata string and return it.
85 118
86The metadata will be a hashref with key C<version> (containing 119The metadata will be a hashref with key C<version> (containing the
87the mandatory version header entries). 120mandatory version header entries) and key C<raw> containing the original
121metadata string.
88 122
89All other headers are represented by arrayrefs (they can be repeated). 123All other headers are represented by arrayrefs (they can be repeated).
90 124
91Since this is confusing, here is a rather verbose example of a parsed 125Since this description is confusing, here is a rather verbose example of a
92manifest: 126parsed manifest:
93 127
94 ( 128 (
129 raw => "Version...",
95 version => { revision => 1 }, 130 version => { revision => 1 },
96 document => [ 131 document => [
97 { 132 {
98 "info.format" => "image/jpeg", 133 info => { format" => "image/jpeg" },
99 name => "background.jpg", 134 name => "background.jpg",
100 "redirect.target" => "freenet:CHK\@ZcagI,ra726bSw" 135 redirect => { target => "freenet:CHK\@ZcagI,ra726bSw" },
101 }, 136 },
102 { 137 {
103 "info.format" => "text/html", 138 info => { format" => "text/html" },
104 name => ".next", 139 name => ".next",
105 "redirect.target" => "freenet:SSK\@ilUPAgM/TFEE/3" 140 redirect => { target => "freenet:SSK\@ilUPAgM/TFEE/3" },
106 }, 141 },
107 { 142 {
108 "info.format" => "text/html", 143 info => { format" => "text/html" },
109 "redirect.target" => "freenet:CHK\@8M8Po8ucwI,8xA" 144 redirect => { target => "freenet:CHK\@8M8Po8ucwI,8xA" },
110 } 145 }
111 ] 146 ]
112 ) 147 )
113 148
114=cut 149=cut
115 150
116sub parse_metadata { 151sub parse_metadata {
117 my $meta;
118
119 my $data = shift; 152 my $data = shift;
153 my $meta = { raw => $data };
154
120 if ($data =~ /^Version\015?\012/gc) { 155 if ($data =~ /^Version\015?\012/gc) {
121 my $hdr = $meta->{version} = {}; 156 my $hdr = $meta->{version} = {};
122 157
123 for (;;) { 158 for (;;) {
124 while ($data =~ /\G([^=\015\012]+)=([^\015\012]*)\015?\012/gc) { 159 while ($data =~ /\G([^=\015\012]+)=([^\015\012]*)\015?\012/gc) {
125 my ($k, $v) = ($1, $2); 160 my ($k, $v) = ($1, $2);
126 my @p = split /\./, tolc $k, 3; 161 my @p = split /\./, tolc $k, 3;
127 162
128 $hdr->{$p[0]} = $v if @p == 1; # lamest code I ever wrote 163 $hdr->{$p[0]} = $v if @p == 1; # lamest code I ever wrote
129 $hdr->{$p[0]}{$p[1]} = $v if @p == 2; 164 $hdr->{$p[0]}{$p[1]} = $v if @p == 2;
130 $hdr->{$p[0]}{$p[1]}{$p[3]} = $v if @p == 3; 165 $hdr->{$p[0]}{$p[1]}{$p[2]} = $v if @p == 3;
131 die "FATAL: 4+ dot metadata" if @p >= 4; 166 die "FATAL: 4+ dot metadata" if @p >= 4;
132 } 167 }
133 168
134 if ($data =~ /\GEndPart\015?\012/gc) { 169 if ($data =~ /\GEndPart\015?\012/gc) {
135 # nop 170 # nop
136 } elsif ($data =~ /\GEnd\015?\012/gc) { 171 } elsif ($data =~ /\GEnd(\015?\012|$)/gc) {
137 last; 172 last;
138 } elsif ($data =~ /\G([A-Za-z0-9.\-]+)\015?\012/gcs) { 173 } elsif ($data =~ /\G([A-Za-z0-9.\-]+)\015?\012/gcs) {
139 push @{$meta->{tolc $1}}, $hdr = {}; 174 push @{$meta->{tolc $1}}, $hdr = {};
140 } elsif ($data =~ /\G(.*)/gcs) { 175 } elsif ($data =~ /\G(.*)/gcs) {
176 print STDERR "metadata format error ($1), please report this string: <<$data>>";
141 die "metadata format error ($1)"; 177 die "metadata format error";
142 } 178 }
143 } 179 }
144 } 180 }
145 181
146 #$meta->{tail} = substr $data, pos $data; 182 #$meta->{tail} = substr $data, pos $data;
147 183
148 $meta; 184 $meta;
149} 185}
150 186
151=item $fcp = new Net::FCP [host => $host][, port => $port] 187=item $fcp = new Net::FCP [host => $host][, port => $port][, progress => \&cb]
152 188
153Create a new virtual FCP connection to the given host and port (default 189Create a new virtual FCP connection to the given host and port (default
154127.0.0.1:8481, or the environment variables C<FREDHOST> and C<FREDPORT>). 190127.0.0.1:8481, or the environment variables C<FREDHOST> and C<FREDPORT>).
155 191
156Connections are virtual because no persistent physical connection is 192Connections are virtual because no persistent physical connection is
193established.
194
195You can install a progress callback that is being called with the Net::FCP
196object, a txn object, the type of the transaction and the attributes. Use
197it like this:
198
199 sub progress_cb {
200 my ($self, $txn, $type, $attr) = @_;
201
202 warn "progress<$txn,$type," . (join ":", %$attr) . ">\n";
203 }
204
205=begin comment
206
157established. However, the existance of the node is checked by executing a 207However, the existance of the node is checked by executing a
158C<ClientHello> transaction. 208C<ClientHello> transaction.
209
210=end
159 211
160=cut 212=cut
161 213
162sub new { 214sub new {
163 my $class = shift; 215 my $class = shift;
172 $self; 224 $self;
173} 225}
174 226
175sub progress { 227sub progress {
176 my ($self, $txn, $type, $attr) = @_; 228 my ($self, $txn, $type, $attr) = @_;
229
230 $self->{progress}->($self, $txn, $type, $attr)
231 if $self->{progress};
177 warn "progress<$txn,$type," . (join ":", %$attr) . ">\n"; 232 #warn "progress<$txn,$type," . (join ":", %$attr) . ">\n";
178} 233}
179 234
180=item $txn = $fcp->txn(type => attr => val,...) 235=item $txn = $fcp->txn(type => attr => val,...)
181 236
182The low-level interface to transactions. Don't use it. 237The low-level interface to transactions. Don't use it.
213 my $txn = "Net::FCP::Txn::$type"->new(fcp => $self, type => tolc $type, attr => \%attr); 268 my $txn = "Net::FCP::Txn::$type"->new(fcp => $self, type => tolc $type, attr => \%attr);
214 269
215 $txn; 270 $txn;
216} 271}
217 272
218sub _txn($&) { 273{ # transactions
274
275my $txn = sub {
219 my ($name, $sub) = @_; 276 my ($name, $sub) = @_;
220 *{"$name\_txn"} = $sub; 277 *{"txn_$name"} = $sub;
221 *{$name} = sub { $sub->(@_)->result }; 278 *{$name} = sub { $sub->(@_)->result };
222} 279};
223 280
224=item $txn = $fcp->txn_client_hello 281=item $txn = $fcp->txn_client_hello
225 282
226=item $nodehello = $fcp->client_hello 283=item $nodehello = $fcp->client_hello
227 284
233 protocol => "1.2", 290 protocol => "1.2",
234 } 291 }
235 292
236=cut 293=cut
237 294
238_txn client_hello => sub { 295$txn->(client_hello => sub {
239 my ($self) = @_; 296 my ($self) = @_;
240 297
241 $self->txn ("client_hello"); 298 $self->txn ("client_hello");
242}; 299});
243 300
244=item $txn = $fcp->txn_client_info 301=item $txn = $fcp->txn_client_info
245 302
246=item $nodeinfo = $fcp->client_info 303=item $nodeinfo = $fcp->client_info
247 304
271 routing_time => "a5", 328 routing_time => "a5",
272 } 329 }
273 330
274=cut 331=cut
275 332
276_txn client_info => sub { 333$txn->(client_info => sub {
277 my ($self) = @_; 334 my ($self) = @_;
278 335
279 $self->txn ("client_info"); 336 $self->txn ("client_info");
280}; 337});
281 338
282=item $txn = $fcp->txn_generate_chk ($metadata, $data) 339=item $txn = $fcp->txn_generate_chk ($metadata, $data[, $cipher])
283 340
284=item $uri = $fcp->generate_chk ($metadata, $data) 341=item $uri = $fcp->generate_chk ($metadata, $data[, $cipher])
285 342
286Creates a new CHK, given the metadata and data. UNTESTED. 343Calculates a CHK, given the metadata and data. C<$cipher> is either
344C<Rijndael> or C<Twofish>, with the latter being the default.
287 345
288=cut 346=cut
289 347
290_txn generate_chk => sub { 348$txn->(generate_chk => sub {
291 my ($self, $metadata, $data) = @_; 349 my ($self, $metadata, $data, $cipher) = @_;
292 350
293 $self->txn (generate_chk => data => "$data$metadata", metadata_length => length $metadata); 351 $self->txn (generate_chk =>
352 data => "$metadata$data",
353 metadata_length => xeh length $metadata,
354 cipher => $cipher || "Twofish");
294}; 355});
295 356
296=item $txn = $fcp->txn_generate_svk_pair 357=item $txn = $fcp->txn_generate_svk_pair
297 358
298=item ($public, $private) = @{ $fcp->generate_svk_pair } 359=item ($public, $private) = @{ $fcp->generate_svk_pair }
299 360
304 "ZnmvMITaTXBMFGl4~jrjuyWxOWg" 365 "ZnmvMITaTXBMFGl4~jrjuyWxOWg"
305 ] 366 ]
306 367
307=cut 368=cut
308 369
309_txn generate_svk_pair => sub { 370$txn->(generate_svk_pair => sub {
310 my ($self) = @_; 371 my ($self) = @_;
311 372
312 $self->txn ("generate_svk_pair"); 373 $self->txn ("generate_svk_pair");
313}; 374});
314 375
315=item $txn = $fcp->txn_insert_private_key ($private) 376=item $txn = $fcp->txn_insert_private_key ($private)
316 377
317=item $uri = $fcp->insert_private_key ($private) 378=item $public = $fcp->insert_private_key ($private)
318 379
319Inserts a private key. $private can be either an insert URI (must start 380Inserts a private key. $private can be either an insert URI (must start
320with freenet:SSK@) or a raw private key (i.e. the private value you get back 381with C<freenet:SSK@>) or a raw private key (i.e. the private value you get
321from C<generate_svk_pair>). 382back from C<generate_svk_pair>).
322 383
323Returns the public key. 384Returns the public key.
324 385
325UNTESTED. 386UNTESTED.
326 387
327=cut 388=cut
328 389
329_txn insert_private_key => sub { 390$txn->(insert_private_key => sub {
330 my ($self, $privkey) = @_; 391 my ($self, $privkey) = @_;
331 392
332 $self->txn (invert_private_key => private => $privkey); 393 $self->txn (invert_private_key => private => $privkey);
333}; 394});
334 395
335=item $txn = $fcp->txn_get_size ($uri) 396=item $txn = $fcp->txn_get_size ($uri)
336 397
337=item $length = $fcp->get_size ($uri) 398=item $length = $fcp->get_size ($uri)
338 399
341 402
342UNTESTED. 403UNTESTED.
343 404
344=cut 405=cut
345 406
346_txn get_size => sub { 407$txn->(get_size => sub {
347 my ($self, $uri) = @_; 408 my ($self, $uri) = @_;
348 409
349 $self->txn (get_size => URI => $uri); 410 $self->txn (get_size => URI => $uri);
350}; 411});
351 412
352=item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]]) 413=item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]])
353 414
354=item ($metadata, $data) = @{ $fcp->client_get ($uri, $htl, $removelocal) 415=item ($metadata, $data) = @{ $fcp->client_get ($uri, $htl, $removelocal)
355 416
356Fetches a (small, as it should fit into memory) file from 417Fetches a (small, as it should fit into memory) file from
357freenet. C<$meta> is the metadata (as returned by C<parse_metadata> or 418freenet. C<$meta> is the metadata (as returned by C<parse_metadata> or
358C<undef>). 419C<undef>).
420
421The C<$uri> should begin with C<freenet:>, but the scheme is currently
422added, if missing.
359 423
360Due to the overhead, a better method to download big files should be used. 424Due to the overhead, a better method to download big files should be used.
361 425
362 my ($meta, $data) = @{ 426 my ($meta, $data) = @{
363 $fcp->client_get ( 427 $fcp->client_get (
365 ) 429 )
366 }; 430 };
367 431
368=cut 432=cut
369 433
370_txn client_get => sub { 434$txn->(client_get => sub {
371 my ($self, $uri, $htl, $removelocal) = @_; 435 my ($self, $uri, $htl, $removelocal) = @_;
372 436
373 $self->txn (client_get => URI => $uri, hops_to_live => ($htl || 15), remove_local_key => $removelocal ? "true" : "false"); 437 $uri =~ s/^freenet://;
438 $uri = "freenet:$uri";
439
440 $self->txn (client_get => URI => $uri, hops_to_live => xeh (defined $htl ? $htl : 15),
441 remove_local_key => $removelocal ? "true" : "false");
374}; 442});
375 443
444=item $txn = $fcp->txn_client_put ($uri, $metadata, $data, $htl, $removelocal)
445
446=item my $uri = $fcp->client_put ($uri, $metadata, $data, $htl, $removelocal);
447
448Insert a new key. If the client is inserting a CHK, the URI may be
449abbreviated as just CHK@. In this case, the node will calculate the
450CHK.
451
452C<$meta> can be a reference or a string (ONLY THE STRING CASE IS IMPLEMENTED!).
453
454THIS INTERFACE IS UNTESTED AND SUBJECT TO CHANGE.
455
456=cut
457
458$txn->(client_put => sub {
459 my ($self, $uri, $meta, $data, $htl, $removelocal) = @_;
460
461 $self->txn (client_put => URI => $uri, xeh (defined $htl ? $htl : 15),
462 remove_local_key => $removelocal ? "true" : "false",
463 data => "$meta$data", metadata_length => xeh length $meta);
464});
465
466} # transactions
467
376=item MISSING: ClientPut 468=item MISSING: (ClientPut), InsertKey
377 469
378=back 470=back
379 471
380=head2 THE Net::FCP::Txn CLASS 472=head2 THE Net::FCP::Txn CLASS
381 473
382All requests (or transactions) are executed in a asynchroneous way (LIE: 474All requests (or transactions) are executed in a asynchronous way. For
383uploads are blocking). For each request, a C<Net::FCP::Txn> object is 475each request, a C<Net::FCP::Txn> object is created (worse: a tcp
384created (worse: a tcp connection is created, too). 476connection is created, too).
385 477
386For each request there is actually a different subclass (and it's possible 478For each request there is actually a different subclass (and it's possible
387to subclass these, although of course not documented). 479to subclass these, although of course not documented).
388 480
389The most interesting method is C<result>. 481The most interesting method is C<result>.
417 while (my ($k, $v) = each %{$self->{attr}}) { 509 while (my ($k, $v) = each %{$self->{attr}}) {
418 $attr .= (Net::FCP::touc $k) . "=$v\012" 510 $attr .= (Net::FCP::touc $k) . "=$v\012"
419 } 511 }
420 512
421 if (defined $data) { 513 if (defined $data) {
422 $attr .= "DataLength=" . (length $data) . "\012"; 514 $attr .= sprintf "DataLength=%x\012", length $data;
423 $data = "Data\012$data"; 515 $data = "Data\012$data";
424 } else { 516 } else {
425 $data = "EndMessage\012"; 517 $data = "EndMessage\012";
426 } 518 }
427 519
434 and !$!{EINPROGRESS} 526 and !$!{EINPROGRESS}
435 and Carp::croak "FCP::txn: unable to connect to $self->{fcp}{host}:$self->{fcp}{port}: $!\n"; 527 and Carp::croak "FCP::txn: unable to connect to $self->{fcp}{host}:$self->{fcp}{port}: $!\n";
436 528
437 $self->{sbuf} = 529 $self->{sbuf} =
438 "\x00\x00\x00\x02" 530 "\x00\x00\x00\x02"
439 . Net::FCP::touc $self->{type} 531 . (Net::FCP::touc $self->{type})
440 . "\012$attr$data"; 532 . "\012$attr$data";
441 533
442 #$fh->shutdown (1); # freenet buggy?, well, it's java... 534 #shutdown $fh, 1; # freenet buggy?, well, it's java...
443 535
444 $self->{fh} = $fh; 536 $self->{fh} = $fh;
445 537
446 $self->{w} = $EVENT->new_from_fh ($fh)->cb(sub { $self->fh_ready_w })->poll(0, 1, 1); 538 $self->{w} = $EVENT->new_from_fh ($fh)->cb(sub { $self->fh_ready_w })->poll(0, 1, 1);
447 539
483 575
484sub userdata($$) { 576sub userdata($$) {
485 my ($self, $data) = @_; 577 my ($self, $data) = @_;
486 $self->{userdata} = $data; 578 $self->{userdata} = $data;
487 $self; 579 $self;
580}
581
582=item $txn->cancel (%attr)
583
584Cancels the operation with a C<cancel> exception anf the given attributes
585(consider at least giving the attribute C<reason>).
586
587UNTESTED.
588
589=cut
590
591sub cancel {
592 my ($self, %attr) = @_;
593 $self->throw (Net::FCP::Exception->new (cancel => { %attr }));
594 $self->set_result;
595 $self->eof;
488} 596}
489 597
490sub fh_ready_w { 598sub fh_ready_w {
491 my ($self) = @_; 599 my ($self) = @_;
492 600
519 } 627 }
520 } elsif ($self->{buf} =~ s/^DataChunk\015?\012Length=([0-9a-fA-F]+)\015?\012Data\015?\012//) { 628 } elsif ($self->{buf} =~ s/^DataChunk\015?\012Length=([0-9a-fA-F]+)\015?\012Data\015?\012//) {
521 $self->{datalen} = hex $1; 629 $self->{datalen} = hex $1;
522 #warn "expecting new datachunk $self->{datalen}\n";#d# 630 #warn "expecting new datachunk $self->{datalen}\n";#d#
523 } elsif ($self->{buf} =~ s/^([a-zA-Z]+)\015?\012(?:(.+?)\015?\012)?EndMessage\015?\012//s) { 631 } elsif ($self->{buf} =~ s/^([a-zA-Z]+)\015?\012(?:(.+?)\015?\012)?EndMessage\015?\012//s) {
632 print "RECV<$1>\n";
524 $self->rcv ($1, { 633 $self->rcv ($1, {
525 map { my ($a, $b) = split /=/, $_, 2; ((Net::FCP::tolc $a), $b) } 634 map { my ($a, $b) = split /=/, $_, 2; ((Net::FCP::tolc $a), $b) }
526 split /\015?\012/, $2 635 split /\015?\012/, $2
527 }); 636 });
528 } else { 637 } else {
532 } else { 641 } else {
533 $self->eof; 642 $self->eof;
534 } 643 }
535} 644}
536 645
537sub rcv_data {
538 my ($self, $chunk) = @_;
539
540 $self->{data} .= $chunk;
541
542 $self->progress ("data", { chunk => length $chunk, total => length $self->{data}, end => $self->{datalength} });
543}
544
545sub rcv { 646sub rcv {
546 my ($self, $type, $attr) = @_; 647 my ($self, $type, $attr) = @_;
547 648
548 $type = Net::FCP::tolc $type; 649 $type = Net::FCP::tolc $type;
549 650
557} 658}
558 659
559# used as a default exception thrower 660# used as a default exception thrower
560sub rcv_throw_exception { 661sub rcv_throw_exception {
561 my ($self, $attr, $type) = @_; 662 my ($self, $attr, $type) = @_;
562 $self->throw (new Net::FCP::Exception $type, $attr); 663 $self->throw (Net::FCP::Exception->new ($type, $attr));
563} 664}
564 665
565*rcv_failed = \&Net::FCP::Txn::rcv_throw_exception; 666*rcv_failed = \&Net::FCP::Txn::rcv_throw_exception;
566*rcv_format_error = \&Net::FCP::Txn::rcv_throw_exception; 667*rcv_format_error = \&Net::FCP::Txn::rcv_throw_exception;
567 668
568sub throw { 669sub throw {
569 my ($self, $exc) = @_; 670 my ($self, $exc) = @_;
570 671
571 $self->{exception} = $exc; 672 $self->{exception} = $exc;
572 $self->set_result (1); 673 $self->set_result;
573 $self->eof; # must be last to avoid loops 674 $self->eof; # must be last to avoid loops
574} 675}
575 676
576sub set_result { 677sub set_result {
577 my ($self, $result) = @_; 678 my ($self, $result) = @_;
589 delete $self->{w}; 690 delete $self->{w};
590 delete $self->{fh}; 691 delete $self->{fh};
591 692
592 delete $self->{fcp}{txn}{$self}; 693 delete $self->{fcp}{txn}{$self};
593 694
594 $self->set_result; # just in case 695 unless (exists $self->{result}) {
696 $self->throw (Net::FCP::Exception->new (short_data => {
697 reason => "unexpected eof or internal node error",
698 }));
699 }
595} 700}
596 701
597sub progress { 702sub progress {
598 my ($self, $type, $attr) = @_; 703 my ($self, $type, $attr) = @_;
704
599 $self->{fcp}->progress ($self, $type, $attr); 705 $self->{fcp}->progress ($self, $type, $attr);
600} 706}
601 707
602=item $result = $txn->result 708=item $result = $txn->result
603 709
604Waits until a result is available and then returns it. 710Waits until a result is available and then returns it.
605 711
606This waiting is (depending on your event model) not very efficient, as it 712This waiting is (depending on your event model) not very efficient, as it
607is done outside the "mainloop". 713is done outside the "mainloop". The biggest problem, however, is that it's
714blocking one thread of execution. Try to use the callback mechanism, if
715possible, and call result from within the callback (or after is has been
716run), as then no waiting is necessary.
608 717
609=cut 718=cut
610 719
611sub result { 720sub result {
612 my ($self) = @_; 721 my ($self) = @_;
643use base Net::FCP::Txn; 752use base Net::FCP::Txn;
644 753
645sub rcv_success { 754sub rcv_success {
646 my ($self, $attr) = @_; 755 my ($self, $attr) = @_;
647 756
648 $self->set_result ($attr); 757 $self->set_result ($attr->{uri});
649} 758}
650 759
651package Net::FCP::Txn::GenerateSVKPair; 760package Net::FCP::Txn::GenerateSVKPair;
652 761
653use base Net::FCP::Txn; 762use base Net::FCP::Txn;
654 763
655sub rcv_success { 764sub rcv_success {
656 my ($self, $attr) = @_; 765 my ($self, $attr) = @_;
657
658 $self->set_result ([$attr->{PublicKey}, $attr->{PrivateKey}]); 766 $self->set_result ([$attr->{PublicKey}, $attr->{PrivateKey}]);
659} 767}
660 768
661package Net::FCP::Txn::InvertPrivateKey; 769package Net::FCP::Txn::InsertPrivateKey;
662 770
663use base Net::FCP::Txn; 771use base Net::FCP::Txn;
664 772
665sub rcv_success { 773sub rcv_success {
666 my ($self, $attr) = @_; 774 my ($self, $attr) = @_;
667
668 $self->set_result ($attr->{PublicKey}); 775 $self->set_result ($attr->{PublicKey});
669} 776}
670 777
671package Net::FCP::Txn::GetSize; 778package Net::FCP::Txn::GetSize;
672 779
673use base Net::FCP::Txn; 780use base Net::FCP::Txn;
674 781
675sub rcv_success { 782sub rcv_success {
676 my ($self, $attr) = @_; 783 my ($self, $attr) = @_;
677
678 $self->set_result ($attr->{Length}); 784 $self->set_result (hex $attr->{Length});
679} 785}
680 786
681package Net::FCP::Txn::GetPut; 787package Net::FCP::Txn::GetPut;
682 788
683# base class for get and put 789# base class for get and put
684 790
685use base Net::FCP::Txn; 791use base Net::FCP::Txn;
686 792
687*rcv_uri_error = \&Net::FCP::Txn::rcv_throw_exception; 793*rcv_uri_error = \&Net::FCP::Txn::rcv_throw_exception;
688*rcv_route_not_found = \&Net::FCP::Txn::rcv_throw_exception; 794*rcv_route_not_found = \&Net::FCP::Txn::rcv_throw_exception;
689 795
690sub rcv_restarted { 796sub rcv_restarted {
691 my ($self, $attr, $type) = @_; 797 my ($self, $attr, $type) = @_;
692 798
693 delete $self->{datalength}; 799 delete $self->{datalength};
701 807
702use base Net::FCP::Txn::GetPut; 808use base Net::FCP::Txn::GetPut;
703 809
704*rcv_data_not_found = \&Net::FCP::Txn::rcv_throw_exception; 810*rcv_data_not_found = \&Net::FCP::Txn::rcv_throw_exception;
705 811
706sub rcv_data_found { 812sub rcv_data {
707 my ($self, $attr, $type) = @_;
708
709 $self->progress ($type, $attr);
710
711 $self->{datalength} = hex $attr->{data_length};
712 $self->{metalength} = hex $attr->{metadata_length};
713}
714
715sub eof {
716 my ($self) = @_; 813 my ($self, $chunk) = @_;
814
815 $self->{data} .= $chunk;
816
817 $self->progress ("data", { chunk => length $chunk, received => length $self->{data}, total => $self->{datalength} });
717 818
718 if ($self->{datalength} == length $self->{data}) { 819 if ($self->{datalength} == length $self->{data}) {
719 my $data = delete $self->{data}; 820 my $data = delete $self->{data};
720 my $meta = Net::FCP::parse_metadata substr $data, 0, $self->{metalength}, ""; 821 my $meta = Net::FCP::parse_metadata substr $data, 0, $self->{metalength}, "";
721 822
722 $self->set_result ([$meta, $data]); 823 $self->set_result ([$meta, $data]);
723 } elsif (!exists $self->{result}) { 824 $self->eof;
724 $self->throw (Net::FCP::Exception->new (short_data => {
725 reason => "unexpected eof or internal node error",
726 received => length $self->{data},
727 expected => $self->{datalength},
728 }));
729 } 825 }
826}
827
828sub rcv_data_found {
829 my ($self, $attr, $type) = @_;
830
831 $self->progress ($type, $attr);
832
833 $self->{datalength} = hex $attr->{data_length};
834 $self->{metalength} = hex $attr->{metadata_length};
730} 835}
731 836
732package Net::FCP::Txn::ClientPut; 837package Net::FCP::Txn::ClientPut;
733 838
734use base Net::FCP::Txn::GetPut; 839use base Net::FCP::Txn::GetPut;
744sub rcv_success { 849sub rcv_success {
745 my ($self, $attr, $type) = @_; 850 my ($self, $attr, $type) = @_;
746 $self->set_result ($attr); 851 $self->set_result ($attr);
747} 852}
748 853
854=back
855
856=head2 The Net::FCP::Exception CLASS
857
858Any unexpected (non-standard) responses that make it impossible to return
859the advertised result will result in an exception being thrown when the
860C<result> method is called.
861
862These exceptions are represented by objects of this class.
863
864=over 4
865
866=cut
867
749package Net::FCP::Exception; 868package Net::FCP::Exception;
750 869
751use overload 870use overload
752 '""' => sub { 871 '""' => sub {
753 "Net::FCP::Exception<<$_[0][0]," . (join ":", %{$_[0][1]}) . ">>\n"; 872 "Net::FCP::Exception<<$_[0][0]," . (join ":", %{$_[0][1]}) . ">>";
754 }; 873 };
874
875=item $exc = new Net::FCP::Exception $type, \%attr
876
877Create a new exception object of the given type (a string like
878C<route_not_found>), and a hashref containing additional attributes
879(usually the attributes of the message causing the exception).
880
881=cut
755 882
756sub new { 883sub new {
757 my ($class, $type, $attr) = @_; 884 my ($class, $type, $attr) = @_;
758 885
759 bless [Net::FCP::tolc $type, { %$attr }], $class; 886 bless [Net::FCP::tolc $type, { %$attr }], $class;
760} 887}
761 888
889=item $exc->type([$type])
890
891With no arguments, returns the exception type. Otherwise a boolean
892indicating wether the exception is of the given type is returned.
893
894=cut
895
896sub type {
897 my ($self, $type) = @_;
898
899 @_ >= 2
900 ? $self->[0] eq $type
901 : $self->[0];
902}
903
904=item $exc->attr([$attr])
905
906With no arguments, returns the attributes. Otherwise the named attribute
907value is returned.
908
909=cut
910
911sub attr {
912 my ($self, $attr) = @_;
913
914 @_ >= 2
915 ? $self->[1]{$attr}
916 : $self->[1];
917}
918
762=back 919=back
763 920
764=head1 SEE ALSO 921=head1 SEE ALSO
765 922
766L<http://freenet.sf.net>. 923L<http://freenet.sf.net>.
772 Marc Lehmann <pcg@goof.com> 929 Marc Lehmann <pcg@goof.com>
773 http://www.goof.com/pcg/marc/ 930 http://www.goof.com/pcg/marc/
774 931
775=cut 932=cut
776 933
934package Net::FCP::Event::Auto;
935
936my @models = (
937 [Coro => Coro::Event::],
938 [Event => Event::],
939 [Glib => Glib::],
940 [Tk => Tk::],
941);
942
943sub AUTOLOAD {
944 $AUTOLOAD =~ s/.*://;
945
946 for (@models) {
947 my ($model, $package) = @$_;
948 if (defined ${"$package\::VERSION"}) {
949 $EVENT = "Net::FCP::Event::$model";
950 eval "require $EVENT"; die if $@;
951 goto &{"$EVENT\::$AUTOLOAD"};
952 }
953 }
954
955 for (@models) {
956 my ($model, $package) = @$_;
957 $EVENT = "Net::FCP::Event::$model";
958 if (eval "require $EVENT") {
959 goto &{"$EVENT\::$AUTOLOAD"};
960 }
961 }
962
963 die "No event module selected for Net::FCP and autodetect failed. Install any of these: Coro, Event, Glib or Tk.";
964}
965
7771; 9661;
778 967

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines