diff options
author | gregor herrmann <gregoa@debian.org> | 2020-01-24 17:08:06 +0100 |
---|---|---|
committer | gregor herrmann <gregoa@debian.org> | 2020-01-24 17:08:06 +0100 |
commit | d20d2f668f1faf341a78af4ff1846fd3e56d281b (patch) | |
tree | 1d2791ff8fb35298c91f69726a2d2fb8efdc87b6 | |
parent | e94e49c7dad19be389216f42fdc051cdb5009722 (diff) | |
parent | a0ffee3b830a0e147851f4462e599fe8eb7a63ff (diff) |
Update upstream source from tag 'upstream/0.25'
Update to upstream version '0.25'
with Debian dir a71a64936bd7ea64a3672e2a16449fa127d58dbd
36 files changed, 770 insertions, 413 deletions
@@ -13,6 +13,7 @@ my $build = Module::Build->new( 'perl' => 5.010, 'Parser::MGC' => '0.04', 'Struct::Dumb' => 0, + 'Sub::Util' => '1.40', }, test_requires => { 'Struct::Dumb' => '0.09', @@ -1,5 +1,18 @@ Revision history for Tangence +0.25 2020-01-14 + [CHANGES] + * Allow servers to disallow access to Registry + * Disallow clients from accessing objects that haven't already been + sent to them + * Customisable root object per connection + * Added $client->get_registry; discourage the ->registry method + * Use core's Sub::Util::set_subname() + * Removed support for protocol minor version 2 + + [BUGFIXES] + * Ensure MSG_SETPROP serialises correctly for non-scalar properties + 0.24 2017-11-14 17:48:45 [BUGFIXES] * Avoid harmless warning about wide characters during SvIV test @@ -1,4 +1,4 @@ -This software is copyright (c) 2017 by Paul Evans <leonerd@leonerd.org.uk>. +This software is copyright (c) 2020 by Paul Evans <leonerd@leonerd.org.uk>. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. @@ -12,7 +12,7 @@ b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- -This software is Copyright (c) 2017 by Paul Evans <leonerd@leonerd.org.uk>. +This software is Copyright (c) 2020 by Paul Evans <leonerd@leonerd.org.uk>. This is free software, licensed under: @@ -272,7 +272,7 @@ That's all there is to it! --- The Artistic License 1.0 --- -This software is Copyright (c) 2017 by Paul Evans <leonerd@leonerd.org.uk>. +This software is Copyright (c) 2020 by Paul Evans <leonerd@leonerd.org.uk>. This is free software, licensed under: @@ -47,6 +47,8 @@ t/23close.t t/30props-cbs.t t/31props-cache.t t/32props-cursor.t +t/33props-set.t +t/40server-security.t t/90close-leak.t t/99pod.t t/Ball.pm @@ -4,7 +4,7 @@ "Paul Evans <leonerd@leonerd.org.uk>" ], "dynamic_config" : 1, - "generated_by" : "Module::Build version 0.422", + "generated_by" : "Module::Build version 0.4224", "license" : [ "perl_5" ], @@ -27,6 +27,7 @@ "List::Util" : "1.29", "Parser::MGC" : "0.04", "Struct::Dumb" : "0", + "Sub::Util" : "1.40", "perl" : "5.01" } }, @@ -45,91 +46,91 @@ "provides" : { "Tangence" : { "file" : "lib/Tangence.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Class" : { "file" : "lib/Tangence/Class.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Client" : { "file" : "lib/Tangence/Client.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Compiler::Parser" : { "file" : "lib/Tangence/Compiler/Parser.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Constants" : { "file" : "lib/Tangence/Constants.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Message" : { "file" : "lib/Tangence/Message.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Meta::Argument" : { "file" : "lib/Tangence/Meta/Argument.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Meta::Class" : { "file" : "lib/Tangence/Meta/Class.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Meta::Event" : { "file" : "lib/Tangence/Meta/Event.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Meta::Field" : { "file" : "lib/Tangence/Meta/Field.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Meta::Method" : { "file" : "lib/Tangence/Meta/Method.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Meta::Property" : { "file" : "lib/Tangence/Meta/Property.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Meta::Struct" : { "file" : "lib/Tangence/Meta/Struct.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Meta::Type" : { "file" : "lib/Tangence/Meta/Type.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Object" : { "file" : "lib/Tangence/Object.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::ObjectProxy" : { "file" : "lib/Tangence/ObjectProxy.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Property" : { "file" : "lib/Tangence/Property.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Registry" : { "file" : "lib/Tangence/Registry.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Server" : { "file" : "lib/Tangence/Server.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Server::Context" : { "file" : "lib/Tangence/Server/Context.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Stream" : { "file" : "lib/Tangence/Stream.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Struct" : { "file" : "lib/Tangence/Struct.pm", - "version" : "0.24" + "version" : "0.25" }, "Tangence::Type" : { "file" : "lib/Tangence/Type.pm" @@ -139,7 +140,7 @@ }, "Tangence::Types" : { "file" : "lib/Tangence/Types.pm", - "version" : "0.24" + "version" : "0.25" } }, "release_status" : "stable", @@ -148,6 +149,6 @@ "http://dev.perl.org/licenses/" ] }, - "version" : "0.24", - "x_serialization_backend" : "JSON::PP version 2.94" + "version" : "0.25", + "x_serialization_backend" : "JSON::PP version 4.04" } @@ -13,7 +13,7 @@ build_requires: configure_requires: Module::Build: '0.4004' dynamic_config: 1 -generated_by: 'Module::Build version 0.422, CPAN::Meta::Converter version 2.150010' +generated_by: 'Module::Build version 0.4224, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html @@ -22,77 +22,77 @@ name: Tangence provides: Tangence: file: lib/Tangence.pm - version: '0.24' + version: '0.25' Tangence::Class: file: lib/Tangence/Class.pm - version: '0.24' + version: '0.25' Tangence::Client: file: lib/Tangence/Client.pm - version: '0.24' + version: '0.25' Tangence::Compiler::Parser: file: lib/Tangence/Compiler/Parser.pm - version: '0.24' + version: '0.25' Tangence::Constants: file: lib/Tangence/Constants.pm - version: '0.24' + version: '0.25' Tangence::Message: file: lib/Tangence/Message.pm - version: '0.24' + version: '0.25' Tangence::Meta::Argument: file: lib/Tangence/Meta/Argument.pm - version: '0.24' + version: '0.25' Tangence::Meta::Class: file: lib/Tangence/Meta/Class.pm - version: '0.24' + version: '0.25' Tangence::Meta::Event: file: lib/Tangence/Meta/Event.pm - version: '0.24' + version: '0.25' Tangence::Meta::Field: file: lib/Tangence/Meta/Field.pm - version: '0.24' + version: '0.25' Tangence::Meta::Method: file: lib/Tangence/Meta/Method.pm - version: '0.24' + version: '0.25' Tangence::Meta::Property: file: lib/Tangence/Meta/Property.pm - version: '0.24' + version: '0.25' Tangence::Meta::Struct: file: lib/Tangence/Meta/Struct.pm - version: '0.24' + version: '0.25' Tangence::Meta::Type: file: lib/Tangence/Meta/Type.pm - version: '0.24' + version: '0.25' Tangence::Object: file: lib/Tangence/Object.pm - version: '0.24' + version: '0.25' Tangence::ObjectProxy: file: lib/Tangence/ObjectProxy.pm - version: '0.24' + version: '0.25' Tangence::Property: file: lib/Tangence/Property.pm - version: '0.24' + version: '0.25' Tangence::Registry: file: lib/Tangence/Registry.pm - version: '0.24' + version: '0.25' Tangence::Server: file: lib/Tangence/Server.pm - version: '0.24' + version: '0.25' Tangence::Server::Context: file: lib/Tangence/Server/Context.pm - version: '0.24' + version: '0.25' Tangence::Stream: file: lib/Tangence/Stream.pm - version: '0.24' + version: '0.25' Tangence::Struct: file: lib/Tangence/Struct.pm - version: '0.24' + version: '0.25' Tangence::Type: file: lib/Tangence/Type.pm Tangence::Type::Primitive: file: lib/Tangence/Type/Primitive.pm Tangence::Types: file: lib/Tangence/Types.pm - version: '0.24' + version: '0.25' requires: Encode: '0' Exporter: '5.57' @@ -100,8 +100,9 @@ requires: List::Util: '1.29' Parser::MGC: '0.04' Struct::Dumb: '0' + Sub::Util: '1.40' perl: '5.01' resources: license: http://dev.perl.org/licenses/ -version: '0.24' +version: '0.25' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' diff --git a/doc/Tangence.txt b/doc/Tangence.txt index 62081d1..205744a 100644 --- a/doc/Tangence.txt +++ b/doc/Tangence.txt @@ -812,60 +812,6 @@ not need serialising by DATAMETA_STRUCT records. type : str smashed : bool - -5. Perl Distribution --------------------- - -The perl distribution is available from - - http://bazaar.leonerd.dyndns.org/perl/Tangence/ - -At some stage when the details become more concrete this will start -gaining inline documentation, but for now it just has some commenting. - -As a rough description of the modules: - -5.1. Shared by server and client - - + Tangence::Constants - Defines various magic numbers used in the wire streaming protocol. - - + Tangence::Stream - Implements most of the lower level wire streaming protocol, including - the symmetric parts of data serialisation. - -5.2. Used by the client - - + Tangence::Connection - The connection to the server. Handles the higher-level client-specific - parts of the wire protocol. - - + Tangence::ObjectProxy - Acts as a proxy to one particular object within the server. Used for - invoking methods, subscribing to events, and interacting with - properties. - -5.3. Used by the server - - + Tangence::Object - A base class for implementing Tangence objects within the server. - - + Tangence::Registry - The object registry; keeps a reference to every Tangence object in the - server. - - + Tangence::Server - A base class for implementing the entire server. - - + Tangence::Server::Connection - Server end of a client connection. Handles most of the higher-level - server-specific parts of the wire protocol. - - + Tangence::Server::Context - An object class to represent the client calling context during the - invocation of a server object method or property change. - - -- Paul "LeoNerd" Evans diff --git a/lib/Tangence.pm b/lib/Tangence.pm index 984c3bb..2e020ef 100644 --- a/lib/Tangence.pm +++ b/lib/Tangence.pm @@ -12,7 +12,7 @@ use warnings; # It is provided simply to keep CPAN happy: # cpan -i Tangence -our $VERSION = '0.24'; +our $VERSION = '0.25'; =head1 NAME diff --git a/lib/Tangence/Class.pm b/lib/Tangence/Class.pm index b7d0e55..460efae 100644 --- a/lib/Tangence/Class.pm +++ b/lib/Tangence/Class.pm @@ -1,7 +1,7 @@ # You may distribute under the terms of either the GNU General Public License # or the Artistic License (the same terms as Perl itself) # -# (C) Paul Evans, 2010-2014 -- leonerd@leonerd.org.uk +# (C) Paul Evans, 2010-2020 -- leonerd@leonerd.org.uk package Tangence::Class; @@ -19,17 +19,9 @@ use Tangence::Meta::Argument; use Carp; -BEGIN { - if( eval { require Sub::Name } ) { - Sub::Name->import(qw( subname )); - } - else { - # Emulate it by just returning the CODEref and ignoring setting the name - *subname = sub { $_[1] }; - } -} +use Sub::Util 1.40 qw( set_subname ); -our $VERSION = '0.24'; +our $VERSION = '0.25'; our %metas; # cache one per class, keyed by _Tangence_ class name @@ -130,7 +122,7 @@ sub define no strict 'refs'; foreach my $name ( keys %subs ) { next if defined &{"${class}::${name}"}; - *{"${class}::${name}"} = subname "${class}::${name}" => $subs{$name}; + *{"${class}::${name}"} = set_subname "${class}::${name}" => $subs{$name}; } } diff --git a/lib/Tangence/Client.pm b/lib/Tangence/Client.pm index 7ca656e..fb1483e 100644 --- a/lib/Tangence/Client.pm +++ b/lib/Tangence/Client.pm @@ -1,7 +1,7 @@ # You may distribute under the terms of either the GNU General Public License # or the Artistic License (the same terms as Perl itself) # -# (C) Paul Evans, 2010-2015 -- leonerd@leonerd.org.uk +# (C) Paul Evans, 2010-2020 -- leonerd@leonerd.org.uk package Tangence::Client; @@ -10,7 +10,7 @@ use warnings; use base qw( Tangence::Stream ); -our $VERSION = '0.24'; +our $VERSION = '0.25'; use Carp; @@ -22,7 +22,7 @@ use Future; use List::Util qw( max ); -use constant VERSION_MINOR_MIN => 2; +use constant VERSION_MINOR_MIN => 3; =head1 NAME @@ -110,7 +110,13 @@ sub rootobj $registry = $client->registry -Returns a L<Tangence::ObjectProxy> to the server's object registry +Returns a L<Tangence::ObjectProxy> to the server's object registry if one has +been received, or C<undef> if not. + +This method is now deprecated in favour of L</get_registry>. Additionally note +that currently the client will attempt to request the registry at connection +time, but a later version of this module will stop doing that, so users who +need access to it should call C<get_registry>. =cut @@ -121,6 +127,35 @@ sub registry return $self->{registry}; } +=head2 get_registry + + $registry = $client->get_registry->get + +Returns a L<Future> that will yield a L<Tangence::ObjectProxy> to the server's +registry object. + +Note that not all servers may permit access to the registry. + +=cut + +sub get_registry +{ + my $self = shift; + + $self->request( + request => Tangence::Message->new( $self, MSG_GETREGISTRY ), + )->then( sub { + my ( $message ) = @_; + my $code = $message->code; + + $code == MSG_RESULT or + return Future->fail( "Cannot get registry - code $code", tangence => $message ); + + $self->registry( TYPE_OBJ->unpack_value( $message ) ); + return Future->done( $self->registry ); + }); +} + sub on_error { my $self = shift; @@ -159,6 +194,11 @@ be passed a L<Tangence::ObjectProxy> to the registry. $on_registry->( $registry ) +Note that in the case that the server does not permit access to the registry +or an error occurs while requesting it, this is invoked with an empty list. + + $on_registry->() + =item version_minor_min => INT Optional minimum minor version to negotiate with the server. This can be used @@ -233,26 +273,15 @@ sub tangence_initialised } ); - $self->request( - request => Tangence::Message->new( $self, MSG_GETREGISTRY ), - - on_response => sub { - my ( $message ) = @_; - my $code = $message->code; - - if( $code == MSG_RESULT ) { - $self->registry( TYPE_OBJ->unpack_value( $message ) ); - $args{on_registry}->( $self->registry ) if $args{on_registry}; - } - elsif( $code == MSG_ERROR ) { - my $msg = $message->unpack_str(); - print STDERR "Cannot get registry - error $msg"; - } - else { - print STDERR "Cannot get registry - code $code\n"; - } + $self->get_registry->then( + sub { + my ( $registry ) = @_; + $args{on_registry}->( $registry ) if $args{on_registry}; + }, + sub { + $args{on_registry}->() if $args{on_registry}; } - ); + )->retain; } sub handle_request_EVENT diff --git a/lib/Tangence/Compiler/Parser.pm b/lib/Tangence/Compiler/Parser.pm index e6caad2..e4ce39a 100644 --- a/lib/Tangence/Compiler/Parser.pm +++ b/lib/Tangence/Compiler/Parser.pm @@ -1,7 +1,7 @@ # You may distribute under the terms of either the GNU General Public License # or the Artistic License (the same terms as Perl itself) # -# (C) Paul Evans, 2011-2014 -- leonerd@leonerd.org.uk +# (C) Paul Evans, 2011-2017 -- leonerd@leonerd.org.uk package Tangence::Compiler::Parser; @@ -12,7 +12,7 @@ use base qw( Parser::MGC ); use feature qw( switch ); # we like given/when no if $] >= 5.017011, warnings => 'experimental::smartmatch'; -our $VERSION = '0.24'; +our $VERSION = '0.25'; use File::Basename qw( dirname ); @@ -391,7 +391,9 @@ in the syntax tree. =cut -=head2 $class = $parser->make_class( name => $name ) +=head2 make_class + + $class = $parser->make_class( name => $name ) Return a new instance of L<Tangence::Meta::Class> to go in a package. The parser will call C<define> on it. @@ -405,7 +407,9 @@ sub make_class return Tangence::Meta::Class->new( @_ ); } -=head2 $struct = $parser->make_struct( name => $name ) +=head2 make_struct + + $struct = $parser->make_struct( name => $name ) Return a new instance of L<Tangence::Meta::Struct> to go in a package. The parser will call C<define> on it. @@ -419,11 +423,17 @@ sub make_struct return Tangence::Meta::Struct->new( @_ ); } -=head2 $method = $parser->make_method( %args ) +=head2 make_method + + $method = $parser->make_method( %args ) + +=head2 make_event -=head2 $event = $parser->make_event( %args ) + $event = $parser->make_event( %args ) -=head2 $property = $parser->make_property( %args ) +=head2 make_property + + $property = $parser->make_property( %args ) Return a new instance of L<Tangence::Meta::Method>, L<Tangence::Meta::Event> or L<Tangence::Meta::Property> to go in a class. @@ -451,7 +461,9 @@ sub make_property return Tangence::Meta::Property->new( @_ ); } -=head2 $argument = $parser->make_argument( %args ) +=head2 make_argument + + $argument = $parser->make_argument( %args ) Return a new instance of L<Tangence::Meta::Argument> to use for a method or event argument. @@ -465,7 +477,9 @@ sub make_argument return Tangence::Meta::Argument->new( @_ ); } -=head2 $field = $parser->make_field( %args ) +=head2 make_field + + $field = $parser->make_field( %args ) Return a new instance of L<Tangence::Meta::Field> to use for a structure type. @@ -478,9 +492,11 @@ sub make_field return Tangence::Meta::Field->new( @_ ); } -=head2 $type = $parser->make_type( $primitive_name ) +=head2 make_type + + $type = $parser->make_type( $primitive_name ) -=head2 $type = $parser->make_type( $aggregate_name => $member_type ) + $type = $parser->make_type( $aggregate_name => $member_type ) Return an instance of L<Tangence::Meta::Type> representing the given primitive or aggregate type name. An implementation is allowed to use diff --git a/lib/Tangence/Constants.pm b/lib/Tangence/Constants.pm index b0ef67c..26f65fe 100644 --- a/lib/Tangence/Constants.pm +++ b/lib/Tangence/Constants.pm @@ -8,7 +8,7 @@ package Tangence::Constants; use strict; use warnings; -our $VERSION = '0.24'; +our $VERSION = '0.25'; use Exporter 'import'; our @EXPORT = qw( diff --git a/lib/Tangence/Message.pm b/lib/Tangence/Message.pm index 38a8465..a66ed9f 100644 --- a/lib/Tangence/Message.pm +++ b/lib/Tangence/Message.pm @@ -13,7 +13,7 @@ use warnings; # restriction could be listed. use 5.010; # pack endian formats -our $VERSION = '0.24'; +our $VERSION = '0.25'; use Carp; diff --git a/lib/Tangence/Meta/Argument.pm b/lib/Tangence/Meta/Argument.pm index d810013..2d02af4 100644 --- a/lib/Tangence/Meta/Argument.pm +++ b/lib/Tangence/Meta/Argument.pm @@ -8,7 +8,7 @@ package Tangence::Meta::Argument; use strict; use warnings; -our $VERSION = '0.24'; +our $VERSION = '0.25'; =head1 NAME @@ -27,7 +27,9 @@ immutable. =cut -=head2 $argument = Tangence::Meta::Argument->new( %args ) +=head2 new + + $argument = Tangence::Meta::Argument->new( %args ) Returns a new instance initialised by the given arguments. @@ -56,7 +58,9 @@ sub new =cut -=head2 $name = $argument->name +=head2 name + + $name = $argument->name Returns the name of the class @@ -68,7 +72,9 @@ sub name return $self->{name}; } -=head2 $type = $argument->type +=head2 type + + $type = $argument->type Return the type as a L<Tangence::Meta::Type> reference. diff --git a/lib/Tangence/Meta/Class.pm b/lib/Tangence/Meta/Class.pm index 369e262..677e5f7 100644 --- a/lib/Tangence/Meta/Class.pm +++ b/lib/Tangence/Meta/Class.pm @@ -1,7 +1,7 @@ # You may distribute under the terms of either the GNU General Public License # or the Artistic License (the same terms as Perl itself) # -# (C) Paul Evans, 2011-2013 -- leonerd@leonerd.org.uk +# (C) Paul Evans, 2011-2017 -- leonerd@leonerd.org.uk package Tangence::Meta::Class; @@ -10,7 +10,7 @@ use warnings; use Carp; -our $VERSION = '0.24'; +our $VERSION = '0.25'; =head1 NAME @@ -27,7 +27,9 @@ Once constructed and defined, such objects are immutable. =cut -=head2 $class = Tangence::Meta::Class->new( name => $name ) +=head2 new + + $class = Tangence::Meta::Class->new( name => $name ) Returns a new instance representing the given name. @@ -41,7 +43,9 @@ sub new return $self; } -=head2 $class->define( %args ) +=head2 define + + $class->define( %args ) Provides a definition for the class. @@ -84,7 +88,9 @@ sub define =cut -=head2 $defined = $class->defined +=head2 defined + + $defined = $class->defined Returns true if a definintion for the class has been provided using C<define>. @@ -96,7 +102,9 @@ sub defined return exists $self->{superclasses}; } -=head2 $name = $class->name +=head2 name + + $name = $class->name Returns the name of the class @@ -108,7 +116,9 @@ sub name return $self->{name}; } -=head2 $perlname = $class->perlname +=head2 perlname + + $perlname = $class->perlname Returns the perl name of the class. This will be the Tangence name, with dots replaced by double colons (C<::>). @@ -122,7 +132,9 @@ sub perlname return $perlname; } -=head2 @superclasses = $class->direct_superclasses +=head2 direct_superclasses + + @superclasses = $class->direct_superclasses Return the direct superclasses in a list of C<Tangence::Meta::Class> references. @@ -136,7 +148,9 @@ sub direct_superclasses return @{ $self->{superclasses} }; } -=head2 $methods = $class->direct_methods +=head2 direct_methods + + $methods = $class->direct_methods Return the methods that this class directly defines (rather than inheriting from superclasses) as a HASH reference mapping names to @@ -151,7 +165,9 @@ sub direct_methods return $self->{methods}; } -=head2 $events = $class->direct_events +=head2 direct_events + + $events = $class->direct_events Return the events that this class directly defines (rather than inheriting from superclasses) as a HASH reference mapping names to @@ -166,7 +182,9 @@ sub direct_events return $self->{events}; } -=head2 $properties = $class->direct_properties +=head2 direct_properties + + $properties = $class->direct_properties Return the properties that this class directly defines (rather than inheriting from superclasses) as a HASH reference mapping names to @@ -188,7 +206,9 @@ all its superclasses =cut -=head2 @superclasses = $class->superclasses +=head2 superclasses + + @superclasses = $class->superclasses Return all the superclasses in a list of unique C<Tangence::Meta::Class> references. @@ -204,7 +224,9 @@ sub superclasses return grep { !$seen{$_}++ } map { $_, $_->superclasses } $self->direct_superclasses; } -=head2 $methods = $class->methods +=head2 methods + + $methods = $class->methods Return all the methods available to this class as a HASH reference mapping names to L<Tangence::Meta::Method> instances. @@ -222,7 +244,9 @@ sub methods return \%methods; } -=head2 $method = $class->method( $name ) +=head2 method + + $method = $class->method( $name ) Return the named method as a L<Tangence::Meta::Method> instance, or C<undef> if no such method exists. @@ -236,7 +260,9 @@ sub method return $self->methods->{$name}; } -=head2 $events = $class->events +=head2 events + + $events = $class->events Return all the events available to this class as a HASH reference mapping names to L<Tangence::Meta::Event> instances. @@ -254,7 +280,9 @@ sub events return \%events; } -=head2 $event = $class->event( $name ) +=head2 event + + $event = $class->event( $name ) Return the named event as a L<Tangence::Meta::Event> instance, or C<undef> if no such event exists. @@ -268,7 +296,9 @@ sub event return $self->events->{$name}; } -=head2 $properties = $class->properties +=head2 properties + + $properties = $class->properties Return all the properties available to this class as a HASH reference mapping names to L<Tangence::Meta::Property> instances. @@ -286,7 +316,9 @@ sub properties return \%properties; } -=head2 $property = $class->property( $name ) +=head2 property + + $property = $class->property( $name ) Return the named property as a L<Tangence::Meta::Property> instance, or C<undef> if no such property exists. diff --git a/lib/Tangence/Meta/Event.pm b/lib/Tangence/Meta/Event.pm index f2de493..6f84c7c 100644 --- a/lib/Tangence/Meta/Event.pm +++ b/lib/Tangence/Meta/Event.pm @@ -1,14 +1,14 @@ # You may distribute under the terms of either the GNU General Public License # or the Artistic License (the same terms as Perl itself) # -# (C) Paul Evans, 2011 -- leonerd@leonerd.org.uk +# (C) Paul Evans, 2011-2017 -- leonerd@leonerd.org.uk package Tangence::Meta::Event; use strict; use warnings; -our $VERSION = '0.24'; +our $VERSION = '0.25'; use Scalar::Util qw( weaken ); @@ -27,7 +27,9 @@ event. Once constructed, such objects are immutable. =cut -=head2 $event = Tangence::Meta::Event->new( %args ) +=head2 new + + $event = Tangence::Meta::Event->new( %args ) Returns a new instance initialised by the given arguments. @@ -64,7 +66,9 @@ sub new =cut -=head2 $class = $event->class +=head2 class + + $class = $event->class Returns the class the event is a member of @@ -76,7 +80,9 @@ sub class return $self->{class}; } -=head2 $name = $event->name +=head2 name + + $name = $event->name Returns the name of the class @@ -88,7 +94,9 @@ sub name return $self->{name}; } -=head2 @arguments = $event->arguments +=head2 arguments + + @arguments = $event->arguments Return the arguments in a list of L<Tangence::Meta::Argument> references. @@ -100,7 +108,9 @@ sub arguments return @{ $self->{arguments} }; } -=head2 @argtypes = $event->argtypes +=head2 argtypes + + @argtypes = $event->argtypes Return the argument types in a list of strings. @@ -119,4 +129,3 @@ Paul Evans <leonerd@leonerd.org.uk> =cut 0x55AA; - diff --git a/lib/Tangence/Meta/Field.pm b/lib/Tangence/Meta/Field.pm index 4974e0e..2572c45 100644 --- a/lib/Tangence/Meta/Field.pm +++ b/lib/Tangence/Meta/Field.pm @@ -1,14 +1,14 @@ # You may distribute under the terms of either the GNU General Public License # or the Artistic License (the same terms as Perl itself) # -# (C) Paul Evans, 2012 -- leonerd@leonerd.org.uk +# (C) Paul Evans, 2012-2017 -- leonerd@leonerd.org.uk package Tangence::Meta::Field; use strict; use warnings; -our $VERSION = '0.24'; +our $VERSION = '0.25'; =head1 NAME @@ -26,7 +26,9 @@ structure. Once constructed, such objects are immutable. =cut -=head2 $field = Tangence::Meta::Field->new( %args ) +=head2 new + + $field = Tangence::Meta::Field->new( %args ) Returns a new instance initialised by the given fields. @@ -55,7 +57,9 @@ sub new =cut -=head2 $name = $field->name +=head2 name + + $name = $field->name Returns the name of the field @@ -67,7 +71,9 @@ sub name return $self->{name}; } -=head2 $type = $field->type +=head2 type + + $type = $field->type Return the type as a L<Tangence::Meta::Type> reference. diff --git a/lib/Tangence/Meta/Method.pm b/lib/Tangence/Meta/Method.pm index f845553..c0c13ff 100644 --- a/lib/Tangence/Meta/Method.pm +++ b/lib/Tangence/Meta/Method.pm @@ -1,14 +1,14 @@ # You may distribute under the terms of either the GNU General Public License # or the Artistic License (the same terms as Perl itself) # -# (C) Paul Evans, 2011-2012 -- leonerd@leonerd.org.uk +# (C) Paul Evans, 2011-2017 -- leonerd@leonerd.org.uk package Tangence::Meta::Method; use strict; use warnings; -our $VERSION = '0.24'; +our $VERSION = '0.25'; use Scalar::Util qw( weaken ); @@ -27,7 +27,9 @@ method. Once constructed, such objects are immutable. =cut -=head2 $method = Tangence::Meta::Method->new( %args ) +=head2 new + + $method = Tangence::Meta::Method->new( %args ) Returns a new instance initialised by the given arguments. @@ -69,7 +71,9 @@ sub new =cut -=head2 $class = $method->class +=head2 class + + $class = $method->class Returns the class the method is a member of @@ -81,7 +85,9 @@ sub class return $self->{class}; } -=head2 $name = $method->name +=head2 name + + $name = $method->name Returns the name of the class @@ -93,7 +99,9 @@ sub name return $self->{name}; } -=head2 @arguments = $method->arguments +=head2 arguments + + @arguments = $method->arguments Return the arguments in a list of L<Tangence::Meta::Argument> references. @@ -105,7 +113,9 @@ sub arguments return @{ $self->{arguments} }; } -=head2 @argtypes = $method->argtypes +=head2 argtype + + @argtypes = $method->argtypes Return the argument types in a list of L<Tangence::Meta::Type> references. @@ -117,7 +127,9 @@ sub argtypes return map { $_->type } $self->arguments; } -=head2 $ret = $method->ret +=head2 ret + + $ret = $method->ret Returns the return type as a L<Tangence::Meta::Type> reference or C<undef> if the method does not return a value. diff --git a/lib/Tangence/Meta/Property.pm b/lib/Tangence/Meta/Property.pm index d6fdd83..da84691 100644 --- a/lib/Tangence/Meta/Property.pm +++ b/lib/Tangence/Meta/Property.pm @@ -1,14 +1,14 @@ # You may distribute under the terms of either the GNU General Public License # or the Artistic License (the same terms as Perl itself) # -# (C) Paul Evans, 2011-2014 -- leonerd@leonerd.org.uk +# (C) Paul Evans, 2011-2017 -- leonerd@leonerd.org.uk package Tangence::Meta::Property; use strict; use warnings; -our $VERSION = '0.24'; +our $VERSION = '0.25'; use Tangence::Constants; @@ -29,7 +29,9 @@ property. Once constructed, such objects are immutable. =cut -=head2 $property = Tangence::Meta::Property->new( %args ) +=head2 new + + $property = Tangence::Meta::Property->new( %args ) Returns a new instance initialised by the given arguments. @@ -73,7 +75,9 @@ sub new =cut -=head2 $class = $property->class +=head2 class + + $class = $property->class Returns the class the property is a member of @@ -85,7 +89,9 @@ sub class return $self->{class}; } -=head2 $name = $property->name +=head2 name + + $name = $property->name Returns the name of the class @@ -97,7 +103,9 @@ sub name return $self->{name}; } -=head2 $dimension = $property->dimension +=head2 dimension + + $dimension = $property->dimension Returns the dimension as one of the C<DIM_*> constants. @@ -109,7 +117,9 @@ sub dimension return $self->{dimension}; } -=head2 $type = $property->type +=head2 type + + $type = $property->type Returns the element type as a L<Tangence::Meta::Type> reference. @@ -121,7 +131,9 @@ sub type return $self->{type}; } -=head2 $type = $property->overall_type +=head2 overall_type + + $type = $property->overall_type Returns the type of the entire collection as a L<Tangence::Meta::Type> reference. For scalar types this will be the element type. For dict types this @@ -151,7 +163,9 @@ sub overall_type } } -=head2 $smashed = $property->smashed +=head2 smashed + + $smashed = $property->smashed Returns true if the property is smashed. diff --git a/lib/Tangence/Meta/Struct.pm b/lib/Tangence/Meta/Struct.pm index 1180c46..cc5e49a 100644 --- a/lib/Tangence/Meta/Struct.pm +++ b/lib/Tangence/Meta/Struct.pm @@ -1,7 +1,7 @@ # You may distribute under the terms of either the GNU General Public License # or the Artistic License (the same terms as Perl itself) # -# (C) Paul Evans, 2012 -- leonerd@leonerd.org.uk +# (C) Paul Evans, 2012-2017 -- leonerd@leonerd.org.uk package Tangence::Meta::Struct; @@ -10,7 +10,7 @@ use warnings; use Carp; -our $VERSION = '0.24'; +our $VERSION = '0.25'; =head1 NAME @@ -28,7 +28,9 @@ Once constructed and defined, such objects are immutable. =cut -=head2 $struct = Tangence::Meta::Struct->new( name => $name ) +=head2 new + + $struct = Tangence::Meta::Struct->new( name => $name ) Returns a new instance representing the given name. @@ -42,7 +44,9 @@ sub new return $self; } -=head2 $struct->define( %args ) +=head2 define + + $struct->define( %args ) Provides a definition for the structure. @@ -71,7 +75,9 @@ sub define =cut -=head2 $defined = $struct->defined +=head2 defined + + $defined = $struct->defined Returns true if a definition of the structure has been provided using C<define>. @@ -84,7 +90,9 @@ sub defined return exists $self->{fields}; } -=head2 $name = $struct->name +=head2 name + + $name = $struct->name Returns the name of the structure @@ -96,7 +104,9 @@ sub name return $self->{name}; } -=head2 @fields = $struct->fields +=head2 fields + + @fields = $struct->fields Returns a list of the fields defined on the structure, in their order of definition. diff --git a/lib/Tangence/Meta/Type.pm b/lib/Tangence/Meta/Type.pm index 45fc233..084a4c4 100644 --- a/lib/Tangence/Meta/Type.pm +++ b/lib/Tangence/Meta/Type.pm @@ -1,7 +1,7 @@ # You may distribute under the terms of either the GNU General Public License # or the Artistic License (the same terms as Perl itself) # -# (C) Paul Evans, 2012 -- leonerd@leonerd.org.uk +# (C) Paul Evans, 2012-2017 -- leonerd@leonerd.org.uk package Tangence::Meta::Type; @@ -10,7 +10,7 @@ use warnings; use Carp; -our $VERSION = '0.24'; +our $VERSION = '0.25'; =head1 NAME @@ -30,11 +30,13 @@ implemented as singletons. =cut -=head2 $type = Tangence::Meta::Type->new( $primitive ) +=head2 new + + $type = Tangence::Meta::Type->new( $primitive ) Returns an instance to represent the given primitive type signature. -=head2 $type = Tangence::Meta::Type->new( $aggregate => $member_type ) + $type = Tangence::Meta::Type->new( $aggregate => $member_type ) Returns an instance to represent the given aggregation of the given type instance. @@ -65,7 +67,9 @@ sub new die "TODO: @_"; } -=head2 $type = Tangence::Meta::Type->new_from_sig( $sig ) +=head2 new_from_sig + + $type = Tangence::Meta::Type->new_from_sig( $sig ) Parses the given full Tangence type signature and returns an instance to represent it. @@ -90,7 +94,9 @@ sub new_from_sig =cut -=head2 $agg = $type->aggregate +=head2 aggregate + + $agg = $type->aggregate Returns C<"prim"> for primitive types, or the aggregation name for list and dict aggregate types. @@ -103,7 +109,9 @@ sub aggregate return $self->[0]; } -=head2 $member_type = $type->member_type +=head2 member_type + + $member_type = $type->member_type Returns the member type for aggregation types. Throws an exception for primitive types. @@ -117,7 +125,9 @@ sub member_type return $self->[1]; } -=head2 $sig = $type->sig +=head2 sig + + $sig = $type->sig Returns the Tangence type signature for the type. diff --git a/lib/Tangence/Object.pm b/lib/Tangence/Object.pm index 9c3e30d..055804e 100644 --- a/lib/Tangence/Object.pm +++ b/lib/Tangence/Object.pm @@ -1,14 +1,14 @@ # You may distribute under the terms of either the GNU General Public License # or the Artistic License (the same terms as Perl itself) # -# (C) Paul Evans, 2010-2016 -- leonerd@leonerd.org.uk +# (C) Paul Evans, 2010-2017 -- leonerd@leonerd.org.uk package Tangence::Object; use strict; use warnings; -our $VERSION = '0.24'; +our $VERSION = '0.25'; use Carp; @@ -75,7 +75,9 @@ sub new =cut -=head2 $obj->destroy +=head2 destroy + + $obj->destroy Requests that the object destroy itself, informing all clients that are aware of it. Once they all report that they have dropped the object, the object is @@ -123,7 +125,9 @@ sub _destroy_really $self->{destroyed} = 1; } -=head2 $id = $obj->id +=head2 id + + $id = $obj->id Returns the object's C<Tangence> ID number @@ -135,7 +139,9 @@ sub id return $self->{id}; } -=head2 $description = $obj->describe +=head2 describe + + $description = $obj->describe Returns a textual description of the object, for internal debugging purposes. Subclasses are encouraged to override this method to return something more @@ -149,7 +155,9 @@ sub describe return ref $self; } -=head2 $registry = $obj->registry +=head2 registry + + $registry = $obj->registry Returns the L<Tangence::Registry> that constructed this object. @@ -182,7 +190,9 @@ sub smash } @keys }; } -=head2 $class = $obj->class +=head2 class + + $class = $obj->class Returns the L<Tangence::Meta::Class> object representing the class of this object. @@ -195,7 +205,9 @@ sub class return ref $self ? $self->{meta} : Tangence::Class->for_perlname( $self ); } -=head2 $method = $obj->can_method( $name ) +=head2 can_method + + $method = $obj->can_method( $name ) Returns the L<Tangence::Meta::Method> object representing the named method, or C<undef> if no such method exists. @@ -208,7 +220,9 @@ sub can_method return $self->class->method( @_ ); } -=head2 $event = $obj->can_event( $name ) +=head2 can_event + + $event = $obj->can_event( $name ) Returns the L<Tangence::Meta::Event> object representing the named event, or C<undef> if no such event exists. @@ -221,7 +235,9 @@ sub can_event return $self->class->event( @_ ); } -=head2 $property = $obj->can_property( $name ) +=head2 can_property + + $property = $obj->can_property( $name ) Returns the L<Tangence::Meta::Property> object representing the named property, or C<undef> if no such property exists. @@ -240,7 +256,9 @@ sub smashkeys return $self->class->smashkeys; } -=head2 $obj->fire_event( $event, @args ) +=head2 fire_event + + $obj->fire_event( $event, @args ) Fires the named event on the object. Each event subscription function will be invoked with the given arguments. @@ -263,7 +281,9 @@ sub fire_event } } -=head2 $id = $obj->subscribe_event( $event, $callback ) +=head2 subscribe_event + + $id = $obj->subscribe_event( $event, $callback ) Subscribes an event-handling callback CODE ref to the named event. When the event is fired by C<fire_event> this callback will be invoked, being passed @@ -291,7 +311,9 @@ sub subscribe_event return $ref + 0; # force numeric context } -=head2 $obj->unsubscribe_event( $event, $id ) +=head2 unsubscribe_event + + $obj->unsubscribe_event( $event, $id ) Removes an event-handling callback previously registered with C<subscribe_event>. @@ -313,7 +335,9 @@ sub unsubscribe_event splice @$sublist, $index, 1, (); } -=head2 $id = $obj->watch_property( $prop, %callbacks ) +=head2 watch_property + + $id = $obj->watch_property( $prop, %callbacks ) Watches a named property for changes, registering a set of callback functions to be invoked when the property changes in certain ways. The set of callbacks @@ -321,34 +345,34 @@ required depends on the dimension of the property being watched. For all property types: - $on_set->( $obj, $value ) + $on_set->( $obj, $value ) For hash properties: - $on_add->( $obj, $key, $value ) - $on_del->( $obj, $key ) + $on_add->( $obj, $key, $value ) + $on_del->( $obj, $key ) For queue properties: - $on_push->( $obj, @values ) - $on_shift->( $obj, $count ) + $on_push->( $obj, @values ) + $on_shift->( $obj, $count ) For array properties: - $on_push->( $obj, @values ) - $on_shift->( $obj, $count ) - $on_splice->( $obj, $index, $count, @values ) - $on_move->( $obj, $index, $delta ) + $on_push->( $obj, @values ) + $on_shift->( $obj, $count ) + $on_splice->( $obj, $index, $count, @values ) + $on_move->( $obj, $index, $delta ) For objset properties: - $on_add->( $obj, $added_object ) - $on_del->( $obj, $deleted_object_id ) + $on_add->( $obj, $added_object ) + $on_del->( $obj, $deleted_object_id ) Alternatively, a single callback may be installed that is invoked after any change of the property, being passed the new value entirely: - $on_updated->( $obj, $value ) + $on_updated->( $obj, $value ) Returns an opaque ID value that can be used to remove this watch by calling C<unwatch_property>. @@ -388,7 +412,9 @@ sub watch_property return $ref + 0; # force numeric context } -=head2 $obj->unwatch_property( $prop, $id ) +=head2 unwatch_property + + $obj->unwatch_property( $prop, $id ) Removes the set of callback functions previously registered with C<watch_property>. @@ -512,7 +538,7 @@ sub handle_request_SETPROP my $pdef = $self->can_property( $prop ) or die "Object does not have property $prop\n"; - my $value = $pdef->type->unpack_value( $message ); + my $value = $pdef->overall_type->unpack_value( $message ); my $m = "set_prop_$prop"; $self->can( $m ) or die "Object cannot set property $prop\n"; diff --git a/lib/Tangence/ObjectProxy.pm b/lib/Tangence/ObjectProxy.pm index 0d3bb21..6a11d51 100644 --- a/lib/Tangence/ObjectProxy.pm +++ b/lib/Tangence/ObjectProxy.pm @@ -1,14 +1,14 @@ # You may distribute under the terms of either the GNU General Public License # or the Artistic License (the same terms as Perl itself) # -# (C) Paul Evans, 2010-2016 -- leonerd@leonerd.org.uk +# (C) Paul Evans, 2010-2020 -- leonerd@leonerd.org.uk package Tangence::ObjectProxy; use strict; use warnings; -our $VERSION = '0.24'; +our $VERSION = '0.25'; use Carp; @@ -427,7 +427,6 @@ sub get_property_element or croak "Class ".$self->classname." does not have a property $property"; my $client = $self->{client}; - $client->_ver_can_getpropelem or croak "Server is too old to support MSG_GETPROPELEM"; my $request = Tangence::Message->new( $client, MSG_GETPROPELEM ) ->pack_int( $self->id ) @@ -504,7 +503,7 @@ sub set_property my $request = Tangence::Message->new( $client, MSG_SETPROP ) ->pack_int( $self->id ) ->pack_str( $property ); - $pdef->type->pack_value( $request, $value ), + $pdef->overall_type->pack_value( $request, $value ); $client->request( request => $request, @@ -708,7 +707,7 @@ sub watch_property_with_cursor my $smashed = $pdef->smashed; if( my $cbs = $self->{props}->{$property}->{cbs} ) { - die "TODO: need to synthesize a second cursor"; + die "TODO: need to synthesize a second cursor for $self"; } $self->{props}->{$property}->{cbs} = [ $callbacks ]; @@ -718,7 +717,6 @@ sub watch_property_with_cursor } my $client = $self->{client}; - $client->_ver_can_cursor or croak "Server is too old to support MSG_WATCH_CUSR"; $pdef->dimension == DIM_QUEUE or croak "Can only iterate on queue-dimension properties"; $client->request( diff --git a/lib/Tangence/Property.pm b/lib/Tangence/Property.pm index 436d28e..fbd8a3e 100644 --- a/lib/Tangence/Property.pm +++ b/lib/Tangence/Property.pm @@ -18,7 +18,7 @@ require Tangence::Type; use Struct::Dumb; struct Instance => [qw( value callbacks cursors )]; -our $VERSION = '0.24'; +our $VERSION = '0.25'; sub build_accessor { diff --git a/lib/Tangence/Registry.pm b/lib/Tangence/Registry.pm index 4d0df92..e98e388 100644 --- a/lib/Tangence/Registry.pm +++ b/lib/Tangence/Registry.pm @@ -1,7 +1,7 @@ # You may distribute under the terms of either the GNU General Public License # or the Artistic License (the same terms as Perl itself) # -# (C) Paul Evans, 2010-2014 -- leonerd@leonerd.org.uk +# (C) Paul Evans, 2010-2017 -- leonerd@leonerd.org.uk package Tangence::Registry; @@ -9,7 +9,7 @@ use strict; use warnings; use base qw( Tangence::Object ); -our $VERSION = '0.24'; +our $VERSION = '0.25'; use Carp; @@ -67,7 +67,9 @@ objects it creates, so it can dispatch incoming messages from clients to them. =cut -=head2 $registry = Tangence::Registry->new +=head2 new + + $registry = Tangence::Registry->new Returns a new instance of a C<Tangence::Registry> object. An entire server requires one registry object; it will be shared among all the client @@ -108,7 +110,9 @@ sub new =cut -=head2 $obj = $registry->get_by_id( $id ) +=head2 get_by_id + + $obj = $registry->get_by_id( $id ) Returns the object with the given object ID. @@ -131,7 +135,9 @@ sub method_get_by_id return $self->get_by_id( $id ); } -=head2 $obj = $registry->construct( $type, @args ) +=head2 construct + + $obj = $registry->construct( $type, @args ) Constructs a new exposed object of the given type, and returns it. Any additional arguments are passed to the object's constructor. @@ -181,7 +187,9 @@ sub destroy_object push @{ $self->{freeids} }, $id; # Recycle the ID } -=head2 $registry->load_tanfile( $tanfile ) +=head2 load_tanfile + + $registry->load_tanfile( $tanfile ) Loads additional Tangence class and struct definitions from the given F<.tan> file. diff --git a/lib/Tangence/Server.pm b/lib/Tangence/Server.pm index 59970f9..f98556a 100644 --- a/lib/Tangence/Server.pm +++ b/lib/Tangence/Server.pm @@ -1,7 +1,7 @@ # You may distribute under the terms of either the GNU General Public License # or the Artistic License (the same terms as Perl itself) # -# (C) Paul Evans, 2011-2016 -- leonerd@leonerd.org.uk +# (C) Paul Evans, 2011-2020 -- leonerd@leonerd.org.uk package Tangence::Server; @@ -10,11 +10,12 @@ use warnings; use base qw( Tangence::Stream ); -our $VERSION = '0.24'; +our $VERSION = '0.25'; use Carp; use Scalar::Util qw( weaken ); +use Sub::Util 1.40 qw( set_subname ); use Tangence::Constants; use Tangence::Types; @@ -23,18 +24,8 @@ use Tangence::Server::Context; use Struct::Dumb; struct CursorObject => [qw( cursor obj )]; -# We will accept any version back to 2 -use constant VERSION_MINOR_MIN => 2; - -BEGIN { - if( eval { require Sub::Name } ) { - Sub::Name->import(qw( subname )); - } - else { - # Emulate it by just returning the CODEref and ignoring setting the name - *subname = sub { $_[1] }; - } -} +# We will accept any version back to 3 +use constant VERSION_MINOR_MIN => 3; =head1 NAME @@ -100,9 +91,11 @@ The following methods are provided by this mixin. sub subscriptions { shift->{subscriptions} ||= [] } sub watches { shift->{watches} ||= [] } -=head2 $server->registry( $registry ) +=head2 registry + + $server->registry( $registry ) -=head2 $registry = $server->registry + $registry = $server->registry Accessor to set or obtain the L<Tangence::Registry> object for the server. @@ -150,22 +143,31 @@ sub get_by_id my $self = shift; my ( $id ) = @_; - return $self->registry->get_by_id( $id ); + # Only permit the client to interact with objects they've already been + # sent, so they cannot gain access by inventing object IDs + $self->peer_hasobj->{$id} or + die "Access not allowed to object with id $id\n"; + + my $obj = $self->registry->get_by_id( $id ) or + die "No such object with id $id\n"; + + return $obj; } sub handle_request_CALL { my $self = shift; my ( $token, $message ) = @_; - - my $objid = $message->unpack_int(); my $ctx = Tangence::Server::Context->new( $self, $token ); - my $object = $self->registry->get_by_id( $objid ) or - return $ctx->responderr( "No such object with id $objid" ); + my $response = eval { + my $objid = $message->unpack_int(); + + my $object = $self->get_by_id( $objid ); - my $response = eval { $object->handle_request_CALL( $ctx, $message ) }; + $object->handle_request_CALL( $ctx, $message ) + }; $@ and return $ctx->responderr( $@ ); $ctx->respond( $response ); @@ -175,76 +177,85 @@ sub handle_request_SUBSCRIBE { my $self = shift; my ( $token, $message ) = @_; - - my $objid = $message->unpack_int(); - my $event = $message->unpack_str(); my $ctx = Tangence::Server::Context->new( $self, $token ); - my $object = $self->registry->get_by_id( $objid ) or - return $ctx->responderr( "No such object with id $objid" ); + my $response = eval { + my $objid = $message->unpack_int(); + my $event = $message->unpack_str(); - weaken( my $weakself = $self ); + my $object = $self->get_by_id( $objid ); - my $id = $object->subscribe_event( $event, - subname "__SUBSCRIBE($event)__" => sub { - $weakself or return; - my $object = shift; + weaken( my $weakself = $self ); - my $message = $object->generate_message_EVENT( $weakself, $event, @_ ); - $weakself->request( - request => $message, - on_response => sub { "IGNORE" }, - ); - } - ); + my $id = $object->subscribe_event( $event, + set_subname "__SUBSCRIBE($event)__" => sub { + $weakself or return; + my $object = shift; + + my $message = $object->generate_message_EVENT( $weakself, $event, @_ ); + $weakself->request( + request => $message, + on_response => sub { "IGNORE" }, + ); + } + ); - push @{ $self->subscriptions }, [ $object, $event, $id ]; + push @{ $self->subscriptions }, [ $object, $event, $id ]; - $ctx->respond( Tangence::Message->new( $self, MSG_SUBSCRIBED ) ); + Tangence::Message->new( $self, MSG_SUBSCRIBED ) + }; + $@ and return $ctx->responderr( $@ ); + + $ctx->respond( $response ); } sub handle_request_UNSUBSCRIBE { my $self = shift; my ( $token, $message ) = @_; - - my $objid = $message->unpack_int(); - my $event = $message->unpack_str(); my $ctx = Tangence::Server::Context->new( $self, $token ); - my $object = $self->registry->get_by_id( $objid ) or - return $ctx->responderr( "No such object with id $objid" ); + my $response = eval { + my $objid = $message->unpack_int(); + my $event = $message->unpack_str(); - my $edef = $object->can_event( $event ) or - return $ctx->responderr( "Object cannot respond to event $event" ); + my $object = $self->get_by_id( $objid ); - # Delete from subscriptions and obtain id - my $id; - @{ $self->subscriptions } = grep { $_->[0] == $object and $_->[1] eq $event and ( $id = $_->[2], 0 ) or 1 } - @{ $self->subscriptions }; - defined $id or - return $ctx->responderr( "Not subscribed to $event" ); + my $edef = $object->can_event( $event ) or + die "Object cannot respond to event $event\n"; - $object->unsubscribe_event( $event, $id ); + # Delete from subscriptions and obtain id + my $id; + @{ $self->subscriptions } = grep { $_->[0] == $object and $_->[1] eq $event and ( $id = $_->[2], 0 ) or 1 } + @{ $self->subscriptions }; + defined $id or + die "Not subscribed to $event\n"; - $ctx->respond( Tangence::Message->new( $self, MSG_OK ) ); + $object->unsubscribe_event( $event, $id ); + + Tangence::Message->new( $self, MSG_OK ) + }; + $@ and return $ctx->responderr( $@ ); + + $ctx->respond( $response ); } sub handle_request_GETPROP { my $self = shift; my ( $token, $message ) = @_; - - my $objid = $message->unpack_int(); my $ctx = Tangence::Server::Context->new( $self, $token ); - my $object = $self->registry->get_by_id( $objid ) or - return $ctx->responderr( "No such object with id $objid" ); + my $response = eval { + my $objid = $message->unpack_int(); - my $response = eval { $object->handle_request_GETPROP( $ctx, $message ) }; + my $object = $self->get_by_id( $objid ); + + $object->handle_request_GETPROP( $ctx, $message ) + }; $@ and return $ctx->responderr( $@ ); $ctx->respond( $response ); @@ -255,14 +266,15 @@ sub handle_request_GETPROPELEM my $self = shift; my ( $token, $message ) = @_; - my $objid = $message->unpack_int(); - my $ctx = Tangence::Server::Context->new( $self, $token ); - my $object = $self->registry->get_by_id( $objid ) or - return $ctx->responderr( "No such object with id $objid" ); + my $response = eval { + my $objid = $message->unpack_int(); + + my $object = $self->get_by_id( $objid ); - my $response = eval { $object->handle_request_GETPROPELEM( $ctx, $message ) }; + $object->handle_request_GETPROPELEM( $ctx, $message ) + }; $@ and return $ctx->responderr( $@ ); $ctx->respond( $response ); @@ -272,15 +284,16 @@ sub handle_request_SETPROP { my $self = shift; my ( $token, $message ) = @_; - - my $objid = $message->unpack_int(); my $ctx = Tangence::Server::Context->new( $self, $token ); - my $object = $self->registry->get_by_id( $objid ) or - return $ctx->responderr( "No such object with id $objid" ); + my $response = eval { + my $objid = $message->unpack_int(); - my $response = eval { $object->handle_request_SETPROP( $ctx, $message ) }; + my $object = $self->get_by_id( $objid ); + + $object->handle_request_SETPROP( $ctx, $message ) + }; $@ and return $ctx->responderr( $@ ); $ctx->respond( $response ); @@ -292,43 +305,46 @@ sub _handle_request_WATCHany { my $self = shift; my ( $token, $message ) = @_; - - my $objid = $message->unpack_int(); - my $prop = $message->unpack_str(); - my $want_initial; - my $from; - if( $message->code == MSG_WATCH ) { - $want_initial = $message->unpack_bool(); - } - elsif( $message->code == MSG_WATCH_CUSR ) { - $from = $message->unpack_int(); - } my $ctx = Tangence::Server::Context->new( $self, $token ); - my $object = $self->registry->get_by_id( $objid ) or - return $ctx->responderr( "No such object with id $objid" ); + my ( $want_initial, $object, $prop ); - my $pdef = $object->can_property( $prop ) or - return $ctx->responderr( "Object does not have property $prop" ); + my $response = eval { + my $objid = $message->unpack_int(); + $prop = $message->unpack_str(); - $self->_install_watch( $object, $prop ); + $object = $self->get_by_id( $objid ); - if( $message->code == MSG_WATCH ) { - $ctx->respond( Tangence::Message->new( $self, MSG_WATCHING ) ); - $self->_send_initial( $object, $prop ) if $want_initial; - } - elsif( $message->code == MSG_WATCH_CUSR ) { - my $m = "cursor_prop_$prop"; - my $cursor = $object->$m( $from ); - my $id = $self->message_state->{next_cursorid}++; - $self->peer_hascursor->{$id} = CursorObject( $cursor, $object ); - $ctx->respond( Tangence::Message->new( $self, MSG_WATCHING_CUSR ) - ->pack_int( $id ) - ->pack_int( 0 ) # first index - ->pack_int( $#{ $object->${\"get_prop_$prop"} } ) # last index - ); - } + my $pdef = $object->can_property( $prop ) or + die "Object does not have property $prop\n"; + + $self->_install_watch( $object, $prop ); + + if( $message->code == MSG_WATCH ) { + $want_initial = $message->unpack_bool(); + + Tangence::Message->new( $self, MSG_WATCHING ) + } + elsif( $message->code == MSG_WATCH_CUSR ) { + my $from = $message->unpack_int(); + + my $m = "cursor_prop_$prop"; + my $cursor = $object->$m( $from ); + my $id = $self->message_state->{next_cursorid}++; + + $self->peer_hascursor->{$id} = CursorObject( $cursor, $object ); + Tangence::Message->new( $self, MSG_WATCHING_CUSR ) + ->pack_int( $id ) + ->pack_int( 0 ) # first index + ->pack_int( $#{ $object->${\"get_prop_$prop"} } ) # last index + } + }; + $@ and return $ctx->responderr( $@ ); + + $ctx->respond( $response ); + + $self->_send_initial( $object, $prop ) if $want_initial; } sub _send_initial @@ -354,28 +370,32 @@ sub handle_request_UNWATCH { my $self = shift; my ( $token, $message ) = @_; - - my $objid = $message->unpack_int(); - my $prop = $message->unpack_str(); my $ctx = Tangence::Server::Context->new( $self, $token ); - my $object = $self->registry->get_by_id( $objid ) or - return $ctx->responderr( "No such object with id $objid" ); + my $response = eval { + my $objid = $message->unpack_int(); + my $prop = $message->unpack_str(); - my $pdef = $object->can_property( $prop ) or - return $ctx->responderr( "Object does not have property $prop" ); + my $object = $self->get_by_id( $objid ); - # Delete from watches and obtain id - my $id; - @{ $self->watches } = grep { $_->[0] == $object and $_->[1] eq $prop and ( $id = $_->[2], 0 ) or 1 } - @{ $self->watches }; - defined $id or - return $ctx->responderr( "Not watching $prop" ); + my $pdef = $object->can_property( $prop ) or + die "Object does not have property $prop\n"; - $object->unwatch_property( $prop, $id ); + # Delete from watches and obtain id + my $id; + @{ $self->watches } = grep { $_->[0] == $object and $_->[1] eq $prop and ( $id = $_->[2], 0 ) or 1 } + @{ $self->watches }; + defined $id or + die "Not watching $prop\n"; - $ctx->respond( Tangence::Message->new( $self, MSG_OK ) ); + $object->unwatch_property( $prop, $id ); + + Tangence::Message->new( $self, MSG_OK ); + }; + $@ and return $ctx->responderr( $@ ); + + $ctx->respond( $response ); } sub handle_request_CUSR_NEXT @@ -461,10 +481,10 @@ sub handle_request_GETROOT my $ctx = Tangence::Server::Context->new( $self, $token ); - my $root = $self->registry->get_by_id( 1 ); - $self->identity( $identity ); + my $root = $self->rootobj( $identity ); + my $response = Tangence::Message->new( $self, MSG_RESULT ); TYPE_OBJ->pack_value( $response, $root ); @@ -478,6 +498,9 @@ sub handle_request_GETREGISTRY my $ctx = Tangence::Server::Context->new( $self, $token ); + $self->permit_registry or + return $ctx->responderr( "This client is not permitted access to the registry" ); + my $response = Tangence::Message->new( $self, MSG_RESULT ); TYPE_OBJ->pack_value( $response, $self->registry ); @@ -507,7 +530,7 @@ sub _install_watch my %callbacks; foreach my $name ( @{ CHANGETYPES->{$dim} } ) { my $how = $change_values{$name}; - $callbacks{$name} = subname "__WATCH($prop:$name)__" => sub { + $callbacks{$name} = set_subname "__WATCH($prop:$name)__" => sub { $weakself or return; my $object = shift; @@ -562,6 +585,50 @@ sub object_destroyed $self->SUPER::object_destroyed( @_ ); } +=head1 OVERRIDEABLE METHODS + +The following methods are provided but intended to be overridden if the +implementing class wishes to provide different behaviour from the default. + +=cut + +=head2 rootobj + + $rootobj = $server->rootobj( $identity ) + +Invoked when a C<GETROOT> message is received from the client, this method +should return a L<Tangence::Object> as root object for the connection. + +The default implementation will return the object with ID 1; i.e. the first +object created in the registry. + +=cut + +sub rootobj +{ + my $self = shift; + + return $self->registry->get_by_id( 1 ); +} + +=head2 permit_registry + + $allow = $server->permit_registry + +Invoked when a C<GETREGISTRY> message is received from the client, this method +should return a boolean to indicate whether the client is allowed to access +the object registry. + +The default implementation always permits this, but an overridden method may +decide to disallow it in some situations. When disabled, a client will not be +able to gain access to any serverside objects other than the root object, and +(recursively) any other objects returned by methods, events or properties on +objects already known. This can be used as a security mechanism. + +=cut + +sub permit_registry { 1; } + =head1 AUTHOR Paul Evans <leonerd@leonerd.org.uk> diff --git a/lib/Tangence/Server/Context.pm b/lib/Tangence/Server/Context.pm index ed7c417..c4a6f7c 100644 --- a/lib/Tangence/Server/Context.pm +++ b/lib/Tangence/Server/Context.pm @@ -8,7 +8,7 @@ package Tangence::Server::Context; use strict; use warnings; -our $VERSION = '0.24'; +our $VERSION = '0.25'; use Carp; @@ -56,6 +56,8 @@ sub responderr my $self = shift; my ( $msg ) = @_; + chomp $msg; # In case of simple ->responderr( $@ ); + $self->respond( Tangence::Message->new( $self->stream, MSG_ERROR ) ->pack_str( $msg ) ); diff --git a/lib/Tangence/Stream.pm b/lib/Tangence/Stream.pm index e54194f..2f49590 100644 --- a/lib/Tangence/Stream.pm +++ b/lib/Tangence/Stream.pm @@ -1,7 +1,7 @@ # You may distribute under the terms of either the GNU General Public License # or the Artistic License (the same terms as Perl itself) # -# (C) Paul Evans, 2011-2016 -- leonerd@leonerd.org.uk +# (C) Paul Evans, 2011-2020 -- leonerd@leonerd.org.uk package Tangence::Stream; @@ -9,7 +9,7 @@ use strict; use warnings; use 5.010; # // -our $VERSION = '0.24'; +our $VERSION = '0.25'; use Carp; @@ -68,14 +68,18 @@ mixin. =cut -=head2 $stream->tangence_write( $data ) +=head2 tangence_write + + $stream->tangence_write( $data ) Write bytes of data to the connected peer. C<$data> will be a plain perl string. =cut -=head2 $stream->handle_request_$CODE( $token, $message ) +=head2 handle_request_$CODE + + $stream->handle_request_$CODE( $token, $message ) Invoked on receipt of a given message code. C<$token> will be some opaque perl scalar value, and C<$message> will be an instance of L<Tangence::Message>. @@ -117,7 +121,9 @@ sub identity return $self->{identity}; } -=head2 $stream->tangence_closed +=head2 tangence_closed + + $stream->tangence_closed Informs the object that the underlying connection has now been closed, and any attachments to C<Tangence::Object> or C<Tangence::ObjectProxy> instances @@ -135,7 +141,9 @@ sub tangence_closed } } -=head2 $stream->tangence_readfrom( $buffer ) +=head2 tangence_readfrom + + $stream->tangence_readfrom( $buffer ) Informs the object that more data has been read from the underlying connection stream. Whole messages will be removed from the beginning of the C<$buffer>, @@ -213,7 +221,9 @@ sub object_destroyed ); } -=head2 $stream->request( %args ) +=head2 request + + $stream->request( %args ) Serialises a message object to pass to the C<tangence_write> method, then enqueues a response handler to be invoked when a reply arrives. Takes the @@ -234,7 +244,9 @@ received. It will be passed the response message: =back -=head2 $response = $stream->request( request => $request )->get +=head2 request (non-void) + + $response = $stream->request( request => $request )->get When called in non-void context, this method returns a L<Future> that will yield the response instead. In this case it should not be given an @@ -280,7 +292,9 @@ sub request return $f; } -=head2 $stream->respond( $token, $message ) +=head2 respond + + $stream->respond( $token, $message ) Serialises a message object to be sent to the C<tangence_write> method. The C<$token> value that was passed to the C<handle_request_> method ensures that @@ -313,7 +327,9 @@ sub respondERROR ); } -=head2 $ver = $stream->minor_version +=head2 minor_version + + $ver = $stream->minor_version Returns the minor version negotiated by the C<MSG_INIT> / C<MSG_INITED> initial message handshake. @@ -329,12 +345,6 @@ sub minor_version # Some (internal) methods that control new protocol features -# wire protocol supports MSG_GETPROPELEM -sub _ver_can_getpropelem { shift->minor_version >= 3 } - -# wire protocol supports MSG_WATCH_CUSR and cursors -sub _ver_can_cursor { shift->minor_version >= 3 } - # wire protocol uses typed smash data sub _ver_can_typed_smash { shift->minor_version >= 4 } diff --git a/lib/Tangence/Struct.pm b/lib/Tangence/Struct.pm index fcf3026..49d62b8 100644 --- a/lib/Tangence/Struct.pm +++ b/lib/Tangence/Struct.pm @@ -4,7 +4,7 @@ use strict; use warnings; use base qw( Tangence::Meta::Struct ); -our $VERSION = '0.24'; +our $VERSION = '0.25'; use Carp; diff --git a/lib/Tangence/Type.pm b/lib/Tangence/Type.pm index 2bb7a63..efc3444 100644 --- a/lib/Tangence/Type.pm +++ b/lib/Tangence/Type.pm @@ -1,7 +1,7 @@ # You may distribute under the terms of either the GNU General Public License # or the Artistic License (the same terms as Perl itself) # -# (C) Paul Evans, 2013-2014 -- leonerd@leonerd.org.uk +# (C) Paul Evans, 2013-2017 -- leonerd@leonerd.org.uk package Tangence::Type; @@ -27,13 +27,15 @@ implementations. =head1 CONSTRUCTOR -=head2 $type = Tangence::Type->new( $primitive_sig ) +=head2 new + + $type = Tangence::Type->new( $primitive_sig ) Returns an instance to represent a primitive type of the given signature. -=head2 $type = Tangence::Type->new( list => $member_type ) + $type = Tangence::Type->new( list => $member_type ) -=head2 $type = Tangence::Type->new( dict => $member_type ) + $type = Tangence::Type->new( dict => $member_type ) Returns an instance to represent a list or dict aggregation containing members of the given type. @@ -69,15 +71,21 @@ sub new =head1 METHODS -=head2 $value = $type->default_value +=head2 default_value + + $value = $type->default_value Returns a value suitable to use as an initial value for object properties. -=head2 $type->pack_value( $message, $value ) +=head2 pack_value + + $type->pack_value( $message, $value ) Appends a value of this type to the end of a L<Tangence::Message>. -=head2 $value = $type->unpack_value( $message ) +=head2 unpack_value + + $value = $type->unpack_value( $message ) Removes a value of this type from the start of a L<Tangence::Message>. diff --git a/lib/Tangence/Types.pm b/lib/Tangence/Types.pm index a56219f..ec4ba42 100644 --- a/lib/Tangence/Types.pm +++ b/lib/Tangence/Types.pm @@ -8,7 +8,7 @@ package Tangence::Types; use strict; use warnings; -our $VERSION = '0.24'; +our $VERSION = '0.25'; use Exporter 'import'; our @EXPORT = qw( diff --git a/t/21client.t b/t/21client.t index 91751dd..bdd92b6 100644 --- a/t/21client.t +++ b/t/21client.t @@ -29,6 +29,9 @@ my $client = TestClient->new(); $client->send_message( $S2C{GETROOT} ); $client->send_message( $S2C{GETREGISTRY} ); + + ok( defined $client->rootobj, 'client has rootobj' ); + ok( defined $client->registry, 'client has registry' ); } my $objproxy = $client->rootobj; diff --git a/t/30props-cbs.t b/t/30props-cbs.t index afb9ca6..e9f8757 100644 --- a/t/30props-cbs.t +++ b/t/30props-cbs.t @@ -3,7 +3,7 @@ use strict; use warnings; -use Test::More tests => 24; +use Test::More; use Test::Memory::Cycle; use Tangence::Constants; diff --git a/t/33props-set.t b/t/33props-set.t new file mode 100644 index 0000000..c9e32a9 --- /dev/null +++ b/t/33props-set.t @@ -0,0 +1,53 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Test::More; + +use Tangence::Registry; + +use lib "."; +use t::TestObj; +use t::TestServerClient; + +my $registry = Tangence::Registry->new( + tanfile => "t/TestObj.tan", +); +my $obj = $registry->construct( + "t::TestObj", +); + +my ( $server, $client ) = make_serverclient( $registry ); + +my $proxy = $client->rootobj; + +# scalar +{ + $proxy->set_property( "scalar", 456 )->get; + + is( $obj->get_prop_scalar, 456, 'set_property on scalar' ); +} + +# array +{ + $proxy->set_property( "array", [ 4, 5, 6 ] )->get; + + is_deeply( $obj->get_prop_array, [ 4, 5, 6 ], 'set_property on array' ); +} + +# queue +{ + $proxy->set_property( "queue", [ 4, 5, 6 ] )->get; + + is_deeply( $obj->get_prop_queue, [ 4, 5, 6 ], 'set_property on queue' ); +} + +# hash +{ + $proxy->set_property( "hash", { four => 4, five => 5 } )->get; + + is_deeply( $obj->get_prop_hash, { four => 4, five => 5 }, 'set_property on hash' ); +} + +done_testing; diff --git a/t/40server-security.t b/t/40server-security.t new file mode 100644 index 0000000..7d338bd --- /dev/null +++ b/t/40server-security.t @@ -0,0 +1,82 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Test::More; + +use Tangence::Constants; +use Tangence::Registry; + +use lib "."; +use t::TestObj; +use t::TestServerClient; + +my $registry = Tangence::Registry->new( + tanfile => "t/TestObj.tan", +); +my $obj = $registry->construct( + "t::TestObj", +); +# generate a second object that exists but we don't tell the client about +my $obj2 = $registry->construct( + "t::TestObj", +); + +my ( $server, $client ) = make_serverclient( $registry ); + +my $proxy = $client->rootobj; + +# gutwrench into the objectproxy to make a new one with a different ID +$proxy->{id} == $obj->id or die "ARGH failed to have correct object ID in proxy"; + +my $proxy2 = { %$proxy, id => $obj2->id }; +bless $proxy2, ref $proxy; + +# $proxy2 should now not work for anything + +# methods +{ + my $f = $proxy2->call_method( "method", 0, "" ); + + like( $f->failure, qr/^Access not allowed to object with id 2/, + 'unseen objects inaccessible by method' ); +} + +# events +{ + my $f = $proxy2->subscribe_event( "event", on_fire => sub {} ); + + like( $f->failure, qr/^Access not allowed to object with id 2/, + 'unseen objects inaccessible by event' ); +} + +# properties +{ + my $f = $proxy2->get_property( "scalar" ); + + like( $f->failure, qr/^Access not allowed to object with id 2/, + 'unseen objects inaccessible by property get' ); + + $f = $proxy2->set_property( "scalar", 123 ); + + like( $f->failure, qr/^Access not allowed to object with id 2/, + 'unseen objects inaccessible by property set' ); + + $f = $proxy2->watch_property( "scalar", on_set => sub {} ); + + like( $f->failure, qr/^Access not allowed to object with id 2/, + 'unseen objects inaccessible by property watch' ); +} + +# as argument to otherwise-allowed object +{ + $proxy->set_property( "objset", [ $proxy ] )->get; # is allowed + + my $f = $proxy->set_property( "objset", [ $proxy2 ] ); + + like( $f->failure, qr/^Access not allowed to object with id 2/, + 'unseen objects not allowed by value' ); +} + +done_testing; diff --git a/t/Conversation.pm b/t/Conversation.pm index 27e5d16..97ca600 100644 --- a/t/Conversation.pm +++ b/t/Conversation.pm @@ -29,7 +29,7 @@ $C2S{INIT} = "\x7f" . "\0\0\0\6" . "\x02" . "\0" . "\x02" . "\4" . - "\x02" . "\2"; + "\x02" . "\3"; # MSG_INITED $S2C{INITED} = |