diff --git a/content/client-lib-development-guide/features.textile b/content/client-lib-development-guide/features.textile index 08e18f8e2f..bf1a49cb4d 100644 --- a/content/client-lib-development-guide/features.textile +++ b/content/client-lib-development-guide/features.textile @@ -54,7 +54,7 @@ h2(#test-guidelines). Test guidelines * @(G1)@ Every test should be executed using all supported protocols (i.e. JSON and "MessagePack":https://msgpack.org/ if supported). This includes both sending & receiving data * @(G2)@ All tests by default are run against a special Ably sandbox environment. This environment allows apps to be provisioned without any authentication that can then be used for client library testing. Bear in mind that all apps created in the sandbox environment are automatically deleted after 60 minutes and have low limits to prevent abuse. Apps are configured by sending a @POST@ request to @https://sandbox-rest.ably.io/apps@ with a JSON body that specifies the keys and their associated capabilities, channel namespace rules and any presence fixture data that is required; see "ably-common test-app-setup.json":https://github.com/ably/ably-common/blob/main/test-resources/test-app-setup.json. Presence fixture data is necessary for the REST library presence tests as there is no way to register presence on a channel in the REST library * @(G3)@ Testing statistics can be tricky due to timing issues and slow test suites as a result of sending requests to generate statistics. As such, we provide a special stats endpoint in our sandbox environment that allows stats to be injected into our metrics system so that stats tests can make predictable assertions. To create stats you must send an authenticated @POST@ request to the stats JSON to @https://sandbox-rest.ably.io/stats@ with the stats data you wish to create. See the "JavaScript stats fixture":https://github.com/ably/ably-js/blob/4e65d4e13eb8750a375b9511e4dd059092c0e481/spec/rest/stats.test.js#L8-L51 and "setup helper":https://github.com/ably/ably-js/blob/4e65d4e13eb8750a375b9511e4dd059092c0e481/spec/common/modules/testapp_manager.js#L158-L182 as an example -* @(G4)@ This spec defines API version 1.2. A client library must identify to Ably the version of the spec it uses in all requests and connections, per "RSC7a":#RSC7a and "RTN2f":#RTN2f. The spec it uses is defined as the latest API version for which the library implements all spec items relating to the wire protocol +* @(G4)@ This spec defines API version 2.0. A client library must identify to Ably the version of the spec it uses in all requests and connections, per "RSC7a":#RSC7a and "RTN2f":#RTN2f. The spec it uses is defined as the latest API version for which the library implements all spec items relating to the wire protocol h2(#rest). REST client library @@ -79,7 +79,7 @@ h3(#restclient). RestClient * @(RSC21)@ @RestClient#push@ attribute provides access to the @Push@ object that was instantiated with the @ClientOptions@ provided in the @RestClient@ constructor * @(RSC22)@ @RestClient#batch@ attribute provides access to the @BatchOperations@ object that was instantiated with the @ClientOptions@ provided in the @RestClient@ constructor * @(RSC7)@ Sends REST requests over HTTP and HTTPS to the REST endpoint @rest.ably.io@ -** @(RSC7a)@ The header @X-Ably-Version: 1.2@ must be included in all REST requests to the Ably endpoint +** @(RSC7a)@ The header @X-Ably-Version: 2.0@ must be included in all REST requests to the Ably endpoint ** @(RSC7b)@ (Please note this clause and the associated header have now been superseded by "RCS7d":#RSC7d) The header @X-Ably-Lib: [lib][.optional variant]?-[version]@ should be included in all REST requests to the Ably endpoint where @[lib]@ is the name of the library such as @js@ for @ably-js@, @[.optional variant]@ is an optional library variant, such as @laravel@ for the @php@ library, which is always delimited with a period such as @php.laravel@, and where @[version]@ is the full client library version using "Semver":http://semver.org/ such as @1.0.2@. For example, the 1.0.0 version of the JavaScript library would use the header @X-Ably-Lib: js-1.0.0@. *** @(RSC7b1)@ When it is not possible to send the @X-Ably-Lib@ header, such as for @JSONP@ requests, the library version should be sent as a query param such as @lib=js-1.0.0@ ** @(RSC7c)@ If the @addRequestIds@ client option is enabled, every REST request to Ably should include in a @request_id@ query string parameter a random string obtained by url-safe base64-encoding a sequence of at least 9 bytes obtained from a source of randomness. This request ID must remain the same if a request is retried to a fallback host per @RSC15@. Any log messages associated with the request should include the request ID. If the request fails, the request ID must be included in the @ErrorInfo@ returned to the user. @@ -448,9 +448,7 @@ h3(#realtime-connection). Connection * @(RTN9)@ @Connection#key@ attribute: ** @(RTN9a)@ Is unset until connected ** @(RTN9b)@ Is a unique private connection key provided by Ably that is used to reconnect and retain connection state following an unexpected disconnection. You should have a test to ensure multiple connected clients have unique connection keys -* @(RTN10)@ @Connection#serial@ attribute: -** @(RTN10a)@ Is unset until connected, and is then set from the @connectionSerial@ attribute of the @CONNECTED@ @ProtocolMessage@ -** @(RTN10b)@ Continues to be updated by every @ProtocolMessage@ received from Ably that contains a @connectionSerial@. A test should exist that checks that the serial is updated when a @ProtocolMessage@ is received with the value from that @ProtocolMessage@. It should not otherwise change; in particular, unlike the library-internal @msgSerial@, it should not change as a result of publishing a message or receiving an @ACK@. +* @(RTN10)@ This clause has been removed as of the 2.0 spec. * @(RTN11)@ @Connection#connect@ function: ** @(RTN11a)@ Explicitly connects to the Ably service if not already connected ** @(RTN11b)@ If the state is @CLOSING@, the client should make a new connection with a new transport instance and remove all references to the old one. In particular, it should make sure that, when the @CLOSED@ @ProtocolMessage@ arrives for the old connection, it doesn't affect the new one. @@ -485,29 +483,38 @@ h3(#realtime-connection). Connection ** @(RTN15g)@ Connection state is only maintained server-side for a brief period, given by the @connectionStateTtl@ in the @connectionDetails@, see "CD2f":#CD2f. If a client has been disconnected for longer than the @connectionStateTtl@, it should not attempt to resume. Instead, it should clear the local connection state, and any connection attempts should be made as for a fresh connection *** @(RTN15g1)@ This check should be made before each connection attempt. It is generally not sufficient to merely clear the connection state when moving to @SUSPENDED@ state (though that may be done too), since the device may have been sleeping / suspended, in which case it may have been many hours since it was last actually connected, even though, having been in the @CONNECTED@ state when it was put to sleep, it has only moved out of that state very recently (after waking up and noticing it's no longer connected) *** @(RTN15g2)@ Another consequence of that is that the measure of whether the client been disconnected for too long (for the purpose of this check) cannot just be whether the client left the @CONNECTED@ state more than @connectionStateTtl@ ago. Instead, it should be whether the difference between the current time and the last activity time is greater than the sum of the @connectionStateTtl@ and the @maxIdleInterval@, where the last activity time is the time of the last known actual sign of activity from Ably per "RTN23a":#RTN23a -*** @(RTN15g3)@ When a connection attempt succeeds after the connection state has been cleared in this way, channels that were previously @ATTACHED@, @ATTACHING@, or @SUSPENDED@ must be automatically reattached, just as if the connection was a resume attempt which failed per "RTN15c3":#RTN15c3 -** @(RTN15b)@ In order for a connection to be resumed and connection state to be recovered, the client must have received a @CONNECTED@ ProtocolMessage which will include a private connection key. To resume that connection, the library reconnects to the "websocket":https://ably.com/topic/websockets endpoint with two additional querystring params: +*** @(RTN15g3)@ When a connection attempt succeeds after the connection state has been cleared in this way, channels that were previously @ATTACHED@, @ATTACHING@, or @SUSPENDED@ must be automatically reattached, just as if the connection was a resume attempt which failed per "RTN15c7":#RTN15c7 +** @(RTN15b)@ In order for a connection to be resumed and connection state to be recovered, the client must have received a @CONNECTED@ ProtocolMessage which will include a private connection key. To resume that connection, the library reconnects to the "websocket":https://ably.com/topic/websockets endpoint with an additional querystring param: *** @(RTN15b1)@ @resume@ is the @ProtocolMessage#connectionKey@ from the most recent @CONNECTED@ @ProtocolMessage@ received -*** @(RTN15b2)@ @connectionSerial@ is the most recent @ProtocolMessage#connectionSerial@ (from any message, not just a @CONNECTED@) received from Ably (equivalently, the @Connection#serial@) +*** @(RTN15b2)@ This clause has been removed as of the 2.0 spec. ** @(RTN15c)@ The system's response to a resume request will be one of the following: -**** @(RTN15c1)@ @CONNECTED@ @ProtocolMessage@ with the same @connectionId@ as the current client, and no @error@. In this case, the server is indicating that the resume succeeded, all channels are still attached, and all backlog messages are available. The client should not change the state of attached channels, and immediately process any messages queued by the connection -**** @(RTN15c2)@ @CONNECTED@ @ProtocolMessage@ with the same @connectionId@ as the current client, and an @error@. In this case, the server is indicating that the resume succeeded but with a non-fatal error, all channels are still attached, and some backlog messages may be unavailable. The @ErrorInfo@ received should be set as the @reason@ in the @CONNECTED@ event, and the @Connection#errorReason@ should be set. The client should not change the state of attached channels, and immediately process any messages queued by the connection. Any channels that are not resumed in full may receive an @ATTACHED@ @ProtocolMessage@ with an @error@, see "RTL12":#RTL12 -**** @(RTN15c3)@ @CONNECTED@ @ProtocolMessage@ with a new @connectionId@ (and usually an @ErrorInfo@ in the @error@ field). In this case, a new connection has been established, the resume was unsuccessful, the channels are no longer attached, and the error indicates the cause of the unsuccessful resume. The @error@ if present should be set as the @reason@ in the @CONNECTED@ event, and as the @Connection#errorReason@. The client library should initiate an attach for channels that are in the @SUSPENDED@ state. For all channels in the @ATTACHING@ or @ATTACHED@ state, the client library should initiate a new attach (i.e. a new @ATTACH@ @ProtocolMessage@ sent for each channel). Finally, the internal @msgSerial@ counter is reset so that the first message published to Ably will contain a @msgSerial@ value of @0@ +**** @(RTN15c6)@ A @CONNECTED@ @ProtocolMessage@ with the same @connectionId@ as the current client (and no @error@ property). This indicates that the resume attempt was valid. The client library should move all channels that were in the @ATTACHING@, @ATTACHED@, or @SUSPENDED@ states to the @ATTACHING@ state, and initiate an @RTL4c@ attach sequence for each. The connection should also process any messages queued per @RTL6c2@ (there is no need to wait for the attaches to finish before processing queued messages). +**** @(RTN15c7)@ @CONNECTED@ @ProtocolMessage@ with a new @connectionId@ and an @ErrorInfo@ in the @error@ field. In this case, the resume was invalid, and the error indicates the cause. The @error@ should be set as the @reason@ in the @CONNECTED@ event, and as the @Connection#errorReason@. The internal @msgSerial@ counter should be reset so that the first message published to Ably will contain a @msgSerial@ of @0@. Otherwise the process is the same as per @RTN16c6@: The client library should move all channels that were in the @ATTACHING@, @ATTACHED@, or @SUSPENDED@ states to the @ATTACHING@ state, and initiate an @RTL4c@ attach sequence for each. The connection should also process any messages queued per @RTL6c2@. **** @(RTN15c5)@ @ERROR@ @ProtocolMessage@ indicating a failure to authenticate as a result of a token error (see "RTN15h":#RTN15h). The transport will be closed by the server. The spec described in "RTN15h":#RTN15h must be followed for a connection being resumed with a token error **** @(RTN15c4)@ Any other @ERROR@ @ProtocolMessage@ indicating a fatal error in the connection. The server will close the transport immediately after. The client should transition to the @FAILED@ state triggering all attached channels to transition to the @FAILED@ state as well. Additionally the @Connection#errorReason@ will be set should be set with the error received from Ably -** @(RTN15f)@ @ACK@ and @NACK@ responses for published messages can only ever be received on the transport connection on which those messages were sent. Therefore, once a transport drops, the client library must re-attempt by re-sending the messages on a new transport if the resume was successful (i.e. the @CONNECTED@ response includes the expected @connectionId@) as mandated by "RTN19":#RTN19. +**** @(RTN15c1)@ This clause has been replaced by RTN15c6 as of the 2.0 spec. +**** @(RTN15c2)@ This clause has been replaced by RTN15c7 as of the 2.0 spec. +**** @(RTN15c3)@ This clause has been replaced by RTN15c7 as of the 2.0 spec. +** @(RTN15f)@ This clause has been removed of the 2.0 spec (redundant to "RTN19a":#RTN19a). ** @(RTN15d)@ Client libraries should have test coverage to ensure connection state recovery is working as expected by forcibly disconnecting a client and checking that messages published on channels are delivered once the connection is resumed ** @(RTN15e)@ When a connection is resumed, the @Connection#key@ may change and will be provided in the first @CONNECTED@ @ProtocolMessage#connectionDetails@ when the connection is established. The client library must update the @Connection#key@ value with the new @connectionKey@ value every time * @(RTN20)@ When the client library can subscribe to the Operating System events for network/internet connectivity changes: ** @(RTN20a)@ When @CONNECTED@, @CONNECTING@ or @DISCONNECTING@, if the operating system indicates that the underlying internet connection is no longer available, then the client library should immediately transition the state to @DISCONNECTED@ with emit a state change with an appropriate @reason@. This state change will automatically trigger the client library to attempt to reconnect, see @RTN15@ above ** @(RTN20b)@ When @DISCONNECTED@ or @SUSPENDED@, if the operating system indicates that the underlying internet connection is now available, the client library should immediately attempt to connect * @(RTN16)@ @Connection@ recovery: -** @(RTN16a)@ Connection recovery follows the resume spec "RTN15c":#RTN15c in respect to the expected response from the server. However, connection recovery is different in that the library has no state at the time of connection and recovers the connection based as a result of @recover@ key being explicitly provided to the Realtime library when instantiated. Once a connection is recovered, all channels must be explicitly attached by the developer -** @(RTN16b)@ @Connection#recoveryKey@ is an attribute composed of the @connectionKey@, and the latest @connectionSerial@ received on the connection, and the current @msgSerial@ -** @(RTN16c)@ @Connection#recoveryKey@ becomes @Null@ when a connection is explicitly @CLOSED@ or @CLOSED@ by the server, as connection state is not retained for connections closed intentionally. The @Connection#key@ and @Connection#id@ is set to @Null@ -** @(RTN16d)@ When a connection is successfully recovered, the @Connection#id@ will be identical to the @id@ of the connection that was recovered, and @Connection#key@ will always be updated to the @ConnectionDetails#connectionKey@ provided in the first @CONNECTED@ @ProtocolMessage@ -** @(RTN16e)@ If the @recover@ option is missing or no longer valid when connecting to Ably, the client will connect anyway, but emit a @ConnectionStateChange@ with a @reason@, and will additionally set the @Connection#errorReason@ with an @ErrorInfo@ object describing the failure -** @(RTN16f)@ The @msgSerial@ component of the @recoveryKey@, unlike the other two components, is not sent to Ably, but rather is used to set the library internal @msgSerial@. (If the recover fails, the counter should be reset to 0 per "RTN15c3":#RTN15c3 ) +** @(RTN16i)@ Connection recovery is similar to connection resumption (see "RTN15c":#RTN15c), except that instead of the library resuming from a time at which that library instance was previously connected, it is doing so from external state provided in the client options, "(TO3i)":#TO3i. Since the library has no state at the time of connection, the channels must be explicitly attached by the user; continuity preservation is achieved by the @channelSerial@s for each channel being stored in the recovery key. +** @(RTN16f)@ When a library is instantiated with the @recover@ client option, it should initialize its internal @msgSerial@ counter to the @msgSerial@ component of the @recoveryKey@. (If the recover fails, the counter should be reset to 0 per "RTN15c7":#RTN15c7 ) +** @(RTN16j)@ When a library is instantiated with the @recover@ client option, for every channel/channelSerial pair in the @recoveryKey@, it should instantiate a corresponding channel and set its "RTL15b":#RTL15b @channelSerial@. +** @(RTN16k)@ When the library first connects to Ably after being instantiated with a @recover@ client option, it should add an additional @recover@ querystring param to the websocket request, set from the @connectionKey@ component of the @recoveryKey@. Once the library has successfully connected to Ably, it should never again supply a @recover@ querystring param. +** @(RTN16g)@ @Connection#getRecoveryKey@ is function that returns a string which incorporates the @connectionKey@, the current @msgSerial@, and a collection of pairs of channel @name@ and current @channelSerial@ for every currently attached channel. This must be serialized in a way which is able to encode any unicode channel name. The SDK may assume that the recovery key will only be consumed by the same type of SDK, so this spec does not specify any particular serialization; however, the format should be forward-compatible through the same major version of the SDK. +*** @(RTN16g1)@ SDKs which choose not to increment their public major version when implementing protocol v2.0 may continue to implement this as a @Connection#recoveryKey@ attribute until the next major version change, using a getter function that's backwards-compatible with attribute access where possible, else by recomputing the recovery key string on every message. +** @(RTN16h)@ @Connection#getRecoveryKey@ should return @Null@ when a the SDK is in the @CLOSED@, @CLOSING@, @FAILED@, or @SUSPENDED@ states, or when it does not have a @connectionKey@ (for example, it has not yet become connected). The @Connection#key@ and @Connection#id@ should also be @Null@ at these times. +** @(RTN16d)@ The library may wish to test that after a connection has been successfully recovered, the @Connection#id@ should be identical to the @id@ of the connection that was recovered, and @Connection#key@ should have been updated to the @ConnectionDetails#connectionKey@ provided in the @CONNECTED@ @ProtocolMessage@. +** @(RTN16l)@ Recovery failures should be handled identically to resume failures, per "RTN15c7":#RTN15c7, "RTN15c5":#RTN15c5, and "RTN15c4":#RTN15c4. +** @(RTN16a)@ This clause has been replaced by RTN16i as of the 2.0 spec. +** @(RTN16b)@ This clause has been removed as of the 2.0 spec and replaced with @RTN16g@. +** @(RTN16c)@ This clause has been replaced by RTN16h as of the 2.0 spec. +** @(RTN16e)@ This clause has been replaced by RTN16l as of the 2.0 spec. * @(RTN17)@ Host Fallback ** @(RTN17b)@ The fallback behavior described by this section, "RTN17":#RTN17, only applies when either: *** @(RTN17b1)@ @ClientOptions#realtimeHost@ has not been set to an explicit value (see "RTC1d":#RTC1d) and @ClientOptions#port@ is not set and @ClientOptions#tlsPort@ is not set @@ -519,12 +526,14 @@ h3(#realtime-connection). Connection ** @(RTN17e)@ If the realtime client is connected to a fallback host endpoint, then for the duration that the transport is connected to that host, all HTTP requests, such as history or token requests, should be first attempted to the same datacenter the realtime connection is established with i.e. the same fallback host must be used as the default HTTP request host. If however the HTTP request against that fallback host fails, then the normal fallback host behavior should be followed attempting the request against another fallback host as described in "RSC15":#RSC15 * @(RTN19)@ Transport state side effects - when a transport is disconnected for any reason: ** @(RTN19a)@ Any @ProtocolMessage@ that is awaiting an @ACK@/@NACK@ on the old transport will not receive the @ACK@/@NACK@ on the new transport. The client library must therefore resend any @ProtocolMessage@ that is awaiting a @ACK@/@NACK@ to Ably in order to receive the expected @ACK@/@NACK@ for that message. The Ably service is responsible for keeping track of messages, ignoring duplicates and responding with suitable @ACK@/@NACK@ messages +*** @(RTN19a1)@ One possible implementation of this requirement would be to add any in-flight messages to the @RTL6c2@ connection-wide queue of messages that will be sent once the connection next becomes @CONNECTED@ +*** @(RTN19a2)@ The @msgSerial@ of the reattempted @ProtocolMessage@s must remain the same as for the original attempt ** @(RTN19b)@ If there are any pending channels i.e. in the @ATTACHING@ or @DETACHING@ state, the respective @ATTACH@ or @DETACH@ message should be resent to Ably * @(RTN23)@ Heartbeats ** @(RTN23a)@ If a transport does not receive any indication of activity on a transport for a period greater than the sum of the @maxIdleInterval@ (which will be sent in the @connectionDetails@ of the most recent @CONNECTED@ message received on that transport) and the @realtimeRequestTimeout@, that transport should be disconnected. Any message (or non-message indicator, see @RTN23b@) received counts as an indication of activity and should reset the timer, not merely heartbeat messages. However, it must be received (that is, sent from the server to the client); client-sent data does not count. ** @(RTN23b)@ When initiating a connection, the client may send a @heartbeats@ param in the querystring, with value @true@ or @false@. If the value is true, the server will use Ably protocol messages (for example, a message with a @HEARTBEAT@ action) to satisfy the @maxIdleInterval@ requirement. If it is false or unspecified, the server is permitted to use any transport-level mechanism (for example, "websocket":https://ably.com/topic/websockets ping frames) to satisfy this. So for example, for "websocket transports":https://ably.com/topic/websockets, if the client is able to observe websocket pings, then it should send @heartbeats=false@. If not, it should send @heartbeats=true@. * @(RTN24)@ A connected client may receive a @CONNECTED@ @ProtocolMessage@ from Ably at any point (though is typically triggered by a reauth, see @RTC8a@). The @connectionDetails@ in the @ProtocolMessage@ must override any stored details, see @RTN21@. The @Connection@ should emit an @UPDATE@ event with a @ConnectionStateChange@ object, which should have both @previous@ and @current@ attributes set to @CONNECTED@, and the @reason@ attribute set to to the @error@ member of the @CONNECTED@ @ProtocolMessage@ (if any). (Note that @UPDATE@ should be the only event emitted: in particular, the library must not emit an @CONNECTED@ event if the client was already connected, see @RTN4h@). -* @(RTN25)@ @Connection#errorReason@ attribute is an optional @ErrorInfo@ object which is set by the library when an error occurs on the connection, as described by "RSA4c1":#RSA4c1, "RSA4d":#RSA4d, "RTN11d":#RTN11d, "RTN14a":#RTN14a, "RTN14b":#RTN14b, "RTN14e":#RTN14e, "RTN14g":#RTN14g, "RTN15c2":#RTN15c2, "RTN15c3":#RTN15c3, "RTN15c4":#RTN15c4, "RTN15d":#RTN15d, "RTN15h":#RTN15h, "RTN15i":#RTN15i, "RTN16e":#RTN16e. +* @(RTN25)@ @Connection#errorReason@ attribute is an optional @ErrorInfo@ object which is set by the library when an error occurs on the connection, as described by "RSA4c1":#RSA4c1, "RSA4d":#RSA4d, "RTN11d":#RTN11d, "RTN14a":#RTN14a, "RTN14b":#RTN14b, "RTN14e":#RTN14e, "RTN14g":#RTN14g, "RTN15c7":#RTN15c7, "RTN15c4":#RTN15c4, "RTN15d":#RTN15d, "RTN15h":#RTN15h, "RTN15i":#RTN15i, "RTN16e":#RTN16e. h3(#realtime-channels). Channels @@ -565,6 +574,7 @@ h3(#realtime-channel). RealtimeChannel *** @(RTL4b1)@ Note that an attach attempt immediately after the library is instantiated, assuming @autoConnect@ (@TO3e@)is not set to @false@, should not raise an error (that is, should fall under @RTL4i@, not @RTL4b@), since the library should be in a @CONNECTING@ state at that point ** @(RTL4i)@ If the connection state is @CONNECTING@ or @DISCONNECTED@, do the operation once the connection state is @CONNECTED@ ** @(RTL4c)@ Otherwise an @ATTACH@ ProtocolMessage is sent to the server, the state transitions to @ATTACHING@ and the channel becomes @ATTACHED@ when the confirmation @ATTACHED@ ProtocolMessage is received +*** @(RTL4c1)@ The @ATTACH@ ProtocolMessage @channelSerial@ field must be set to the @channelSerial@ of the most recent message/presence ProtocolMessage received on that channel (which will have been stored in the channel per @RTL15b@). If no messages have been received on the channel, the field may be set to @null@ or omitted. ** @(RTL4f)@ Once an @ATTACH@ @ProtocolMessage@ is sent, if an @ATTACHED@ @ProtocolMessage@ is not received within the "default realtime request timeout":#defaults, the attach request should be treated as though it has failed and the channel should transition to the @SUSPENDED@ state. The channel will then be subsequently automatically re-attached as described in "RTL13":#RTL13 ** @(RTL4d)@ A callback (or other language-idiomatic equivalent) can be provided that is called when the channel next moves to one of @ATTACHED@, @DETACHED@, @SUSPENDED@, or @FAILED@ states. In the case of @ATTACHED@ the callback is called with no argument. In all other cases it is called with an @ErrorInfo@ corresponding to the @ChannelStateChange.reason@ of the state change (or a fallback if there is no @reason@) to indicate that the attach has failed. (Note: when combined with RTL4f, this means that if the connection is @CONNECTED@, the callback is guaranteed to be called within @realtimeRequestTimeout@ of the @attach()@ call) ** @(RTL4e)@ If the user does not have sufficient permissions to attach to the channel, the channel will transition to @FAILED@ and set the @RealtimeChannel#errorReason@ @@ -639,6 +649,7 @@ h3(#realtime-channel). RealtimeChannel * @(RTL12)@ An attached channel may receive an additional @ATTACHED@ @ProtocolMessage@ from Ably at any point. (This is typically triggered following a transport being resumed to indicate a partial loss of message continuity on that channel, in which case the @ProtocolMessage@ will have a @resumed@ flag set to false). If and only if the @resumed@ flag is false, this should result in the channel emitting an @UPDATE@ event with a @ChannelStateChange@ object. The @ChannelStateChange@ object should have both @previous@ and @current@ attributes set to @attached@, the @reason@ attribute set to to the @error@ member of the @ATTACHED@ @ProtocolMessage@ (if any), and the @resumed@ attribute set per the @RESUMED@ bitflag of the @ATTACHED@ @ProtocolMessage@. (Note that @UPDATE@ should be the only event emitted: in particular, the library must not emit an @ATTACHED@ event if the channel was already attached, see @RTL2g@). * @(RTL15)@ @RealtimeChannel#properties@ attribute is a @ChannelProperties@ object representing properties of the channel state. @properties@ is a publicly accessible member of the channel, but it is an experimental and unstable API. It has the following attributes: ** @(RTL15a)@ @attachSerial@ is unset when the channel is instantiated, and is updated with the @channelSerial@ from each @ATTACHED@ @ProtocolMessage@ received from Ably with a matching @channel@ attribute. The @attachSerial@ value is used for @untilAttach@ queries, see "RTL10b":#RTL10b +** @(RTL15b)@ @channelSerial@ is updated whenever a @ProtocolMessage@ with either @Message@ or @Presence@ actions is received on a channel, and is set to the @TR4c@ @channelSerial@ of that @ProtocolMessage@. * @(RTL13)@ If the channel receives a server initiated @DETACHED@ message when it is in the @ATTACHING@, @ATTACHED@ or @SUSPENDED@ state (i.e. the client has not explicitly requested a detach putting the channel into the @DETACHING@ state), then the following applies: ** @(RTL13a)@ If the channel is in the @ATTACHED@ or @SUSPENDED@ states, an attempt to reattach the channel should be made immediately by sending a new @ATTACH@ message and the channel should transition to the @ATTACHING@ state with the error emitted in the @ChannelStateChange@ event. ** @(RTL13b)@ If the attempt to re-attach fails, or if the channel was already in the @ATTACHING@ state, the channel will transition to the @SUSPENDED@ state and the error will be emitted in the @ChannelStateChange@ event. An attempt to re-attach the channel automatically will then be made after the period defined by "@RTB1@":#RTB1. When re-attaching the channel, the channel will transition to the @ATTACHING@ state. If that request to attach fails i.e. it times out or a @DETACHED@ message is received, then the process described here in @RTL13b@ will be repeated, indefinitely @@ -683,17 +694,18 @@ h3(#realtime-presence). RealtimePresence ** @(RTP18c)@ a @SYNC@ may also be sent with no @channelSerial@ attribute. In this case, the sync data is entirely contained within that @ProtocolMessage@ * @(RTP19)@ If the @PresenceMap@ has existing members when a @SYNC@ is started, the client library must ensure that members no longer present on the channel are removed from the local @PresenceMap@ once the sync is complete. In order to do this, the client library must keep track of any members that have not been added or updated in the @PresenceMap@ during the sync process. Note that a member can be added or updated when received in a @SYNC@ message or when received in a @PRESENCE@ message during the sync process. Once the sync is complete, the members in the @PresenceMap@ that have not been added or updated should be removed from the @PresenceMap@ and a @LEAVE@ event should be published for each. The @PresenceMessage@ published should contain the original attributes of the presence member with the @action@ set to @LEAVE@, @PresenceMessage#id@ set to @null@, and the @timestamp@ set to the current time. This behavior should be tested as follows: @ENTER@ presence on a channel, wait for @SYNC@ to complete, inject a member directly into the local @PresenceMap@ so that it only exists locally and not on the server, send a @SYNC@ message with the @channel@ attribute populated with the current channel which will trigger a server initiated @SYNC@. A @LEAVE@ event should then be published for the injected member, and checking the @PresenceMap@ should reveal that the member was removed and the valid member entered for this connection is still present ** @(RTP19a)@ If the @PresenceMap@ has existing members when an @ATTACHED@ message is received without a @HAS_PRESENCE@ flag, the client library should emit a @LEAVE@ event for each existing member, and the @PresenceMessage@ published should contain the original attributes of the presence member with the @action@ set to @LEAVE@, @PresenceMessage#id@ set to @null@, and the @timestamp@ set to the current time. Once complete, all members in the @PresenceMap@ should be removed as there are no members present on the channel -* @(RTP17)@ The RealtimePresence object should also keep a second @PresenceMap@ containing only members that match the current @connectionId@. Any incoming presence message that satisfies @RTP17b@ should be applied to this object in the same way as for the normal @PresenceMap@. This object should be private and is used to maintain a list of members that need to be automatically re-entered by the @RealtimePresence@ object when required to by @RTP17c@. +* @(RTP17)@ The RealtimePresence object should also keep a second @PresenceMap@ containing only members that match the current @connectionId@. Any incoming presence message that satisfies @RTP17b@ should be applied to this object in the same way as for the normal @PresenceMap@. This object should be private and is used to maintain a list of members that need to be automatically re-entered by the @RealtimePresence@ object when required to by @RTP17f@. ** @(RTP17a)@ All members belonging to the current connection are published as a @PresenceMessage@ on the @RealtimeChannel@ by the server irrespective of whether the client has permission to subscribe or the @RealtimeChannel@ is configured to publish presence events. A test should exist that attaches to a @RealtimeChannel@ with a @presence@ capability and without a @subscribe@ capability. It should then enter the @RealtimeChannel@ and ensure that the member entered from the current connection is present in the internal and public presence set available via "@RealtimePresence#get@":#RTP11 ** @(RTP17b)@ The events that should be applied to the @RTP17@ presence map are: any @ENTER@, @PRESENT@ or @UPDATE@ event with a @connectionId@ that matches the current client's @connectionId@; any @LEAVE@ event with a @connectionId@ that matches the current client's @connectionId@ and is not a 'synthesized leave' (an event that has a connectionId which is not an initial substring of its id, per "@RTP2b1@":#RTP2b1 ) -** @(RTP17c)@ The RealtimePresence object should perform automatic re-entry in the following situations: -*** @(RTP17c1)@ After a @SYNC@ operation has completed, per "@RTP18b@":#RTP18b -*** @(RTP17c2)@ When an @ATTACHED@ message is received with no "@HAS_PRESENCE@":#TR3a flag (so no @SYNC@ is expected as the server does not believe anyone is currently present) -** @(RTP17d)@ Automatic re-entry consists of, for each member of the @RTP17@ internal @PresenceMap@ whose @memberKey@ is not also a member of the normal @PresenceMap@, publishing a @PresenceMessage@ with an @ENTER@ action using the @clientId@ and @data@ attributes from that member, and removing that member from the internal @PresenceMap@ +** @(RTP17f)@ The RealtimePresence object should perform automatic re-entry whenever a channel moves into the @ATTACHED@ state. (It does not need to do so for @RTL12@ @ATTACHED@ events received on already-@ATTACHED@ channels). +** @(RTP17g)@ Automatic re-entry consists of, for each member of the @RTP17@ internal @PresenceMap@, publishing a @PresenceMessage@ with an @ENTER@ action using the @clientId@, @data@, and @id@ attributes from that member. +** @(RTP17c)@ This clause and its subclauses have been replaced by @RTN17f@ as of the 2.0 spec. +** @(RTP17d)@ This clause has been replaced by @RTN17g@ as of the 2.0 spec. ** @(RTP17e)@ If the publish attempt fails for an automatic presence @ENTER@ (for example, by Ably rejecting it with a @NACK@), an @UPDATE@ event should be emitted on the channel with @resumed@ set to true and @reason@ set to an @ErrorInfo@ object with @code@ @91004@, a @message@ indicating that an automatic re-enter has failed and indicating the @clientId@, and @cause@ set to the reason for the enter failure. The error should also be logged at @warn@ level or higher. * @(RTP4)@ Ensure a test exists that enters 250 members using @RealtimePresence#enterClient@ on a single connection, and checks for @PRESENT@ events to be emitted on another connection for each member, and once sync is complete, all 250 members should be present in a @RealtimePresence#get@ request * @(RTP5)@ RealtimeChannel state change side effects: ** @(RTP5a)@ If the channel enters the @DETACHED@ or @FAILED@ state then all queued presence messages will fail immediately, and the @PresenceMap@ and "internal PresenceMap (see RTP17)":#RTP17 is cleared. The latter ensures members are not automatically re-entered if the @RealtimeChannel@ later becomes attached. Since channels in the @DETACHED@ and @FAILED@ states will not receive any presence updates from Ably, presence events (specifically @LEAVE@) should not be emitted when the @PresenceMap@ is cleared as each presence member's state is unknown +*** @(RTP5a1)@ A channel entering the @DETACHED@, @SUSPENDED@, or @FAILED@ state should also clear any @channelSerial@ it has stored per @RTL15b@. ** @(RTP5f)@ If the channel enters the @SUSPENDED@ state then all queued presence messages will fail immediately, and the @PresenceMap@ is maintained. This ensures that if the channel later becomes @ATTACHED@, it will only publish presence events for the changes in the @PresenceMap@ that have occurred whilst the client was disconnected. A test should exist for a channel that is in the @SUSPENDED@ state containing presence members to transition to the @ATTACHED@ state, and following the @SYNC@ process after attaching, any members present before and after the sync should not emit presence events, all other changes should be reflected in the @PresenceMap@ and should emit presence events on the channel ** @(RTP5b)@ If a channel enters the @ATTACHED@ state then all queued presence messages will be sent immediately. A presence @SYNC@ may be initiated per "@RTP1@":#RTP1 * @(RTP16)@ Connection state conditions: @@ -1260,7 +1272,7 @@ h4. ProtocolMessage ** @(TR4b)@ @channel@ string ** @(TR4c)@ @channelSerial@ string ** @(TR4d)@ @connectionId@ string -** @(TR4f)@ @connectionSerial@ long +** @(TR4f)@ This attribute has been removed as of the 2.0 spec ** @(TR4o)@ @connectionDetails@ @ConnectionDetails@ object - provides details on the constraints or defaults for the connection such as max message size, client ID or connection state TTL ** @(TR4g)@ @count@ integer ** @(TR4h)@ @error@ @ErrorInfo@ object @@ -1430,7 +1442,8 @@ h4. ConnectionDetails h4. ChannelProperties * @(CP1)@ properties of a channel and its state * @(CP2)@ The attributes of @ChannelProperties@ consist of: -** @(CP2a)@ @attachSerial@ string - contains the last @channelSerial@ received in an @ATTACHED@ @ProtocolMesage@ for the channel, see "RTL15a":#RTL15a +** @(CP2a)@ @attachSerial@ string - contains the last @channelSerial@ received in an @ATTACHED@ @ProtocolMessage@ for the channel, see "RTL15a":#RTL15a +** @(CP2b)@ @channelSerial@ string - contains the last @channelSerial@ received in any message or presence @ProtocolMesage@ on the channel, see "RTL15b":#RTL15b h4. ChannelDetails @@ -1880,9 +1893,10 @@ class MessageFilter: // MFI* refTimeserial: string // MFI2b refType: string // MFI2c name: string // MFI2d - + class ChannelProperties: // CP* attachSerial: String // CP2a + channelSerial: String // CP2b class BatchOperations: // BO* publish([BatchSpec]) => BatchResult // BO2a @@ -2076,7 +2090,6 @@ class ProtocolMessage: // internal channelSerial: String? // TR4c connectionDetails: ConnectionDetails? // RSA7b3, RTN19, TR4o connectionId: String? // RTN15c1, TR4d - connectionSerial: Int? // RTN10c, TR4f count: Int? // TR4g error: ErrorInfo? // RTN15c2, TR4h flags: Int? // TR4i; bitfield containing zero or more boolean flags specified in TR3 @@ -2115,8 +2128,7 @@ class Connection: // RTN* errorReason: ErrorInfo? // RTN25 id: String? // RTN8 key: String? // RTN9 - recoveryKey: String? // RTN16b, RTN16c - serial: Int? // RTN10 + getRecoveryKey(): String? // RTN16g, RTN16c state: ConnectionState // RTN4d close() // RTN12 connect() // RTC1b, RTN3, RTN11