Skip to content

Sometimes gnats server responds with a "PING" of its own instead of "OK" after initial connection, causing is_connected to never get set on the client. #93

@lemonkey

Description

@lemonkey

This can lead to situations where an attempt to connect via a NATS instance using await finishes waiting for the asynchronous connection to complete, yet the NATS instance is not formally connected via the is_connected property being set to True.

When the gnats server responds to a connect command with "OK", if we proceed to immediately write "PING", the server will respond with the expected "PONG".

But when the gnats server responds to a connect command with its own "PING", when we immediately write "PING", the next op from the io_reader buffer will be the server's own "PING" instead of the expected "PONG", which is what client.py is using to consider the client connected.

My proposed fix is to simply add an extra next_op = yield from self._io_reader.readline() after the main connect command is written, but before we send the client's "PING" to the server.

The value of next_op after the initial connection will still be either "OK" or sometimes "PING". In either case, after we write our own "PING" from the client and then drain the writer, the next op read from the io_reader buffer will consistenly be "OK" and then "PONG" (unless there was an actual problem with the client's PING from being recognized by the gnats server).

With the fix, example of when server responds with expected "OK" after initial connection:

client.py: writing connect_cmd [b'CONNECT {"auth_token": "xxxxxxx", "echo": true, "lang": "python3", "pedantic": true, "protocol": 1, "verbose": true, "version": "0.8.2"}\r\n']
client.py: next_op [b'+OK\r\n']
client.py: writing ping_proto [b'PING\r\n']
client.py: next_op [b'PONG\r\n']
client.py: client is_connected set to True!
client.py: process_connect_init() complete

With the fix, example of when server responds with unexpected "PING" after initial connection:

client.py: writing connect_cmd [b'CONNECT {"auth_token": "xxxxxxx", "echo": true, "lang": "python3", "pedantic": true, "protocol": 1, "verbose": true, "version": "0.8.2"}\r\n']
client.py: next_op [b'PING\r\n']
client.py: writing ping_proto [b'PING\r\n']
client.py: next_op [b'+OK\r\n']
client.py: next_op [b'PONG\r\n']
client.py: client is_connected set to True!
client.py: process_connect_init() complete

Note that in the above case, the "OK" is read from the io_reader because verbose is currently set to True.

Without the fix mentioned above, we can sometimes see:

client.py: writing connect_cmd [b'CONNECT {"auth_token": "xxxxxxx", "echo": true, "lang": "python3", "pedantic": true, "protocol": 1, "verbose": true, "version": "0.8.2"}\r\n']
client.py: writing ping_proto [b'PING\r\n']
client.py: next_op [b'PING\r\n']
client.py: client is_connected is NOT set to True!
client.py: process_connect_init() complete

https://github.com/nats-io/asyncio-nats/blob/a370dcc6733a3f32544568afe5885ffdf6c96905/nats/aio/client.py#L1311

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions