user1361529
user1361529

Reputation: 2697

Perl: forking an WSS connection results in closure of the socket when child exits (doesn't happen with WS)

(Edited to provide a reduced test case as per comments below)

I'm facing an odd situation, where if I fork a "WSS" connection to send a message, the socket gets closed when the child exits. However, when I fork to process a "WS" connection, the connection remains open when the child exits.

Server code:

use Net::WebSocket::Server;
use IO::Socket::SSL;

$SIG{CHLD}='IGNORE';

my $enable_ssl = 1; # If you make this one the problem reveals itself

# you need to point this to your own certs
my $ssl_cert_file =  "/etc/letsencrypt/live/mydomain/fullchain.pem";
my $ssl_key_file =  "/etc/letsencrypt/live/mydomain/privkey.pem";

# To show the problem, all I'm doing is I'm forking and sending current time
sub process {
    my $serv = shift;
    my $pid = fork();
    if ($pid == 0 ) {
        print ("fork start\n");
        $_->send_utf8(time) for $serv->connections;
        print ("fork end\n");
        exit 0;
    }
}

my $ssl_server;

if ($ssl_enable) {
$ssl_server = IO::Socket::SSL->new(
              Listen        => 10,
              LocalPort     => 9000,
              Proto         => 'tcp',
              Reuse     => 1,
              ReuseAddr     => 1,
              SSL_cert_file => $ssl_cert_file,
              SSL_key_file  => $ssl_key_file
            );
 }

Net::WebSocket::Server->new(
    listen => $enable_ssl? $ssl_server: 9000,
    tick_period=>5,
    on_tick=> sub {
        my ($serv) = @_;
        process($serv);
        #$_->send_utf8(time) for $serv->connections;
    },
 )->start;

Here is the client code:

my $client = AnyEvent::WebSocket::Client->new;

# replace with your server
$client->connect("wss://myserver:9000")->cb(sub {
  our $connection = eval { shift->recv };
  if($@) {
    print ("connection error");
    warn $@;
    return;
  }

  # recieve message from the websocket...
  $connection->on(each_message => sub {
    my($connection, $message) = @_;
    my $msg = $message->body;
    print ("GOT $msg\n");
  });


});

AnyEvent->condvar->recv;

Expected behavior

The client will keep displaying timestamps

Observed behavior

The client gets the very first message and prints it. When the server exits its fork, the client stops getting any more messages and the connection terminates

How to make it work

We have two options:

  1. Don't fork in server. Send the message directly in process sub
  2. Don't use SSL

Therefore, my conclusion is SSL+fork == problem.

Thoughts?

Upvotes: 1

Views: 701

Answers (1)

Steffen Ullrich
Steffen Ullrich

Reputation: 123340

Therefore, my conclusion is SSL+fork == problem.

Yes, the problem is first doing the SSL handshake and then forking. This way a user space SSL state will be created in the parent and with the fork duplicated in the child and these two SSL states get out of sync on the first SSL data send or received. This means it is not possible to deal with the same SSL socket from two processes.

If it is really necessary that both parent and child process use the same SSL connection to the peer than the child must use the parent as a "proxy", i.e. the child does not communicate directly with the SSL peer but the child needs to communicate in plain with the parent (for example by using a socketpair) which then can forward the communication to the SSL peer. This way the SSL state is only maintained in the parent process.

But given that only a single message should be handled at a time for a single connection it might be possible instead to not fork for a single tick but fork a child for each connection which handles then all messages in this connection. In this case the SSL handshake can be done in full in the child by listening in the parent to a TCP and not SSL socket, forking on_connect and then upgrading the connection to SSL in the client using IO::Socket::start_SSL. This would also have the advantage that the blocking SSL handshake (which involves several round trips and thus takes some time) would be done in the forked child and would not make the parent block.

Upvotes: 3

Related Questions