jjmoka has asked for the wisdom of the Perl Monks concerning the following question:

========================= SOLVED (update/solution at the end of this post)
I have 1 old MongoDB and 1 new Atlas cluster
I have for them 2 valid connection strings:
   MONGO_STRING='mongodb://user:password@host_1:27017,host_2:27017,host_3:27017/?authSource=admin&replicaSet=xxxx'
   ATLAS_STRING='mongodb://user:password@host_atlas:1024,host_atlas:1025,host_atlas:1026/?authSource=admin&replicaSet=xxxx&ssl=true'
I can connect successfully on the shell with both:
     > mongo "$MONGO_STRING"    # OK.1 (I'm in and I can operate on db & collections)
     > mongo "$ATLAS_STRING"    # OK.2 ( "  " ...)  
Just a few differences between the 2 strings though:

1. For MONGO: there are 3 distinct hosts listening on the same port (default 27017) and no ssl
2. For ATLAS: there is only 1 hostname, different ports and ssl on. The single host name actually maps 3 distinct hosts, as I can see if I nslookup it

            host_atlas        canonical name = ..........
            Name:   host_1
            Address: 10.65.4.239
            Name:   host_2
            Address: 10.65.76.221
            Name:   host_3
            Address: 10.65.154.200

I can connect also with a small Perl script which uses MongoDB

#file: test.Mongo.pl use strict; use warnings; use MongoDB; # also loads MongoDB::MongoClient use Data::Dumper; my $connect_string=$ARGV[0]; my $client = MongoDB::MongoClient->new({'host' => $connect_string}); my $doc = $client->get_database('mydb')->get_collection('mycoll')- +>find_one(); $client->disconnect; print Dumper $doc;
     > perl  test.Mongo.pl "$MONGO_STRING"    # OK.3 
     > perl  test.Mongo.pl "$ATLAS_STRING"    # OK.4 

I then try to use Mango, the driver under Mojolicious with the following script:

#file: test.Mango.pl use strict; use warnings; use Mango; use feature 'state'; use Data::Dumper; my $connect_string=$ARGV[0]; sub mango { state $m = Mango->new($connect_string)} my $doc = mango->db('mydb')->collection('mycoll')->find_one(); print Dumper $doc;
     > perl test.Mango.pl "$MONGO_STRING"    # OK.5 
     > perl test.Mango.pl "$ATLAS_STRING"    # ERROR:  Premature connection close at /var/..../local/lib/perl5/Mango/Cursor/Query.pm line 112. 

So I have one wrong scenario out of 6. The scenario with ssl=true via Mojolicious, internally managed via IO::Socket::SSL.

Running the last 2 cases with
export MOJO_EVENTEMITTER_DEBUG=1
I have some more insights:
> perl test.Mango.pl "$MONGO_STRING" #OK.5 -- Emit connect in Mojo::IOLoop::Client (1) -- Emit write in Mojo::IOLoop::Stream (0) -- Emit drain in Mojo::IOLoop::Stream (0) -- Emit read in Mojo::IOLoop::Stream (1) -- Emit write in Mojo::IOLoop::Stream (0) -- Emit drain in Mojo::IOLoop::Stream (0) -- Emit read in Mojo::IOLoop::Stream (1) -- Emit write in Mojo::IOLoop::Stream (0) -- Emit drain in Mojo::IOLoop::Stream (0) -- Emit read in Mojo::IOLoop::Stream (1) -- Emit write in Mojo::IOLoop::Stream (0) -- Emit drain in Mojo::IOLoop::Stream (0) -- Emit read in Mojo::IOLoop::Stream (1) -- Emit connection in Mango (0) -- Emit finish in Mojo::IOLoop::Delay (1) -- Emit write in Mojo::IOLoop::Stream (0) -- Emit drain in Mojo::IOLoop::Stream (0) -- Emit read in Mojo::IOLoop::Stream (1) $VAR1 = { '_id' => bless( { 'oid' => '_L+.\'' }, 'Mango::BSON::ObjectID' ), ..... };

> perl test.Mango.pl "$ATLAS_STRING" # ERROR: Premature connec +tion close at /var/..../local/lib/perl5/Mango/Cursor/Query.pm line 11 +2. -- Emit connect in Mojo::IOLoop::Client (1) -- Emit write in Mojo::IOLoop::Stream (0) -- Emit drain in Mojo::IOLoop::Stream (0) -- Emit close in Mojo::IOLoop::Stream (2) Premature connection close at /var/..../local/lib/perl5/Mango/Cursor/ +Query.pm line 112.
Debugging I have this point
Mojo::IOLoop::Stream::_read(/var/..../local/lib/perl5/Mojo/IOLoop/Stre +am.pm:103): 103: return if $! == EAGAIN || $! == EINTR || $! == EWOULDBLOCK; DB<8> x $! 0 'Connection reset by peer'

Any advice about what might inhibit the last scenario to keep the connection alive is very welcome.

Here some info about the server certificate in case they might be useful:

    > echo | openssl s_client -servername host_atlas -connect host_atlas:1024 2>/dev/null
CONNECTED(00000006)
---
Certificate chain
 0 s:CN = *.xxxx.mongodb.net
   i:C = US, O = Let's Encrypt, CN = R3
 1 s:C = US, O = Let's Encrypt, CN = R3
   i:C = US, O = Internet Security Research Group, CN = ISRG Root X1
---
Server certificate
-----BEGIN CERTIFICATE-----
EwJSMzAeFw0yMjAyMDYwNTE0MTdaFw0yMjA1MDcwNTE0MTZaMB4xHDAaBgNVBAMM
.....
YAgPUAnXVevMMyoFDuMs4kBwyLVDKwJKSl==
-----END CERTIFICATE-----
subject=CN = *.xxxx.mongodb.net

