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.11 by root, Tue Sep 9 18:52:39 2003 UTC vs.
Revision 1.19 by root, Sun Sep 14 09:48:01 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
40=head2 FREENET BASICS
41
42Ok, this section will not explain any freenet basics to you, just some
43problems I found that you might want to avoid:
44
45=over 4
46
47=item freenet URIs are _NOT_ URIs
48
49Whenever a "uri" is required by the protocol, freenet expects a kind of
50URI prefixed with the "freenet:" scheme, e.g. "freenet:CHK...". However,
51these are not URIs, as freeent fails to parse them correctly, that is, you
52must unescape an escaped characters ("%2c" => ",") yourself. Maybe in the
53future this library will do it for you, so watch out for this incompatible
54change.
55
56=item Numbers are in HEX
57
58Virtually every number in the FCP protocol is in hex. Be sure to use
59C<hex()> on all such numbers, as the module (currently) does nothing to
60convert these for you.
61
62=back
63
40=head2 THE Net::FCP CLASS 64=head2 THE Net::FCP CLASS
41 65
42=over 4 66=over 4
43 67
44=cut 68=cut
45 69
46package Net::FCP; 70package Net::FCP;
47 71
48use Carp; 72use Carp;
49use IO::Socket::INET;
50 73
51$VERSION = 0.05; 74$VERSION = 0.08;
52 75
53no warnings; 76no warnings;
54 77
55our $EVENT = Net::FCP::Event::Auto::; 78our $EVENT = Net::FCP::Event::Auto::;
56$EVENT = Net::FCP::Event::Event;#d# 79$EVENT = Net::FCP::Event::Event;#d#
62 if (/^event=(\w+)$/) { 85 if (/^event=(\w+)$/) {
63 $EVENT = "Net::FCP::Event::$1"; 86 $EVENT = "Net::FCP::Event::$1";
64 } 87 }
65 } 88 }
66 eval "require $EVENT"; 89 eval "require $EVENT";
90 die $@ if $@;
67} 91}
68 92
69sub touc($) { 93sub touc($) {
70 local $_ = shift; 94 local $_ = shift;
71 1 while s/((?:^|_)(?:svk|chk|uri)(?:_|$))/\U$1/; 95 1 while s/((?:^|_)(?:svk|chk|uri)(?:_|$))/\U$1/;
93 117
94 ( 118 (
95 version => { revision => 1 }, 119 version => { revision => 1 },
96 document => [ 120 document => [
97 { 121 {
98 "info.format" => "image/jpeg", 122 info => { format" => "image/jpeg" },
99 name => "background.jpg", 123 name => "background.jpg",
100 "redirect.target" => "freenet:CHK\@ZcagI,ra726bSw" 124 redirect => { target => "freenet:CHK\@ZcagI,ra726bSw" },
101 }, 125 },
102 { 126 {
103 "info.format" => "text/html", 127 info => { format" => "text/html" },
104 name => ".next", 128 name => ".next",
105 "redirect.target" => "freenet:SSK\@ilUPAgM/TFEE/3" 129 redirect => { target => "freenet:SSK\@ilUPAgM/TFEE/3" },
106 }, 130 },
107 { 131 {
108 "info.format" => "text/html", 132 info => { format" => "text/html" },
109 "redirect.target" => "freenet:CHK\@8M8Po8ucwI,8xA" 133 redirect => { target => "freenet:CHK\@8M8Po8ucwI,8xA" },
110 } 134 }
111 ] 135 ]
112 ) 136 )
113 137
114=cut 138=cut
121 my $hdr = $meta->{version} = {}; 145 my $hdr = $meta->{version} = {};
122 146
123 for (;;) { 147 for (;;) {
124 while ($data =~ /\G([^=\015\012]+)=([^\015\012]*)\015?\012/gc) { 148 while ($data =~ /\G([^=\015\012]+)=([^\015\012]*)\015?\012/gc) {
125 my ($k, $v) = ($1, $2); 149 my ($k, $v) = ($1, $2);
126 $hdr->{tolc $k} = $v; 150 my @p = split /\./, tolc $k, 3;
151
152 $hdr->{$p[0]} = $v if @p == 1; # lamest code I ever wrote
153 $hdr->{$p[0]}{$p[1]} = $v if @p == 2;
154 $hdr->{$p[0]}{$p[1]}{$p[2]} = $v if @p == 3;
155 die "FATAL: 4+ dot metadata" if @p >= 4;
127 } 156 }
128 157
129 if ($data =~ /\GEndPart\015?\012/gc) { 158 if ($data =~ /\GEndPart\015?\012/gc) {
159 # nop
130 } elsif ($data =~ /\GEnd\015?\012/gc) { 160 } elsif ($data =~ /\GEnd(\015?\012|$)/gc) {
131 last; 161 last;
132 } elsif ($data =~ /\G([A-Za-z0-9.\-]+)\015?\012/gcs) { 162 } elsif ($data =~ /\G([A-Za-z0-9.\-]+)\015?\012/gcs) {
133 push @{$meta->{tolc $1}}, $hdr = {}; 163 push @{$meta->{tolc $1}}, $hdr = {};
134 } elsif ($data =~ /\G(.*)/gcs) { 164 } elsif ($data =~ /\G(.*)/gcs) {
165 print STDERR "metadata format error ($1), please report this string: <<$data>>";
135 die "metadata format error ($1)"; 166 die "metadata format error";
136 } 167 }
137 } 168 }
138 } 169 }
139 170
140 #$meta->{tail} = substr $data, pos $data; 171 #$meta->{tail} = substr $data, pos $data;
146 177
147Create a new virtual FCP connection to the given host and port (default 178Create a new virtual FCP connection to the given host and port (default
148127.0.0.1:8481, or the environment variables C<FREDHOST> and C<FREDPORT>). 179127.0.0.1:8481, or the environment variables C<FREDHOST> and C<FREDPORT>).
149 180
150Connections are virtual because no persistent physical connection is 181Connections are virtual because no persistent physical connection is
182established.
183
184=begin comment
185
151established. However, the existance of the node is checked by executing a 186However, the existance of the node is checked by executing a
152C<ClientHello> transaction. 187C<ClientHello> transaction.
188
189=end
153 190
154=cut 191=cut
155 192
156sub new { 193sub new {
157 my $class = shift; 194 my $class = shift;
158 my $self = bless { @_ }, $class; 195 my $self = bless { @_ }, $class;
159 196
160 $self->{host} ||= $ENV{FREDHOST} || "127.0.0.1"; 197 $self->{host} ||= $ENV{FREDHOST} || "127.0.0.1";
161 $self->{port} ||= $ENV{FREDPORt} || 8481; 198 $self->{port} ||= $ENV{FREDPORT} || 8481;
162 199
163 $self->{nodehello} = $self->client_hello 200 #$self->{nodehello} = $self->client_hello
164 or croak "unable to get nodehello from node\n"; 201 # or croak "unable to get nodehello from node\n";
165 202
166 $self; 203 $self;
167} 204}
168 205
169sub progress { 206sub progress {
170 my ($self, $txn, $type, $attr) = @_; 207 my ($self, $txn, $type, $attr) = @_;
171 warn "progress<$txn,$type," . (join ":", %$attr) . ">\n"; 208 #warn "progress<$txn,$type," . (join ":", %$attr) . ">\n";
172} 209}
173 210
174=item $txn = $fcp->txn(type => attr => val,...) 211=item $txn = $fcp->txn(type => attr => val,...)
175 212
176The low-level interface to transactions. Don't use it. 213The low-level interface to transactions. Don't use it.
214
215Here are some examples of using transactions:
216
217The blocking case, no (visible) transactions involved:
218
219 my $nodehello = $fcp->client_hello;
220
221A transaction used in a blocking fashion:
222
223 my $txn = $fcp->txn_client_hello;
224 ...
225 my $nodehello = $txn->result;
226
227Or shorter:
228
229 my $nodehello = $fcp->txn_client_hello->result;
230
231Setting callbacks:
232
233 $fcp->txn_client_hello->cb(
234 sub { my $nodehello => $_[0]->result }
235 );
177 236
178=cut 237=cut
179 238
180sub txn { 239sub txn {
181 my ($self, $type, %attr) = @_; 240 my ($self, $type, %attr) = @_;
185 my $txn = "Net::FCP::Txn::$type"->new(fcp => $self, type => tolc $type, attr => \%attr); 244 my $txn = "Net::FCP::Txn::$type"->new(fcp => $self, type => tolc $type, attr => \%attr);
186 245
187 $txn; 246 $txn;
188} 247}
189 248
190sub _txn($&) { 249{ # transactions
250
251my $txn = sub {
191 my ($name, $sub) = @_; 252 my ($name, $sub) = @_;
192 *{"$name\_txn"} = $sub; 253 *{"txn_$name"} = $sub;
193 *{$name} = sub { $sub->(@_)->result }; 254 *{$name} = sub { $sub->(@_)->result };
194} 255};
195 256
196=item $txn = $fcp->txn_client_hello 257=item $txn = $fcp->txn_client_hello
197 258
198=item $nodehello = $fcp->client_hello 259=item $nodehello = $fcp->client_hello
199 260
205 protocol => "1.2", 266 protocol => "1.2",
206 } 267 }
207 268
208=cut 269=cut
209 270
210_txn client_hello => sub { 271$txn->(client_hello => sub {
211 my ($self) = @_; 272 my ($self) = @_;
212 273
213 $self->txn ("client_hello"); 274 $self->txn ("client_hello");
214}; 275});
215 276
216=item $txn = $fcp->txn_client_info 277=item $txn = $fcp->txn_client_info
217 278
218=item $nodeinfo = $fcp->client_info 279=item $nodeinfo = $fcp->client_info
219 280
243 routing_time => "a5", 304 routing_time => "a5",
244 } 305 }
245 306
246=cut 307=cut
247 308
248_txn client_info => sub { 309$txn->(client_info => sub {
249 my ($self) = @_; 310 my ($self) = @_;
250 311
251 $self->txn ("client_info"); 312 $self->txn ("client_info");
252}; 313});
253 314
254=item $txn = $fcp->txn_generate_chk ($metadata, $data) 315=item $txn = $fcp->txn_generate_chk ($metadata, $data)
255 316
256=item $uri = $fcp->generate_chk ($metadata, $data) 317=item $uri = $fcp->generate_chk ($metadata, $data)
257 318
258Creates a new CHK, given the metadata and data. UNTESTED. 319Creates a new CHK, given the metadata and data. UNTESTED.
259 320
260=cut 321=cut
261 322
262_txn generate_chk => sub { 323$txn->(generate_chk => sub {
263 my ($self, $metadata, $data) = @_; 324 my ($self, $metadata, $data) = @_;
264 325
265 $self->txn (generate_chk => data => "$data$metadata", metadata_length => length $metadata); 326 $self->txn (generate_chk => data => "$metadata$data", metadata_length => length $metadata);
266}; 327});
267 328
268=item $txn = $fcp->txn_generate_svk_pair 329=item $txn = $fcp->txn_generate_svk_pair
269 330
270=item ($public, $private) = @{ $fcp->generate_svk_pair } 331=item ($public, $private) = @{ $fcp->generate_svk_pair }
271 332
276 "ZnmvMITaTXBMFGl4~jrjuyWxOWg" 337 "ZnmvMITaTXBMFGl4~jrjuyWxOWg"
277 ] 338 ]
278 339
279=cut 340=cut
280 341
281_txn generate_svk_pair => sub { 342$txn->(generate_svk_pair => sub {
282 my ($self) = @_; 343 my ($self) = @_;
283 344
284 $self->txn ("generate_svk_pair"); 345 $self->txn ("generate_svk_pair");
285}; 346});
286 347
287=item $txn = $fcp->txn_insert_private_key ($private) 348=item $txn = $fcp->txn_insert_private_key ($private)
288 349
289=item $uri = $fcp->insert_private_key ($private) 350=item $public = $fcp->insert_private_key ($private)
290 351
291Inserts a private key. $private can be either an insert URI (must start 352Inserts a private key. $private can be either an insert URI (must start
292with freenet:SSK@) or a raw private key (i.e. the private value you get back 353with C<freenet:SSK@>) or a raw private key (i.e. the private value you get
293from C<generate_svk_pair>). 354back from C<generate_svk_pair>).
294 355
295Returns the public key. 356Returns the public key.
296 357
297UNTESTED. 358UNTESTED.
298 359
299=cut 360=cut
300 361
301_txn insert_private_key => sub { 362$txn->(insert_private_key => sub {
302 my ($self, $privkey) = @_; 363 my ($self, $privkey) = @_;
303 364
304 $self->txn (invert_private_key => private => $privkey); 365 $self->txn (invert_private_key => private => $privkey);
305}; 366});
306 367
307=item $txn = $fcp->txn_get_size ($uri) 368=item $txn = $fcp->txn_get_size ($uri)
308 369
309=item $length = $fcp->get_size ($uri) 370=item $length = $fcp->get_size ($uri)
310 371
313 374
314UNTESTED. 375UNTESTED.
315 376
316=cut 377=cut
317 378
318_txn get_size => sub { 379$txn->(get_size => sub {
319 my ($self, $uri) = @_; 380 my ($self, $uri) = @_;
320 381
321 $self->txn (get_size => URI => $uri); 382 $self->txn (get_size => URI => $uri);
322}; 383});
323 384
324=item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]]) 385=item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]])
325 386
326=item ($metadata, $data) = @{ $fcp->client_get ($uri, $htl, $removelocal) 387=item ($metadata, $data) = @{ $fcp->client_get ($uri, $htl, $removelocal)
327 388
337 ) 398 )
338 }; 399 };
339 400
340=cut 401=cut
341 402
342_txn client_get => sub { 403$txn->(client_get => sub {
343 my ($self, $uri, $htl, $removelocal) = @_; 404 my ($self, $uri, $htl, $removelocal) = @_;
344 405
345 $self->txn (client_get => URI => $uri, hops_to_live => ($htl || 15), remove_local => $removelocal*1); 406 $self->txn (client_get => URI => $uri, hops_to_live => (defined $htl ? $htl :15),
407 remove_local_key => $removelocal ? "true" : "false");
346}; 408});
347 409
410=item $txn = $fcp->txn_client_put ($uri, $metadata, $data, $htl, $removelocal)
411
412=item my $uri = $fcp->client_put ($uri, $metadata, $data, $htl, $removelocal);
413
414Insert a new key. If the client is inserting a CHK, the URI may be
415abbreviated as just CHK@. In this case, the node will calculate the
416CHK.
417
418C<$meta> can be a reference or a string (ONLY THE STRING CASE IS IMPLEMENTED!).
419
420THIS INTERFACE IS UNTESTED AND SUBJECT TO CHANGE.
421
422=cut
423
424$txn->(client_put => sub {
425 my ($self, $uri, $meta, $data, $htl, $removelocal) = @_;
426
427 $self->txn (client_put => URI => $uri, hops_to_live => (defined $htl ? $htl :15),
428 remove_local_key => $removelocal ? "true" : "false",
429 data => "$meta$data", metadata_length => length $meta);
430});
431
432} # transactions
433
348=item MISSING: ClientPut 434=item MISSING: (ClientPut), InsretKey
349 435
350=back 436=back
351 437
352=head2 THE Net::FCP::Txn CLASS 438=head2 THE Net::FCP::Txn CLASS
353 439
364 450
365=cut 451=cut
366 452
367package Net::FCP::Txn; 453package Net::FCP::Txn;
368 454
455use Fcntl;
456use Socket;
457
369=item new arg => val,... 458=item new arg => val,...
370 459
371Creates a new C<Net::FCP::Txn> object. Not normally used. 460Creates a new C<Net::FCP::Txn> object. Not normally used.
372 461
373=cut 462=cut
374 463
375sub new { 464sub new {
376 my $class = shift; 465 my $class = shift;
377 my $self = bless { @_ }, $class; 466 my $self = bless { @_ }, $class;
467
468 $self->{signal} = $EVENT->new_signal;
469
470 $self->{fcp}{txn}{$self} = $self;
378 471
379 my $attr = ""; 472 my $attr = "";
380 my $data = delete $self->{attr}{data}; 473 my $data = delete $self->{attr}{data};
381 474
382 while (my ($k, $v) = each %{$self->{attr}}) { 475 while (my ($k, $v) = each %{$self->{attr}}) {
388 $data = "Data\012$data"; 481 $data = "Data\012$data";
389 } else { 482 } else {
390 $data = "EndMessage\012"; 483 $data = "EndMessage\012";
391 } 484 }
392 485
393 my $fh = new IO::Socket::INET 486 socket my $fh, PF_INET, SOCK_STREAM, 0
394 PeerHost => $self->{fcp}{host}, 487 or Carp::croak "unable to create new tcp socket: $!";
395 PeerPort => $self->{fcp}{port}
396 or Carp::croak "FCP::txn: unable to connect to $self->{fcp}{host}:$self->{fcp}{port}: $!\n";
397
398 binmode $fh, ":raw"; 488 binmode $fh, ":raw";
489 fcntl $fh, F_SETFL, O_NONBLOCK;
490 connect $fh, (sockaddr_in $self->{fcp}{port}, inet_aton $self->{fcp}{host})
491 and !$!{EWOULDBLOCK}
492 and !$!{EINPROGRESS}
493 and Carp::croak "FCP::txn: unable to connect to $self->{fcp}{host}:$self->{fcp}{port}: $!\n";
399 494
400 if (0) { 495 $self->{sbuf} =
401 print 496 "\x00\x00\x00\x02"
402 Net::FCP::touc $self->{type}, "\012",
403 $attr,
404 $data, "\012";
405 }
406
407 print $fh
408 "\x00\x00", "\x00\x02", # SESSID, PRESID
409 Net::FCP::touc $self->{type}, "\012", 497 . Net::FCP::touc $self->{type}
410 $attr, 498 . "\012$attr$data";
411 $data;
412 499
413 #$fh->shutdown (1); # freenet buggy?, well, it's java... 500 #$fh->shutdown (1); # freenet buggy?, well, it's java...
414 501
415 $self->{fh} = $fh; 502 $self->{fh} = $fh;
416 503
417 $EVENT->reg_r_cb ($self); 504 $self->{w} = $EVENT->new_from_fh ($fh)->cb(sub { $self->fh_ready_w })->poll(0, 1, 1);
418 505
419 $self; 506 $self;
420} 507}
421 508
509=item $txn = $txn->cb ($coderef)
510
511Sets a callback to be called when the request is finished. The coderef
512will be called with the txn as it's sole argument, so it has to call
513C<result> itself.
514
515Returns the txn object, useful for chaining.
516
517Example:
518
519 $fcp->txn_client_get ("freenet:CHK....")
520 ->userdata ("ehrm")
521 ->cb(sub {
522 my $data = shift->result;
523 });
524
525=cut
526
527sub cb($$) {
528 my ($self, $cb) = @_;
529 $self->{cb} = $cb;
530 $self;
531}
532
422=item $userdata = $txn->userdata ([$userdata]) 533=item $txn = $txn->userdata ([$userdata])
423 534
424Get and/or set user-specific data. This is useful in progress callbacks. 535Set user-specific data. This is useful in progress callbacks. The data can be accessed
536using C<< $txn->{userdata} >>.
425 537
426=cut 538Returns the txn object, useful for chaining.
427 539
540=cut
541
428sub userdata($;$) { 542sub userdata($$) {
429 my ($self, $data) = @_; 543 my ($self, $data) = @_;
430 $self->{userdata} = $data if @_ >= 2; 544 $self->{userdata} = $data;
431 $self->{userdata}; 545 $self;
432} 546}
433 547
548=item $txn->cancel (%attr)
549
550Cancels the operation with a C<cancel> exception anf the given attributes
551(consider at least giving the attribute C<reason>).
552
553UNTESTED.
554
555=cut
556
557sub cancel {
558 my ($self, %attr) = @_;
559 $self->throw (Net::FCP::Exception->new (cancel => { %attr }));
560 $self->set_result;
561 $self->eof;
562}
563
434sub fh_ready { 564sub fh_ready_w {
565 my ($self) = @_;
566
567 my $len = syswrite $self->{fh}, $self->{sbuf};
568
569 if ($len > 0) {
570 substr $self->{sbuf}, 0, $len, "";
571 unless (length $self->{sbuf}) {
572 fcntl $self->{fh}, F_SETFL, 0;
573 $self->{w}->cb(sub { $self->fh_ready_r })->poll (1, 0, 1);
574 }
575 } elsif (defined $len) {
576 $self->throw (Net::FCP::Exception->new (network_error => { reason => "unexpected end of file while writing" }));
577 } else {
578 $self->throw (Net::FCP::Exception->new (network_error => { reason => "$!" }));
579 }
580}
581
582sub fh_ready_r {
435 my ($self) = @_; 583 my ($self) = @_;
436 584
437 if (sysread $self->{fh}, $self->{buf}, 65536, length $self->{buf}) { 585 if (sysread $self->{fh}, $self->{buf}, 65536, length $self->{buf}) {
438 for (;;) { 586 for (;;) {
439 if ($self->{datalen}) { 587 if ($self->{datalen}) {
588 #warn "expecting new datachunk $self->{datalen}, got ".(length $self->{buf})."\n";#d#
440 if (length $self->{buf} >= $self->{datalen}) { 589 if (length $self->{buf} >= $self->{datalen}) {
441 $self->rcv_data (substr $self->{buf}, 0, delete $self->{datalen}, ""); 590 $self->rcv_data (substr $self->{buf}, 0, delete $self->{datalen}, "");
442 } else { 591 } else {
443 last; 592 last;
444 } 593 }
445 } elsif ($self->{buf} =~ s/^DataChunk\015?\012Length=([0-9a-fA-F]+)\015?\012Data\015?\012//) { 594 } elsif ($self->{buf} =~ s/^DataChunk\015?\012Length=([0-9a-fA-F]+)\015?\012Data\015?\012//) {
446 $self->{datalen} = hex $1; 595 $self->{datalen} = hex $1;
596 #warn "expecting new datachunk $self->{datalen}\n";#d#
447 } elsif ($self->{buf} =~ s/^([a-zA-Z]+)\015?\012(?:(.+?)\015?\012)?EndMessage\015?\012//s) { 597 } elsif ($self->{buf} =~ s/^([a-zA-Z]+)\015?\012(?:(.+?)\015?\012)?EndMessage\015?\012//s) {
448 $self->rcv ($1, { 598 $self->rcv ($1, {
449 map { my ($a, $b) = split /=/, $_, 2; ((Net::FCP::tolc $a), $b) } 599 map { my ($a, $b) = split /=/, $_, 2; ((Net::FCP::tolc $a), $b) }
450 split /\015?\012/, $2 600 split /\015?\012/, $2
451 }); 601 });
452 } else { 602 } else {
453 last; 603 last;
454 } 604 }
455 } 605 }
456 } else { 606 } else {
457 $EVENT->unreg_r_cb ($self);
458 delete $self->{fh};
459 $self->eof; 607 $self->eof;
460 } 608 }
461}
462
463sub rcv_data {
464 my ($self, $chunk) = @_;
465
466 $self->{data} .= $chunk;
467
468 $self->progress ("data", { chunk => length $chunk, total => length $self->{data}, end => $self->{datalength} });
469} 609}
470 610
471sub rcv { 611sub rcv {
472 my ($self, $type, $attr) = @_; 612 my ($self, $type, $attr) = @_;
473 613
480 } else { 620 } else {
481 warn "received unexpected reply type '$type' for '$self->{type}', ignoring\n"; 621 warn "received unexpected reply type '$type' for '$self->{type}', ignoring\n";
482 } 622 }
483} 623}
484 624
625# used as a default exception thrower
626sub rcv_throw_exception {
627 my ($self, $attr, $type) = @_;
628 $self->throw (Net::FCP::Exception->new ($type, $attr));
629}
630
631*rcv_failed = \&Net::FCP::Txn::rcv_throw_exception;
632*rcv_format_error = \&Net::FCP::Txn::rcv_throw_exception;
633
485sub throw { 634sub throw {
486 my ($self, $exc) = @_; 635 my ($self, $exc) = @_;
487 636
488 $self->{exception} = $exc; 637 $self->{exception} = $exc;
489 $self->set_result (1); 638 $self->set_result;
639 $self->eof; # must be last to avoid loops
490} 640}
491 641
492sub set_result { 642sub set_result {
493 my ($self, $result) = @_; 643 my ($self, $result) = @_;
494 644
495 $self->{result} = $result unless exists $self->{result}; 645 unless (exists $self->{result}) {
646 $self->{result} = $result;
647 $self->{cb}->($self) if exists $self->{cb};
648 $self->{signal}->send;
649 }
496} 650}
497 651
498sub eof { 652sub eof {
499 my ($self) = @_; 653 my ($self) = @_;
500 $self->set_result; 654
655 delete $self->{w};
656 delete $self->{fh};
657
658 delete $self->{fcp}{txn}{$self};
659
660 unless (exists $self->{result}) {
661 $self->throw (Net::FCP::Exception->new (short_data => {
662 reason => "unexpected eof or internal node error",
663 }));
664 }
501} 665}
502 666
503sub progress { 667sub progress {
504 my ($self, $type, $attr) = @_; 668 my ($self, $type, $attr) = @_;
505 $self->{fcp}->progress ($self, $type, $attr); 669 $self->{fcp}->progress ($self, $type, $attr);
515=cut 679=cut
516 680
517sub result { 681sub result {
518 my ($self) = @_; 682 my ($self) = @_;
519 683
520 $EVENT->wait_event while !exists $self->{result}; 684 $self->{signal}->wait while !exists $self->{result};
521 685
522 die $self->{exception} if $self->{exception}; 686 die $self->{exception} if $self->{exception};
523 687
524 return $self->{result}; 688 return $self->{result};
525}
526
527sub DESTROY {
528 $EVENT->unreg_r_cb ($_[0]);
529 #$EVENT->unreg_w_cb ($_[0]);
530} 689}
531 690
532package Net::FCP::Txn::ClientHello; 691package Net::FCP::Txn::ClientHello;
533 692
534use base Net::FCP::Txn; 693use base Net::FCP::Txn;
563 722
564use base Net::FCP::Txn; 723use base Net::FCP::Txn;
565 724
566sub rcv_success { 725sub rcv_success {
567 my ($self, $attr) = @_; 726 my ($self, $attr) = @_;
568
569 $self->set_result ([$attr->{PublicKey}, $attr->{PrivateKey}]); 727 $self->set_result ([$attr->{PublicKey}, $attr->{PrivateKey}]);
570} 728}
571 729
572package Net::FCP::Txn::InvertPrivateKey; 730package Net::FCP::Txn::InsertPrivateKey;
573 731
574use base Net::FCP::Txn; 732use base Net::FCP::Txn;
575 733
576sub rcv_success { 734sub rcv_success {
577 my ($self, $attr) = @_; 735 my ($self, $attr) = @_;
578
579 $self->set_result ($attr->{PublicKey}); 736 $self->set_result ($attr->{PublicKey});
580} 737}
581 738
582package Net::FCP::Txn::GetSize; 739package Net::FCP::Txn::GetSize;
583 740
584use base Net::FCP::Txn; 741use base Net::FCP::Txn;
585 742
586sub rcv_success { 743sub rcv_success {
587 my ($self, $attr) = @_; 744 my ($self, $attr) = @_;
588
589 $self->set_result ($attr->{Length}); 745 $self->set_result ($attr->{Length});
590} 746}
591 747
748package Net::FCP::Txn::GetPut;
749
750# base class for get and put
751
752use base Net::FCP::Txn;
753
754*rcv_uri_error = \&Net::FCP::Txn::rcv_throw_exception;
755*rcv_route_not_found = \&Net::FCP::Txn::rcv_throw_exception;
756
757sub rcv_restarted {
758 my ($self, $attr, $type) = @_;
759
760 delete $self->{datalength};
761 delete $self->{metalength};
762 delete $self->{data};
763
764 $self->progress ($type, $attr);
765}
766
592package Net::FCP::Txn::ClientGet; 767package Net::FCP::Txn::ClientGet;
593 768
594use base Net::FCP::Txn; 769use base Net::FCP::Txn::GetPut;
770
771*rcv_data_not_found = \&Net::FCP::Txn::rcv_throw_exception;
772
773sub rcv_data {
774 my ($self, $chunk) = @_;
775
776 $self->{data} .= $chunk;
777
778 $self->progress ("data", { chunk => length $chunk, received => length $self->{data}, total => $self->{datalength} });
779
780 if ($self->{datalength} == length $self->{data}) {
781 my $data = delete $self->{data};
782 my $meta = Net::FCP::parse_metadata substr $data, 0, $self->{metalength}, "";
783
784 $self->set_result ([$meta, $data]);
785 }
786}
595 787
596sub rcv_data_found { 788sub rcv_data_found {
597 my ($self, $attr, $type) = @_; 789 my ($self, $attr, $type) = @_;
598 790
599 $self->progress ($type, $attr); 791 $self->progress ($type, $attr);
600 792
601 $self->{datalength} = hex $attr->{data_length}; 793 $self->{datalength} = hex $attr->{data_length};
602 $self->{metalength} = hex $attr->{metadata_length}; 794 $self->{metalength} = hex $attr->{metadata_length};
603} 795}
604 796
605sub rcv_route_not_found { 797package Net::FCP::Txn::ClientPut;
606 my ($self, $attr, $type) = @_;
607 798
608 $self->throw (new Net::FCP::Exception $type, $attr); 799use base Net::FCP::Txn::GetPut;
609}
610 800
611sub rcv_data_not_found { 801*rcv_size_error = \&Net::FCP::Txn::rcv_throw_exception;
612 my ($self, $attr, $type) = @_; 802*rcv_key_collision = \&Net::FCP::Txn::rcv_throw_exception;
613 803
614 $self->throw (new Net::FCP::Exception $type, $attr); 804sub rcv_pending {
615}
616
617sub rcv_format_error {
618 my ($self, $attr, $type) = @_;
619
620 $self->throw (new Net::FCP::Exception $type, $attr);
621}
622
623sub rcv_restarted {
624 my ($self, $attr, $type) = @_; 805 my ($self, $attr, $type) = @_;
625 $self->progress ($type, $attr); 806 $self->progress ($type, $attr);
626} 807}
627 808
628sub eof { 809sub rcv_success {
629 my ($self) = @_; 810 my ($self, $attr, $type) = @_;
630
631 my $data = delete $self->{data};
632 my $meta = Net::FCP::parse_metadata substr $data, 0, $self->{metalength}, "";
633
634 $self->set_result ([$meta, $data]); 811 $self->set_result ($attr);
635} 812}
813
814=back
815
816=head2 The Net::FCP::Exception CLASS
817
818Any unexpected (non-standard) responses that make it impossible to return
819the advertised result will result in an exception being thrown when the
820C<result> method is called.
821
822These exceptions are represented by objects of this class.
823
824=over 4
825
826=cut
636 827
637package Net::FCP::Exception; 828package Net::FCP::Exception;
638 829
639use overload 830use overload
640 '""' => sub { 831 '""' => sub {
641 "Net::FCP::Exception<<$_[0][0]," . (join ":", %{$_[0][1]}) . ">>\n"; 832 "Net::FCP::Exception<<$_[0][0]," . (join ":", %{$_[0][1]}) . ">>\n";
642 }; 833 };
643 834
835=item $exc = new Net::FCP::Exception $type, \%attr
836
837Create a new exception object of the given type (a string like
838C<route_not_found>), and a hashref containing additional attributes
839(usually the attributes of the message causing the exception).
840
841=cut
842
644sub new { 843sub new {
645 my ($class, $type, $attr) = @_; 844 my ($class, $type, $attr) = @_;
646 845
647 bless [$type, { %$attr }], $class; 846 bless [Net::FCP::tolc $type, { %$attr }], $class;
847}
848
849=item $exc->type([$type])
850
851With no arguments, returns the exception type. Otherwise a boolean
852indicating wether the exception is of the given type is returned.
853
854=cut
855
856sub type {
857 my ($self, $type) = @_;
858
859 @_ >= 2
860 ? $self->[0] eq $type
861 : $self->[0];
862}
863
864=item $exc->attr([$attr])
865
866With no arguments, returns the attributes. Otherwise the named attribute
867value is returned.
868
869=cut
870
871sub attr {
872 my ($self, $attr) = @_;
873
874 @_ >= 2
875 ? $self->[1]{$attr}
876 : $self->[1];
648} 877}
649 878
650=back 879=back
651 880
652=head1 SEE ALSO 881=head1 SEE ALSO

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines