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.5 by root, Mon Sep 8 00:35:44 2003 UTC vs.
Revision 1.22 by root, Wed Sep 17 05:05:33 2003 UTC

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 18
19=head1 WARNING 19=head1 WARNING
20 20
21This module is alpha. While it probably won't destroy (much :) of your 21This module is alpha. While it probably won't destroy (much :) of your
22data, it currently works only with the Event module (alkthough the event 22data, it currently falls short of what it should provide (intelligent uri
23mechanism is fully pluggable). 23following, splitfile downloads, healing...)
24
25=head2 IMPORT TAGS
26
27Nothing much can be "imported" from this module right now. There are,
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
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
24 66
25=head2 THE Net::FCP CLASS 67=head2 THE Net::FCP CLASS
26 68
27=over 4 69=over 4
28 70
29=cut 71=cut
30 72
31package Net::FCP; 73package Net::FCP;
32 74
33use Carp; 75use Carp;
34use IO::Socket::INET;
35 76
36$VERSION = 0.02; 77$VERSION = 0.5;
37 78
38sub event_reg_cb { 79no warnings;
39 my ($obj) = @_;
40 require Event;
41 80
42 $obj->{eventdata} = Event->io ( 81our $EVENT = Net::FCP::Event::Auto::;
43 fd => $obj->{fh}, 82
44 poll => 'r', 83sub import {
45 cb => sub { 84 shift;
46 $obj->fh_ready; 85
86 for (@_) {
87 if (/^event=(\w+)$/) {
88 $EVENT = "Net::FCP::Event::$1";
89 eval "require $EVENT";
47 }, 90 }
48 ); 91 }
92 die $@ if $@;
49} 93}
50
51sub event_unreg_cb {
52 $_[0]{eventdata}
53 and (delete $_[0]{eventdata})->cancel;
54}
55
56sub event_wait_cb {
57 Event::one_event();
58}
59
60$regcb = \&event_reg_cb;
61$unregcb = \&event_unreg_cb;
62$waitcb = \&event_wait_cb;
63 94
64sub touc($) { 95sub touc($) {
65 local $_ = shift; 96 local $_ = shift;
66 1 while s/((?:^|_)(?:svk|chk|uri)(?:_|$))/\U$1/; 97 1 while s/((?:^|_)(?:svk|chk|uri)(?:_|$))/\U$1/;
67 s/(?:^|_)(.)/\U$1/g; 98 s/(?:^|_)(.)/\U$1/g;
72 local $_ = shift; 103 local $_ = shift;
73 s/(?<=[a-z])(?=[A-Z])/_/g; 104 s/(?<=[a-z])(?=[A-Z])/_/g;
74 lc $_; 105 lc $_;
75} 106}
76 107
108=item $meta = Net::FCP::parse_metadata $string
109
110Parse a metadata string and return it.
111
112The metadata will be a hashref with key C<version> (containing the
113mandatory version header entries) and key C<raw> containing the original
114metadata string.
115
116All other headers are represented by arrayrefs (they can be repeated).
117
118Since this description is confusing, here is a rather verbose example of a
119parsed manifest:
120
121 (
122 raw => "Version...",
123 version => { revision => 1 },
124 document => [
125 {
126 info => { format" => "image/jpeg" },
127 name => "background.jpg",
128 redirect => { target => "freenet:CHK\@ZcagI,ra726bSw" },
129 },
130 {
131 info => { format" => "text/html" },
132 name => ".next",
133 redirect => { target => "freenet:SSK\@ilUPAgM/TFEE/3" },
134 },
135 {
136 info => { format" => "text/html" },
137 redirect => { target => "freenet:CHK\@8M8Po8ucwI,8xA" },
138 }
139 ]
140 )
141
142=cut
143
144sub parse_metadata {
145 my $data = shift;
146 my $meta = { raw => $data };
147
148 if ($data =~ /^Version\015?\012/gc) {
149 my $hdr = $meta->{version} = {};
150
151 for (;;) {
152 while ($data =~ /\G([^=\015\012]+)=([^\015\012]*)\015?\012/gc) {
153 my ($k, $v) = ($1, $2);
154 my @p = split /\./, tolc $k, 3;
155
156 $hdr->{$p[0]} = $v if @p == 1; # lamest code I ever wrote
157 $hdr->{$p[0]}{$p[1]} = $v if @p == 2;
158 $hdr->{$p[0]}{$p[1]}{$p[2]} = $v if @p == 3;
159 die "FATAL: 4+ dot metadata" if @p >= 4;
160 }
161
162 if ($data =~ /\GEndPart\015?\012/gc) {
163 # nop
164 } elsif ($data =~ /\GEnd(\015?\012|$)/gc) {
165 last;
166 } elsif ($data =~ /\G([A-Za-z0-9.\-]+)\015?\012/gcs) {
167 push @{$meta->{tolc $1}}, $hdr = {};
168 } elsif ($data =~ /\G(.*)/gcs) {
169 print STDERR "metadata format error ($1), please report this string: <<$data>>";
170 die "metadata format error";
171 }
172 }
173 }
174
175 #$meta->{tail} = substr $data, pos $data;
176
177 $meta;
178}
179
77=item $fcp = new Net::FCP [host => $host][, port => $port] 180=item $fcp = new Net::FCP [host => $host][, port => $port]
78 181
79Create a new virtual FCP connection to the given host and port (default 182Create a new virtual FCP connection to the given host and port (default
80127.0.0.1:8481, or the environment variables C<FREDHOST> and C<FREDPORT>). 183127.0.0.1:8481, or the environment variables C<FREDHOST> and C<FREDPORT>).
81 184
82Connections are virtual because no persistent physical connection is 185Connections are virtual because no persistent physical connection is
186established.
187
188=begin comment
189
83established. However, the existance of the node is checked by executing a 190However, the existance of the node is checked by executing a
84C<ClientHello> transaction. 191C<ClientHello> transaction.
192
193=end
85 194
86=cut 195=cut
87 196
88sub new { 197sub new {
89 my $class = shift; 198 my $class = shift;
90 my $self = bless { @_ }, $class; 199 my $self = bless { @_ }, $class;
91 200
92 $self->{host} ||= $ENV{FREDHOST} || "127.0.0.1"; 201 $self->{host} ||= $ENV{FREDHOST} || "127.0.0.1";
93 $self->{port} ||= $ENV{FREDPORt} || 8481; 202 $self->{port} ||= $ENV{FREDPORT} || 8481;
94 203
95 $self->{nodehello} = $self->client_hello 204 #$self->{nodehello} = $self->client_hello
96 or croak "unable to get nodehello from node\n"; 205 # or croak "unable to get nodehello from node\n";
97 206
98 $self; 207 $self;
99} 208}
100 209
210sub progress {
211 my ($self, $txn, $type, $attr) = @_;
212 #warn "progress<$txn,$type," . (join ":", %$attr) . ">\n";
213}
214
101=item $txn = $fcp->txn(type => attr => val,...) 215=item $txn = $fcp->txn(type => attr => val,...)
102 216
103The low-level interface to transactions. Don't use it. 217The low-level interface to transactions. Don't use it.
218
219Here are some examples of using transactions:
220
221The blocking case, no (visible) transactions involved:
222
223 my $nodehello = $fcp->client_hello;
224
225A transaction used in a blocking fashion:
226
227 my $txn = $fcp->txn_client_hello;
228 ...
229 my $nodehello = $txn->result;
230
231Or shorter:
232
233 my $nodehello = $fcp->txn_client_hello->result;
234
235Setting callbacks:
236
237 $fcp->txn_client_hello->cb(
238 sub { my $nodehello => $_[0]->result }
239 );
104 240
105=cut 241=cut
106 242
107sub txn { 243sub txn {
108 my ($self, $type, %attr) = @_; 244 my ($self, $type, %attr) = @_;
112 my $txn = "Net::FCP::Txn::$type"->new(fcp => $self, type => tolc $type, attr => \%attr); 248 my $txn = "Net::FCP::Txn::$type"->new(fcp => $self, type => tolc $type, attr => \%attr);
113 249
114 $txn; 250 $txn;
115} 251}
116 252
117sub _txn($&) { 253{ # transactions
254
255my $txn = sub {
118 my ($name, $sub) = @_; 256 my ($name, $sub) = @_;
119 *{"$name\_txn"} = $sub; 257 *{"txn_$name"} = $sub;
120 *{$name} = sub { $sub->(@_)->result }; 258 *{$name} = sub { $sub->(@_)->result };
121} 259};
122 260
123=item $txn = $fcp->txn_client_hello 261=item $txn = $fcp->txn_client_hello
124 262
125=item $nodehello = $fcp->client_hello 263=item $nodehello = $fcp->client_hello
126 264
132 protocol => "1.2", 270 protocol => "1.2",
133 } 271 }
134 272
135=cut 273=cut
136 274
137_txn client_hello => sub { 275$txn->(client_hello => sub {
138 my ($self) = @_; 276 my ($self) = @_;
139 277
140 $self->txn ("client_hello"); 278 $self->txn ("client_hello");
141}; 279});
142 280
143=item $txn = $fcp->txn_client_info 281=item $txn = $fcp->txn_client_info
144 282
145=item $nodeinfo = $fcp->client_info 283=item $nodeinfo = $fcp->client_info
146 284
170 routing_time => "a5", 308 routing_time => "a5",
171 } 309 }
172 310
173=cut 311=cut
174 312
175_txn client_info => sub { 313$txn->(client_info => sub {
176 my ($self) = @_; 314 my ($self) = @_;
177 315
178 $self->txn ("client_info"); 316 $self->txn ("client_info");
179}; 317});
180 318
181=item $txn = $fcp->txn_generate_chk ($metadata, $data) 319=item $txn = $fcp->txn_generate_chk ($metadata, $data[, $cipher])
182 320
183=item $uri = $fcp->generate_chk ($metadata, $data) 321=item $uri = $fcp->generate_chk ($metadata, $data[, $cipher])
184 322
185Creates a new CHK, given the metadata and data. UNTESTED. 323Calculcates a CHK, given the metadata and data. C<$cipher> is either
324C<Rijndael> or C<Twofish>, with the latter being the default.
186 325
187=cut 326=cut
188 327
189_txn generate_chk => sub { 328$txn->(generate_chk => sub {
190 my ($self, $metadata, $data) = @_; 329 my ($self, $metadata, $data, $cipher) = @_;
191 330
192 $self->txn (generate_chk => data => "$data$metadata", meta_data_length => length $metadata); 331 $self->txn (generate_chk =>
332 data => "$metadata$data",
333 metadata_length => length $metadata,
334 cipher => $cipher || "Twofish");
193}; 335});
194 336
195=item $txn = $fcp->txn_generate_svk_pair 337=item $txn = $fcp->txn_generate_svk_pair
196 338
197=item ($public, $private) = @{ $fcp->generate_svk_pair } 339=item ($public, $private) = @{ $fcp->generate_svk_pair }
198 340
203 "ZnmvMITaTXBMFGl4~jrjuyWxOWg" 345 "ZnmvMITaTXBMFGl4~jrjuyWxOWg"
204 ] 346 ]
205 347
206=cut 348=cut
207 349
208_txn generate_svk_pair => sub { 350$txn->(generate_svk_pair => sub {
209 my ($self) = @_; 351 my ($self) = @_;
210 352
211 $self->txn ("generate_svk_pair"); 353 $self->txn ("generate_svk_pair");
212}; 354});
213 355
214=item $txn = $fcp->txn_insert_private_key ($private) 356=item $txn = $fcp->txn_insert_private_key ($private)
215 357
216=item $uri = $fcp->insert_private_key ($private) 358=item $public = $fcp->insert_private_key ($private)
217 359
218Inserts a private key. $private can be either an insert URI (must start 360Inserts a private key. $private can be either an insert URI (must start
219with freenet:SSK@) or a raw private key (i.e. the private value you get back 361with C<freenet:SSK@>) or a raw private key (i.e. the private value you get
220from C<generate_svk_pair>). 362back from C<generate_svk_pair>).
221 363
222Returns the public key. 364Returns the public key.
223 365
224UNTESTED. 366UNTESTED.
225 367
226=cut 368=cut
227 369
228_txn insert_private_key => sub { 370$txn->(insert_private_key => sub {
229 my ($self, $privkey) = @_; 371 my ($self, $privkey) = @_;
230 372
231 $self->txn (invert_private_key => private => $privkey); 373 $self->txn (invert_private_key => private => $privkey);
232}; 374});
233 375
234=item $txn = $fcp->txn_get_size ($uri) 376=item $txn = $fcp->txn_get_size ($uri)
235 377
236=item $length = $fcp->get_size ($uri) 378=item $length = $fcp->get_size ($uri)
237 379
240 382
241UNTESTED. 383UNTESTED.
242 384
243=cut 385=cut
244 386
245_txn get_size => sub { 387$txn->(get_size => sub {
246 my ($self, $uri) = @_; 388 my ($self, $uri) = @_;
247 389
248 $self->txn (get_size => URI => $uri); 390 $self->txn (get_size => URI => $uri);
249}; 391});
250 392
251=item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]]) 393=item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]])
252 394
253=item ($data, $metadata) = @{ $fcp->client_get ($uri, $htl, $removelocal) 395=item ($metadata, $data) = @{ $fcp->client_get ($uri, $htl, $removelocal)
254 396
255Fetches a (small, as it should fit into memory) file from freenet. 397Fetches a (small, as it should fit into memory) file from
398freenet. C<$meta> is the metadata (as returned by C<parse_metadata> or
399C<undef>).
256 400
257Due to the overhead, a better method to download big fiels should be used. 401Due to the overhead, a better method to download big files should be used.
258 402
259 my ($data, $meta) = @{ 403 my ($meta, $data) = @{
260 $fcp->client_get ( 404 $fcp->client_get (
261 "freenet:CHK@hdXaxkwZ9rA8-SidT0AN-bniQlgPAwI,XdCDmBuGsd-ulqbLnZ8v~w" 405 "freenet:CHK@hdXaxkwZ9rA8-SidT0AN-bniQlgPAwI,XdCDmBuGsd-ulqbLnZ8v~w"
262 ) 406 )
263 }; 407 };
264 408
265=cut 409=cut
266 410
267_txn client_get => sub { 411$txn->(client_get => sub {
268 my ($self, $uri, $htl, $removelocal) = @_; 412 my ($self, $uri, $htl, $removelocal) = @_;
269 413
270 $self->txn (client_get => URI => $uri, hops_to_live => ($htl || 15), remove_local => $removelocal*1); 414 $self->txn (client_get => URI => $uri, hops_to_live => (defined $htl ? $htl :15),
415 remove_local_key => $removelocal ? "true" : "false");
271}; 416});
272 417
418=item $txn = $fcp->txn_client_put ($uri, $metadata, $data, $htl, $removelocal)
419
420=item my $uri = $fcp->client_put ($uri, $metadata, $data, $htl, $removelocal);
421
422Insert a new key. If the client is inserting a CHK, the URI may be
423abbreviated as just CHK@. In this case, the node will calculate the
424CHK.
425
426C<$meta> can be a reference or a string (ONLY THE STRING CASE IS IMPLEMENTED!).
427
428THIS INTERFACE IS UNTESTED AND SUBJECT TO CHANGE.
429
430=cut
431
432$txn->(client_put => sub {
433 my ($self, $uri, $meta, $data, $htl, $removelocal) = @_;
434
435 $self->txn (client_put => URI => $uri, hops_to_live => (defined $htl ? $htl :15),
436 remove_local_key => $removelocal ? "true" : "false",
437 data => "$meta$data", metadata_length => length $meta);
438});
439
440} # transactions
441
273=item MISSING: ClientPut 442=item MISSING: (ClientPut), InsretKey
274 443
275=back 444=back
276 445
277=head2 THE Net::FCP::Txn CLASS 446=head2 THE Net::FCP::Txn CLASS
278 447
289 458
290=cut 459=cut
291 460
292package Net::FCP::Txn; 461package Net::FCP::Txn;
293 462
463use Fcntl;
464use Socket;
465
294=item new arg => val,... 466=item new arg => val,...
295 467
296Creates a new C<Net::FCP::Txn> object. Not normally used. 468Creates a new C<Net::FCP::Txn> object. Not normally used.
297 469
298=cut 470=cut
299 471
300sub new { 472sub new {
301 my $class = shift; 473 my $class = shift;
302 my $self = bless { @_ }, $class; 474 my $self = bless { @_ }, $class;
303 475
476 $self->{signal} = $EVENT->new_signal;
477
478 $self->{fcp}{txn}{$self} = $self;
479
304 my $attr = ""; 480 my $attr = "";
305 my $data = delete $self->{attr}{data}; 481 my $data = delete $self->{attr}{data};
306 482
307 while (my ($k, $v) = each %{$self->{attr}}) { 483 while (my ($k, $v) = each %{$self->{attr}}) {
308 $attr .= (Net::FCP::touc $k) . "=$v\012" 484 $attr .= (Net::FCP::touc $k) . "=$v\012"
309 } 485 }
310 486
311 if (defined $data) { 487 if (defined $data) {
312 $attr .= "DataLength=" . (length $data) . "\012"; 488 $attr .= sprintf "DataLength=%x\012", length $data;
313 $data = "Data\012$data"; 489 $data = "Data\012$data";
314 } else { 490 } else {
315 $data = "EndMessage\012"; 491 $data = "EndMessage\012";
316 } 492 }
317 493
318 my $fh = new IO::Socket::INET 494 socket my $fh, PF_INET, SOCK_STREAM, 0
319 PeerHost => $self->{fcp}{host}, 495 or Carp::croak "unable to create new tcp socket: $!";
320 PeerPort => $self->{fcp}{port}
321 or Carp::croak "FCP::txn: unable to connect to $self->{fcp}{host}:$self->{fcp}{port}: $!\n";
322
323 binmode $fh, ":raw"; 496 binmode $fh, ":raw";
497 fcntl $fh, F_SETFL, O_NONBLOCK;
498 connect $fh, (sockaddr_in $self->{fcp}{port}, inet_aton $self->{fcp}{host})
499 and !$!{EWOULDBLOCK}
500 and !$!{EINPROGRESS}
501 and Carp::croak "FCP::txn: unable to connect to $self->{fcp}{host}:$self->{fcp}{port}: $!\n";
324 502
325 if (0) { 503 $self->{sbuf} =
326 print 504 "\x00\x00\x00\x02"
327 Net::FCP::touc $self->{type}, "\012",
328 $attr,
329 $data, "\012";
330 }
331
332 print $fh
333 "\x00\x00", "\x00\x02", # SESSID, PRESID
334 Net::FCP::touc $self->{type}, "\012", 505 . (Net::FCP::touc $self->{type})
335 $attr, 506 . "\012$attr$data";
336 $data;
337 507
338 #$fh->shutdown (1); # freenet buggy?, well, it's java... 508 #shutdown $fh, 1; # freenet buggy?, well, it's java...
339 509
340 $self->{fh} = $fh; 510 $self->{fh} = $fh;
341 511
342 $Net::FCP::regcb->($self); 512 $self->{w} = $EVENT->new_from_fh ($fh)->cb(sub { $self->fh_ready_w })->poll(0, 1, 1);
343 513
344 $self; 514 $self;
345} 515}
346 516
517=item $txn = $txn->cb ($coderef)
518
519Sets a callback to be called when the request is finished. The coderef
520will be called with the txn as it's sole argument, so it has to call
521C<result> itself.
522
523Returns the txn object, useful for chaining.
524
525Example:
526
527 $fcp->txn_client_get ("freenet:CHK....")
528 ->userdata ("ehrm")
529 ->cb(sub {
530 my $data = shift->result;
531 });
532
533=cut
534
535sub cb($$) {
536 my ($self, $cb) = @_;
537 $self->{cb} = $cb;
538 $self;
539}
540
541=item $txn = $txn->userdata ([$userdata])
542
543Set user-specific data. This is useful in progress callbacks. The data can be accessed
544using C<< $txn->{userdata} >>.
545
546Returns the txn object, useful for chaining.
547
548=cut
549
550sub userdata($$) {
551 my ($self, $data) = @_;
552 $self->{userdata} = $data;
553 $self;
554}
555
556=item $txn->cancel (%attr)
557
558Cancels the operation with a C<cancel> exception anf the given attributes
559(consider at least giving the attribute C<reason>).
560
561UNTESTED.
562
563=cut
564
565sub cancel {
566 my ($self, %attr) = @_;
567 $self->throw (Net::FCP::Exception->new (cancel => { %attr }));
568 $self->set_result;
569 $self->eof;
570}
571
347sub fh_ready { 572sub fh_ready_w {
573 my ($self) = @_;
574
575 my $len = syswrite $self->{fh}, $self->{sbuf};
576
577 if ($len > 0) {
578 substr $self->{sbuf}, 0, $len, "";
579 unless (length $self->{sbuf}) {
580 fcntl $self->{fh}, F_SETFL, 0;
581 $self->{w}->cb(sub { $self->fh_ready_r })->poll (1, 0, 1);
582 }
583 } elsif (defined $len) {
584 $self->throw (Net::FCP::Exception->new (network_error => { reason => "unexpected end of file while writing" }));
585 } else {
586 $self->throw (Net::FCP::Exception->new (network_error => { reason => "$!" }));
587 }
588}
589
590sub fh_ready_r {
348 my ($self) = @_; 591 my ($self) = @_;
349 592
350 if (sysread $self->{fh}, $self->{buf}, 65536, length $self->{buf}) { 593 if (sysread $self->{fh}, $self->{buf}, 65536, length $self->{buf}) {
351 for (;;) { 594 for (;;) {
352 if ($self->{datalen}) { 595 if ($self->{datalen}) {
596 #warn "expecting new datachunk $self->{datalen}, got ".(length $self->{buf})."\n";#d#
353 if (length $self->{buf} >= $self->{datalen}) { 597 if (length $self->{buf} >= $self->{datalen}) {
354 $self->rcv_data (substr $self->{buf}, 0, $self->{datalen}, ""); 598 $self->rcv_data (substr $self->{buf}, 0, delete $self->{datalen}, "");
355 } else { 599 } else {
356 last; 600 last;
357 } 601 }
358 } elsif ($self->{buf} =~ s/^DataChunk\015?\012Length=([0-9a-fA-F]+)\015?\012Data\015?\012//) { 602 } elsif ($self->{buf} =~ s/^DataChunk\015?\012Length=([0-9a-fA-F]+)\015?\012Data\015?\012//) {
359 $self->{datalen} = hex $1; 603 $self->{datalen} = hex $1;
604 #warn "expecting new datachunk $self->{datalen}\n";#d#
360 } elsif ($self->{buf} =~ s/^([a-zA-Z]+)\015?\012(.*?)\015?\012EndMessage\015?\012//s) { 605 } elsif ($self->{buf} =~ s/^([a-zA-Z]+)\015?\012(?:(.+?)\015?\012)?EndMessage\015?\012//s) {
361 $self->rcv ($1, { 606 $self->rcv ($1, {
362 map { my ($a, $b) = split /=/, $_, 2; ((Net::FCP::tolc $a), $b) } 607 map { my ($a, $b) = split /=/, $_, 2; ((Net::FCP::tolc $a), $b) }
363 split /\015?\012/, $2 608 split /\015?\012/, $2
364 }); 609 });
365 } else { 610 } else {
366 last; 611 last;
367 } 612 }
368 } 613 }
369 } else { 614 } else {
370 $Net::FCP::unregcb->($self);
371 delete $self->{fh};
372 $self->eof; 615 $self->eof;
373 } 616 }
374}
375
376sub rcv_data {
377 my ($self, $chunk) = @_;
378
379 $self->{data} .= $chunk;
380} 617}
381 618
382sub rcv { 619sub rcv {
383 my ($self, $type, $attr) = @_; 620 my ($self, $type, $attr) = @_;
384 621
391 } else { 628 } else {
392 warn "received unexpected reply type '$type' for '$self->{type}', ignoring\n"; 629 warn "received unexpected reply type '$type' for '$self->{type}', ignoring\n";
393 } 630 }
394} 631}
395 632
633# used as a default exception thrower
634sub rcv_throw_exception {
635 my ($self, $attr, $type) = @_;
636 $self->throw (Net::FCP::Exception->new ($type, $attr));
637}
638
639*rcv_failed = \&Net::FCP::Txn::rcv_throw_exception;
640*rcv_format_error = \&Net::FCP::Txn::rcv_throw_exception;
641
642sub throw {
643 my ($self, $exc) = @_;
644
645 $self->{exception} = $exc;
646 $self->set_result;
647 $self->eof; # must be last to avoid loops
648}
649
396sub set_result { 650sub set_result {
397 my ($self, $result) = @_; 651 my ($self, $result) = @_;
398 652
399 $self->{result} = $result unless exists $self->{result}; 653 unless (exists $self->{result}) {
654 $self->{result} = $result;
655 $self->{cb}->($self) if exists $self->{cb};
656 $self->{signal}->send;
657 }
400} 658}
401 659
402sub eof { 660sub eof {
403 my ($self) = @_; 661 my ($self) = @_;
404 $self->set_result; 662
663 delete $self->{w};
664 delete $self->{fh};
665
666 delete $self->{fcp}{txn}{$self};
667
668 unless (exists $self->{result}) {
669 $self->throw (Net::FCP::Exception->new (short_data => {
670 reason => "unexpected eof or internal node error",
671 }));
672 }
673}
674
675sub progress {
676 my ($self, $type, $attr) = @_;
677 $self->{fcp}->progress ($self, $type, $attr);
405} 678}
406 679
407=item $result = $txn->result 680=item $result = $txn->result
408 681
409Waits until a result is available and then returns it. 682Waits until a result is available and then returns it.
414=cut 687=cut
415 688
416sub result { 689sub result {
417 my ($self) = @_; 690 my ($self) = @_;
418 691
419 $Net::FCP::waitcb->() while !exists $self->{result}; 692 $self->{signal}->wait while !exists $self->{result};
693
694 die $self->{exception} if $self->{exception};
420 695
421 return $self->{result}; 696 return $self->{result};
422}
423
424sub DESTROY {
425 $Net::FCP::unregcb->($_[0]);
426} 697}
427 698
428package Net::FCP::Txn::ClientHello; 699package Net::FCP::Txn::ClientHello;
429 700
430use base Net::FCP::Txn; 701use base Net::FCP::Txn;
450use base Net::FCP::Txn; 721use base Net::FCP::Txn;
451 722
452sub rcv_success { 723sub rcv_success {
453 my ($self, $attr) = @_; 724 my ($self, $attr) = @_;
454 725
455 $self->set_result ($attr); 726 $self->set_result ($attr->{uri});
456} 727}
457 728
458package Net::FCP::Txn::GenerateSVKPair; 729package Net::FCP::Txn::GenerateSVKPair;
459 730
460use base Net::FCP::Txn; 731use base Net::FCP::Txn;
461 732
462sub rcv_success { 733sub rcv_success {
463 my ($self, $attr) = @_; 734 my ($self, $attr) = @_;
464
465 $self->set_result ([$attr->{PublicKey}, $attr->{PrivateKey}]); 735 $self->set_result ([$attr->{PublicKey}, $attr->{PrivateKey}]);
466} 736}
467 737
468package Net::FCP::Txn::InvertPrivateKey; 738package Net::FCP::Txn::InsertPrivateKey;
469 739
470use base Net::FCP::Txn; 740use base Net::FCP::Txn;
471 741
472sub rcv_success { 742sub rcv_success {
473 my ($self, $attr) = @_; 743 my ($self, $attr) = @_;
474
475 $self->set_result ($attr->{PublicKey}); 744 $self->set_result ($attr->{PublicKey});
476} 745}
477 746
478package Net::FCP::Txn::GetSize; 747package Net::FCP::Txn::GetSize;
479 748
480use base Net::FCP::Txn; 749use base Net::FCP::Txn;
481 750
482sub rcv_success { 751sub rcv_success {
483 my ($self, $attr) = @_; 752 my ($self, $attr) = @_;
484
485 $self->set_result ($attr->{Length}); 753 $self->set_result ($attr->{Length});
486} 754}
487 755
756package Net::FCP::Txn::GetPut;
757
758# base class for get and put
759
760use base Net::FCP::Txn;
761
762*rcv_uri_error = \&Net::FCP::Txn::rcv_throw_exception;
763*rcv_route_not_found = \&Net::FCP::Txn::rcv_throw_exception;
764
765sub rcv_restarted {
766 my ($self, $attr, $type) = @_;
767
768 delete $self->{datalength};
769 delete $self->{metalength};
770 delete $self->{data};
771
772 $self->progress ($type, $attr);
773}
774
488package Net::FCP::Txn::ClientGet; 775package Net::FCP::Txn::ClientGet;
489 776
490use base Net::FCP::Txn; 777use base Net::FCP::Txn::GetPut;
778
779*rcv_data_not_found = \&Net::FCP::Txn::rcv_throw_exception;
780
781sub rcv_data {
782 my ($self, $chunk) = @_;
783
784 $self->{data} .= $chunk;
785
786 $self->progress ("data", { chunk => length $chunk, received => length $self->{data}, total => $self->{datalength} });
787
788 if ($self->{datalength} == length $self->{data}) {
789 my $data = delete $self->{data};
790 my $meta = Net::FCP::parse_metadata substr $data, 0, $self->{metalength}, "";
791
792 $self->set_result ([$meta, $data]);
793 $self->eof;
794 }
795}
491 796
492sub rcv_data_found { 797sub rcv_data_found {
798 my ($self, $attr, $type) = @_;
799
800 $self->progress ($type, $attr);
801
802 $self->{datalength} = hex $attr->{data_length};
803 $self->{metalength} = hex $attr->{metadata_length};
804}
805
806package Net::FCP::Txn::ClientPut;
807
808use base Net::FCP::Txn::GetPut;
809
810*rcv_size_error = \&Net::FCP::Txn::rcv_throw_exception;
811*rcv_key_collision = \&Net::FCP::Txn::rcv_throw_exception;
812
813sub rcv_pending {
814 my ($self, $attr, $type) = @_;
815 $self->progress ($type, $attr);
816}
817
818sub rcv_success {
819 my ($self, $attr, $type) = @_;
820 $self->set_result ($attr);
821}
822
823=back
824
825=head2 The Net::FCP::Exception CLASS
826
827Any unexpected (non-standard) responses that make it impossible to return
828the advertised result will result in an exception being thrown when the
829C<result> method is called.
830
831These exceptions are represented by objects of this class.
832
833=over 4
834
835=cut
836
837package Net::FCP::Exception;
838
839use overload
840 '""' => sub {
841 "Net::FCP::Exception<<$_[0][0]," . (join ":", %{$_[0][1]}) . ">>";
842 };
843
844=item $exc = new Net::FCP::Exception $type, \%attr
845
846Create a new exception object of the given type (a string like
847C<route_not_found>), and a hashref containing additional attributes
848(usually the attributes of the message causing the exception).
849
850=cut
851
852sub new {
853 my ($class, $type, $attr) = @_;
854
855 bless [Net::FCP::tolc $type, { %$attr }], $class;
856}
857
858=item $exc->type([$type])
859
860With no arguments, returns the exception type. Otherwise a boolean
861indicating wether the exception is of the given type is returned.
862
863=cut
864
865sub type {
866 my ($self, $type) = @_;
867
868 @_ >= 2
869 ? $self->[0] eq $type
870 : $self->[0];
871}
872
873=item $exc->attr([$attr])
874
875With no arguments, returns the attributes. Otherwise the named attribute
876value is returned.
877
878=cut
879
880sub attr {
493 my ($self, $attr) = @_; 881 my ($self, $attr) = @_;
494 882
495 $self->{datalength} = hex $attr->{data_length}; 883 @_ >= 2
496 $self->{metalength} = hex $attr->{meta_data_length}; 884 ? $self->[1]{$attr}
497} 885 : $self->[1];
498
499sub eof {
500 my ($self) = @_;
501 #use PApp::Util; warn PApp::Util::dumpval $self;
502 my $data = delete $self->{data};
503 $self->set_result ([
504 (substr $data, 0, $self->{datalength}-$self->{metalength}),
505 (substr $data, $self->{datalength}-$self->{metalength}),
506 ]);
507} 886}
508 887
509=back 888=back
510 889
511=head1 SEE ALSO 890=head1 SEE ALSO
519 Marc Lehmann <pcg@goof.com> 898 Marc Lehmann <pcg@goof.com>
520 http://www.goof.com/pcg/marc/ 899 http://www.goof.com/pcg/marc/
521 900
522=cut 901=cut
523 902
903package Net::FCP::Event::Auto;
904
905my @models = (
906 [Coro => Coro::Event:: ],
907 [Event => Event::],
908 [Glib => Glib:: ],
909 [Tk => Tk::],
910);
911
912sub AUTOLOAD {
913 $AUTOLOAD =~ s/.*://;
914
915 for (@models) {
916 my ($model, $package) = @$_;
917 if (defined ${"$package\::VERSION"}) {
918 $EVENT = "Net::FCP::Event::$model";
919 eval "require $EVENT"; die if $@;
920 goto &{"$EVENT\::$AUTOLOAD"};
921 }
922 }
923
924 for (@models) {
925 my ($model, $package) = @$_;
926 $EVENT = "Net::FCP::Event::$model";
927 if (eval "require $EVENT") {
928 goto &{"$EVENT\::$AUTOLOAD"};
929 }
930 }
931
932 die "No event module selected for Net::FCP and autodetect failed. Install any of these: Coro, Event, Glib or Tk.";
933}
934
5241; 9351;
525 936

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines