Net::REPL::Client: Allow read/print portions to be overridden separately.
[thrasher.git] / perl / lib / Thrasher / Socket.pm
blobc56b0f541419711864485e48a36532296a6760f6
1 package Thrasher::Socket;
2 use strict;
3 use warnings;
5 =pod
7 =head1 NAME
9 Thrasher::Socket - manage the socket connection to the XMPP server.
11 =head1 DESCRIPTION
13 Thrasher::Socket manages the connection to the XMPP server, and
14 once the connection is successfully established, feeds the socket
15 into the Component.
17 =cut
19 use POSIX;
20 use Socket;
21 use IO::Socket;
22 use Thrasher::Log qw(log debug);
24 sub new {
25 my $class = shift;
26 my $self = {};
28 $self->{host} = shift;
29 $self->{port} = shift;
30 $self->{event_loop} = shift;
32 bless $self, $class;
33 return $self;
36 # Returns the function to feed to the component to write to
37 # this socket.
38 sub write_function {
39 my $self = shift;
40 my $writer = sub {
41 debug("OUT: " . join('', @_));
43 $self->write(@_);
45 return $writer;
48 sub write {
49 my $self = shift;
51 my $socket = $self->{socket};
52 for my $data (@_) {
53 print $socket $data;
55 flush $socket;
58 # dies on failure
59 sub connect {
60 my $self = shift;
61 if ($self->{connected}) {
62 return;
65 my $proto = getprotobyname('tcp');
66 my $iaddr = inet_aton($self->{host});
67 my $paddr = sockaddr_in($self->{port}, $iaddr);
69 my $socket;
71 log("Beginning socket connection to $self->{host}:$self->{port}...");
72 socket($socket, PF_INET, SOCK_STREAM, $proto)
73 or die "SOCK_CREATE: socket failed to create";
74 connect($socket, $paddr)
75 or die "SOCK_CONNECT: socket failed to connect";
76 log("connected.");
78 binmode($socket, ':encoding(utf8)');
80 # No blocking here
81 fcntl($socket, F_SETFL, O_NONBLOCK);
83 $self->{socket} = $socket;
84 $self->{connected} = 1;
87 sub establish_fd_watch {
88 my $self = shift;
90 $self->{watch_handle} = $self->{event_loop}->add_fd_watch
91 ($self->fileno, $Thrasher::EventLoop::IN,
92 $self->{read_closure});
95 sub terminate_fd_watch {
96 my $self = shift;
97 if (!exists($self->{watch_handle})) {
98 return;
101 $self->{event_loop}->remove_fd_watch($self->{watch_handle});
102 delete($self->{watch_handle});
105 sub get_socket {
106 my $self = shift;
107 if (!$self->{connected}) {
108 die "No socket to get.";
111 return $self->{socket};
114 sub read {
115 my $self = shift;
117 if (!$self->{connected}) {
118 die('Trying to read from an unconnected socket.');
121 my $value;
122 my $return = sysread($self->{socket}, $value, 65535);
123 if ($return) {
124 return $value;
126 # For now restart on all read errors--dunno what state changes we
127 # missed reading. Might eventually be able to reconnect only the
128 # component w/o reconnecting everyone to the public IM network.
129 elsif (! defined($return)) {
130 if ($!{'EAGAIN'}) {
131 # Not ready yet. That's OK--return undef and try again later.
132 return;
134 else {
135 die("Error reading from component socket: $!");
138 else {
139 die('EOF when reading from component socket!');
143 sub close {
144 my $self = shift;
145 $self->{socket}->close;
146 $self->terminate_fd_watch;
147 delete $self->{connected};
148 delete $self->{socket};
151 sub fileno {
152 my $self = shift;
153 return $self->{socket}->fileno;
156 sub eof {
157 my $self = shift;
158 return $self->{socket}->eof;