issuer=C = US, O = Let's Encrypt, CN = R3

---
Acceptable client certificate CA names
CN = 51275a38f58fe700ac6c85a8, OU = Atlas, O = MongoDB Inc
Client Certificate Types: RSA sign, DSA sign, ECDSA sign
Requested Signature Algorithms: RSA+SHA512:DSA+SHA512:ECDSA+SHA512:RSA+SHA384:DSA+SHA384:ECDSA+SHA384:RSA+SHA256:DSA+SHA256:ECDSA+SHA256:RSA+SHA224:DSA+SHA224:ECDSA+SHA224:RSA+SHA1:DSA+SHA1:ECDSA+SHA1
Shared Requested Signature Algorithms: RSA+SHA512:DSA+SHA512:ECDSA+SHA512:RSA+SHA384:DSA+SHA384:ECDSA+SHA384:RSA+SHA256:DSA+SHA256:ECDSA+SHA256:RSA+SHA224:DSA+SHA224:ECDSA+SHA224:RSA+SHA1:DSA+SHA1:ECDSA+SHA1
Peer signing digest: SHA256
Peer signature type: RSA
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 4031 bytes and written 472 bytes
Verification: OK
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 4096 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: 6AA99AF2921EB04DCED95A84108D4C019081F49A7BB4174B901EF6BBBDB64E54
    Session-ID-ctx:
    Master-Key: 57EF1F3C....64DC81D
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - 55 59 6f 27 90 00 ee 98-25 75 ef c6 46 a3 b1 ab   UYo'....%u..F...
    ....    
    00b0 - 8d 9b cd 1b 35 bf f2 22-77 70 0c ee 01 36 a3 b0   ....5.."wp...6..

    Start Time: 1646263624
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: no
---
Thank you.
========================= SOLUTION
I share what has sorted, in case anyone in the future should come here with a similar issue. The Mango from_string function
is not able to parse all the args within the command string (i.e. ?authSource=admin&replicaSet=xxxx&ssl=true)
authSource wouldn't be a problem anyhow because admin is the default.
But replicaSet and ssl are just ignored. They should be added as an optional arg. This how it worked:
my %extra; $extra{replica_set_name} = 'xxxx'; $extra{tls} = 1; #Note, it's "tls" (not "ssl" like it would be +in the connection string) sub mango { state $m = Mango->new($connect_string, %extra);}

Replies are listed 'Best First'.
Re: Mojolicious connection to Atlas ssl cluster fails: "Premature connection close"
by Anonymous Monk on Mar 03, 2022 at 06:28 UTC

    > So I have one wrong scenario out of 6. The scenario with ssl=true via Mojolicious, internally managed via IO::Socket::SSL.

    ...
    > openssl s_client -servername host_atlas -connect host_atlas:1024 CONNECTED(00000006)

    Did you tried connecting to failed host with IO::Socket::SSL yourself? Like:

    #!/usr/bin/env perl use strict; use IO::Socket::SSL; my $cl = IO::Socket::SSL->new('host_atlas:1024') or die "error=$!, ssl_error=$SSL_ERROR";
      thanks,
      yes, I tried to isolate some pieces with the following snippets:
      use IO::Socket::SSL; my $client = IO::Socket::SSL->new('host_atlas:1024') or die "error=$!, ssl_error=$SSL_ERROR"; print 'done';
      gives
         >perl test.pl
          done%
      
      use Mojo::IOLoop::Client; my $client = Mojo::IOLoop::Client->new; $client->on(connect => sub ($client, $handle) {print "on_connect\n"}); $client->on(error => sub ($client, $err) {print "on_error\n"}); $client->connect({address => 'host_atlas', port => 1024}); $client->reactor->start unless $client->reactor->is_running; print 'done';
      gives
         >perl test.pl
         -- Emit connect in Mojo::IOLoop::Client (1)
         on_connect
         done%
      
      use feature 'state'; use Mango; use Mojo::IOLoop::Client; my $connect_string=$ARGV[0]; sub mango { state $m = Mango->new($connect_string);} mango->db('mydb')->list_collections(sub { my ($db, $err, $cursor) = @_; print "YESSSSSSSSSSS !!!!\n"; }); Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
      gives
         >perl test.pl "$ATLAS_STRING"
         -- Emit connect in Mojo::IOLoop::Client (1)
         -- Emit write in Mojo::IOLoop::Stream (0)
         -- Emit drain in Mojo::IOLoop::Stream (0)
         -- Emit close in Mojo::IOLoop::Stream (2)
         -- Emit error in Mojo::Reactor::Poll (1)
         Mojo::Reactor::Poll: I/O watcher failed: Can't use an undefined value as an ARRAY reference at /var/..../local/lib/perl5/Mango/Cursor.pm line 11.
         done%
      

        One more check: the only thing that differs between OK and FAIL tests - is SSL? Or there are different MongoDB versions? MongoDB is not friendly to community, and I think there could be changes in versions which will break compatibility, so versions of servers is important (at least to know it they differs or same).

        If I were you, first thing to do is to search call stack sub by sub to find where this error comes from, placing debugging prints everywhere. Then I would try to file an issue (https://github.com/oliwer/mango/issues). If filing an issue will not resolve the problem, try to reach Sebastian Riedel himself on mojo IRC (https://web.libera.chat/#mojo) and ask him what to do. I've done it myself when stuck in Mojolicious. He have sharp tongue, but what could you do? And then, if nothing helps, you can always return to call stack one more time, and try to find the cause yourself. Who knows, where this adventure will lead you to?

      Oops, was not logged in.