1Test::POE::Server::TCP(U3s)er Contributed Perl DocumentatTieosnt::POE::Server::TCP(3)
2
3
4
6 Test::POE::Server::TCP - A POE Component providing TCP server services
7 for test cases
8
10 A very simple echo server with logging of requests by each client:
11
12 use strict;
13 use POE;
14 use Test::POE::Server::TCP;
15
16 POE::Session->create(
17 package_states => [
18 'main' => [qw(
19 _start
20 testd_connected
21 testd_disconnected
22 testd_client_input
23 )],
24 ],
25 );
26
27 $poe_kernel->run();
28 exit 0;
29
30 sub _start {
31 # Spawn the Test::POE::Server::TCP server.
32 $_[HEAP]->{testd} = Test::POE::Server::TCP->spawn(
33 address => '127.0.0.1',
34 port => 0,
35 );
36 return;
37 }
38
39 sub testd_connected {
40 my ($heap,$id) = @_[HEAP,ARG0];
41
42 # A client connected the unique ID is in ARG0
43 # Create a blank arrayref for this client on *our* heap
44
45 $heap->{clients}->{ $id } = [ ];
46
47 return;
48 }
49
50 sub testd_client_input {
51 my ($kernel,$heap,$sender,$id,$input) = @_[KERNEL,HEAP,SENDER,ARG0,ARG1];
52
53 # The client sent us a line of input
54 # lets store it
55
56 push @{ $heap->{clients}->{ $id }, $input;
57
58 # Okay, we are an echo server so lets send it back to the client
59 # We know the SENDER so can always obtain the server object.
60
61 my $testd = $sender->get_heap();
62 $testd->send_to_client( $id, $input );
63
64 # Or even
65
66 # $sender->get_heap()->send_to_client( $id, $input );
67
68 # Alternatively we could just post back to the SENDER
69
70 # $kernel->post( $sender, 'send_to_client', $id, $input );
71
72 return;
73 }
74
75 sub testd_disconnected {
76 my ($heap,$id) = @_[HEAP,ARG0];
77
78 # Client disconnected for whatever reason
79 # We need to free up our storage
80
81 delete $heap->{clients}->{ $id };
82
83 return;
84 }
85
86 Using the module in a testcase:
87
88 use strict;
89 use Test::More;
90 use POE qw(Wheel::SocketFactory Wheel::ReadWrite Filter::Line);
91 use Test::POE::Server::TCP;
92
93 plan tests => 5;
94
95 my @data = (
96 'This is a test',
97 'This is another test',
98 'This is the last test',
99 );
100
101 POE::Session->create(
102 package_states => [
103 'main' => [qw(
104 _start
105 _sock_up
106 _sock_fail
107 _sock_in
108 _sock_err
109 testd_connected
110 testd_disconnected
111 testd_client_input
112 )],
113 ],
114 heap => { data => \@data, },
115 );
116
117 $poe_kernel->run();
118 exit 0;
119
120 sub _start {
121 $_[HEAP]->{testd} = Test::POE::Server::TCP->spawn(
122 address => '127.0.0.1',
123 port => 0,
124 );
125 return;
126 }
127
128 sub testd_registered {
129 my ($heap,$object) = @_[HEAP,ARG0];
130 $heap->{port} = $object->port();
131 $heap->{factory} = POE::Wheel::SocketFactory->new(
132 RemoteAddress => '127.0.0.1',
133 RemotePort => $heap->{port},
134 SuccessEvent => '_sock_up',
135 FailureEvent => '_sock_fail',
136 );
137 return;
138 }
139
140 sub _sock_up {
141 my ($heap,$socket) = @_[HEAP,ARG0];
142 delete $heap->{factory};
143 $heap->{socket} = POE::Wheel::ReadWrite->new(
144 Handle => $socket,
145 InputEvent => '_sock_in',
146 ErrorEvent => '_sock_err',
147 );
148 $heap->{socket}->put( $heap->{data}->[0] );
149 return;
150 }
151
152 sub _sock_fail {
153 my $heap = $_[HEAP];
154 delete $heap->{factory};
155 $heap->{testd}->shutdown();
156 return;
157 }
158
159 sub _sock_in {
160 my ($heap,$input) = @_[HEAP,ARG0];
161 my $data = shift @{ $heap->{data} };
162 ok( $input eq $data, 'Data matched' );
163 unless ( scalar @{ $heap->{data} } ) {
164 delete $heap->{socket};
165 return;
166 }
167 $heap->{socket}->put( $heap->{data}->[0] );
168 return;
169 }
170
171 sub _sock_err {
172 delete $_[HEAP]->{socket};
173 return;
174 }
175
176 sub testd_connected {
177 my ($heap,$state,$id) = @_[HEAP,STATE,ARG0];
178 pass($state);
179 return;
180 }
181
182 sub testd_disconnected {
183 pass($_[STATE]);
184 $poe_kernel->post( $_[SENDER], 'shutdown' );
185 return;
186 }
187
188 sub testd_client_input {
189 my ($sender,$id,$input) = @_[SENDER,ARG0,ARG1];
190 my $testd = $_[SENDER]->get_heap();
191 $testd->send_to_client( $id, $input );
192 return;
193 }
194
196 Test::POE::Server::TCP is a POE component that provides a TCP server
197 framework for inclusion in client component test cases, instead of
198 having to roll your own.
199
200 Once registered with the component, a session will receive events
201 related to client connects, disconnects, input and flushed output. Each
202 of these events will refer to a unique client ID which may be used in
203 communication with the component when sending data to the client or
204 disconnecting a client connection.
205
207 "spawn"
208 Takes a number of optional arguments:
209
210 'alias', set an alias on the component;
211 'address', bind the listening socket to a particular address;
212 'port', listen on a particular port, default is 0, assign a random port;
213 'options', a hashref of POE::Session options;
214 'filter', specify a POE::Filter to use for client connections, default is POE::Filter::Line;
215 'inputfilter', specify a POE::Filter for client input;
216 'outputfilter', specify a POE::Filter for output to clients;
217 'prefix', specify a different prefix than 'testd' for events;
218
219 The semantics for "filter", "inputfilter" and "outputfilter" are
220 the same as for POE::Component::Server::TCP in that one may provide
221 either a "SCALAR", "ARRAYREF" or an "OBJECT".
222
223 If the component is "spawn"ed within another session it will
224 automatically "register" the parent session to receive "all"
225 events.
226
228 "session_id"
229 Returns the POE::Session ID of the component.
230
231 "shutdown"
232 Terminates the component. Shuts down the listener and disconnects
233 connected clients.
234
235 "send_to_client"
236 Send some output to a connected client. First parameter must be a
237 valid client id. Second parameter is a string of text to send. The
238 second parameter may also be an arrayref of items to send to the
239 client. If the filter you have used requires an arrayref as input,
240 nest that arrayref within another arrayref.
241
242 "send_to_all_clients"
243 Send some output to all connected clients. The parameter is a
244 string of text to send. The parameter may also be an arrayref of
245 items to send to the clients. If the filter you have used requires
246 an arrayref as input, nest that arrayref within another arrayref.
247
248 "client_info"
249 Retrieve socket information of a given client. Requires a valid
250 client ID as a parameter. If called in a list context it returns a
251 list consisting of, in order, the client address, the client TCP
252 port, our address and our TCP port. In a scalar context it returns
253 a HASHREF with the following keys:
254
255 'peeraddr', the client address;
256 'peerport', the client TCP port;
257 'sockaddr', our address;
258 'sockport', our TCP port;
259
260 "client_wheel"
261 Retrieve the POE::Wheel::ReadWrite object of a given client.
262 Requires a valid client ID as a parameter. This enables one to
263 manipulate the given POE::Wheel::ReadWrite object, say to switch
264 POE::Filter.
265
266 "disconnect"
267 Places a client connection in pending disconnect state. Requires a
268 valid client ID as a parameter. Set this, then send an applicable
269 message to the client using send_to_client() and the client
270 connection will be terminated.
271
272 "terminate"
273 Immediately disconnects a client conenction. Requires a valid
274 client ID as a parameter.
275
276 "pause_listening"
277 Stops the underlying listening socket from accepting new
278 connections. This lets you test whether you handle the connection
279 timing out gracefully.
280
281 "resume_listening"
282 The companion of "pause_listening"
283
284 "getsockname"
285 Access to the POE::Wheel::SocketFactory method of the underlying
286 listening socket.
287
288 "port"
289 Returns the port that the component is listening on.
290
291 "start_listener"
292 If the listener fails on "listen" you can attempt to restart it
293 with this.
294
296 These are events that the component will accept:
297
298 "register"
299 Takes N arguments: a list of event names that your session wants to
300 listen for, minus the 'testd_' prefix.
301
302 Registering for 'all' will cause it to send all TESTD-related
303 events to you; this is the easiest way to handle it.
304
305 "unregister"
306 Takes N arguments: a list of event names which you don't want to
307 receive. If you've previously done a 'register' for a particular
308 event which you no longer care about, this event will tell the
309 POP3D to stop sending them to you. (If you haven't, it just ignores
310 you. No big deal).
311
312 "shutdown"
313 Terminates the component. Shuts down the listener and disconnects
314 connected clients.
315
316 "send_to_client"
317 Send some output to a connected client. First parameter must be a
318 valid client id. Second parameter is a string of text to send. The
319 second parameter may also be an arrayref of items to send to the
320 client. If the filter you have used requires an arrayref as input,
321 nest that arrayref within another arrayref.
322
323 "send_to_all_clients"
324 Send some output to all connected clients. The parameter is a
325 string of text to send. The parameter may also be an arrayref of
326 items to send to the clients. If the filter you have used requires
327 an arrayref as input, nest that arrayref within another arrayref.
328
329 "disconnect"
330 Places a client connection in pending disconnect state. Requires a
331 valid client ID as a parameter. Set this, then send an applicable
332 message to the client using send_to_client() and the client
333 connection will be terminated.
334
335 "terminate"
336 Immediately disconnects a client conenction. Requires a valid
337 client ID as a parameter.
338
339 "start_listener"
340 If the listener fails on "listen" you can attempt to restart it
341 with this.
342
344 The component sends the following events to registered sessions. If you
345 have changed the "prefix" option in "spawn" then substitute "testd"
346 with the event prefix that you specified.
347
348 "testd_registered"
349 This event is sent to a registering session. ARG0 is the
350 Test::POE::Server::TCP object.
351
352 "testd_listener_failed"
353 Generated if the component cannot either start a listener or there
354 is a problem accepting client connections. ARG0 contains the name
355 of the operation that failed. ARG1 and ARG2 hold numeric and
356 string values for $!, respectively.
357
358 If the operation was "listen", the component will remove the
359 listener. You may attempt to start it again using
360 "start_listener".
361
362 "testd_connected"
363 Generated whenever a client connects to the component. ARG0 is the
364 client ID, ARG1 is the client's IP address, ARG2 is the client's
365 TCP port. ARG3 is our IP address and ARG4 is our socket port.
366
367 "testd_disconnected"
368 Generated whenever a client disconnects. ARG0 is the client ID.
369
370 "testd_client_input"
371 Generated whenever a client sends us some traffic. ARG0 is the
372 client ID, ARG1 is the data sent ( tokenised by whatever
373 POE::Filter you specified.
374
375 "testd_client_flushed"
376 Generated whenever anything we send to the client is actually
377 flushed down the 'line'. ARG0 is the client ID.
378
380 Chris "BinGOs" Williams <chris@bingosnet.co.uk>
381
382 with code borrowed from POE::Component::Server::TCP by Rocco Caputo,
383 Ann Barcomb and Jos Boumans.
384
386 Copyright X Chris Williams, Rocco Caputo, Ann Barcomb and Jos Boumans.
387
388 This module may be used, modified, and distributed under the same terms
389 as Perl itself. Please see the license that came with your Perl
390 distribution for details.
391
393 POE
394
395 POE::Component::Server::TCP
396
397
398
399perl v5.12.1 2010-03-20 Test::POE::Server::TCP(3)