/
Client.pm
199 lines (147 loc) · 4.54 KB
/
Client.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# A simple socket client. Generic enough to be used for INET and UNIX
# sockets, although we may need to specialize for each kind later.
# TODO - This is a simple strawman implementation. It needs
# refinement.
package Reflex::Client;
# vim: ts=2 sw=2 noexpandtab
use Moose;
use Reflex::Stream;
extends 'Reflex::Connector';
with 'Reflex::Role::Collectible';
use Reflex::Trait::Watched qw(watches);
has protocol => (
is => 'rw',
isa => 'Str',
default => 'Reflex::Stream',
);
watches connection => (
isa => 'Maybe[Reflex::Stream]',
# Maps $self->put() to $self->connection()->put().
# TODO - Would be nice to have something like this for outbout
# events. See on_connection_data() later in this module for more.
handles => ['put'],
);
sub on_connection {
my ($self, $socket) = @_;
$self->connection(
$self->protocol()->new(
handle => $socket->handle(),
rd => 1,
)
);
$self->emit( -name => "connected" );
#$self->re_emit( $socket, -name => "connected" );
}
sub on_error {
my ($self, $error) = @_;
# TODO - Emit rather than warn.
warn $error->formatted(), "\n";
}
sub on_connection_closed {
my ($self, $eof) = @_;
$self->connection()->stop();
# TODO - Emit rather than warn.
warn "server closed connection.\n";
}
sub on_connection_failure {
my ($self, $error) = @_;
$self->connection()->stop();
# TODO - Emit rather than warn.
warn $error->formatted(), "\n";
}
# This odd construct lets us rethrow a low-level event as a
# higher-level event. It's similar to the way Moose "handles" works,
# although in the other (outbound) direction.
#
# TODO - It's rather inefficient to rethrow like this at runtime.
# Some compile- or init-time remapping construct would be better.
#
# TODO - While we're rethrowing, we should consider a generic facility
# for passing -type through.
sub on_connection_data {
my ($self, $data) = @_;
$self->re_emit( $data, -name => "data" );
}
sub stop {
my $self = shift();
$self->connection(undef);
$self->stopped();
};
__PACKAGE__->meta->make_immutable;
1;
__END__
=for Pod::Coverage on_connection_closed on_connection_data on_connection_failure stop
=head1 NAME
Reflex::Client - A non-blocking socket client.
=head1 SYNOPSIS
This is a complete working TCP echo client. It's the version of
eg/eg-35-tcp-client.pl available at the time of this writing.
use lib qw(../lib);
{
package TcpEchoClient;
use Moose;
extends 'Reflex::Client';
sub on_client_connected {
my ($self, $event) = @_;
$self->connection()->put("Hello, world!\n");
};
sub on_client_data {
my ($self, $event) = @_;
# Not chomped.
warn "got from server: ", $event->data();
# Disconnect after we receive the echo.
$self->stop();
}
}
TcpEchoClient->new(
remote_addr => '127.0.0.1',
remote_port => 12345,
)->run_all();
=head1 DESCRIPTION
Reflex::Client is scheduled for substantial changes. One of its base
classes, Reflex::Handle, will be deprecated in favor of
Reflex::Role::Readable and Reflex::Role::Writable. Hopefully
Reflex::Client's interfaces won't change much as a result, but
there are no guarantees.
Your ideas and feedback for Reflex::Client's future implementation
are welcome.
Reflex::Client is a high-level base class for non-blocking socket
clients. As with other Reflex::Base classes, this one may be
subclassed, composed with "has", or driven inline with promises.
=head2 Attributes
Reflex::Client extends (and includes the attributes of)
Reflex::Connector, which extends Reflex::Handle. It also provides its
own attributes.
=head3 protocol
The "protocol" attribute contains the name of a class that will handle
I/O for the client. It contains "Reflex::Stream" by default.
Protocol classes should extend Reflex::Stream or at least follow its
interface.
=head2 Public Methods
Reflex::Client extends Reflex::Handle, but it currently provides no
additional methods.
=head2 Events
Reflex::Client emits some of its own high-level events based on its
components' activities.
=head3 connected
Reflex::Client emits "connected" to notify consumers when the client
has connected, and it's safe to begin sending data.
=head3 data
Reflex::Client emits stream data with the "data" event. This event is
provided by Reflex::Stream. Please see L<Reflex::Stream/data> for the
most current documentation.
=head1 EXAMPLES
eg/eg-35-tcp-client.pl subclasses Reflex::Client as TcpEchoClient.
=head1 SEE ALSO
L<Reflex>
L<Reflex::Client>
L<Reflex/ACKNOWLEDGEMENTS>
L<Reflex/ASSISTANCE>
L<Reflex/AUTHORS>
L<Reflex/BUGS>
L<Reflex/BUGS>
L<Reflex/CONTRIBUTORS>
L<Reflex/COPYRIGHT>
L<Reflex/LICENSE>
L<Reflex/TODO>
=cut