summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Ryder <tom@sanctum.geek.nz>2021-02-07 01:50:18 +1300
committerTom Ryder <tom@sanctum.geek.nz>2021-02-07 01:50:18 +1300
commite7a405464cd8102314c94b8984d509c4029792d6 (patch)
treedc352abee450425bc878a1ef485a8f7a1852a90b
downloadPOE-Component-Client-WebSocket-e7a405464cd8102314c94b8984d509c4029792d6.tar.gz
POE-Component-Client-WebSocket-e7a405464cd8102314c94b8984d509c4029792d6.zip
Commit v0.27 as found on CPANv0.27
I couldn't find the upstream for this; if I can get in contact with the author, I'll re-patch my work over the top.
-rw-r--r--.gitignore18
-rw-r--r--Changes29
-rw-r--r--MANIFEST19
-rw-r--r--Makefile.PL52
-rw-r--r--README85
-rw-r--r--ignore.txt18
-rw-r--r--lib/POE/Component/Client/WebSocket.pm619
-rw-r--r--makeNotes1
-rw-r--r--t/00-load.t13
-rw-r--r--t/manifest.t15
-rw-r--r--t/pod-coverage.t24
-rw-r--r--t/pod.t16
-rw-r--r--xt/boilerplate.t57
13 files changed, 966 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b9c2fe6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,18 @@
+Makefile
+Makefile.old
+Build
+Build.bat
+META.*
+MYMETA.*
+.build/
+_build/
+cover_db/
+blib/
+inc/
+.lwpcookies
+.last_cover_stats
+nytprof.out
+pod2htm*.tmp
+pm_to_blib
+POE-Component-Client-WebSocket-*
+POE-Component-Client-WebSocket-*.tar.gz
diff --git a/Changes b/Changes
new file mode 100644
index 0000000..7d3388f
--- /dev/null
+++ b/Changes
@@ -0,0 +1,29 @@
+Revision history for POE-Component-Client-WebSocket
+
+0.27 01/05/2017 07:49
+ Connected missing socket_death handler (alerts of upstream disconnected)
+
+0.26 18/03/2017 14:01
+ Had to push version due to error uploading 0.25, see 0.25 for change
+
+0.25 18/03/2017 14:00
+ Updated the connect method to remove POE::HTTP::Parser after lots of long runnning debug problems
+
+0.24 21/03/2017 10:24
+ Updated defaults to make all messages masked and fin by default, also reversed order of Changes to be more standard
+
+0.23 30/01/2016 12:00
+ Forgot to create version entry for this version, unsure what changes (added on 0.24)
+
+0.22 29/01/2016 17:16
+ Checking for mystery extra directory in tarbell, few DOC updates
+
+0.21 29/01/2016 04:50
+ Corrected a few more documentation errors, added parent method
+
+0.20 29/01/2016 04:31
+ Version bump to clean up the make file and related files
+
+0.01 29/01/2016 03:46
+ First version, hackily bashed together in 12 hours, it does work though :)
+
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..2fa1e0d
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,19 @@
+Changes
+ignore.txt
+lib/POE/Component/Client/WebSocket.pm
+Makefile
+Makefile.PL
+makeNotes
+MANIFEST This list of files
+MANIFEST.bak
+MANIFEST.SKIP
+MYMETA.json
+MYMETA.yml
+README
+t/00-load.t
+t/manifest.t
+t/pod-coverage.t
+t/pod.t
+xt/boilerplate.t
+META.yml Module YAML meta-data (added by MakeMaker)
+META.json Module JSON meta-data (added by MakeMaker)
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644
index 0000000..5f0f02f
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,52 @@
+use 5.006;
+use strict;
+use warnings;
+use ExtUtils::MakeMaker;
+
+my %WriteMakefileArgs = (
+ "NAME" => 'POE::Component::Client::WebSocket',
+ "AUTHOR" => q{Paul G Webster <daemon@cpan.org>},
+ "MIN_PERL_VERSION" => "5.006",
+ "ABSTRACT" => "A simplistic websocket client for use in POE applications.",
+ "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => 0 },
+ "DISTNAME" => "POE-Component-Client-WebSocket",
+ "LICENSE" => "artistic_2",
+ "PREREQ_PM" => {
+ "POE" => 0,
+ "POE::Filter::SSL" => 0,
+ "POE::Filter::HTTP::Parser" => 0,
+ "POE::Filter::Stackable" => 0,
+ "URI" => 0,
+ "MIME::Base64" => 0,
+ "Protocol::WebSocket::Frame" => 0,
+ },
+ TEST_REQUIRES => {
+ 'Test::More' => 0,
+ },
+ VERSION_FROM => 'lib/POE/Component/Client/WebSocket.pm',
+ "test" => {
+ "TESTS" => "t/*.t"
+ }
+);
+
+my %FallbackPrereqs = (
+ "POE" => 0,
+ "POE::Filter::SSL" => 0,
+ "POE::Filter::HTTP::Parser" => 0,
+ "POE::Filter::Stackable" => 0,
+ "URI" => 0,
+ "MIME::Base64" => 0,
+ "Protocol::WebSocket::Frame" => 0,
+);
+
+unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) {
+ delete $WriteMakefileArgs{TEST_REQUIRES};
+ delete $WriteMakefileArgs{BUILD_REQUIRES};
+ $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs;
+}
+
+delete $WriteMakefileArgs{CONFIGURE_REQUIRES}
+ unless eval { ExtUtils::MakeMaker->VERSION(6.52) };
+
+WriteMakefile(%WriteMakefileArgs);
+
diff --git a/README b/README
new file mode 100644
index 0000000..a8c07cb
--- /dev/null
+++ b/README
@@ -0,0 +1,85 @@
+POE-Component-Client-WebSocket
+
+The README is used to introduce the module and provide instructions on
+how to install the module, any machine dependencies it may have (for
+example C compilers and installed libraries) and any other information
+that should be provided before the module is installed.
+
+A README file is required for CPAN modules since CPAN extracts the README
+file from a module distribution so that people browsing the archive
+can use it to get an idea of the module's uses. It is usually a good idea
+to provide version information here so that people can decide whether
+fixes for the module are worth downloading.
+
+
+INSTALLATION
+
+To install this module, run the following commands:
+
+ perl Makefile.PL
+ make
+ make test
+ make install
+
+SUPPORT AND DOCUMENTATION
+
+After installing, you can find documentation for this module with the
+perldoc command.
+
+ perldoc POE::Component::Client::WebSocket
+
+You can also look for information at:
+
+ RT, CPAN's request tracker (report bugs here)
+ http://rt.cpan.org/NoAuth/Bugs.html?Dist=POE-Component-Client-WebSocket
+
+ AnnoCPAN, Annotated CPAN documentation
+ http://annocpan.org/dist/POE-Component-Client-WebSocket
+
+ CPAN Ratings
+ http://cpanratings.perl.org/d/POE-Component-Client-WebSocket
+
+ Search CPAN
+ http://search.cpan.org/dist/POE-Component-Client-WebSocket/
+
+
+LICENSE AND COPYRIGHT
+
+Copyright (C) 2016 Paul G Webster
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the the Artistic License (2.0). You may obtain a
+copy of the full license at:
+
+L<http://www.perlfoundation.org/artistic_license_2_0>
+
+Any use, modification, and distribution of the Standard or Modified
+Versions is governed by this Artistic License. By using, modifying or
+distributing the Package, you accept this license. Do not use, modify,
+or distribute the Package, if you do not accept this license.
+
+If your Modified Version has been derived from a Modified Version made
+by someone other than you, you are nevertheless required to ensure that
+your Modified Version complies with the requirements of this license.
+
+This license does not grant you the right to use any trademark, service
+mark, tradename, or logo of the Copyright Holder.
+
+This license includes the non-exclusive, worldwide, free-of-charge
+patent license to make, have made, use, offer to sell, sell, import and
+otherwise transfer the Package with respect to any patent claims
+licensable by the Copyright Holder that are necessarily infringed by the
+Package. If you institute patent litigation (including a cross-claim or
+counterclaim) against any party alleging that the Package constitutes
+direct or contributory patent infringement, then this Artistic License
+to you shall terminate on the date that such litigation is filed.
+
+Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
+AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
+THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
+YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
+CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
+CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/ignore.txt b/ignore.txt
new file mode 100644
index 0000000..b9c2fe6
--- /dev/null
+++ b/ignore.txt
@@ -0,0 +1,18 @@
+Makefile
+Makefile.old
+Build
+Build.bat
+META.*
+MYMETA.*
+.build/
+_build/
+cover_db/
+blib/
+inc/
+.lwpcookies
+.last_cover_stats
+nytprof.out
+pod2htm*.tmp
+pm_to_blib
+POE-Component-Client-WebSocket-*
+POE-Component-Client-WebSocket-*.tar.gz
diff --git a/lib/POE/Component/Client/WebSocket.pm b/lib/POE/Component/Client/WebSocket.pm
new file mode 100644
index 0000000..a78ba2e
--- /dev/null
+++ b/lib/POE/Component/Client/WebSocket.pm
@@ -0,0 +1,619 @@
+package POE::Component::Client::WebSocket;
+
+use 5.006;
+use strict;
+use warnings;
+
+use vars qw($VERSION);
+$VERSION = '0.27';
+
+use Carp qw(carp croak);
+use Errno qw(ETIMEDOUT ECONNRESET);
+
+# Explicit use to import the parameter constants;
+use POE::Session;
+use POE::Driver::SysRW;
+use POE::Wheel::ReadWrite;
+use POE::Wheel::SocketFactory;
+use POE::Filter::Stackable;
+use POE::Filter::SSL;
+use POE::Filter::Stream;
+
+# Other various stuff
+use URI::Split qw(uri_split);
+use MIME::Base64;
+use Data::Dumper;
+
+# Crazy frame stuff
+use Protocol::WebSocket::Frame;
+
+# HTTP parsing modules
+require HTTP::Request;
+require HTTP::Response;
+
+# Global stuff for checks etc.
+my $validOpts = {
+ types => {
+ 'continuation' => 1,
+ 'text' => 1,
+ 'binary' => 1,
+ 'ping' => 1,
+ 'pong' => 1,
+ 'close' => 1,
+ }
+};
+
+=head1 NAME
+
+POE::Component::Client::WebSocket - A POE compatible websocket client
+
+=head1 VERSION
+
+Version 0.22
+
+=head1 WARNING: Work in progress! Only uploaded early for testing purposes!
+
+This module appears to work perfectly, however its not really been tested that much and I will be amazed if there are not bugs.
+
+=head1 SYNOPSIS
+
+ #!/usr/bin/env perl
+
+ use warnings;
+ use strict;
+
+ use POE qw(Component::Client::WebSocket);
+
+ POE::Session->create(
+ inline_states => {
+ _start => sub {
+ my $ws = POE::Component::Client::WebSocket->new('wss://echo.websocket.org');
+ $ws->handler('connected','connected');
+ $ws->connect;
+
+ $_[HEAP]->{ws} = $ws;
+
+ $_[KERNEL]->yield("next")
+ },
+ next => sub {
+ $_[KERNEL]->delay(next => 1);
+ },
+ websocket_read => sub {
+ my ($kernel,$read) = @_[KERNEL,ARG0];
+
+ print "Read: $read\n";
+ },
+ websocket_disconnected => sub {
+ warn "Disconnected";
+ },
+ connected => sub {
+ my $req = $_[ARG0];
+ },
+ websocket_handshake => sub {
+ my $res = $_[ARG0];
+
+ $_[KERNEL]->post( $_[SENDER]->ID, 'send', 1234 );
+
+ $_[HEAP]->{ws}->send(5678);
+ },
+ },
+ );
+
+ POE::Kernel->run();
+ exit;
+
+
+=head1 SUBROUTINES/METHODS
+
+=head2 new
+
+Create a new object, takes 1 argument.. a fully qualified websocket URI.
+
+=cut
+
+sub new {
+ my ($class,$uri) = @_;
+
+ my $self = bless {
+ alias => __PACKAGE__,
+ session => 0,
+ }, $class;
+
+ my ($scheme, $auth, $path, $query, $frag) = uri_split($uri);
+ my ($host,$port) = split(/:/,$auth);
+
+ # If we are on WSS we are probably using https, if not probably http
+ if (!$port) {
+ if (uc($scheme) eq 'WSS') { $port = 443 }
+ else { $port = 80 }
+ }
+
+
+ my $key = "";
+ for (1..16) { $key .= int(rand(9)) }
+
+ $self->{session} = POE::Session->create(
+ package_states => [
+ $self => {
+ _start => '_start',
+ _stop => '_stop',
+ keep_alive => '_keep_alive',
+ connect => '_connect',
+ handler => '_handler',
+ origin => '_origin',
+ send => '_send',
+ parent => '_parent',
+ socket_birth => '_socket_birth',
+ socket_death => '_socket_death',
+ socket_input => '_socket_input',
+ }
+ ],
+ heap => {
+ parent => POE::Kernel->get_active_session()->ID,
+ handlers => {
+ read => 'websocket_read',
+ connected => 'websocket_connected',
+ handshake => 'websocket_handshake',
+ disconnected => 'websocket_disconnected',
+ error => 'websocket_error',
+ },
+ uri => {
+ scheme => $scheme,
+ auth => $auth,
+ path => $path,
+ query => $query,
+ frag => $frag,
+ host => $host,
+ port => $port,
+ },
+ req => {
+ 'origin' => 'http://'.$host,
+ 'sec-websocket-key' => encode_base64($key),
+ },
+ _state => {
+ run => 1,
+ frame => Protocol::WebSocket::Frame->new,
+ },
+
+ }
+ );
+
+ $self->{id} = $self->{session}->ID;
+
+ return $self;
+}
+
+=head1 Default handlers and arguments
+
+All of these can be changed with the 'handler' function, note the 'event key'.
+
+=head2 read (websocket_read)
+
+event key: read
+default handler: websocket_read
+
+Data that has been read and decoded from the server.
+
+
+=head2 disconnected (websocket_disconnected)
+
+event key: disconnected
+default handler: websocket_disconnected
+
+Called when the websocket is disconnected.
+
+=head2 connected (websocket_connected)
+
+event key: connected
+default handler: websocket_connected
+
+Called when the websocket is connected (before the handshake) ARG0 contains the HTTP::Request sent.
+
+=head2 handshake (websocket_handshake)
+
+event key: handshake
+default handler: websocket_handshake
+
+Called when the socket handshake is completed, ARG0 contains the HTTP::Response from the server.
+
+=head2 error (websocket_error)
+
+event key: error
+default handler: websocket_error
+
+Called when an error is retrieved, this is a bit vague at the moment ARG0 should contain something.
+
+=head1 OOP mappings from obj to POE
+
+These can be called with a standard POE style POST or directly from the object (see SYNOPSIS for examples of both)
+
+=head2 connect
+
+Start the connection
+
+=cut
+
+sub connect { my $self = shift; POE::Kernel->post( $self->{session}->ID, 'connect', @_ ) }
+
+=head2 handler
+
+Adjust the handlers events are sent to
+
+=cut
+
+sub handler {
+ my ($self,$target,$destination) = @_;
+
+ return if ( (!$target) || (!$destination) );
+
+ POE::Kernel->post(
+ $self->{session}->ID,
+ 'handler',
+ $target,
+ $destination
+ );
+}
+
+=head2 origin
+
+Change the origin from the automatically generated one to something else.
+
+=cut
+
+sub origin {
+ my ($self,$target) = @_;
+
+ return if (!$target);
+
+ POE::Kernel->post(
+ $self->{session}->ID,
+ 'origin',
+ $target
+ );
+}
+
+=head2 parent
+
+Override the 'send to' parent for the module, by default this is the module that the component was started from.
+
+=cut
+
+sub parent {
+ my ($self,$target) = @_;
+
+ return if (!$target);
+
+ POE::Kernel->post(
+ $self->{session}->ID,
+ 'parent',
+ $target
+ );
+}
+
+=head2 send
+
+Send data to the server, arguments are:
+ 1: 'data' the information you want to send.
+ 2: 'type' the type of information to send (default 'text')
+ 3: 'fin' wether to send the 'fin' flag (default 1)
+ 4: 'masked' wether the frame should be masked (default 0)
+
+=cut
+
+sub send {
+ my ($self,$data,$type,$fin,$masked) = @_;
+
+ return if (!$data);
+
+ POE::Kernel->post(
+ $self->{session}->ID,
+ 'send',
+ $data,$type,$fin,$masked
+ );
+}
+
+
+=head1 Internal functions (do not call these directly)
+
+=head2 _start
+
+Initial start handler
+
+=cut
+
+sub _start {
+ my ($kernel,$heap) = @_[KERNEL,HEAP];
+
+ $kernel->yield('keep_alive');
+}
+
+=head2 _stop
+
+Default stop handler for tidying things up
+
+=cut
+
+sub _stop {
+}
+
+=head2 _keep_alive
+
+Do not allow the module to stop running
+
+=cut
+
+sub _keep_alive {
+ my ($kernel,$heap) = @_[KERNEL,HEAP];
+
+ return if (!$heap->{_state}->{run});
+
+ $kernel->delay_add('keep_alive' => 1);
+}
+
+=head2 _connect
+
+Initate a connect to the websocket
+
+=cut
+
+sub _connect {
+ my ($kernel,$heap) = @_[KERNEL,HEAP];
+
+ $heap->{socket} = POE::Wheel::SocketFactory->new(
+ RemoteAddress => $heap->{uri}->{host},
+ RemotePort => $heap->{uri}->{port},
+ SuccessEvent => 'socket_birth',
+ FailureEvent => 'socket_death',
+ );}
+
+=head2 _handler
+
+Adjust the distribution map for handlers
+
+=cut
+
+sub _handler {
+ my ($kernel,$heap,$target,$destination) = @_[KERNEL,HEAP,ARG0,ARG1];
+
+ $heap->{handlers}->{lc($target)} = $destination;
+}
+
+=head2 _origin
+
+Change the origin used in the opening handshake
+
+=cut
+
+sub _origin {
+ my ($kernel,$heap,$target) = @_[KERNEL,HEAP,ARG0];
+
+ $heap->{req}->{origin} = $target;
+}
+
+=head2 _parent
+
+Change the currently targeted session to communicate events with.
+
+=cut
+
+sub _parent {
+ my ($kernel,$heap,$target) = @_[KERNEL,HEAP,ARG0];
+
+ $heap->{parent} = $target;
+}
+
+=head2 _send
+
+Send a frame encoded request to the server
+
+=cut
+
+sub _send {
+ my ($kernel,$heap,$data,$type,$fin,$masked) = @_[KERNEL,HEAP,ARG0,ARG1,ARG2,ARG3];
+
+ $data = "" if (!$data);
+ $type = 'text' if ( (!$type) || (! $validOpts->{types}->{$type}) );
+ $fin = 1 if ((!defined $fin) || ($fin !~ m#^[01]$#));
+ $masked = 1 if ((!defined $masked) || ($fin !~ m#^[01]$#));
+
+ my $frame = Protocol::WebSocket::Frame->new( buffer => $data, type => $type, fin => $fin, masked => $masked );
+ $heap->{wheel}->put($frame->to_bytes);
+}
+
+=head2 _socket_birth
+
+Handle a socket when it connects to something
+
+=cut
+
+sub _socket_birth {
+ my ($kernel, $socket, $sockid, $heap) = @_[KERNEL, ARG0, ARG3, HEAP];
+
+ if ( uc($heap->{uri}->{scheme}) eq 'WSS' ) {
+ $heap->{_state}->{sslfilter} = POE::Filter::SSL->new(client=>1);
+
+ $heap->{filters}->{output} = POE::Filter::Stackable->new(Filters => [ $heap->{_state}->{sslfilter} ]);
+ $heap->{filters}->{input} = POE::Filter::Stackable->new(Filters => [ $heap->{_state}->{sslfilter} ]);
+ } else {
+ $heap->{filters}->{output} = POE::Filter::Stackable->new(Filters => []);
+ $heap->{filters}->{input} = POE::Filter::Stackable->new(Filters => []);
+ }
+
+ $heap->{filters}->{output}->push(POE::Filter::Stream->new());
+ $heap->{filters}->{input}->push(POE::Filter::Stream->new());
+
+ $heap->{wheel} = POE::Wheel::ReadWrite->new(
+ Handle => $socket,
+ Driver => POE::Driver::SysRW->new(),
+ OutputFilter => $heap->{filters}->{output},
+ InputFilter => $heap->{filters}->{input},
+ InputEvent => 'socket_input',
+ ErrorEvent => 'socket_death',
+ );
+
+ my $request = HTTP::Request->new(GET => '/');
+ $request->protocol('HTTP/1.1');
+ $request->header(
+ Upgrade => 'WebSocket',
+ Connection => 'Upgrade',
+ Host => $heap->{uri}->{host},
+ Origin => $heap->{req}->{origin},
+ 'Sec-WebSocket-Key' => $heap->{req}->{'sec-websocket-key'},
+ 'Sec-WebSocket-Protocol' => 'chat',
+ 'Sec-WebSocket-Version' => 13,
+ );
+
+ # Add a lock for when to stop reading the stream
+ $heap->{httpresp} = 1;
+
+ # Send the request to the server
+ $heap->{wheel}->put($request->as_string());
+
+ # Incase we want to investigate what we sent later.
+ $heap->{_state}->{req} = $request;
+
+ # Post back a copy of the request
+ $kernel->post( $heap->{parent}, $heap->{handlers}->{'connected'}, $request );
+}
+
+=head2 _socket_death
+
+Handle a socket when it is disconnected
+
+=cut
+
+sub _socket_death {
+ my ($kernel,$heap) = @_[KERNEL,HEAP];
+
+ $kernel->post( $heap->{parent}, $heap->{handlers}->{disconnected} )
+}
+
+=head2 _socket_input
+
+Read data from the socket
+
+=cut
+
+sub _socket_input {
+ my ($kernel,$heap,$buf) = @_[KERNEL,HEAP,ARG0];
+
+ if ( $heap->{httpresp} ) {
+ $heap->{httpbuf} .= $buf;
+
+ if ( $heap->{httpbuf} =~ m#\r\n$#m ) {
+ # Remove the lock
+ delete $heap->{httpresp};
+
+ # Keep a copy of the response we got incase we want to have a look at it later
+ $heap->{_state}->{res} = $buf;
+
+ # Create an investigatable response object
+ my $resp = HTTP::Response->parse($heap->{httpbuf});
+
+ # Lets see if we can proceed
+ if ($resp->code() == 101) {
+ # Ok connected
+
+ # Send a copy of the handshake back to the users space
+ $kernel->post( $heap->{parent}, $heap->{handlers}->{'handshake'}, $resp );
+ }
+ }
+
+ return;
+ }
+
+ $heap->{_state}->{frame}->append($buf);
+
+ while (my $frame = $heap->{_state}->{frame}->next) { $kernel->post( $heap->{parent}, $heap->{handlers}->{read}, $frame ) }
+}
+
+=head1 AUTHOR
+
+Paul G Webster, C<< <daemon at cpan.org> >>
+
+=head1 BUGS
+
+Please report any bugs or feature requests to C<bug-poe-component-client-websocket at rt.cpan.org>, or through
+the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=POE-Component-Client-WebSocket>. I will be notified, and then you'll
+automatically be notified of progress on your bug as I make changes.
+
+
+
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command.
+
+ perldoc POE::Component::Client::WebSocket
+
+
+You can also look for information at:
+
+=over 4
+
+=item * RT: CPAN's request tracker (report bugs here)
+
+L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=POE-Component-Client-WebSocket>
+
+=item * AnnoCPAN: Annotated CPAN documentation
+
+L<http://annocpan.org/dist/POE-Component-Client-WebSocket>
+
+=item * CPAN Ratings
+
+L<http://cpanratings.perl.org/d/POE-Component-Client-WebSocket>
+
+=item * Search CPAN
+
+L<http://search.cpan.org/dist/POE-Component-Client-WebSocket/>
+
+=back
+
+
+=head1 ACKNOWLEDGEMENTS
+
+
+=head1 LICENSE AND COPYRIGHT
+
+Copyright 2016 Paul G Webster.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the the Artistic License (2.0). You may obtain a
+copy of the full license at:
+
+L<http://www.perlfoundation.org/artistic_license_2_0>
+
+Any use, modification, and distribution of the Standard or Modified
+Versions is governed by this Artistic License. By using, modifying or
+distributing the Package, you accept this license. Do not use, modify,
+or distribute the Package, if you do not accept this license.
+
+If your Modified Version has been derived from a Modified Version made
+by someone other than you, you are nevertheless required to ensure that
+your Modified Version complies with the requirements of this license.
+
+This license does not grant you the right to use any trademark, service
+mark, tradename, or logo of the Copyright Holder.
+
+This license includes the non-exclusive, worldwide, free-of-charge
+patent license to make, have made, use, offer to sell, sell, import and
+otherwise transfer the Package with respect to any patent claims
+licensable by the Copyright Holder that are necessarily infringed by the
+Package. If you institute patent litigation (including a cross-claim or
+counterclaim) against any party alleging that the Package constitutes
+direct or contributory patent infringement, then this Artistic License
+to you shall terminate on the date that such litigation is filed.
+
+Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
+AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
+THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
+YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
+CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
+CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+=cut
+
+1; # End of POE::Component::Client::WebSocket
diff --git a/makeNotes b/makeNotes
new file mode 100644
index 0000000..cdd5753
--- /dev/null
+++ b/makeNotes
@@ -0,0 +1 @@
+perl Makefile.PL; make manifest; make distdir; make dist
diff --git a/t/00-load.t b/t/00-load.t
new file mode 100644
index 0000000..caccc29
--- /dev/null
+++ b/t/00-load.t
@@ -0,0 +1,13 @@
+#!perl -T
+use 5.006;
+use strict;
+use warnings;
+use Test::More;
+
+plan tests => 1;
+
+BEGIN {
+ use_ok( 'POE::Component::Client::WebSocket' ) || print "Bail out!\n";
+}
+
+diag( "Testing POE::Component::Client::WebSocket $POE::Component::Client::WebSocket::VERSION, Perl $], $^X" );
diff --git a/t/manifest.t b/t/manifest.t
new file mode 100644
index 0000000..e0b558e
--- /dev/null
+++ b/t/manifest.t
@@ -0,0 +1,15 @@
+#!perl -T
+use 5.006;
+use strict;
+use warnings;
+use Test::More;
+
+unless ( $ENV{RELEASE_TESTING} ) {
+ plan( skip_all => "Author tests not required for installation" );
+}
+
+my $min_tcm = 0.9;
+eval "use Test::CheckManifest $min_tcm";
+plan skip_all => "Test::CheckManifest $min_tcm required" if $@;
+
+ok_manifest();
diff --git a/t/pod-coverage.t b/t/pod-coverage.t
new file mode 100644
index 0000000..f5728a5
--- /dev/null
+++ b/t/pod-coverage.t
@@ -0,0 +1,24 @@
+#!perl -T
+use 5.006;
+use strict;
+use warnings;
+use Test::More;
+
+unless ( $ENV{RELEASE_TESTING} ) {
+ plan( skip_all => "Author tests not required for installation" );
+}
+
+# Ensure a recent version of Test::Pod::Coverage
+my $min_tpc = 1.08;
+eval "use Test::Pod::Coverage $min_tpc";
+plan skip_all => "Test::Pod::Coverage $min_tpc required for testing POD coverage"
+ if $@;
+
+# Test::Pod::Coverage doesn't require a minimum Pod::Coverage version,
+# but older versions don't recognize some common documentation styles
+my $min_pc = 0.18;
+eval "use Pod::Coverage $min_pc";
+plan skip_all => "Pod::Coverage $min_pc required for testing POD coverage"
+ if $@;
+
+all_pod_coverage_ok();
diff --git a/t/pod.t b/t/pod.t
new file mode 100644
index 0000000..4d3a0ce
--- /dev/null
+++ b/t/pod.t
@@ -0,0 +1,16 @@
+#!perl -T
+use 5.006;
+use strict;
+use warnings;
+use Test::More;
+
+unless ( $ENV{RELEASE_TESTING} ) {
+ plan( skip_all => "Author tests not required for installation" );
+}
+
+# Ensure a recent version of Test::Pod
+my $min_tp = 1.22;
+eval "use Test::Pod $min_tp";
+plan skip_all => "Test::Pod $min_tp required for testing POD" if $@;
+
+all_pod_files_ok();
diff --git a/xt/boilerplate.t b/xt/boilerplate.t
new file mode 100644
index 0000000..5f988aa
--- /dev/null
+++ b/xt/boilerplate.t
@@ -0,0 +1,57 @@
+#!perl -T
+use 5.006;
+use strict;
+use warnings;
+use Test::More;
+
+plan tests => 3;
+
+sub not_in_file_ok {
+ my ($filename, %regex) = @_;
+ open( my $fh, '<', $filename )
+ or die "couldn't open $filename for reading: $!";
+
+ my %violated;
+
+ while (my $line = <$fh>) {
+ while (my ($desc, $regex) = each %regex) {
+ if ($line =~ $regex) {
+ push @{$violated{$desc}||=[]}, $.;
+ }
+ }
+ }
+
+ if (%violated) {
+ fail("$filename contains boilerplate text");
+ diag "$_ appears on lines @{$violated{$_}}" for keys %violated;
+ } else {
+ pass("$filename contains no boilerplate text");
+ }
+}
+
+sub module_boilerplate_ok {
+ my ($module) = @_;
+ not_in_file_ok($module =>
+ 'the great new $MODULENAME' => qr/ - The great new /,
+ 'boilerplate description' => qr/Quick summary of what the module/,
+ 'stub function definition' => qr/function[12]/,
+ );
+}
+
+TODO: {
+ local $TODO = "Need to replace the boilerplate text";
+
+ not_in_file_ok(README =>
+ "The README is used..." => qr/The README is used/,
+ "'version information here'" => qr/to provide version information/,
+ );
+
+ not_in_file_ok(Changes =>
+ "placeholder date/time" => qr(Date/time)
+ );
+
+ module_boilerplate_ok('lib/POE/Component/Client/WebSocket.pm');
+
+
+}
+