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

Comparing cvsroot/Net-FCP/FCP.pm (file contents):
Revision 1.15 by root, Wed Sep 10 05:14:03 2003 UTC vs.
Revision 1.28 by root, Thu May 13 16:13:42 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.06; 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) {
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) = @_;
177 warn "progress<$txn,$type," . (join ":", %$attr) . ">\n"; 229
230 $self->{progress}->($self, $txn, $type, $attr)
231 if $self->{progress};
178} 232}
179 233
180=item $txn = $fcp->txn(type => attr => val,...) 234=item $txn = $fcp->txn(type => attr => val,...)
181 235
182The low-level interface to transactions. Don't use it. 236The low-level interface to transactions. Don't use it.
213 my $txn = "Net::FCP::Txn::$type"->new(fcp => $self, type => tolc $type, attr => \%attr); 267 my $txn = "Net::FCP::Txn::$type"->new(fcp => $self, type => tolc $type, attr => \%attr);
214 268
215 $txn; 269 $txn;
216} 270}
217 271
218sub _txn($&) { 272{ # transactions
273
274my $txn = sub {
219 my ($name, $sub) = @_; 275 my ($name, $sub) = @_;
220 *{"$name\_txn"} = $sub; 276 *{"txn_$name"} = $sub;
221 *{$name} = sub { $sub->(@_)->result }; 277 *{$name} = sub { $sub->(@_)->result };
222} 278};
223 279
224=item $txn = $fcp->txn_client_hello 280=item $txn = $fcp->txn_client_hello
225 281
226=item $nodehello = $fcp->client_hello 282=item $nodehello = $fcp->client_hello
227 283
233 protocol => "1.2", 289 protocol => "1.2",
234 } 290 }
235 291
236=cut 292=cut
237 293
238_txn client_hello => sub { 294$txn->(client_hello => sub {
239 my ($self) = @_; 295 my ($self) = @_;
240 296
241 $self->txn ("client_hello"); 297 $self->txn ("client_hello");
242}; 298});
243 299
244=item $txn = $fcp->txn_client_info 300=item $txn = $fcp->txn_client_info
245 301
246=item $nodeinfo = $fcp->client_info 302=item $nodeinfo = $fcp->client_info
247 303
271 routing_time => "a5", 327 routing_time => "a5",
272 } 328 }
273 329
274=cut 330=cut
275 331
276_txn client_info => sub { 332$txn->(client_info => sub {
277 my ($self) = @_; 333 my ($self) = @_;
278 334
279 $self->txn ("client_info"); 335 $self->txn ("client_info");
280}; 336});
281 337
282=item $txn = $fcp->txn_generate_chk ($metadata, $data) 338=item $txn = $fcp->txn_generate_chk ($metadata, $data[, $cipher])
283 339
284=item $uri = $fcp->generate_chk ($metadata, $data) 340=item $uri = $fcp->generate_chk ($metadata, $data[, $cipher])
285 341
286Creates a new CHK, given the metadata and data. UNTESTED. 342Calculates a CHK, given the metadata and data. C<$cipher> is either
343C<Rijndael> or C<Twofish>, with the latter being the default.
287 344
288=cut 345=cut
289 346
290_txn generate_chk => sub { 347$txn->(generate_chk => sub {
291 my ($self, $metadata, $data) = @_; 348 my ($self, $metadata, $data, $cipher) = @_;
292 349
293 $self->txn (generate_chk => data => "$data$metadata", metadata_length => length $metadata); 350 $self->txn (generate_chk =>
351 data => "$metadata$data",
352 metadata_length => xeh length $metadata,
353 cipher => $cipher || "Twofish");
294}; 354});
295 355
296=item $txn = $fcp->txn_generate_svk_pair 356=item $txn = $fcp->txn_generate_svk_pair
297 357
298=item ($public, $private) = @{ $fcp->generate_svk_pair } 358=item ($public, $private) = @{ $fcp->generate_svk_pair }
299 359
304 "ZnmvMITaTXBMFGl4~jrjuyWxOWg" 364 "ZnmvMITaTXBMFGl4~jrjuyWxOWg"
305 ] 365 ]
306 366
307=cut 367=cut
308 368
309_txn generate_svk_pair => sub { 369$txn->(generate_svk_pair => sub {
310 my ($self) = @_; 370 my ($self) = @_;
311 371
312 $self->txn ("generate_svk_pair"); 372 $self->txn ("generate_svk_pair");
313}; 373});
314 374
315=item $txn = $fcp->txn_insert_private_key ($private) 375=item $txn = $fcp->txn_insert_private_key ($private)
316 376
317=item $uri = $fcp->insert_private_key ($private) 377=item $public = $fcp->insert_private_key ($private)
318 378
319Inserts a private key. $private can be either an insert URI (must start 379Inserts 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 380with C<freenet:SSK@>) or a raw private key (i.e. the private value you get
321from C<generate_svk_pair>). 381back from C<generate_svk_pair>).
322 382
323Returns the public key. 383Returns the public key.
324 384
325UNTESTED. 385UNTESTED.
326 386
327=cut 387=cut
328 388
329_txn insert_private_key => sub { 389$txn->(insert_private_key => sub {
330 my ($self, $privkey) = @_; 390 my ($self, $privkey) = @_;
331 391
332 $self->txn (invert_private_key => private => $privkey); 392 $self->txn (invert_private_key => private => $privkey);
333}; 393});
334 394
335=item $txn = $fcp->txn_get_size ($uri) 395=item $txn = $fcp->txn_get_size ($uri)
336 396
337=item $length = $fcp->get_size ($uri) 397=item $length = $fcp->get_size ($uri)
338 398
341 401
342UNTESTED. 402UNTESTED.
343 403
344=cut 404=cut
345 405
346_txn get_size => sub { 406$txn->(get_size => sub {
347 my ($self, $uri) = @_; 407 my ($self, $uri) = @_;
348 408
349 $self->txn (get_size => URI => $uri); 409 $self->txn (get_size => URI => $uri);
350}; 410});
351 411
352=item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]]) 412=item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]])
353 413
354=item ($metadata, $data) = @{ $fcp->client_get ($uri, $htl, $removelocal) 414=item ($metadata, $data) = @{ $fcp->client_get ($uri, $htl, $removelocal)
355 415
356Fetches a (small, as it should fit into memory) file from 416Fetches a (small, as it should fit into memory) file from
357freenet. C<$meta> is the metadata (as returned by C<parse_metadata> or 417freenet. C<$meta> is the metadata (as returned by C<parse_metadata> or
358C<undef>). 418C<undef>).
419
420The C<$uri> should begin with C<freenet:>, but the scheme is currently
421added, if missing.
359 422
360Due to the overhead, a better method to download big files should be used. 423Due to the overhead, a better method to download big files should be used.
361 424
362 my ($meta, $data) = @{ 425 my ($meta, $data) = @{
363 $fcp->client_get ( 426 $fcp->client_get (
365 ) 428 )
366 }; 429 };
367 430
368=cut 431=cut
369 432
370_txn client_get => sub { 433$txn->(client_get => sub {
371 my ($self, $uri, $htl, $removelocal) = @_; 434 my ($self, $uri, $htl, $removelocal) = @_;
372 435
373 $self->txn (client_get => URI => $uri, hops_to_live => ($htl || 15), remove_local_key => $removelocal ? "true" : "false"); 436 $uri =~ s/^freenet://;
437 $uri = "freenet:$uri";
438
439 $self->txn (client_get => URI => $uri, hops_to_live => xeh (defined $htl ? $htl : 15),
440 remove_local_key => $removelocal ? "true" : "false");
374}; 441});
375 442
443=item $txn = $fcp->txn_client_put ($uri, $metadata, $data, $htl, $removelocal)
444
445=item my $uri = $fcp->client_put ($uri, $metadata, $data, $htl, $removelocal);
446
447Insert a new key. If the client is inserting a CHK, the URI may be
448abbreviated as just CHK@. In this case, the node will calculate the
449CHK.
450
451C<$meta> can be a reference or a string (ONLY THE STRING CASE IS IMPLEMENTED!).
452
453THIS INTERFACE IS UNTESTED AND SUBJECT TO CHANGE.
454
455=cut
456
457$txn->(client_put => sub {
458 my ($self, $uri, $meta, $data, $htl, $removelocal) = @_;
459
460 $self->txn (client_put => URI => $uri, xeh (defined $htl ? $htl : 15),
461 remove_local_key => $removelocal ? "true" : "false",
462 data => "$meta$data", metadata_length => xeh length $meta);
463});
464
465} # transactions
466
376=item MISSING: ClientPut 467=item MISSING: (ClientPut), InsertKey
377 468
378=back 469=back
379 470
380=head2 THE Net::FCP::Txn CLASS 471=head2 THE Net::FCP::Txn CLASS
381 472
382All requests (or transactions) are executed in a asynchroneous way (LIE: 473All requests (or transactions) are executed in a asynchronous way. For
383uploads are blocking). For each request, a C<Net::FCP::Txn> object is 474each request, a C<Net::FCP::Txn> object is created (worse: a tcp
384created (worse: a tcp connection is created, too). 475connection is created, too).
385 476
386For each request there is actually a different subclass (and it's possible 477For each request there is actually a different subclass (and it's possible
387to subclass these, although of course not documented). 478to subclass these, although of course not documented).
388 479
389The most interesting method is C<result>. 480The most interesting method is C<result>.
417 while (my ($k, $v) = each %{$self->{attr}}) { 508 while (my ($k, $v) = each %{$self->{attr}}) {
418 $attr .= (Net::FCP::touc $k) . "=$v\012" 509 $attr .= (Net::FCP::touc $k) . "=$v\012"
419 } 510 }
420 511
421 if (defined $data) { 512 if (defined $data) {
422 $attr .= "DataLength=" . (length $data) . "\012"; 513 $attr .= sprintf "DataLength=%x\012", length $data;
423 $data = "Data\012$data"; 514 $data = "Data\012$data";
424 } else { 515 } else {
425 $data = "EndMessage\012"; 516 $data = "EndMessage\012";
426 } 517 }
427 518
434 and !$!{EINPROGRESS} 525 and !$!{EINPROGRESS}
435 and Carp::croak "FCP::txn: unable to connect to $self->{fcp}{host}:$self->{fcp}{port}: $!\n"; 526 and Carp::croak "FCP::txn: unable to connect to $self->{fcp}{host}:$self->{fcp}{port}: $!\n";
436 527
437 $self->{sbuf} = 528 $self->{sbuf} =
438 "\x00\x00\x00\x02" 529 "\x00\x00\x00\x02"
439 . Net::FCP::touc $self->{type} 530 . (Net::FCP::touc $self->{type})
440 . "\012$attr$data"; 531 . "\012$attr$data";
441 532
442 #$fh->shutdown (1); # freenet buggy?, well, it's java... 533 #shutdown $fh, 1; # freenet buggy?, well, it's java...
443 534
444 $self->{fh} = $fh; 535 $self->{fh} = $fh;
445 536
446 $self->{w} = $EVENT->new_from_fh ($fh)->cb(sub { $self->fh_ready_w })->poll(0, 1, 1); 537 $self->{w} = $EVENT->new_from_fh ($fh)->cb(sub { $self->fh_ready_w })->poll(0, 1, 1);
447 538
483 574
484sub userdata($$) { 575sub userdata($$) {
485 my ($self, $data) = @_; 576 my ($self, $data) = @_;
486 $self->{userdata} = $data; 577 $self->{userdata} = $data;
487 $self; 578 $self;
579}
580
581=item $txn->cancel (%attr)
582
583Cancels the operation with a C<cancel> exception anf the given attributes
584(consider at least giving the attribute C<reason>).
585
586UNTESTED.
587
588=cut
589
590sub cancel {
591 my ($self, %attr) = @_;
592 $self->throw (Net::FCP::Exception->new (cancel => { %attr }));
593 $self->set_result;
594 $self->eof;
488} 595}
489 596
490sub fh_ready_w { 597sub fh_ready_w {
491 my ($self) = @_; 598 my ($self) = @_;
492 599
532 } else { 639 } else {
533 $self->eof; 640 $self->eof;
534 } 641 }
535} 642}
536 643
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 { 644sub rcv {
546 my ($self, $type, $attr) = @_; 645 my ($self, $type, $attr) = @_;
547 646
548 $type = Net::FCP::tolc $type; 647 $type = Net::FCP::tolc $type;
549 648
567 666
568sub throw { 667sub throw {
569 my ($self, $exc) = @_; 668 my ($self, $exc) = @_;
570 669
571 $self->{exception} = $exc; 670 $self->{exception} = $exc;
572 $self->set_result (1); 671 $self->set_result;
573 $self->eof; # must be last to avoid loops 672 $self->eof; # must be last to avoid loops
574} 673}
575 674
576sub set_result { 675sub set_result {
577 my ($self, $result) = @_; 676 my ($self, $result) = @_;
589 delete $self->{w}; 688 delete $self->{w};
590 delete $self->{fh}; 689 delete $self->{fh};
591 690
592 delete $self->{fcp}{txn}{$self}; 691 delete $self->{fcp}{txn}{$self};
593 692
594 $self->set_result; # just in case 693 unless (exists $self->{result}) {
694 $self->throw (Net::FCP::Exception->new (short_data => {
695 reason => "unexpected eof or internal node error",
696 }));
697 }
595} 698}
596 699
597sub progress { 700sub progress {
598 my ($self, $type, $attr) = @_; 701 my ($self, $type, $attr) = @_;
702
599 $self->{fcp}->progress ($self, $type, $attr); 703 $self->{fcp}->progress ($self, $type, $attr);
600} 704}
601 705
602=item $result = $txn->result 706=item $result = $txn->result
603 707
604Waits until a result is available and then returns it. 708Waits until a result is available and then returns it.
605 709
606This waiting is (depending on your event model) not very efficient, as it 710This waiting is (depending on your event model) not very efficient, as it
607is done outside the "mainloop". 711is done outside the "mainloop". The biggest problem, however, is that it's
712blocking one thread of execution. Try to use the callback mechanism, if
713possible, and call result from within the callback (or after is has been
714run), as then no waiting is necessary.
608 715
609=cut 716=cut
610 717
611sub result { 718sub result {
612 my ($self) = @_; 719 my ($self) = @_;
643use base Net::FCP::Txn; 750use base Net::FCP::Txn;
644 751
645sub rcv_success { 752sub rcv_success {
646 my ($self, $attr) = @_; 753 my ($self, $attr) = @_;
647 754
648 $self->set_result ($attr); 755 $self->set_result ($attr->{uri});
649} 756}
650 757
651package Net::FCP::Txn::GenerateSVKPair; 758package Net::FCP::Txn::GenerateSVKPair;
652 759
653use base Net::FCP::Txn; 760use base Net::FCP::Txn;
654 761
655sub rcv_success { 762sub rcv_success {
656 my ($self, $attr) = @_; 763 my ($self, $attr) = @_;
657
658 $self->set_result ([$attr->{PublicKey}, $attr->{PrivateKey}]); 764 $self->set_result ([$attr->{PublicKey}, $attr->{PrivateKey}]);
659} 765}
660 766
661package Net::FCP::Txn::InvertPrivateKey; 767package Net::FCP::Txn::InsertPrivateKey;
662 768
663use base Net::FCP::Txn; 769use base Net::FCP::Txn;
664 770
665sub rcv_success { 771sub rcv_success {
666 my ($self, $attr) = @_; 772 my ($self, $attr) = @_;
667
668 $self->set_result ($attr->{PublicKey}); 773 $self->set_result ($attr->{PublicKey});
669} 774}
670 775
671package Net::FCP::Txn::GetSize; 776package Net::FCP::Txn::GetSize;
672 777
673use base Net::FCP::Txn; 778use base Net::FCP::Txn;
674 779
675sub rcv_success { 780sub rcv_success {
676 my ($self, $attr) = @_; 781 my ($self, $attr) = @_;
677
678 $self->set_result ($attr->{Length}); 782 $self->set_result (hex $attr->{Length});
679} 783}
680 784
681package Net::FCP::Txn::GetPut; 785package Net::FCP::Txn::GetPut;
682 786
683# base class for get and put 787# base class for get and put
684 788
685use base Net::FCP::Txn; 789use base Net::FCP::Txn;
686 790
687*rcv_uri_error = \&Net::FCP::Txn::rcv_throw_exception; 791*rcv_uri_error = \&Net::FCP::Txn::rcv_throw_exception;
688*rcv_route_not_found = \&Net::FCP::Txn::rcv_throw_exception; 792*rcv_route_not_found = \&Net::FCP::Txn::rcv_throw_exception;
689 793
690sub rcv_restarted { 794sub rcv_restarted {
691 my ($self, $attr, $type) = @_; 795 my ($self, $attr, $type) = @_;
692 796
693 delete $self->{datalength}; 797 delete $self->{datalength};
701 805
702use base Net::FCP::Txn::GetPut; 806use base Net::FCP::Txn::GetPut;
703 807
704*rcv_data_not_found = \&Net::FCP::Txn::rcv_throw_exception; 808*rcv_data_not_found = \&Net::FCP::Txn::rcv_throw_exception;
705 809
706sub rcv_data_found { 810sub 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) = @_; 811 my ($self, $chunk) = @_;
812
813 $self->{data} .= $chunk;
814
815 $self->progress ("data", { chunk => length $chunk, received => length $self->{data}, total => $self->{datalength} });
717 816
718 if ($self->{datalength} == length $self->{data}) { 817 if ($self->{datalength} == length $self->{data}) {
719 my $data = delete $self->{data}; 818 my $data = delete $self->{data};
720 my $meta = Net::FCP::parse_metadata substr $data, 0, $self->{metalength}, ""; 819 my $meta = Net::FCP::parse_metadata substr $data, 0, $self->{metalength}, "";
721 820
722 $self->set_result ([$meta, $data]); 821 $self->set_result ([$meta, $data]);
723 } elsif (!exists $self->{result}) { 822 $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 } 823 }
824}
825
826sub rcv_data_found {
827 my ($self, $attr, $type) = @_;
828
829 $self->progress ($type, $attr);
830
831 $self->{datalength} = hex $attr->{data_length};
832 $self->{metalength} = hex $attr->{metadata_length};
730} 833}
731 834
732package Net::FCP::Txn::ClientPut; 835package Net::FCP::Txn::ClientPut;
733 836
734use base Net::FCP::Txn::GetPut; 837use base Net::FCP::Txn::GetPut;
744sub rcv_success { 847sub rcv_success {
745 my ($self, $attr, $type) = @_; 848 my ($self, $attr, $type) = @_;
746 $self->set_result ($attr); 849 $self->set_result ($attr);
747} 850}
748 851
852=back
853
854=head2 The Net::FCP::Exception CLASS
855
856Any unexpected (non-standard) responses that make it impossible to return
857the advertised result will result in an exception being thrown when the
858C<result> method is called.
859
860These exceptions are represented by objects of this class.
861
862=over 4
863
864=cut
865
749package Net::FCP::Exception; 866package Net::FCP::Exception;
750 867
751use overload 868use overload
752 '""' => sub { 869 '""' => sub {
753 "Net::FCP::Exception<<$_[0][0]," . (join ":", %{$_[0][1]}) . ">>\n"; 870 "Net::FCP::Exception<<$_[0][0]," . (join ":", %{$_[0][1]}) . ">>";
754 }; 871 };
872
873=item $exc = new Net::FCP::Exception $type, \%attr
874
875Create a new exception object of the given type (a string like
876C<route_not_found>), and a hashref containing additional attributes
877(usually the attributes of the message causing the exception).
878
879=cut
755 880
756sub new { 881sub new {
757 my ($class, $type, $attr) = @_; 882 my ($class, $type, $attr) = @_;
758 883
759 bless [Net::FCP::tolc $type, { %$attr }], $class; 884 bless [Net::FCP::tolc $type, { %$attr }], $class;
760} 885}
761 886
887=item $exc->type([$type])
888
889With no arguments, returns the exception type. Otherwise a boolean
890indicating wether the exception is of the given type is returned.
891
892=cut
893
894sub type {
895 my ($self, $type) = @_;
896
897 @_ >= 2
898 ? $self->[0] eq $type
899 : $self->[0];
900}
901
902=item $exc->attr([$attr])
903
904With no arguments, returns the attributes. Otherwise the named attribute
905value is returned.
906
907=cut
908
909sub attr {
910 my ($self, $attr) = @_;
911
912 @_ >= 2
913 ? $self->[1]{$attr}
914 : $self->[1];
915}
916
762=back 917=back
763 918
764=head1 SEE ALSO 919=head1 SEE ALSO
765 920
766L<http://freenet.sf.net>. 921L<http://freenet.sf.net>.
772 Marc Lehmann <pcg@goof.com> 927 Marc Lehmann <pcg@goof.com>
773 http://www.goof.com/pcg/marc/ 928 http://www.goof.com/pcg/marc/
774 929
775=cut 930=cut
776 931
932package Net::FCP::Event::Auto;
933
934my @models = (
935 [Coro => Coro::Event::],
936 [Event => Event::],
937 [Glib => Glib::],
938 [Tk => Tk::],
939);
940
941sub AUTOLOAD {
942 $AUTOLOAD =~ s/.*://;
943
944 for (@models) {
945 my ($model, $package) = @$_;
946 if (defined ${"$package\::VERSION"}) {
947 $EVENT = "Net::FCP::Event::$model";
948 eval "require $EVENT"; die if $@;
949 goto &{"$EVENT\::$AUTOLOAD"};
950 }
951 }
952
953 for (@models) {
954 my ($model, $package) = @$_;
955 $EVENT = "Net::FCP::Event::$model";
956 if (eval "require $EVENT") {
957 goto &{"$EVENT\::$AUTOLOAD"};
958 }
959 }
960
961 die "No event module selected for Net::FCP and autodetect failed. Install any of these: Coro, Event, Glib or Tk.";
962}
963
7771; 9641;
778 965

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines