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.28 by root, Thu May 13 16:13:42 2004 UTC vs.
Revision 1.39 by root, Tue Nov 28 15:18:17 2006 UTC

13 13
14=head1 DESCRIPTION 14=head1 DESCRIPTION
15 15
16See L<http://freenet.sourceforge.net/index.php?page=fcp> for a description 16See L<http://freenet.sourceforge.net/index.php?page=fcp> for a description
17of what the messages do. I am too lazy to document all this here. 17of what the messages do. I am too lazy to document all this here.
18
19The module uses L<AnyEvent> to find a suitable Event module.
18 20
19=head1 WARNING 21=head1 WARNING
20 22
21This module is alpha. While it probably won't destroy (much :) of your 23This module is alpha. While it probably won't destroy (much :) of your
22data, it currently falls short of what it should provide (intelligent uri 24data, it currently falls short of what it should provide (intelligent uri
23following, splitfile downloads, healing...) 25following, splitfile downloads, healing...)
24 26
25=head2 IMPORT TAGS 27=head2 IMPORT TAGS
26 28
27Nothing much can be "imported" from this module right now. There are, 29Nothing much can be "imported" from this module right now.
28however, certain "import tags" that can be used to select the event model
29to be used.
30
31Event models are implemented as modules under the C<Net::FCP::Event::xyz>
32class, where C<xyz> is the event model to use. The default is C<Event> (or
33later C<Auto>).
34
35The import tag to use is named C<event=xyz>, e.g. C<event=Event>,
36C<event=Glib> etc.
37
38You should specify the event module to use only in the main program.
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 30
43=head2 FREENET BASICS 31=head2 FREENET BASICS
44 32
45Ok, this section will not explain any freenet basics to you, just some 33Ok, this section will not explain any freenet basics to you, just some
46problems I found that you might want to avoid: 34problems I found that you might want to avoid:
72 60
73package Net::FCP; 61package Net::FCP;
74 62
75use Carp; 63use Carp;
76 64
77$VERSION = 0.6; 65$VERSION = '1.0';
78 66
79no warnings; 67no warnings;
80 68
81our $EVENT = Net::FCP::Event::Auto::; 69use AnyEvent;
82 70
83sub import { 71use Net::FCP::Metadata;
84 shift; 72use Net::FCP::Util qw(tolc touc xeh);
85
86 for (@_) {
87 if (/^event=(\w+)$/) {
88 $EVENT = "Net::FCP::Event::$1";
89 eval "require $EVENT";
90 }
91 }
92 die $@ if $@;
93}
94
95sub touc($) {
96 local $_ = shift;
97 1 while s/((?:^|_)(?:svk|chk|uri)(?:_|$))/\U$1/;
98 s/(?:^|_)(.)/\U$1/g;
99 $_;
100}
101
102sub tolc($) {
103 local $_ = shift;
104 1 while s/(SVK|CHK|URI)([^_])/$1\_$2/i;
105 1 while s/([^_])(SVK|CHK|URI)/$1\_$2/i;
106 s/(?<=[a-z])(?=[A-Z])/_/g;
107 lc $_;
108}
109
110# the opposite of hex
111sub xeh($) {
112 sprintf "%x", $_[0];
113}
114
115=item $meta = Net::FCP::parse_metadata $string
116
117Parse a metadata string and return it.
118
119The metadata will be a hashref with key C<version> (containing the
120mandatory version header entries) and key C<raw> containing the original
121metadata string.
122
123All other headers are represented by arrayrefs (they can be repeated).
124
125Since this description is confusing, here is a rather verbose example of a
126parsed manifest:
127
128 (
129 raw => "Version...",
130 version => { revision => 1 },
131 document => [
132 {
133 info => { format" => "image/jpeg" },
134 name => "background.jpg",
135 redirect => { target => "freenet:CHK\@ZcagI,ra726bSw" },
136 },
137 {
138 info => { format" => "text/html" },
139 name => ".next",
140 redirect => { target => "freenet:SSK\@ilUPAgM/TFEE/3" },
141 },
142 {
143 info => { format" => "text/html" },
144 redirect => { target => "freenet:CHK\@8M8Po8ucwI,8xA" },
145 }
146 ]
147 )
148
149=cut
150
151sub parse_metadata {
152 my $data = shift;
153 my $meta = { raw => $data };
154
155 if ($data =~ /^Version\015?\012/gc) {
156 my $hdr = $meta->{version} = {};
157
158 for (;;) {
159 while ($data =~ /\G([^=\015\012]+)=([^\015\012]*)\015?\012/gc) {
160 my ($k, $v) = ($1, $2);
161 my @p = split /\./, tolc $k, 3;
162
163 $hdr->{$p[0]} = $v if @p == 1; # lamest code I ever wrote
164 $hdr->{$p[0]}{$p[1]} = $v if @p == 2;
165 $hdr->{$p[0]}{$p[1]}{$p[2]} = $v if @p == 3;
166 die "FATAL: 4+ dot metadata" if @p >= 4;
167 }
168
169 if ($data =~ /\GEndPart\015?\012/gc) {
170 # nop
171 } elsif ($data =~ /\GEnd(\015?\012|$)/gc) {
172 last;
173 } elsif ($data =~ /\G([A-Za-z0-9.\-]+)\015?\012/gcs) {
174 push @{$meta->{tolc $1}}, $hdr = {};
175 } elsif ($data =~ /\G(.*)/gcs) {
176 print STDERR "metadata format error ($1), please report this string: <<$data>>";
177 die "metadata format error";
178 }
179 }
180 }
181
182 #$meta->{tail} = substr $data, pos $data;
183
184 $meta;
185}
186 73
187=item $fcp = new Net::FCP [host => $host][, port => $port][, progress => \&cb] 74=item $fcp = new Net::FCP [host => $host][, port => $port][, progress => \&cb]
188 75
189Create a new virtual FCP connection to the given host and port (default 76Create a new virtual FCP connection to the given host and port (default
190127.0.0.1:8481, or the environment variables C<FREDHOST> and C<FREDPORT>). 77127.0.0.1:8481, or the environment variables C<FREDHOST> and C<FREDPORT>).
200 my ($self, $txn, $type, $attr) = @_; 87 my ($self, $txn, $type, $attr) = @_;
201 88
202 warn "progress<$txn,$type," . (join ":", %$attr) . ">\n"; 89 warn "progress<$txn,$type," . (join ":", %$attr) . ">\n";
203 } 90 }
204 91
205=begin comment
206
207However, the existance of the node is checked by executing a
208C<ClientHello> transaction.
209
210=end
211
212=cut 92=cut
213 93
214sub new { 94sub new {
215 my $class = shift; 95 my $class = shift;
216 my $self = bless { @_ }, $class; 96 my $self = bless { @_ }, $class;
217 97
218 $self->{host} ||= $ENV{FREDHOST} || "127.0.0.1"; 98 $self->{host} ||= $ENV{FREDHOST} || "127.0.0.1";
219 $self->{port} ||= $ENV{FREDPORT} || 8481; 99 $self->{port} ||= $ENV{FREDPORT} || 8481;
220 100
221 #$self->{nodehello} = $self->client_hello
222 # or croak "unable to get nodehello from node\n";
223
224 $self; 101 $self;
225} 102}
226 103
227sub progress { 104sub progress {
228 my ($self, $txn, $type, $attr) = @_; 105 my ($self, $txn, $type, $attr) = @_;
229 106
230 $self->{progress}->($self, $txn, $type, $attr) 107 $self->{progress}->($self, $txn, $type, $attr)
231 if $self->{progress}; 108 if $self->{progress};
232} 109}
233 110
234=item $txn = $fcp->txn(type => attr => val,...) 111=item $txn = $fcp->txn (type => attr => val,...)
235 112
236The low-level interface to transactions. Don't use it. 113The low-level interface to transactions. Don't use it unless you have
237 114"special needs". Instead, use predefiend transactions like this:
238Here are some examples of using transactions:
239 115
240The blocking case, no (visible) transactions involved: 116The blocking case, no (visible) transactions involved:
241 117
242 my $nodehello = $fcp->client_hello; 118 my $nodehello = $fcp->client_hello;
243 119
262sub txn { 138sub txn {
263 my ($self, $type, %attr) = @_; 139 my ($self, $type, %attr) = @_;
264 140
265 $type = touc $type; 141 $type = touc $type;
266 142
267 my $txn = "Net::FCP::Txn::$type"->new(fcp => $self, type => tolc $type, attr => \%attr); 143 my $txn = "Net::FCP::Txn::$type"->new (fcp => $self, type => tolc $type, attr => \%attr);
268 144
269 $txn; 145 $txn;
270} 146}
271 147
272{ # transactions 148{ # transactions
345=cut 221=cut
346 222
347$txn->(generate_chk => sub { 223$txn->(generate_chk => sub {
348 my ($self, $metadata, $data, $cipher) = @_; 224 my ($self, $metadata, $data, $cipher) = @_;
349 225
226 $metadata = Net::FCP::Metadata::build_metadata $metadata;
227
350 $self->txn (generate_chk => 228 $self->txn (generate_chk =>
351 data => "$metadata$data", 229 data => "$metadata$data",
352 metadata_length => xeh length $metadata, 230 metadata_length => xeh length $metadata,
353 cipher => $cipher || "Twofish"); 231 cipher => $cipher || "Twofish");
354}); 232});
355 233
356=item $txn = $fcp->txn_generate_svk_pair 234=item $txn = $fcp->txn_generate_svk_pair
357 235
358=item ($public, $private) = @{ $fcp->generate_svk_pair } 236=item ($public, $private, $crypto) = @{ $fcp->generate_svk_pair }
359 237
360Creates a new SVK pair. Returns an arrayref. 238Creates a new SVK pair. Returns an arrayref with the public key, the
239private key and a crypto key, which is just additional entropy.
361 240
362 [ 241 [
363 "hKs0-WDQA4pVZyMPKNFsK1zapWY", 242 "acLx4dux9fvvABH15Gk6~d3I-yw",
364 "ZnmvMITaTXBMFGl4~jrjuyWxOWg" 243 "cPoDkDMXDGSMM32plaPZDhJDxSs",
244 "BH7LXCov0w51-y9i~BoB3g",
365 ] 245 ]
246
247A private key (for inserting) can be constructed like this:
248
249 SSK@<private_key>,<crypto_key>/<name>
250
251It can be used to insert data. The corresponding public key looks like this:
252
253 SSK@<public_key>PAgM,<crypto_key>/<name>
254
255Watch out for the C<PAgM>-part!
366 256
367=cut 257=cut
368 258
369$txn->(generate_svk_pair => sub { 259$txn->(generate_svk_pair => sub {
370 my ($self) = @_; 260 my ($self) = @_;
371 261
372 $self->txn ("generate_svk_pair"); 262 $self->txn ("generate_svk_pair");
373}); 263});
374 264
375=item $txn = $fcp->txn_insert_private_key ($private) 265=item $txn = $fcp->txn_invert_private_key ($private)
376 266
377=item $public = $fcp->insert_private_key ($private) 267=item $public = $fcp->invert_private_key ($private)
378 268
379Inserts a private key. $private can be either an insert URI (must start 269Inverts a private key (returns the public key). C<$private> can be either
380with C<freenet:SSK@>) or a raw private key (i.e. the private value you get 270an insert URI (must start with C<freenet:SSK@>) or a raw private key (i.e.
381back from C<generate_svk_pair>). 271the private value you get back from C<generate_svk_pair>).
382 272
383Returns the public key. 273Returns the public key.
384 274
385UNTESTED.
386
387=cut 275=cut
388 276
389$txn->(insert_private_key => sub { 277$txn->(invert_private_key => sub {
390 my ($self, $privkey) = @_; 278 my ($self, $privkey) = @_;
391 279
392 $self->txn (invert_private_key => private => $privkey); 280 $self->txn (invert_private_key => private => $privkey);
393}); 281});
394 282
397=item $length = $fcp->get_size ($uri) 285=item $length = $fcp->get_size ($uri)
398 286
399Finds and returns the size (rounded up to the nearest power of two) of the 287Finds and returns the size (rounded up to the nearest power of two) of the
400given document. 288given document.
401 289
402UNTESTED.
403
404=cut 290=cut
405 291
406$txn->(get_size => sub { 292$txn->(get_size => sub {
407 my ($self, $uri) = @_; 293 my ($self, $uri) = @_;
408 294
411 297
412=item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]]) 298=item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]])
413 299
414=item ($metadata, $data) = @{ $fcp->client_get ($uri, $htl, $removelocal) 300=item ($metadata, $data) = @{ $fcp->client_get ($uri, $htl, $removelocal)
415 301
416Fetches a (small, as it should fit into memory) file from 302Fetches a (small, as it should fit into memory) key content block from
417freenet. C<$meta> is the metadata (as returned by C<parse_metadata> or 303freenet. C<$meta> is a C<Net::FCP::Metadata> object or C<undef>).
418C<undef>).
419 304
420The C<$uri> should begin with C<freenet:>, but the scheme is currently 305The C<$uri> should begin with C<freenet:>, but the scheme is currently
421added, if missing. 306added, if missing.
422
423Due to the overhead, a better method to download big files should be used.
424 307
425 my ($meta, $data) = @{ 308 my ($meta, $data) = @{
426 $fcp->client_get ( 309 $fcp->client_get (
427 "freenet:CHK@hdXaxkwZ9rA8-SidT0AN-bniQlgPAwI,XdCDmBuGsd-ulqbLnZ8v~w" 310 "freenet:CHK@hdXaxkwZ9rA8-SidT0AN-bniQlgPAwI,XdCDmBuGsd-ulqbLnZ8v~w"
428 ) 311 )
431=cut 314=cut
432 315
433$txn->(client_get => sub { 316$txn->(client_get => sub {
434 my ($self, $uri, $htl, $removelocal) = @_; 317 my ($self, $uri, $htl, $removelocal) = @_;
435 318
436 $uri =~ s/^freenet://; 319 $uri =~ s/^freenet://; $uri = "freenet:$uri";
437 $uri = "freenet:$uri";
438 320
439 $self->txn (client_get => URI => $uri, hops_to_live => xeh (defined $htl ? $htl : 15), 321 $self->txn (client_get => URI => $uri, hops_to_live => xeh (defined $htl ? $htl : 15),
440 remove_local_key => $removelocal ? "true" : "false"); 322 remove_local_key => $removelocal ? "true" : "false");
441}); 323});
442 324
444 326
445=item my $uri = $fcp->client_put ($uri, $metadata, $data, $htl, $removelocal); 327=item my $uri = $fcp->client_put ($uri, $metadata, $data, $htl, $removelocal);
446 328
447Insert a new key. If the client is inserting a CHK, the URI may be 329Insert 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 330abbreviated as just CHK@. In this case, the node will calculate the
449CHK. 331CHK. If the key is a private SSK key, the node will calculcate the public
332key and the resulting public URI.
450 333
451C<$meta> can be a reference or a string (ONLY THE STRING CASE IS IMPLEMENTED!). 334C<$meta> can be a hash reference (same format as returned by
335C<Net::FCP::parse_metadata>) or a string.
452 336
453THIS INTERFACE IS UNTESTED AND SUBJECT TO CHANGE. 337The result is an arrayref with the keys C<uri>, C<public_key> and C<private_key>.
454 338
455=cut 339=cut
456 340
457$txn->(client_put => sub { 341$txn->(client_put => sub {
458 my ($self, $uri, $meta, $data, $htl, $removelocal) = @_; 342 my ($self, $uri, $metadata, $data, $htl, $removelocal) = @_;
459 343
460 $self->txn (client_put => URI => $uri, xeh (defined $htl ? $htl : 15), 344 $metadata = Net::FCP::Metadata::build_metadata $metadata;
345 $uri =~ s/^freenet://; $uri = "freenet:$uri";
346
347 $self->txn (client_put => URI => $uri,
348 hops_to_live => xeh (defined $htl ? $htl : 15),
461 remove_local_key => $removelocal ? "true" : "false", 349 remove_local_key => $removelocal ? "true" : "false",
462 data => "$meta$data", metadata_length => xeh length $meta); 350 data => "$metadata$data", metadata_length => xeh length $metadata);
463}); 351});
464 352
465} # transactions 353} # transactions
466
467=item MISSING: (ClientPut), InsertKey
468 354
469=back 355=back
470 356
471=head2 THE Net::FCP::Txn CLASS 357=head2 THE Net::FCP::Txn CLASS
472 358
496 382
497sub new { 383sub new {
498 my $class = shift; 384 my $class = shift;
499 my $self = bless { @_ }, $class; 385 my $self = bless { @_ }, $class;
500 386
501 $self->{signal} = $EVENT->new_signal; 387 $self->{signal} = AnyEvent->condvar;
502 388
503 $self->{fcp}{txn}{$self} = $self; 389 $self->{fcp}{txn}{$self} = $self;
504 390
505 my $attr = ""; 391 my $attr = "";
506 my $data = delete $self->{attr}{data}; 392 my $data = delete $self->{attr}{data};
518 404
519 socket my $fh, PF_INET, SOCK_STREAM, 0 405 socket my $fh, PF_INET, SOCK_STREAM, 0
520 or Carp::croak "unable to create new tcp socket: $!"; 406 or Carp::croak "unable to create new tcp socket: $!";
521 binmode $fh, ":raw"; 407 binmode $fh, ":raw";
522 fcntl $fh, F_SETFL, O_NONBLOCK; 408 fcntl $fh, F_SETFL, O_NONBLOCK;
523 connect $fh, (sockaddr_in $self->{fcp}{port}, inet_aton $self->{fcp}{host}) 409 connect $fh, (sockaddr_in $self->{fcp}{port}, inet_aton $self->{fcp}{host});
524 and !$!{EWOULDBLOCK}
525 and !$!{EINPROGRESS}
526 and Carp::croak "FCP::txn: unable to connect to $self->{fcp}{host}:$self->{fcp}{port}: $!\n"; 410# and Carp::croak "FCP::txn: unable to connect to $self->{fcp}{host}:$self->{fcp}{port}: $!\n";
527 411
528 $self->{sbuf} = 412 $self->{sbuf} =
529 "\x00\x00\x00\x02" 413 "\x00\x00\x00\x02"
530 . (Net::FCP::touc $self->{type}) 414 . (Net::FCP::touc $self->{type})
531 . "\012$attr$data"; 415 . "\012$attr$data";
532 416
533 #shutdown $fh, 1; # freenet buggy?, well, it's java... 417 #shutdown $fh, 1; # freenet buggy?, well, it's java...
534 418
535 $self->{fh} = $fh; 419 $self->{fh} = $fh;
536 420
537 $self->{w} = $EVENT->new_from_fh ($fh)->cb(sub { $self->fh_ready_w })->poll(0, 1, 1); 421 $self->{w} = AnyEvent->io (fh => $fh, poll => 'w', cb => sub { $self->fh_ready_w });
538 422
539 $self; 423 $self;
540} 424}
541 425
542=item $txn = $txn->cb ($coderef) 426=item $txn = $txn->cb ($coderef)
578 $self; 462 $self;
579} 463}
580 464
581=item $txn->cancel (%attr) 465=item $txn->cancel (%attr)
582 466
583Cancels the operation with a C<cancel> exception anf the given attributes 467Cancels the operation with a C<cancel> exception and the given attributes
584(consider at least giving the attribute C<reason>). 468(consider at least giving the attribute C<reason>).
585 469
586UNTESTED. 470UNTESTED.
587 471
588=cut 472=cut
601 485
602 if ($len > 0) { 486 if ($len > 0) {
603 substr $self->{sbuf}, 0, $len, ""; 487 substr $self->{sbuf}, 0, $len, "";
604 unless (length $self->{sbuf}) { 488 unless (length $self->{sbuf}) {
605 fcntl $self->{fh}, F_SETFL, 0; 489 fcntl $self->{fh}, F_SETFL, 0;
606 $self->{w}->cb(sub { $self->fh_ready_r })->poll (1, 0, 1); 490 $self->{w} = AnyEvent->io (fh => $self->{fh}, poll => 'r', cb => sub { $self->fh_ready_r });
607 } 491 }
608 } elsif (defined $len) { 492 } elsif (defined $len) {
609 $self->throw (Net::FCP::Exception->new (network_error => { reason => "unexpected end of file while writing" })); 493 $self->throw (Net::FCP::Exception->new (network_error => { reason => "unexpected end of file while writing" }));
610 } else { 494 } else {
611 $self->throw (Net::FCP::Exception->new (network_error => { reason => "$!" })); 495 $self->throw (Net::FCP::Exception->new (network_error => { reason => "$!" }));
613} 497}
614 498
615sub fh_ready_r { 499sub fh_ready_r {
616 my ($self) = @_; 500 my ($self) = @_;
617 501
618 if (sysread $self->{fh}, $self->{buf}, 65536, length $self->{buf}) { 502 if (sysread $self->{fh}, $self->{buf}, 16384 + 1024, length $self->{buf}) {
619 for (;;) { 503 for (;;) {
620 if ($self->{datalen}) { 504 if ($self->{datalen}) {
621 #warn "expecting new datachunk $self->{datalen}, got ".(length $self->{buf})."\n";#d# 505 #warn "expecting new datachunk $self->{datalen}, got ".(length $self->{buf})."\n";#d#
622 if (length $self->{buf} >= $self->{datalen}) { 506 if (length $self->{buf} >= $self->{datalen}) {
623 $self->rcv_data (substr $self->{buf}, 0, delete $self->{datalen}, ""); 507 $self->rcv_data (substr $self->{buf}, 0, delete $self->{datalen}, "");
676 my ($self, $result) = @_; 560 my ($self, $result) = @_;
677 561
678 unless (exists $self->{result}) { 562 unless (exists $self->{result}) {
679 $self->{result} = $result; 563 $self->{result} = $result;
680 $self->{cb}->($self) if exists $self->{cb}; 564 $self->{cb}->($self) if exists $self->{cb};
681 $self->{signal}->send; 565 $self->{signal}->broadcast;
682 } 566 }
683} 567}
684 568
685sub eof { 569sub eof {
686 my ($self) = @_; 570 my ($self) = @_;
759 643
760use base Net::FCP::Txn; 644use base Net::FCP::Txn;
761 645
762sub rcv_success { 646sub rcv_success {
763 my ($self, $attr) = @_; 647 my ($self, $attr) = @_;
764 $self->set_result ([$attr->{PublicKey}, $attr->{PrivateKey}]); 648 $self->set_result ([$attr->{public_key}, $attr->{private_key}, $attr->{crypto_key}]);
765} 649}
766 650
767package Net::FCP::Txn::InsertPrivateKey; 651package Net::FCP::Txn::InvertPrivateKey;
768 652
769use base Net::FCP::Txn; 653use base Net::FCP::Txn;
770 654
771sub rcv_success { 655sub rcv_success {
772 my ($self, $attr) = @_; 656 my ($self, $attr) = @_;
773 $self->set_result ($attr->{PublicKey}); 657 $self->set_result ($attr->{public_key});
774} 658}
775 659
776package Net::FCP::Txn::GetSize; 660package Net::FCP::Txn::GetSize;
777 661
778use base Net::FCP::Txn; 662use base Net::FCP::Txn;
779 663
780sub rcv_success { 664sub rcv_success {
781 my ($self, $attr) = @_; 665 my ($self, $attr) = @_;
782 $self->set_result (hex $attr->{Length}); 666 $self->set_result (hex $attr->{length});
783} 667}
784 668
785package Net::FCP::Txn::GetPut; 669package Net::FCP::Txn::GetPut;
786 670
787# base class for get and put 671# base class for get and put
814 698
815 $self->progress ("data", { chunk => length $chunk, received => length $self->{data}, total => $self->{datalength} }); 699 $self->progress ("data", { chunk => length $chunk, received => length $self->{data}, total => $self->{datalength} });
816 700
817 if ($self->{datalength} == length $self->{data}) { 701 if ($self->{datalength} == length $self->{data}) {
818 my $data = delete $self->{data}; 702 my $data = delete $self->{data};
819 my $meta = Net::FCP::parse_metadata substr $data, 0, $self->{metalength}, ""; 703 my $meta = new Net::FCP::Metadata (substr $data, 0, $self->{metalength}, "");
820 704
821 $self->set_result ([$meta, $data]); 705 $self->set_result ([$meta, $data]);
822 $self->eof; 706 $self->eof;
823 } 707 }
824} 708}
835package Net::FCP::Txn::ClientPut; 719package Net::FCP::Txn::ClientPut;
836 720
837use base Net::FCP::Txn::GetPut; 721use base Net::FCP::Txn::GetPut;
838 722
839*rcv_size_error = \&Net::FCP::Txn::rcv_throw_exception; 723*rcv_size_error = \&Net::FCP::Txn::rcv_throw_exception;
840*rcv_key_collision = \&Net::FCP::Txn::rcv_throw_exception;
841 724
842sub rcv_pending { 725sub rcv_pending {
843 my ($self, $attr, $type) = @_; 726 my ($self, $attr, $type) = @_;
844 $self->progress ($type, $attr); 727 $self->progress ($type, $attr);
845} 728}
846 729
847sub rcv_success { 730sub rcv_success {
848 my ($self, $attr, $type) = @_; 731 my ($self, $attr, $type) = @_;
849 $self->set_result ($attr); 732 $self->set_result ($attr);
733}
734
735sub rcv_key_collision {
736 my ($self, $attr, $type) = @_;
737 $self->set_result ({ key_collision => 1, %$attr });
850} 738}
851 739
852=back 740=back
853 741
854=head2 The Net::FCP::Exception CLASS 742=head2 The Net::FCP::Exception CLASS
922 810
923=head1 BUGS 811=head1 BUGS
924 812
925=head1 AUTHOR 813=head1 AUTHOR
926 814
927 Marc Lehmann <pcg@goof.com> 815 Marc Lehmann <schmorp@schmorp.de>
928 http://www.goof.com/pcg/marc/ 816 http://home.schmorp.de/
929 817
930=cut 818=cut
931 819
932package Net::FCP::Event::Auto; 8201
933 821
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
9641;
965

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines