ably/ably-java

Connection remains open when close is sent immediately

Closed this issue · 2 comments

With the following basic code:


public class App {
public static void main(String[] args) throws AblyException{

	

    ClientOptions options = new ClientOptions();
    options.key = "<redacted>";

    AblyRealtime ably = new AblyRealtime(options);

    ably.connection.close();
    
    
}
}

The Ably connection state goes from connecting to closing but the client goes to connected after attempting a connection to a fallback host

(VERBOSE): io.ably.lib.transport.ConnectionManager: setState(): setting closing; reason {ErrorInfo message=Can't attach when not in an active state (See https://help.ably.io/error/10000) code=10000 statusCode=200 href=https://help.ably.io/error/10000}
New state is closing
(VERBOSE): io.ably.lib.transport.ConnectionManager: Requesting connection close
(DEBUG): io.ably.lib.transport.WebSocketTransport: send(); action = close
(VERBOSE): io.ably.lib.transport.WebSocketTransport: send(): close: {"action":7,"flags":0,"count":0,"timestamp":0}
(VERBOSE): io.ably.lib.transport.ConnectionManager: onTransportUnavailable()
(VERBOSE): io.ably.lib.http.HttpCore: HTTP request: https://internet-up.ably-realtime.com/is-the-internet-up.txt GET
(VERBOSE): io.ably.lib.http.HttpCore:   Ably-Agent: ably-java/1.2.39 jre/17.0.4.1
(VERBOSE): io.ably.lib.http.HttpCore:   Accept: application/json
(VERBOSE): io.ably.lib.http.HttpCore:   X-Ably-Version: 2
(DEBUG): io.ably.lib.transport.WebSocketTransport: onOpen()
(VERBOSE): io.ably.lib.http.HttpCore: HTTP response:
(VERBOSE): io.ably.lib.http.HttpCore: CF-RAY: 88e709c3ebbc93e3-LHR
(VERBOSE): io.ably.lib.http.HttpCore: Server: cloudflare
(VERBOSE): io.ably.lib.http.HttpCore: Connection: keep-alive
(VERBOSE): io.ably.lib.http.HttpCore: Last-Modified: Wed, 14 Jan 2015 16:12:25 GMT
(VERBOSE): io.ably.lib.http.HttpCore: x-amz-version-id: null
(VERBOSE): io.ably.lib.http.HttpCore: Date: Tue, 04 Jun 2024 09:53:50 GMT
(VERBOSE): io.ably.lib.http.HttpCore: Accept-Ranges: bytes
(VERBOSE): io.ably.lib.http.HttpCore: CF-Cache-Status: HIT
(VERBOSE): io.ably.lib.http.HttpCore: NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
(VERBOSE): io.ably.lib.http.HttpCore: ETag: "eb4585ad9fe0426781ed7c49252f8225"
(VERBOSE): io.ably.lib.http.HttpCore: Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=LhX%2BLA9NNeP%2B4d1%2B2VvHxnNDroqQtAPlqtTt%2BtOvaOHF72cSQj1mWMniW1ViYO4wSsOLwQGzPlZ5zhpSjsBcz5lcqU03DodukUErWi38ROBae6c35ecqopFOOUO2E243kg2xHGuY1yCtTNjoTJJg"}],"group":"cf-nel","max_age":604800}
(VERBOSE): io.ably.lib.http.HttpCore: x-amz-request-id: 3YJB4YRNQG910R4P
(VERBOSE): io.ably.lib.http.HttpCore: x-amz-id-2: dlZGe/Q8QrCKpoqmFeRWpBStC9dZIJpk1XeiIcu+Cd3S7y3mynf5KI3dvrS0R08cFHiFfxgFeXambnXxhEP0Gg==
(VERBOSE): io.ably.lib.http.HttpCore: Content-Length: 4
(VERBOSE): io.ably.lib.http.HttpCore: Age: 4850
(VERBOSE): io.ably.lib.http.HttpCore: Content-Type: text/plain
(VERBOSE): io.ably.lib.http.HttpCore: 
yes

(VERBOSE): io.ably.lib.transport.ConnectionManager: checkFallback: fallback to A.ably-realtime.com
(VERBOSE): io.ably.lib.transport.ConnectionManager: requestState(): requesting connecting; id = null
(VERBOSE): io.ably.lib.transport.ConnectionManager: setState(): setting connecting; reason null
New state is connecting
(DEBUG): io.ably.lib.transport.WebSocketTransport: close()
(DEBUG): io.ably.lib.transport.ITransport: getConnectParams: params = [key:<redacted>, v:2, format:msgpack, heartbeats:false, agent:ably-java/1.2.39 jre/17.0.4.1]
(DEBUG): io.ably.lib.transport.WebSocketTransport: connect(); wsUri = wss://A.ably-realtime.com:443/?key=<redacted>&v=2&format=msgpack&heartbeats=false&agent=ably-java%2F1.2.39%20jre%2F17.0.4.1
(DEBUG): io.ably.lib.transport.WebSocketTransport: onMessage(): msg (binary) = io.ably.lib.types.ProtocolMessage@4ec86158
(DEBUG): io.ably.lib.transport.WebSocketTransport: onClose(): wsCode = 1000; wsReason = ; remote = false
(VERBOSE): io.ably.lib.transport.ConnectionManager: onTransportUnavailable()
(VERBOSE): io.ably.lib.transport.ConnectionManager: onTransportUnavailable: ignoring disconnection event from superseded transport
(DEBUG): io.ably.lib.transport.WebSocketTransport: onOpen()
(DEBUG): io.ably.lib.transport.WebSocketTransport: onMessage(): msg (binary) = io.ably.lib.types.ProtocolMessage@d14a50a
(VERBOSE): io.ably.lib.transport.ConnectionManager: onMessage() (transport = io.ably.lib.transport.WebSocketTransport {wss://A.ably-realtime.com:443/?key=<redacted>&v=2&format=msgpack&heartbeats=false&agent=ably-java%2F1.2.39%20jre%2F17.0.4.1}): connected: {"action":4,"flags":0,"count":0,"connectionId":"<redacted>","timestamp":0,"connectionDetails":{"clientId":"*","connectionKey":"<redacted>","serverId":"frontend.defa.2.us-east-2-A.i-04e042ae33690ca68.86fcJIHfQBcdGR","maxMessageSize":65536,"maxInboundRate":75,"maxOutboundRate":75,"maxFrameSize":262144,"maxIdleInterval":15000,"connectionStateTtl":120000}}
(VERBOSE): io.ably.lib.transport.ConnectionManager: requestState(): requesting connected; id = <redacted>
(DEBUG): io.ably.lib.transport.ConnectionManager: Wait ended by action: io.ably.lib.transport.ConnectionManager$AsynchronousStateChangeAction@4c9d38fd
(VERBOSE): io.ably.lib.transport.ConnectionManager: setState(): setting connected; reason null

According to RTN12f

If the connection state is CONNECTING, moves immediately to CLOSING. If the connection attempt succeeds, ie. a CONNECTED ProtocolMessage arrives from Ably, then do as specified in RTN12a. If it doesn’t succeed, move to CLOSED.

Workaround is to wait for the connected event via a connection state listener

┆Issue is synchronized with this Jira Task by Unito

Since autoConnect is enabled by default, it will send CONNECT message before calling explicit close method. This means it will get to connected state once CONNECTED message is received. Also, I am not sure about order in which close msg is sent since both attach and close can be sent at the same time and there can be a race condition while checking channel state when close msg is sent.
Also, issue doesn't seem that critical.
Users will always get ably instance into CONNECTED state, perform some operation on it and then explicitly close the connection.

Btw, is this raised by customer @mclark-ably