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.24 by root, Sun Sep 28 08:58:07 2003 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;
77 local $_ = shift; 103 local $_ = shift;
78 s/(?<=[a-z])(?=[A-Z])/_/g; 104 s/(?<=[a-z])(?=[A-Z])/_/g;
79 lc $_; 105 lc $_;
80} 106}
81 107
108# the opposite of hex
109sub xeh($) {
110 sprintf "%x", $_[0];
111}
112
82=item $meta = Net::FCP::parse_metadata $string 113=item $meta = Net::FCP::parse_metadata $string
83 114
84Parse a metadata string and return it. 115Parse a metadata string and return it.
85 116
86The metadata will be a hashref with key C<version> (containing 117The metadata will be a hashref with key C<version> (containing the
87the mandatory version header entries). 118mandatory version header entries) and key C<raw> containing the original
119metadata string.
88 120
89All other headers are represented by arrayrefs (they can be repeated). 121All other headers are represented by arrayrefs (they can be repeated).
90 122
91Since this is confusing, here is a rather verbose example of a parsed 123Since this description is confusing, here is a rather verbose example of a
92manifest: 124parsed manifest:
93 125
94 ( 126 (
127 raw => "Version...",
95 version => { revision => 1 }, 128 version => { revision => 1 },
96 document => [ 129 document => [
97 { 130 {
98 "info.format" => "image/jpeg", 131 info => { format" => "image/jpeg" },
99 name => "background.jpg", 132 name => "background.jpg",
100 "redirect.target" => "freenet:CHK\@ZcagI,ra726bSw" 133 redirect => { target => "freenet:CHK\@ZcagI,ra726bSw" },
101 }, 134 },
102 { 135 {
103 "info.format" => "text/html", 136 info => { format" => "text/html" },
104 name => ".next", 137 name => ".next",
105 "redirect.target" => "freenet:SSK\@ilUPAgM/TFEE/3" 138 redirect => { target => "freenet:SSK\@ilUPAgM/TFEE/3" },
106 }, 139 },
107 { 140 {
108 "info.format" => "text/html", 141 info => { format" => "text/html" },
109 "redirect.target" => "freenet:CHK\@8M8Po8ucwI,8xA" 142 redirect => { target => "freenet:CHK\@8M8Po8ucwI,8xA" },
110 } 143 }
111 ] 144 ]
112 ) 145 )
113 146
114=cut 147=cut
115 148
116sub parse_metadata { 149sub parse_metadata {
117 my $meta;
118
119 my $data = shift; 150 my $data = shift;
151 my $meta = { raw => $data };
152
120 if ($data =~ /^Version\015?\012/gc) { 153 if ($data =~ /^Version\015?\012/gc) {
121 my $hdr = $meta->{version} = {}; 154 my $hdr = $meta->{version} = {};
122 155
123 for (;;) { 156 for (;;) {
124 while ($data =~ /\G([^=\015\012]+)=([^\015\012]*)\015?\012/gc) { 157 while ($data =~ /\G([^=\015\012]+)=([^\015\012]*)\015?\012/gc) {
125 my ($k, $v) = ($1, $2); 158 my ($k, $v) = ($1, $2);
126 my @p = split /\./, tolc $k, 3; 159 my @p = split /\./, tolc $k, 3;
127 160
128 $hdr->{$p[0]} = $v if @p == 1; # lamest code I ever wrote 161 $hdr->{$p[0]} = $v if @p == 1; # lamest code I ever wrote
129 $hdr->{$p[0]}{$p[1]} = $v if @p == 2; 162 $hdr->{$p[0]}{$p[1]} = $v if @p == 2;
130 $hdr->{$p[0]}{$p[1]}{$p[3]} = $v if @p == 3; 163 $hdr->{$p[0]}{$p[1]}{$p[2]} = $v if @p == 3;
131 die "FATAL: 4+ dot metadata" if @p >= 4; 164 die "FATAL: 4+ dot metadata" if @p >= 4;
132 } 165 }
133 166
134 if ($data =~ /\GEndPart\015?\012/gc) { 167 if ($data =~ /\GEndPart\015?\012/gc) {
135 # nop 168 # nop
136 } elsif ($data =~ /\GEnd\015?\012/gc) { 169 } elsif ($data =~ /\GEnd(\015?\012|$)/gc) {
137 last; 170 last;
138 } elsif ($data =~ /\G([A-Za-z0-9.\-]+)\015?\012/gcs) { 171 } elsif ($data =~ /\G([A-Za-z0-9.\-]+)\015?\012/gcs) {
139 push @{$meta->{tolc $1}}, $hdr = {}; 172 push @{$meta->{tolc $1}}, $hdr = {};
140 } elsif ($data =~ /\G(.*)/gcs) { 173 } elsif ($data =~ /\G(.*)/gcs) {
174 print STDERR "metadata format error ($1), please report this string: <<$data>>";
141 die "metadata format error ($1)"; 175 die "metadata format error";
142 } 176 }
143 } 177 }
144 } 178 }
145 179
146 #$meta->{tail} = substr $data, pos $data; 180 #$meta->{tail} = substr $data, pos $data;
152 186
153Create a new virtual FCP connection to the given host and port (default 187Create 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>). 188127.0.0.1:8481, or the environment variables C<FREDHOST> and C<FREDPORT>).
155 189
156Connections are virtual because no persistent physical connection is 190Connections are virtual because no persistent physical connection is
191established.
192
193=begin comment
194
157established. However, the existance of the node is checked by executing a 195However, the existance of the node is checked by executing a
158C<ClientHello> transaction. 196C<ClientHello> transaction.
197
198=end
159 199
160=cut 200=cut
161 201
162sub new { 202sub new {
163 my $class = shift; 203 my $class = shift;
172 $self; 212 $self;
173} 213}
174 214
175sub progress { 215sub progress {
176 my ($self, $txn, $type, $attr) = @_; 216 my ($self, $txn, $type, $attr) = @_;
177 warn "progress<$txn,$type," . (join ":", %$attr) . ">\n"; 217 #warn "progress<$txn,$type," . (join ":", %$attr) . ">\n";
178} 218}
179 219
180=item $txn = $fcp->txn(type => attr => val,...) 220=item $txn = $fcp->txn(type => attr => val,...)
181 221
182The low-level interface to transactions. Don't use it. 222The low-level interface to transactions. Don't use it.
213 my $txn = "Net::FCP::Txn::$type"->new(fcp => $self, type => tolc $type, attr => \%attr); 253 my $txn = "Net::FCP::Txn::$type"->new(fcp => $self, type => tolc $type, attr => \%attr);
214 254
215 $txn; 255 $txn;
216} 256}
217 257
218sub _txn($&) { 258{ # transactions
259
260my $txn = sub {
219 my ($name, $sub) = @_; 261 my ($name, $sub) = @_;
220 *{"$name\_txn"} = $sub; 262 *{"txn_$name"} = $sub;
221 *{$name} = sub { $sub->(@_)->result }; 263 *{$name} = sub { $sub->(@_)->result };
222} 264};
223 265
224=item $txn = $fcp->txn_client_hello 266=item $txn = $fcp->txn_client_hello
225 267
226=item $nodehello = $fcp->client_hello 268=item $nodehello = $fcp->client_hello
227 269
233 protocol => "1.2", 275 protocol => "1.2",
234 } 276 }
235 277
236=cut 278=cut
237 279
238_txn client_hello => sub { 280$txn->(client_hello => sub {
239 my ($self) = @_; 281 my ($self) = @_;
240 282
241 $self->txn ("client_hello"); 283 $self->txn ("client_hello");
242}; 284});
243 285
244=item $txn = $fcp->txn_client_info 286=item $txn = $fcp->txn_client_info
245 287
246=item $nodeinfo = $fcp->client_info 288=item $nodeinfo = $fcp->client_info
247 289
271 routing_time => "a5", 313 routing_time => "a5",
272 } 314 }
273 315
274=cut 316=cut
275 317
276_txn client_info => sub { 318$txn->(client_info => sub {
277 my ($self) = @_; 319 my ($self) = @_;
278 320
279 $self->txn ("client_info"); 321 $self->txn ("client_info");
280}; 322});
281 323
282=item $txn = $fcp->txn_generate_chk ($metadata, $data) 324=item $txn = $fcp->txn_generate_chk ($metadata, $data[, $cipher])
283 325
284=item $uri = $fcp->generate_chk ($metadata, $data) 326=item $uri = $fcp->generate_chk ($metadata, $data[, $cipher])
285 327
286Creates a new CHK, given the metadata and data. UNTESTED. 328Calculcates a CHK, given the metadata and data. C<$cipher> is either
329C<Rijndael> or C<Twofish>, with the latter being the default.
287 330
288=cut 331=cut
289 332
290_txn generate_chk => sub { 333$txn->(generate_chk => sub {
291 my ($self, $metadata, $data) = @_; 334 my ($self, $metadata, $data, $cipher) = @_;
292 335
293 $self->txn (generate_chk => data => "$data$metadata", metadata_length => length $metadata); 336 $self->txn (generate_chk =>
337 data => "$metadata$data",
338 metadata_length => xeh length $metadata,
339 cipher => $cipher || "Twofish");
294}; 340});
295 341
296=item $txn = $fcp->txn_generate_svk_pair 342=item $txn = $fcp->txn_generate_svk_pair
297 343
298=item ($public, $private) = @{ $fcp->generate_svk_pair } 344=item ($public, $private) = @{ $fcp->generate_svk_pair }
299 345
304 "ZnmvMITaTXBMFGl4~jrjuyWxOWg" 350 "ZnmvMITaTXBMFGl4~jrjuyWxOWg"
305 ] 351 ]
306 352
307=cut 353=cut
308 354
309_txn generate_svk_pair => sub { 355$txn->(generate_svk_pair => sub {
310 my ($self) = @_; 356 my ($self) = @_;
311 357
312 $self->txn ("generate_svk_pair"); 358 $self->txn ("generate_svk_pair");
313}; 359});
314 360
315=item $txn = $fcp->txn_insert_private_key ($private) 361=item $txn = $fcp->txn_insert_private_key ($private)
316 362
317=item $uri = $fcp->insert_private_key ($private) 363=item $public = $fcp->insert_private_key ($private)
318 364
319Inserts a private key. $private can be either an insert URI (must start 365Inserts 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 366with C<freenet:SSK@>) or a raw private key (i.e. the private value you get
321from C<generate_svk_pair>). 367back from C<generate_svk_pair>).
322 368
323Returns the public key. 369Returns the public key.
324 370
325UNTESTED. 371UNTESTED.
326 372
327=cut 373=cut
328 374
329_txn insert_private_key => sub { 375$txn->(insert_private_key => sub {
330 my ($self, $privkey) = @_; 376 my ($self, $privkey) = @_;
331 377
332 $self->txn (invert_private_key => private => $privkey); 378 $self->txn (invert_private_key => private => $privkey);
333}; 379});
334 380
335=item $txn = $fcp->txn_get_size ($uri) 381=item $txn = $fcp->txn_get_size ($uri)
336 382
337=item $length = $fcp->get_size ($uri) 383=item $length = $fcp->get_size ($uri)
338 384
341 387
342UNTESTED. 388UNTESTED.
343 389
344=cut 390=cut
345 391
346_txn get_size => sub { 392$txn->(get_size => sub {
347 my ($self, $uri) = @_; 393 my ($self, $uri) = @_;
348 394
349 $self->txn (get_size => URI => $uri); 395 $self->txn (get_size => URI => $uri);
350}; 396});
351 397
352=item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]]) 398=item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]])
353 399
354=item ($metadata, $data) = @{ $fcp->client_get ($uri, $htl, $removelocal) 400=item ($metadata, $data) = @{ $fcp->client_get ($uri, $htl, $removelocal)
355 401
365 ) 411 )
366 }; 412 };
367 413
368=cut 414=cut
369 415
370_txn client_get => sub { 416$txn->(client_get => sub {
371 my ($self, $uri, $htl, $removelocal) = @_; 417 my ($self, $uri, $htl, $removelocal) = @_;
372 418
373 $self->txn (client_get => URI => $uri, hops_to_live => ($htl || 15), remove_local_key => $removelocal ? "true" : "false"); 419 $self->txn (client_get => URI => $uri, hops_to_live => xeh (defined $htl ? $htl : 15),
420 remove_local_key => $removelocal ? "true" : "false");
374}; 421});
375 422
423=item $txn = $fcp->txn_client_put ($uri, $metadata, $data, $htl, $removelocal)
424
425=item my $uri = $fcp->client_put ($uri, $metadata, $data, $htl, $removelocal);
426
427Insert a new key. If the client is inserting a CHK, the URI may be
428abbreviated as just CHK@. In this case, the node will calculate the
429CHK.
430
431C<$meta> can be a reference or a string (ONLY THE STRING CASE IS IMPLEMENTED!).
432
433THIS INTERFACE IS UNTESTED AND SUBJECT TO CHANGE.
434
435=cut
436
437$txn->(client_put => sub {
438 my ($self, $uri, $meta, $data, $htl, $removelocal) = @_;
439
440 $self->txn (client_put => URI => $uri, xeh (defined $htl ? $htl : 15),
441 remove_local_key => $removelocal ? "true" : "false",
442 data => "$meta$data", metadata_length => xeh length $meta);
443});
444
445} # transactions
446
376=item MISSING: ClientPut 447=item MISSING: (ClientPut), InsertKey
377 448
378=back 449=back
379 450
380=head2 THE Net::FCP::Txn CLASS 451=head2 THE Net::FCP::Txn CLASS
381 452
382All requests (or transactions) are executed in a asynchroneous way (LIE: 453All requests (or transactions) are executed in a asynchronous way. For
383uploads are blocking). For each request, a C<Net::FCP::Txn> object is 454each request, a C<Net::FCP::Txn> object is created (worse: a tcp
384created (worse: a tcp connection is created, too). 455connection is created, too).
385 456
386For each request there is actually a different subclass (and it's possible 457For each request there is actually a different subclass (and it's possible
387to subclass these, although of course not documented). 458to subclass these, although of course not documented).
388 459
389The most interesting method is C<result>. 460The most interesting method is C<result>.
417 while (my ($k, $v) = each %{$self->{attr}}) { 488 while (my ($k, $v) = each %{$self->{attr}}) {
418 $attr .= (Net::FCP::touc $k) . "=$v\012" 489 $attr .= (Net::FCP::touc $k) . "=$v\012"
419 } 490 }
420 491
421 if (defined $data) { 492 if (defined $data) {
422 $attr .= "DataLength=" . (length $data) . "\012"; 493 $attr .= sprintf "DataLength=%x\012", length $data;
423 $data = "Data\012$data"; 494 $data = "Data\012$data";
424 } else { 495 } else {
425 $data = "EndMessage\012"; 496 $data = "EndMessage\012";
426 } 497 }
427 498
434 and !$!{EINPROGRESS} 505 and !$!{EINPROGRESS}
435 and Carp::croak "FCP::txn: unable to connect to $self->{fcp}{host}:$self->{fcp}{port}: $!\n"; 506 and Carp::croak "FCP::txn: unable to connect to $self->{fcp}{host}:$self->{fcp}{port}: $!\n";
436 507
437 $self->{sbuf} = 508 $self->{sbuf} =
438 "\x00\x00\x00\x02" 509 "\x00\x00\x00\x02"
439 . Net::FCP::touc $self->{type} 510 . (Net::FCP::touc $self->{type})
440 . "\012$attr$data"; 511 . "\012$attr$data";
441 512
442 #$fh->shutdown (1); # freenet buggy?, well, it's java... 513 #shutdown $fh, 1; # freenet buggy?, well, it's java...
443 514
444 $self->{fh} = $fh; 515 $self->{fh} = $fh;
445 516
446 $self->{w} = $EVENT->new_from_fh ($fh)->cb(sub { $self->fh_ready_w })->poll(0, 1, 1); 517 $self->{w} = $EVENT->new_from_fh ($fh)->cb(sub { $self->fh_ready_w })->poll(0, 1, 1);
447 518
483 554
484sub userdata($$) { 555sub userdata($$) {
485 my ($self, $data) = @_; 556 my ($self, $data) = @_;
486 $self->{userdata} = $data; 557 $self->{userdata} = $data;
487 $self; 558 $self;
559}
560
561=item $txn->cancel (%attr)
562
563Cancels the operation with a C<cancel> exception anf the given attributes
564(consider at least giving the attribute C<reason>).
565
566UNTESTED.
567
568=cut
569
570sub cancel {
571 my ($self, %attr) = @_;
572 $self->throw (Net::FCP::Exception->new (cancel => { %attr }));
573 $self->set_result;
574 $self->eof;
488} 575}
489 576
490sub fh_ready_w { 577sub fh_ready_w {
491 my ($self) = @_; 578 my ($self) = @_;
492 579
532 } else { 619 } else {
533 $self->eof; 620 $self->eof;
534 } 621 }
535} 622}
536 623
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 { 624sub rcv {
546 my ($self, $type, $attr) = @_; 625 my ($self, $type, $attr) = @_;
547 626
548 $type = Net::FCP::tolc $type; 627 $type = Net::FCP::tolc $type;
549 628
557} 636}
558 637
559# used as a default exception thrower 638# used as a default exception thrower
560sub rcv_throw_exception { 639sub rcv_throw_exception {
561 my ($self, $attr, $type) = @_; 640 my ($self, $attr, $type) = @_;
562 $self->throw (new Net::FCP::Exception $type, $attr); 641 $self->throw (Net::FCP::Exception->new ($type, $attr));
563} 642}
564 643
565*rcv_failed = \&Net::FCP::Txn::rcv_throw_exception; 644*rcv_failed = \&Net::FCP::Txn::rcv_throw_exception;
566*rcv_format_error = \&Net::FCP::Txn::rcv_throw_exception; 645*rcv_format_error = \&Net::FCP::Txn::rcv_throw_exception;
567 646
568sub throw { 647sub throw {
569 my ($self, $exc) = @_; 648 my ($self, $exc) = @_;
570 649
571 $self->{exception} = $exc; 650 $self->{exception} = $exc;
572 $self->set_result (1); 651 $self->set_result;
573 $self->eof; # must be last to avoid loops 652 $self->eof; # must be last to avoid loops
574} 653}
575 654
576sub set_result { 655sub set_result {
577 my ($self, $result) = @_; 656 my ($self, $result) = @_;
589 delete $self->{w}; 668 delete $self->{w};
590 delete $self->{fh}; 669 delete $self->{fh};
591 670
592 delete $self->{fcp}{txn}{$self}; 671 delete $self->{fcp}{txn}{$self};
593 672
594 $self->set_result; # just in case 673 unless (exists $self->{result}) {
674 $self->throw (Net::FCP::Exception->new (short_data => {
675 reason => "unexpected eof or internal node error",
676 }));
677 }
595} 678}
596 679
597sub progress { 680sub progress {
598 my ($self, $type, $attr) = @_; 681 my ($self, $type, $attr) = @_;
599 $self->{fcp}->progress ($self, $type, $attr); 682 $self->{fcp}->progress ($self, $type, $attr);
602=item $result = $txn->result 685=item $result = $txn->result
603 686
604Waits until a result is available and then returns it. 687Waits until a result is available and then returns it.
605 688
606This waiting is (depending on your event model) not very efficient, as it 689This waiting is (depending on your event model) not very efficient, as it
607is done outside the "mainloop". 690is done outside the "mainloop". The biggest problem, however, is that it's
691blocking one thread of execution. Try to use the callback mechanism, if
692possible, and call result from within the callback (or after is has been
693run), as then no waiting is necessary.
608 694
609=cut 695=cut
610 696
611sub result { 697sub result {
612 my ($self) = @_; 698 my ($self) = @_;
643use base Net::FCP::Txn; 729use base Net::FCP::Txn;
644 730
645sub rcv_success { 731sub rcv_success {
646 my ($self, $attr) = @_; 732 my ($self, $attr) = @_;
647 733
648 $self->set_result ($attr); 734 $self->set_result ($attr->{uri});
649} 735}
650 736
651package Net::FCP::Txn::GenerateSVKPair; 737package Net::FCP::Txn::GenerateSVKPair;
652 738
653use base Net::FCP::Txn; 739use base Net::FCP::Txn;
654 740
655sub rcv_success { 741sub rcv_success {
656 my ($self, $attr) = @_; 742 my ($self, $attr) = @_;
657
658 $self->set_result ([$attr->{PublicKey}, $attr->{PrivateKey}]); 743 $self->set_result ([$attr->{PublicKey}, $attr->{PrivateKey}]);
659} 744}
660 745
661package Net::FCP::Txn::InvertPrivateKey; 746package Net::FCP::Txn::InsertPrivateKey;
662 747
663use base Net::FCP::Txn; 748use base Net::FCP::Txn;
664 749
665sub rcv_success { 750sub rcv_success {
666 my ($self, $attr) = @_; 751 my ($self, $attr) = @_;
667
668 $self->set_result ($attr->{PublicKey}); 752 $self->set_result ($attr->{PublicKey});
669} 753}
670 754
671package Net::FCP::Txn::GetSize; 755package Net::FCP::Txn::GetSize;
672 756
673use base Net::FCP::Txn; 757use base Net::FCP::Txn;
674 758
675sub rcv_success { 759sub rcv_success {
676 my ($self, $attr) = @_; 760 my ($self, $attr) = @_;
677
678 $self->set_result ($attr->{Length}); 761 $self->set_result (hex $attr->{Length});
679} 762}
680 763
681package Net::FCP::Txn::GetPut; 764package Net::FCP::Txn::GetPut;
682 765
683# base class for get and put 766# base class for get and put
701 784
702use base Net::FCP::Txn::GetPut; 785use base Net::FCP::Txn::GetPut;
703 786
704*rcv_data_not_found = \&Net::FCP::Txn::rcv_throw_exception; 787*rcv_data_not_found = \&Net::FCP::Txn::rcv_throw_exception;
705 788
706sub rcv_data_found { 789sub 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) = @_; 790 my ($self, $chunk) = @_;
791
792 $self->{data} .= $chunk;
793
794 $self->progress ("data", { chunk => length $chunk, received => length $self->{data}, total => $self->{datalength} });
717 795
718 if ($self->{datalength} == length $self->{data}) { 796 if ($self->{datalength} == length $self->{data}) {
719 my $data = delete $self->{data}; 797 my $data = delete $self->{data};
720 my $meta = Net::FCP::parse_metadata substr $data, 0, $self->{metalength}, ""; 798 my $meta = Net::FCP::parse_metadata substr $data, 0, $self->{metalength}, "";
721 799
722 $self->set_result ([$meta, $data]); 800 $self->set_result ([$meta, $data]);
723 } elsif (!exists $self->{result}) { 801 $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 } 802 }
803}
804
805sub rcv_data_found {
806 my ($self, $attr, $type) = @_;
807
808 $self->progress ($type, $attr);
809
810 $self->{datalength} = hex $attr->{data_length};
811 $self->{metalength} = hex $attr->{metadata_length};
730} 812}
731 813
732package Net::FCP::Txn::ClientPut; 814package Net::FCP::Txn::ClientPut;
733 815
734use base Net::FCP::Txn::GetPut; 816use base Net::FCP::Txn::GetPut;
744sub rcv_success { 826sub rcv_success {
745 my ($self, $attr, $type) = @_; 827 my ($self, $attr, $type) = @_;
746 $self->set_result ($attr); 828 $self->set_result ($attr);
747} 829}
748 830
831=back
832
833=head2 The Net::FCP::Exception CLASS
834
835Any unexpected (non-standard) responses that make it impossible to return
836the advertised result will result in an exception being thrown when the
837C<result> method is called.
838
839These exceptions are represented by objects of this class.
840
841=over 4
842
843=cut
844
749package Net::FCP::Exception; 845package Net::FCP::Exception;
750 846
751use overload 847use overload
752 '""' => sub { 848 '""' => sub {
753 "Net::FCP::Exception<<$_[0][0]," . (join ":", %{$_[0][1]}) . ">>\n"; 849 "Net::FCP::Exception<<$_[0][0]," . (join ":", %{$_[0][1]}) . ">>";
754 }; 850 };
851
852=item $exc = new Net::FCP::Exception $type, \%attr
853
854Create a new exception object of the given type (a string like
855C<route_not_found>), and a hashref containing additional attributes
856(usually the attributes of the message causing the exception).
857
858=cut
755 859
756sub new { 860sub new {
757 my ($class, $type, $attr) = @_; 861 my ($class, $type, $attr) = @_;
758 862
759 bless [Net::FCP::tolc $type, { %$attr }], $class; 863 bless [Net::FCP::tolc $type, { %$attr }], $class;
760} 864}
761 865
866=item $exc->type([$type])
867
868With no arguments, returns the exception type. Otherwise a boolean
869indicating wether the exception is of the given type is returned.
870
871=cut
872
873sub type {
874 my ($self, $type) = @_;
875
876 @_ >= 2
877 ? $self->[0] eq $type
878 : $self->[0];
879}
880
881=item $exc->attr([$attr])
882
883With no arguments, returns the attributes. Otherwise the named attribute
884value is returned.
885
886=cut
887
888sub attr {
889 my ($self, $attr) = @_;
890
891 @_ >= 2
892 ? $self->[1]{$attr}
893 : $self->[1];
894}
895
762=back 896=back
763 897
764=head1 SEE ALSO 898=head1 SEE ALSO
765 899
766L<http://freenet.sf.net>. 900L<http://freenet.sf.net>.
772 Marc Lehmann <pcg@goof.com> 906 Marc Lehmann <pcg@goof.com>
773 http://www.goof.com/pcg/marc/ 907 http://www.goof.com/pcg/marc/
774 908
775=cut 909=cut
776 910
911package Net::FCP::Event::Auto;
912
913my @models = (
914 [Coro => Coro::Event:: ],
915 [Event => Event::],
916 [Glib => Glib:: ],
917 [Tk => Tk::],
918);
919
920sub AUTOLOAD {
921 $AUTOLOAD =~ s/.*://;
922
923 for (@models) {
924 my ($model, $package) = @$_;
925 if (defined ${"$package\::VERSION"}) {
926 $EVENT = "Net::FCP::Event::$model";
927 eval "require $EVENT"; die if $@;
928 goto &{"$EVENT\::$AUTOLOAD"};
929 }
930 }
931
932 for (@models) {
933 my ($model, $package) = @$_;
934 $EVENT = "Net::FCP::Event::$model";
935 if (eval "require $EVENT") {
936 goto &{"$EVENT\::$AUTOLOAD"};
937 }
938 }
939
940 die "No event module selected for Net::FCP and autodetect failed. Install any of these: Coro, Event, Glib or Tk.";
941}
942
7771; 9431;
778 944

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines