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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines