ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-XMPP2/lib/Net/XMPP2/SimpleConnection.pm
Revision: 1.11
Committed: Fri Jul 6 15:19:52 2007 UTC (19 years, 2 months ago) by elmex
Branch: MAIN
CVS Tags: HEAD
Changes since 1.10: +0 -2 lines
Log Message:
fixed some bugs in max write length and limit searcher

File Contents

# User Rev Content
1 elmex 1.1 package Net::XMPP2::SimpleConnection;
2 elmex 1.7 use strict;
3    
4 elmex 1.1 use IO::Socket::INET;
5     use Errno;
6     use Fcntl;
7     use Encode;
8     use Net::SSLeay;
9 elmex 1.5 use IO::Handle;
10 elmex 1.1
11     BEGIN {
12     Net::SSLeay::load_error_strings ();
13     Net::SSLeay::SSLeay_add_ssl_algorithms ();
14     Net::SSLeay::randomize ();
15     }
16    
17     =head1 NAME
18    
19 elmex 1.9 Net::XMPP2::SimpleConnection - Low level TCP/TLS connection
20 elmex 1.1
21     =head1 SYNOPSIS
22    
23     package foo;
24     use Net::XMPP2::SimpleConnection;
25    
26     our @ISA = qw/Net::XMPP2::SimpleConnection/;
27    
28     =head1 DESCRIPTION
29    
30     This module only implements the basic low level socket and SSL handling stuff.
31 elmex 1.8 It is used by L<Net::XMPP2::Connection> and you shouldn't mess with it :-)
32    
33     (NOTE: This is the part of Net::XMPP2 which I feel least confident about :-)
34 elmex 1.1
35     =cut
36    
37     sub new {
38     my $this = shift;
39     my $class = ref($this) || $this;
40 elmex 1.10 my $self = {
41     disconnect_cb => sub {},
42     max_write_length => 4096,
43     @_
44     };
45 elmex 1.1 bless $self, $class;
46     return $self;
47     }
48    
49     sub set_block {
50     my ($self) = @_;
51     my $flags = 0;
52     fcntl($self->{socket}, F_GETFL, $flags)
53     or die "Couldn't get flags for HANDLE : $!\n";
54     $flags &= ~O_NONBLOCK;
55     fcntl($self->{socket}, F_SETFL, $flags)
56     or die "Couldn't set flags for HANDLE: $!\n";
57     }
58    
59     sub set_noblock {
60     my ($self) = @_;
61     my $flags = 0;
62     fcntl($self->{socket}, F_GETFL, $flags)
63     or die "Couldn't get flags for HANDLE : $!\n";
64     $flags |= O_NONBLOCK;
65     fcntl($self->{socket}, F_SETFL, $flags)
66     or die "Couldn't set flags for HANDLE: $!\n";
67     }
68    
69     sub connect {
70     my ($self, $host, $port) = @_;
71    
72     $self->{socket}
73     and return 1;
74    
75     my $sock = IO::Socket::INET->new (
76     PeerAddr => $host,
77     PeerPort => $port,
78     Proto => 'tcp',
79     Blocking => 1
80     );
81     return undef unless $sock;;
82    
83     $self->{socket} = $sock;
84     $self->{host} = $host;
85     $self->{port} = $port;
86    
87     $self->set_noblock;
88    
89 elmex 1.4 binmode $sock, ":raw";
90 elmex 1.1
91     $self->{r} =
92     AnyEvent->io (poll => 'r', fh => $sock, cb => sub {
93     my $l = sysread $sock, my $data, 1024;
94    
95     if ($l) {
96 elmex 1.4 $self->{read_buffer} .= decode_utf8 $data;
97 elmex 1.1 $self->handle_data (\$self->{read_buffer});
98    
99     } else {
100     return if $! == Errno::EAGAIN;
101     if (defined $l) {
102 elmex 1.5 $self->disconnect ("EOF from server '$self->{host}:$self->{port}'");
103 elmex 1.1 return;
104    
105     } else {
106 elmex 1.5 $self->disconnect ("Error while reading from server '$self->{host}:$port': $!");
107 elmex 1.1 return;
108     }
109     }
110     });
111     return 1;
112     }
113    
114     sub end_sockets {
115     my ($self) = @_;
116     delete $self->{r};
117     delete $self->{w};
118     if (delete $self->{ssl_enabled}) {
119     Net::SSLeay::free ($self->{ssl});
120     delete $self->{ssl};
121     Net::SSLeay::CTX_free ($self->{ctx});
122     delete $self->{ctx};
123     }
124 elmex 1.5 close ($self->{socket});
125     delete $self->{socket};
126 elmex 1.1 }
127    
128     sub try_ssl_write {
129     my ($self) = @_;
130    
131 elmex 1.10 my $data = substr $self->{write_buffer}, 0, $self->{max_write_length};
132     my $l = Net::SSLeay::write ($self->{ssl}, $data);
133 elmex 1.1
134     if ($l <= 0) {
135     if ($l == 0) {
136 elmex 1.5 $self->disconnect ("unexpected EOF from server (ssl) '$self->{host}:$self->{port}'");
137 elmex 1.1 return;
138    
139     } else {
140     my $err2 = Net::SSLeay::get_error $self->{ssl}, $l;
141     if ($err2 == 2 || $err2 == 3) {
142     delete $self->{w};
143     $self->make_ssl_write_watcher ($err2 == 2 ? 'r' : 'w');
144     return;
145     }
146    
147     if ($! != Errno::EAGAIN
148     or my $err = Net::SSLeay::ERR_get_error) {
149    
150 elmex 1.5 $self->disconnect (
151 elmex 1.1 sprintf (
152     "Error while writing from server '$self->{host}:$self->{port}': (%d|%s|%s)",
153     $err2, (Net::SSLeay::ERR_error_string $err), "$!")
154     );
155     return;
156     }
157     }
158     } else {
159 elmex 1.2 $self->debug_wrote_data (substr $self->{write_buffer}, 0, $l);
160     $self->{write_buffer} = substr $self->{write_buffer}, $l;
161     if (length ($self->{write_buffer}) <= 0) {
162     delete $self->{w};
163     }
164 elmex 1.1 }
165     }
166    
167     sub try_ssl_read {
168     my ($self) = @_;
169 elmex 1.2 my $r = Net::SSLeay::read ($self->{ssl});
170    
171     if (defined $r) {
172 elmex 1.7 if ($r eq '') {
173     if (my $err = Net::SSLeay::ERR_get_error) {
174     $self->disconnect (
175     sprintf (
176     "Error while reading from server '$self->{host}:$self->{port}':"
177     ."(%s|%s)",
178     (Net::SSLeay::ERR_error_string $err), "$!")
179     );
180     return;
181     }
182     # is this right? $r = '' => EOF? sucky Net::SSLeay... arg...
183     $self->disconnect ("EOF from server '$self->{host}:$self->{port}'.");
184     return;
185     }
186    
187 elmex 1.2 $self->{read_buffer} .= decode_utf8 ($r);
188     $self->handle_data (\$self->{read_buffer});
189     } else {
190 elmex 1.7 my $err2 = Net::SSLeay::get_error $self->{ssl}, $r;
191 elmex 1.2 if ($err2 == 2 || $err2 == 3) {
192 elmex 1.6 #d# warn "READ RETRY $err2\n";
193 elmex 1.2 delete $self->{r};
194     $self->make_ssl_read_watcher ($err2 == 2 ? 'r' : 'w');
195     return;
196     }
197    
198     if ($! != Errno::EAGAIN
199     or my $err = Net::SSLeay::ERR_get_error) {
200 elmex 1.1
201 elmex 1.5 $self->disconnect (
202 elmex 1.2 sprintf (
203     "Error while reading from server '$self->{host}:$self->{port}':"
204     ."(%d|%s|%s)",
205     $err2, (Net::SSLeay::ERR_error_string $err), "$!")
206     );
207 elmex 1.1 return;
208     }
209     }
210     }
211    
212     sub write_data {
213     my ($self, $data) = @_;
214     #return unless $self->{r};
215    
216     my $cl = $self->{socket};
217 elmex 1.4 $self->{write_buffer} .= encode_utf8 ($data);
218 elmex 1.1
219     unless ($self->{w}) {
220 elmex 1.2 $self->{w} =
221     AnyEvent->io (poll => 'w', fh => $cl, cb => sub {
222     if (not $self->{ssl_enabled}) {
223 elmex 1.1 if (my $data = $self->{write_buffer}) {
224 elmex 1.10 $data = substr $data, 0, $self->{max_write_length};
225 elmex 1.1 my $len = syswrite $cl, $data;
226     unless ($len) {
227     return if $! == Errno::EAGAIN;
228     if (not defined $len) {
229     warn "error when writing data on $self->{host}:$self->{port}: $!";
230     return;
231     } else {
232     delete $self->{w};
233     }
234     }
235    
236     if ($len == length $self->{write_buffer}) {
237     delete $self->{w};
238     }
239    
240     $self->debug_wrote_data (substr $self->{write_buffer}, 0, $len);
241     $self->{write_buffer} = substr $self->{write_buffer}, $len;
242     }
243 elmex 1.2 } else {
244     $self->try_ssl_write;
245     }
246     });
247     }
248     }
249    
250     sub make_ssl_read_watcher {
251     my ($self, $poll) = @_;
252     return if $self->{r};
253 elmex 1.1
254 elmex 1.2 $poll ||= 'r';
255     $self->{r} =
256     AnyEvent->io (poll => $poll, fh => $self->{socket}, cb => sub {
257     $self->try_ssl_read;
258     });
259     }
260    
261     sub make_ssl_write_watcher {
262     my ($self, $poll) = @_;
263     return if $self->{w};
264    
265     $poll ||= 'w';
266     $self->{w} =
267     AnyEvent->io (poll => $poll, fh => $self->{socket}, cb => sub {
268     $self->try_ssl_write;
269     });
270 elmex 1.1 }
271    
272     sub enable_ssl {
273     my ($self) = @_;
274    
275     $Net::SSLeay::ssl_version = 10; # Insist on TLSv1
276    
277     $self->{ssl_enabled} = 1;
278    
279 elmex 1.3 #d# warn "START TLS!\n";
280 elmex 1.1
281     $self->{r} = undef;
282     $self->{w} = undef;
283    
284     $self->{ctx} = Net::SSLeay::CTX_new ();
285 elmex 1.3
286 elmex 1.2 # enable SSL_MODE_ENABLE_PARTIAL_WRITE and SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
287     Net::SSLeay::CTX_set_mode($self->{ctx}, 1 | 2);
288 elmex 1.3
289 elmex 1.1 $self->{ssl} = Net::SSLeay::new ($self->{ctx});
290    
291     Net::SSLeay::set_fd ($self->{ssl}, fileno $self->{socket});
292     #d# warn "CONNECT\n";
293     Net::SSLeay::connect $self->{ssl};
294     #d# warn "CONNECT END\n";
295     binmode $self->{socket}, ":bytes";
296    
297     $self->{ssl_read_data} = "";
298     $self->make_ssl_read_watcher;
299     }
300    
301 elmex 1.5 sub disconnect {
302     my ($self, $msg) = @_;
303     $self->end_sockets;
304     $self->{disconnect_cb}->($self->{host}, $self->{port}, $msg);
305     }
306    
307 elmex 1.1 1;