2 # Before `make install' is performed this script should be runnable with
3 # `make test'. After `make install' it should work as `perl t/nonblock.t'
10 use Errno qw( EAGAIN EINPROGRESS EPIPE ECONNRESET );
13 use vars qw( $SSL_SERVER_ADDR );
14 do "t/ssl_settings.req" || do "ssl_settings.req";
16 if ( ! eval "use 5.006; use IO::Select; return 1" ) {
17 print "1..0 # Skipped: no support for nonblocking sockets\n";
20 if ( grep { $^O =~m{$_} } qw( MacOS VOS vmesa riscos amigaos ) ) {
21 print "1..0 # Skipped: fork not implemented on this platform\n";
25 $SIG{PIPE} = 'IGNORE'; # use EPIPE not signal handler
30 #################################################################
31 # create Server socket before forking client, so that it is
32 # guaranteed to be listening
33 #################################################################
34 my %extra_options = $Net::SSLeay::VERSION>=1.16 ?
36 SSL_key_file => "certs/client-key.enc",
37 SSL_passwd_cb => sub { return "opossum" }
39 SSL_key_file => "certs/client-key.pem"
43 # first create simple non-blocking tcp-server
45 my $server = IO::Socket::INET->new(
47 LocalAddr => $SSL_SERVER_ADDR,
52 print "not ok: $!\n", exit if !$server; # Address in use?
53 ok("Server Initialization");
55 my ($SSL_SERVER_PORT) = unpack_sockaddr_in( $server->sockname );
57 defined( my $pid = fork() ) || die $!;
60 ############################################################
61 # CLIENT == child process
62 ############################################################
66 my %extra_options = $Net::SSLeay::VERSION>=1.16 ?
68 SSL_key_file => "certs/server-key.enc",
69 SSL_passwd_cb => sub { return "bluebell" },
71 SSL_key_file => "certs/server-key.pem"
74 # fast: try connect_SSL immediatly after sending plain text
75 # connect_SSL should fail on the first attempt because server
77 # slow: wait before calling connect_SSL
78 # connect_SSL should succeed, because server was already waiting
80 for my $test ( 'fast','slow' ) {
82 # initial socket is unconnected, tcp, nonblocking
83 my $to_server = IO::Socket::INET->new( Proto => 'tcp', Blocking => 0 );
85 my $server_addr = pack_sockaddr_in(
87 inet_aton( $SSL_SERVER_ADDR )
90 # nonblocking connect of tcp socket
92 $to_server->connect( $server_addr ) && last;
93 if ( $! == EINPROGRESS ) {
94 diag( 'connect in progress' );
95 IO::Select->new( $to_server )->can_read(30) && next;
99 diag( 'connect failed: '.$! );
103 ok( "client tcp connect" );
105 # send some plain text on non-ssl socket
106 syswrite( $to_server,'plaintext' ) || print "not ";
107 ok( "write plain text" );
109 # let server catch up, so that it awaits my connection
110 # so that connect_SSL does not have to wait
111 sleep(5) if ( $test eq 'slow' );
113 # upgrade to SSL socket w/o connection yet
114 if ( ! IO::Socket::SSL->start_SSL( $to_server,
115 SSL_startHandshake => 0,
116 SSL_version => 'TLSv1',
117 SSL_cipher_list => 'HIGH',
120 diag( 'start_SSL return undef' );
122 } elsif ( !UNIVERSAL::isa( $to_server,'IO::Socket::SSL' ) ) {
123 diag( 'failed to upgrade socket' );
126 ok( "upgrade client to IO::Socket::SSL" );
128 # SSL handshake thru connect_SSL
129 # if $test eq 'fast' we expect one failed attempt because server
130 # did not call accept_SSL yet
133 $to_server->connect_SSL && last;
135 if ( $SSL_ERROR == SSL_WANT_READ ) {
137 IO::Select->new($to_server)->can_read(30) && next; # retry if can read
138 } elsif ( $SSL_ERROR == SSL_WANT_WRITE ) {
139 IO::Select->new($to_server)->can_write(30) && next; # retry if can write
141 diag( "failed to connect: ".$to_server->errstr );
147 if ( $test ne 'slow' ) {
148 print "not " if !$attempts;
149 ok( "nonblocking connect with $attempts attempts" );
153 # we send up to 100000 bytes, server reads first 10 bytes and then sleeps
154 # before reading more. In total server only reads 30000 bytes
155 # the sleep will cause the internal buffers to fill up so that the syswrite
156 # should return with EAGAIN+SSL_WANT_WRITE.
157 # the socket close should cause EPIPE or ECONNRESET
159 my $msg = "1234567890";
163 # set send buffer to 8192 so it will definitly fail writing all 100000 bytes in it
164 # linux allocates twice as much (see tcp(7)) but it's small enough anyway
166 setsockopt( $to_server, SOL_SOCKET, SO_SNDBUF, pack( "I",8192 ));
167 diag( "sndbuf=".unpack( "I",getsockopt( $to_server, SOL_SOCKET, SO_SNDBUF )));
171 # the next test might fail because setsockopt(... SO_SNDBUF...) failed
172 $test_might_fail = 1;
176 for( my $i=0;$i<10000;$i++ ) {
179 my $n = syswrite( $to_server,$msg,length($msg)-$offset,$offset );
180 if ( !defined($n) ) {
181 diag( "\$!=$! \$SSL_ERROR=$SSL_ERROR send=$bytes_send" );
182 if ( $! == EAGAIN ) {
183 if ( $SSL_ERROR == SSL_WANT_WRITE ) {
184 diag( 'wait for write' );
186 IO::Select->new($to_server)->can_write(30);
187 diag( "can write again" );
188 } elsif ( $SSL_ERROR == SSL_WANT_READ ) {
189 diag( 'wait for read' );
190 IO::Select->new($to_server)->can_read(30);
192 } elsif ( ( $! == EPIPE || $! == ECONNRESET ) && $bytes_send > 30000 ) {
193 diag( "connection closed hard" );
200 } elsif ( $n == 0 ) {
201 diag( "connection closed" );
204 diag( "syswrite returned $n!" );
210 if ( $n + $offset == 10 ) {
214 diag( "partial write of $n new offset=$offset" );
221 if ( $test_might_fail ) {
222 ok( " write attempts failed, but OK nevertheless because setsockopt failed" );
224 print "not " if !$attempts;
227 ok( "multiple write attempts" );
230 print "not " if $bytes_send < 30000;
231 ok( "30000 bytes send" );
236 ############################################################
237 # SERVER == parent process
238 ############################################################
240 # pendant to tests in client. Where client is slow (sleep
241 # between plain text sending and connect_SSL) I need to
242 # be fast and where client is fast I need to be slow (sleep
243 # between receiving plain text and accept_SSL)
245 foreach my $test ( 'slow','fast' ) {
247 # accept a connection
248 IO::Select->new( $server )->can_read(30);
249 my $from_client = $server->accept or print "not ";
252 diag( "failed to accept: $!" );
256 # make client non-blocking!
257 $from_client->blocking(0);
259 # read plain text data
262 sysread( $from_client, $buf,9 ) && last;
263 die "sysread failed: $!" if $! != EAGAIN;
264 IO::Select->new( $from_client )->can_read(30);
266 $buf eq 'plaintext' || print "not ";
267 ok( "received plain text" );
269 # upgrade socket to IO::Socket::SSL
271 if ( ! IO::Socket::SSL->start_SSL( $from_client,
272 SSL_startHandshake => 0,
274 SSL_verify_mode => 0x00,
275 SSL_ca_file => "certs/test-ca.pem",
277 SSL_cert_file => "certs/client-cert.pem",
278 SSL_version => 'TLSv1',
279 SSL_cipher_list => 'HIGH',
282 diag( 'start_SSL return undef' );
284 } elsif ( !UNIVERSAL::isa( $from_client,'IO::Socket::SSL' ) ) {
285 diag( 'failed to upgrade socket' );
288 ok( "upgrade to_client to IO::Socket::SSL" );
290 sleep(5) if $test eq 'slow'; # wait until client calls connect_SSL
292 # SSL handshake thru accept_SSL
293 # if test is 'fast' (e.g. client is 'slow') we excpect the first
294 # accept_SSL attempt to fail because client did not call connect_SSL yet
297 $from_client->accept_SSL && last;
299 if ( $SSL_ERROR == SSL_WANT_READ ) {
301 IO::Select->new($from_client)->can_read(30) && next; # retry if can read
302 } elsif ( $SSL_ERROR == SSL_WANT_WRITE ) {
304 IO::Select->new($from_client)->can_write(30) && next; # retry if can write
306 diag( "failed to accept: ".$from_client->errstr );
310 ok( "ssl accept handshake done" );
312 if ( $test eq 'fast' ) {
313 print "not " if !$attempts;
314 ok( "nonblocking accept_SSL with $attempts attempts" );
318 # then sleeping so that buffers from client to server gets
319 # filled up and clients receives EAGAIN+SSL_WANT_WRITE
321 IO::Select->new( $from_client )->can_read(30);
322 ( sysread( $from_client, $buf,10 ) == 10 ) || print "not ";
324 ok( "received client message" );
327 my $bytes_received = 10;
329 # read up to 30000 bytes from client, then close the socket
331 while ( ( my $diff = 30000 - $bytes_received ) > 0 ) {
332 my $n = sysread( $from_client,my $buf,$diff );
333 if ( !defined($n) ) {
334 diag( "\$!=$! \$SSL_ERROR=$SSL_ERROR" );
335 if ( $! == EAGAIN ) {
336 if ( $SSL_ERROR == SSL_WANT_READ ) {
338 IO::Select->new($from_client)->can_read(30);
339 } elsif ( $SSL_ERROR == SSL_WANT_WRITE ) {
341 IO::Select->new($from_client)->can_write(30);
348 } elsif ( $n == 0 ) {
349 diag( "connection closed" );
352 diag( "sysread returned $n!" );
357 $bytes_received += $n;
358 diag( "read of $n bytes" );
361 diag( "read $bytes_received" );
365 # wait until client exits
373 sub ok { print "ok # [$ID] @_\n"; }
374 sub diag { print "# @_\n" }