Loop/Architecture/Rooms: Difference between revisions

No edit summary
 
(76 intermediate revisions by 4 users not shown)
Line 1: Line 1:
Based on early user feedback, we will be adding [https://people.mozilla.org/~dhenein/loop/rooms/ a "rooms" metaphor] to the Loop product. Initially, this will be based on a "link as a room" model, although we will likely explore link-free rooms experiences for logged-in users in the future.
Based on early user feedback, we will be adding [https://people.mozilla.org/~dhenein/loop/rooms/ a "rooms" metaphor] to the Loop product. Initially, this will be based on a "link as a room" model, although we will likely explore link-free rooms experiences for logged-in users in the future.


Many of the network operations required for rooms are very similar to those we currently use for link-based calling (that is, the "/call-url" and "/calls" endpoints). However, the parameters and behaviors are sufficiently different that re-using those codepaths would have caused more confusion and work than simply defining new ones. However, the conceptual model that developers have around how these endpoints work can be useful in understanding the rooms design. Consequently, the network API for rooms uses "/room-url" and "/rooms" endpoints in a way that is congruent with the "/call-url" and "/calls" endpoints. Note, however, that no websockets connection is necessary, as call progress information is not conveyed.
Many of the network operations required for rooms are very similar to those we currently use for link-based calling (that is, the "/call-url" and "/calls" endpoints). However, the parameters and behaviors are sufficiently different that re-using those codepaths would have caused more confusion and work than simply defining new ones. However, the conceptual model that developers have around how these endpoints work can be useful in understanding the rooms design.
 
Also in the case of Rooms there is no such thing as a call, rooms are always open so that people can enter them at any times.
 
Consequently, the network API for rooms uses the "/rooms" endpoint.
Note, however, that no websockets connection is necessary, as call progress information is not conveyed.


Similar to the call-url design, users can generate rooms both when logged in to FxA and when not logged in.
Similar to the call-url design, users can generate rooms both when logged in to FxA and when not logged in.
Line 11: Line 16:
! Date !! Author !! Changes
! Date !! Author !! Changes
|-
|-
| Wed Sep 10 09:11:58 CDT 2014 || Adam Roach || Initial version
| Wed Sep 10 16:07:58 CDT 2014 || Adam Roach || Initial version
|-
| Thu Sep 11 19:28:32 UTC 2014 || Adam Roach || Updates after initial feedback: now uses multiple push URLs. Renamed "operation" to "action" to be consistent with other loop-server endpoints.
|-
| Tue Sep 23 16:23:51 CEST 2014 || Rémy Hubscher || Merge /rooms and /room-url endpoints since they describe the same /rooms concept. i.e: We don't have multiple rooms for the same /room-url as we have multiple /calls for the same /call-url
|-
| Thu Sep 25 16:07:42 CEST 2014 || Alexis Métaireau || Rename PUT /rooms/:token into PATCH /rooms/:token.
|-
| Wed Oct 1 14:55:42 CEST 2014 || Rémy Hubscher || Add the ctime parameter on GET /rooms/:token.
|-
| Thu Oct 2 16:32:42 CEST 2014 || Rémy Hubscher || Improve the notification description.
|-
| Thu Oct 3 16:32:42 CEST 2014 || Alexis Métaireau || Use Numbers instead of Strings in JSON outputs / inputs.
|-
| Mon Oct  6 14:51:52 UTC 2014 || Adam Roach || Added room URL to GET /rooms and GET /rooms/:token
|-
| Thu Oct  9 15:40:15 UTC 2014 || Adam Roach || Added sessionToken to "action: refresh" and "action: leave" operations for correlation and auth.
|-
| Wed Oct 22 15:16:38 UTC 2014 || Adam Roach || Modifications based on discussion in {{Bug|1074665}}: list of rooms now contains the same information as is indicated when retrieving a single room directly. Additionally, when getting a versioned list of rooms, any rooms that have been deleted since the indicated version will be listed, with a "deleted: true" field.
|-
| Mon Dec 29 16:55:10 UTC 2014 || Rémy Hubscher || Proposal to handle {{Bug|1106562}}: Multiple rooms deletion in one HTTP call.
|-
| Wed Feb 25 22:26:48 UTC 2015 || Adam Roach || Added new operation to <tt>POST /rooms/:token</tt> to send room session state to Loop server for logging purposes.
|}
|}


Line 24: Line 51:
[https://tokbox.com/opentok/libraries/server/node/#generatingtokens When the Loop server generates a new token for a user], it optionally includes a "data" field. This field is then communicated to other participants in the room when the corresponding user joins the room: the [https://tokbox.com/opentok/libraries/client/js/reference/ConnectionEvent.html ConnectionEvent] on [https://tokbox.com/opentok/libraries/client/js/reference/Session.html Session] contains a [https://tokbox.com/opentok/libraries/client/js/reference/Connection.html Connection object], which includes a "data" field. This is the same "data" as was provided by the server when the token was created.
[https://tokbox.com/opentok/libraries/server/node/#generatingtokens When the Loop server generates a new token for a user], it optionally includes a "data" field. This field is then communicated to other participants in the room when the corresponding user joins the room: the [https://tokbox.com/opentok/libraries/client/js/reference/ConnectionEvent.html ConnectionEvent] on [https://tokbox.com/opentok/libraries/client/js/reference/Session.html Session] contains a [https://tokbox.com/opentok/libraries/client/js/reference/Connection.html Connection object], which includes a "data" field. This is the same "data" as was provided by the server when the token was created.


The Loop server, when generating a toke for a user in a room, will set the contents of the "data" field to a JSON string, containing the following information:
The Loop server, when generating a token for a user in a room, will set the contents of the "data" field to a JSON string, containing the following information:


  {
  {
     "displayName": "Alexis",
     "displayName": "Alexis",
     "account": "alexis@example.com",
     "account": "alexis@example.com",
     "id": "2a1787a6-4a73-43b5-ae3e-906ec1e763cb"
     "roomConnectionId": "2a1787a6-4a73-43b5-ae3e-906ec1e763cb"
  }
  }


* '''displayName''' - The user-friendly name that should be displayed for this participant
* '''displayName''' - The user-friendly name that should be displayed for this participant
* '''account''' - If the user is logged in, this is the FxA account name or MSISDN that was used to authenticate the user for this session
* '''account''' - If the user is logged in, this is the FxA account name or MSISDN that was used to authenticate the user for this session
* '''id''' - An id, unique within the room for the lifetime of the room, used to identify a participant ''for the duration of one instance of joining the room''. If the user departs and re-joins, this id will change. Note that this document uses a UUID in its examples, since they can be used in this fashion without having to store additional state; however, anything that is unique within a room for its entire lifetime (e.g., a constantly increasing integer) would be suitable as well.
* '''roomConnectionId''' - An id, unique within the room for the lifetime of the room, used to identify a participant ''for the duration of one instance of joining the room''. If the user departs and re-joins, this id will change. Note that this document uses a UUID in its examples, since they can be used in this fashion without having to store additional state; however, anything that is unique within a room for its entire lifetime (e.g., a constantly increasing integer) would be suitable as well.


== Room Size Handling ==
== Room Size Handling ==
Line 43: Line 70:
Additionally, due to device constraints (as well as due to the potential to have multiple versions of the client deployed simultaneously), it is possible in the future to have clients that support fewer participants in a room than the room has been configured to support. To deal with this situation, when a client attempts to join a room, it includes the maximum number of participants it is capable of handling at one time. When determining whether to admit a new user, the Loop server takes into consideration both the configured room maximum size ''and'' the client capabilities of those clients currently in (and attempting to join) the session.
Additionally, due to device constraints (as well as due to the potential to have multiple versions of the client deployed simultaneously), it is possible in the future to have clients that support fewer participants in a room than the room has been configured to support. To deal with this situation, when a client attempts to join a room, it includes the maximum number of participants it is capable of handling at one time. When determining whether to admit a new user, the Loop server takes into consideration both the configured room maximum size ''and'' the client capabilities of those clients currently in (and attempting to join) the session.


For example: consider a configured to support up to 4 users.  
For example: consider a server configured to support up to 4 users.  
# A first user joins with a client that supports up to 3 users. His presence temporarily reduces the room capacity to 3.
# A first user joins with a client that supports up to 3 users. His presence temporarily reduces the room capacity to 3.
# A second user joins with a client that supports up to 3 clients. The server admits this user, since the room is not yet full.  
# A second user joins with a client that supports up to 3 clients. The server admits this user, since the room is not yet full.  
Line 60: Line 87:
However, to avoid circumstances in which clients depart unexpectedly (e.g. crashes, loss of network connectivity) from keeping them in the list of room participants, we need to employ a soft-state mechanism. Once a client joins a room, it need to periodically contact the Loop server to indicate that it is still a member of that room. If the client goes significantly longer than expected to perform this periodic action, they are considered to be no longer part of the session, and are removed from the participant list.
However, to avoid circumstances in which clients depart unexpectedly (e.g. crashes, loss of network connectivity) from keeping them in the list of room participants, we need to employ a soft-state mechanism. Once a client joins a room, it need to periodically contact the Loop server to indicate that it is still a member of that room. If the client goes significantly longer than expected to perform this periodic action, they are considered to be no longer part of the session, and are removed from the participant list.


This scheme works as follows: when a user initially joins a room (using "POST /rooms/{token}"), the response indicates an expiration time, measured in seconds, during which the client must perform another POST to the same "/rooms/{token}" endpoint. If the this number of seconds (plus some grace period, on the order of 30 seconds) passes without a subsequent POST, then the user is removed from the list of participants.
This scheme works as follows: when a user initially joins a room (using "POST /rooms/{token}"), the response indicates an expiration time, measured in seconds, during which the client must perform another POST to the same "/rooms/{token}" endpoint. If this number of seconds (plus some grace period, on the order of 30 seconds) passes without a subsequent POST, then the user is removed from the list of participants.


This refresh period is communicated by the server each time it receives a POST. The duration selected for this refresh period is a trade-off between server load and responsiveness to users departing unexpectedly. While it is initially anticipated that the value will be a static configuration, the server may choose to adjust it dynamically based on server load (thus providing rapid recovery when it has the capacity to do so, but shedding load when necessary during periods of heavy use).
This refresh period is communicated by the server each time it receives a POST. The duration selected for this refresh period is a trade-off between server load and responsiveness to users departing unexpectedly. While it is initially anticipated that the value will be a static configuration, the server may choose to adjust it dynamically based on server load (thus providing rapid recovery when it has the capacity to do so, but shedding load when necessary during periods of heavy use).


The suggested period for this timer for initial deployment is 600 seconds (10 minutes).
The suggested period for this timer for initial deployment is 600 seconds (10 minutes).
''Note that the server does not need to proactively track timeouts to make this scheme work. The following would work just fine:''
# ''When a user joins, store a "must refresh by" time (say, 10 minutes in the future) after which they are no longer considered part of the conference.''
# ''Every time a user does a refresh, update this time.''
# ''Whenever someone performs an action that relies on the list of (or number of) users in the room, check whether any have a "must refresh by" time in the past. If so, remove them from the room before producing the result.''


== Removing Participants from a Room ==
== Removing Participants from a Room ==
Line 72: Line 105:


== Room Owner Notification ==
== Room Owner Notification ==
When the status of a room changes (e.g., a user joins or leaves), ''and'' the owner is not a member of the room, then the Loop server will cause a notification to be sent to the room owner by way of a push notification. The room owners' client can then retrieve a list of the users' rooms (that is, all those rooms that have changed on or after the indicated version), and take appropriate action. For the current UX, these actions include an unobtrusive audio notification when a user joins a room that the owner is not yet in, as well as (probably) a count of users and/or rooms waiting for the owner to join.
When the status of a room changes (e.g., a user joins or leaves, a room is created or deleted), the Loop server will cause a notification to be sent to the room owner by way of a push notification. This will helps to have a consistent room list on the owner devices.
 
The room owners' client can then retrieve a list of the users' rooms (that is, all those rooms that have changed on or after the indicated version), and take appropriate action. For the current UX, these actions include an unobtrusive audio notification when a user joins a room as well as a count of users and rooms waiting for the owner to join.
 
'''''Note:''' The push notification needs to be sent whenever the output of <tt>GET /rooms</tt> would be different. This includes operations such as: user joins a room, user leaves a room, new room is created, and existing room is deleted.''
 
To avoid performing a slew of queries whenever a push notification arrives, transitioning to a rooms model will also necessitate storing different push server endpoints for different events. Otherwise, for a logged in user, arrival of a push notification would result in four separate HTTP operations towards the Loop server:
# A request to GET /calls?version=<version> for non-account-associated calls (although note that these will be going away shortly)
# A request to GET /calls?version=<version> for FxA calls
# A request to GET /rooms?version=<version> for non-account-associated calls
# A request to GET /rooms?version=<version> for FxA calls
 
This necessitates a small handful of changes on the client side; in particular:
 
MozLoopPushHandler needs to be updated to store an array of channelIDs, and turn any per-channel attributes into objects keyed on the channel ID. At the class level:
 
  FXA_CALLS:  "25389583-921f-4169-a426-a4673658944b",
  FXA_ROOMS:  "6add272a-d316-477c-8335-f00f73dfde71",
  GUEST_CALLS: "801f754b-686b-43ec-bd83-1419bbf58388",
  GUEST_ROOMS: "19d3f799-a8f3-4328-9822-b7cd02765832",
  channelIDs: [this.FXA_CALLS, this.FXA_ROOMS, this.GUEST_CALLS, this.GUEST_ROOMS],
  pushUrls: {},
  registered: {},
 
_onRegister needs to update the handling of successful channel registrations:
 
  this.registered[msg.channelID] = true;
  if (this.pushUrl[msg.channelID] !== msg.pushEndpoint) {
    this.pushUrls[msg.channelID] = msg.pushEndpoint;
    this._registerCallback(null, this.pushUrl, msg.channelID);
  }
 
And the handling of "notification" events needs to include the channel id:
 
  case "notification":
    msg.updates.forEach((update) => {
      if (update.channelID in this.channelIDs) {
        this._notificationCallback(update.version, update.channelID);
      }
    });
    break;
 
Of course, the MozLoopService needs to be adapted to handle multiple calls to its registration callback, and needs to check the channelID parameter on its notification callback to determine which of the four possible states it should check.
 
This also means that calls to POST /registration need to be modified to include multiple URLs. See below.
 
== Loop Server API Changes ==
Because we will be using different push server endpoints for rooms than we do for calls, it is necessary to convey more than one push server endpoint in the registration message:
 
POST /registration HTTP/1.1
Accept: application/json
Content-Type: application/json; charset=utf-8
Host: localhost:5000
{
    "simplePushURL": "https://example.org/wg0asg83t_74s",
    '''"simplePushURLs": {
        "calls": "https://example.org/wg0asg83t_74s",
        "rooms": "https://example.org/vnd7a85ls9gh"
      }'''
}
 
The idea behind this move, is to make it easy in the future to add new topics.
 
'''simplePushURL''' becomes deprecated, if set, it is used as the value for ''simplePushURLs.calls''
'''simplePushURLs''' is the topic array, omitted topics will be unset and notification on this topics will not be handled.


== Loop Server API Additions ==
== Loop Server API Additions ==
To accommodate rooms, the Loop Server API will need the following additions.
To accommodate rooms, the Loop Server API will need the following additions.


=== POST /room-url ===
=== POST /rooms ===
This generates a new room and returns the associated URL that can be used to join the room.
This generates a new room and returns the associated URL that can be used to join the room.


  POST /room-url HTTP/1.1
  POST /rooms HTTP/1.1
  Accept: application/json
  Accept: application/json
  Accept-Encoding: gzip, deflate
  Accept-Encoding: gzip, deflate
Line 89: Line 187:
  {
  {
     "roomName": "UX Discussion",
     "roomName": "UX Discussion",
     "expiresIn": "5",
     "expiresIn": 5,
     "roomOwner": "Alexis",
     "roomOwner": "Alexis",
     "maxSize": "2"
     "maxSize": 2
  }
  }


Line 107: Line 205:
  {
  {
     "roomToken": "_nxD4V4FflQ",
     "roomToken": "_nxD4V4FflQ",
     "roomUrl": "http://localhost:3000/room/_nxD4V4FflQ",
     "roomUrl": "http://localhost:3000/rooms/_nxD4V4FflQ",
     "expiresAt": 1405534180
     "expiresAt": 1405534180
  }
  }
Line 117: Line 215:
''Server implementation note: The Loop server should contact the TokBox servers and retrieve a sessionId for the room at room creation time. This sessionId should be stored persistently with rest of the information associated with the room.''
''Server implementation note: The Loop server should contact the TokBox servers and retrieve a sessionId for the room at room creation time. This sessionId should be stored persistently with rest of the information associated with the room.''


=== PUT /room-url/{token} ===
=== PATCH /rooms/{token} ===
The "PUT /room-url/{token}" endpoint is used to update information about an existing room.
The "PATCH /rooms/{token}" endpoint is used to update information about an existing room.


  PUT /room-url HTTP/1.1
  PATCH /rooms/_nxD4V4FflQ HTTP/1.1
  Accept: application/json
  Accept: application/json
  Accept-Encoding: gzip, deflate
  Accept-Encoding: gzip, deflate
Line 128: Line 226:
    
    
  {
  {
     "roomName": "Follow-up from UX Discussion",
     "context": {
     "expiresIn": "24"
      "value": "PWjHj89HBS-...ICUX3Iqd9ZsfDNLoUeAb5KGJgEtDy-7ag52rYY5mGgP2GQ==",
      "alg": "AES-GCM",
      "wrappedKey": "KLPCJEy8vewUeHFFLtvMNA"
    },
     "expiresIn": 24
  }
  }


The parameters for this endpoint are the same as for "POST /room-url". Any omitted parameters are not updated.
The parameters for this endpoint are the same as for "POST /rooms". Any omitted parameters are not updated.


  HTTP/1.1 200 OK
  HTTP/1.1 200 OK
Line 146: Line 248:
* '''expiresAt''' - The date after which the room will no longer be valid (in seconds since the Unix epoch).
* '''expiresAt''' - The date after which the room will no longer be valid (in seconds since the Unix epoch).


=== DELETE /room-url/{token} ===
=== DELETE /rooms/{token} ===
Destroys a previously-created room.
Destroys a previously-created room.


  DELETE /room-url/_nxD4V4FflQ HTTP/1.1
  DELETE /rooms/_nxD4V4FflQ HTTP/1.1
  Accept: */*
  Accept: */*
  Accept-Encoding: gzip, deflate
  Accept-Encoding: gzip, deflate
Line 160: Line 262:
  Date: Wed, 16 Jul 2014 13:12:46 GMT
  Date: Wed, 16 Jul 2014 13:12:46 GMT
  Server-Authorization: <stripped>
  Server-Authorization: <stripped>
''Client implementation note: Before deleting a room, the client should check room membership and forceDisconnect() all current participants''
=== PATCH /rooms ===
Destroys a list of user's previously-created rooms.
PATCH /rooms HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Authorization: <stripped>
Content-Length: 0
Host: localhost:5000
{"deleteRoomTokens": ["_nxD4V4FflQ", "_xaB2Z5GdTV"]}
HTTP/1.1 207 Multi-Status
Connection: keep-alive
Date: Wed, 16 Jul 2014 13:12:46 GMT
Server-Authorization: <stripped>
{
    "responses": {
      "_nxD4V4FflQ": { "code": 200 },
      "_xaB2Z5GdTV": { "code": 200 }
    }
}
In case of error for some token:
HTTP/1.1 207 Multi-Status
Connection: keep-alive
Date: Wed, 16 Jul 2014 13:12:46 GMT
Server-Authorization: <stripped>
{
  "responses": {
    "_nxD4V4FflQ": { "code": 200 },
    "_xaB2Z5GdTV": { "code": 404, errno: 105, message: "Room not found" },
  }
}


''Client implementation note: Before deleting a room, the client should check room membership and forceDisconnect() all current participants''
''Client implementation note: Before deleting a room, the client should check room membership and forceDisconnect() all current participants''
Line 165: Line 309:
=== GET /rooms/{token} ===
=== GET /rooms/{token} ===
This endpoint is used to retrieve information about a single room, including a list of room participants.
This endpoint is used to retrieve information about a single room, including a list of room participants.
<s>Because providing this information to users who are not in the room would be surprising for those in the room, we only allow the room owner and users in the room to access this method. Room owners are authenticated via their HAWK credentials, while room participants are authenticated via the sessionToken bearer token provided to them when they joined the room.</s>


  GET /rooms/3jKS_Els9IU HTTP/1.1
  GET /rooms/3jKS_Els9IU HTTP/1.1
  Accept: */*
  Accept: */*
  Accept-Encoding: gzip, deflate
  Accept-Encoding: gzip, deflate
'''Authorization: <elided>'''
  Host: localhost:5000
  Host: localhost:5000
* For a desktop client user, the "Authorization" header field is populated with the HAWK token (using a scheme of "Hawk"), just like it is for other requests.
* For the standalone client, the "Authorization" header field is encoded using [http://tools.ietf.org/html/rfc1945#section-11.1 Basic authentication]. The user ID portion is the sessionToken provided to the user when they joined the room, and the password is blank.


  HTTP/1.1 200 OK
  HTTP/1.1 200 OK
Line 180: Line 331:
   
   
  {
  {
    "roomToken": "3jKS_Els9IU",
     "roomName": "UX Discussion",
     "roomName": "UX Discussion",
    "roomUrl": "http://localhost:3000/rooms/3jKS_Els9IU",
     "roomOwner": "Alexis",
     "roomOwner": "Alexis",
     "maxSize": "2",
     "maxSize": 2,
     "clientMaxSize": "2",
     "clientMaxSize": 2,
     "urlCreationDate": 1405517546,
    "creationTime": 1405517546,
     "ctime": 1405517824,
     "expiresAt": 1405534180,
     "expiresAt": 1405534180,
     "participants": [
     "participants": [
         { "displayName": "Alexis", "account": "alexis@example.com", "id": "2a1787a6-4a73-43b5-ae3e-906ec1e763cb" },
         { "displayName": "Alexis", "account": "alexis@example.com", "roomConnectionId": "2a1787a6-4a73-43b5-ae3e-906ec1e763cb" },
         { "displayName": "Adam", "id": "781f012b-f1ea-4ce1-9105-7cfc36fb4ec7" }
         { "displayName": "Adam", "roomConnectionId": "781f012b-f1ea-4ce1-9105-7cfc36fb4ec7" }
       ]
       ]
  }
  }


* '''roomToken''' - The token that uniquely identifies this room
* '''roomName''' - The room-owner-assigned name used to identify this room.  
* '''roomName''' - The room-owner-assigned name used to identify this room.  
* '''roomUrl''' - A URL that can be given to other users to allow them to join the room.
* '''roomOwner''' - The user-friendly display name indicating the name of the room's owner.
* '''roomOwner''' - The user-friendly display name indicating the name of the room's owner.
* '''maxSize''' - The maximum number of users allowed in the room at one time (as configured by the room owner).
* '''maxSize''' - The maximum number of users allowed in the room at one time (as configured by the room owner).
* '''clientMaxSize''' - The current maximum number of users allowed in the room, as constrained by the clients currently participating in the session. If no client has a supported size smaller than "maxSize", then this will be equal to "maxSize". Under no circumstances can "clientMaxSize" be larger than "maxSize".
* '''clientMaxSize''' - The current maximum number of users allowed in the room, as constrained by the clients currently participating in the session. If no client has a supported size smaller than "maxSize", then this will be equal to "maxSize". Under no circumstances can "clientMaxSize" be larger than "maxSize".
* '''urlCreationDate''' - The time (in seconds since the Unix epoch) at which the room was created.
* '''creationTime''' - The time (in seconds since the Unix epoch) at which the room was created.
* '''expiresAt''' - The time (in seconds since the Unix epoch) at which the room goes away.
* '''expiresAt''' - The time (in seconds since the Unix epoch) at which the room goes away.
* '''participants''' - An array containing a list of the current room participants. Each participant is formatted with the same fields as described in [[#User Identification in a Room]].
* '''participants''' - An array containing a list of the current room participants. Each participant is formatted with the same fields as described in [[#User Identification in a Room]].
* '''ctime''' - Similar in spirit to the Unix filesystem "ctime" (change time) attribute. The time, in seconds since the Unix epoch, that any of the following happened to the room:
** The room was created
** The owner modified its attributes with "PATCH /rooms/{token}"
** A user joined the room
** A user left the room


=== POST /rooms/{token} ===
=== POST /rooms/{token} ===
The "POST /rooms/{token}" endpoint is used for a variety of purposes. The JSON object in its body contains an "operation" parameter to indicate which of these purposes the operation is being used for.
The "POST /rooms/{token}" endpoint is used for a variety of purposes. The JSON object in its body contains an "action" parameter to indicate which of these purposes the operation is being used for.


==== Joining a Room ====
==== Joining a Room ====
Server implementation note: associate user token with sessionToken here. Room owner must have "moderator" privileges.
Server implementation note: associate user token with sessionToken here. Room owner must have "moderator" privileges.
* For a desktop client user, the "Authorization" header field is populated with the HAWK token (using a scheme of "Hawk"), just like it is for other requests.
* For the standalone client, on this endpoint action there is no Authorization header


  POST /rooms/QzBbvGmIZWU HTTP/1.1
  POST /rooms/QzBbvGmIZWU HTTP/1.1
Line 210: Line 374:
  Accept-Encoding: gzip, deflate
  Accept-Encoding: gzip, deflate
  Content-Type: application/json; charset=utf-8
  Content-Type: application/json; charset=utf-8
Authorization: <stripped>
  Host: localhost:5000
  Host: localhost:5000
   
   
  {
  {
     "operation": "join",
     "action": "join",
     "displayName": "Adam",
     "displayName": "Adam",
     "clientMaxSize": "2"
     "clientMaxSize": 2
  }
  }


* '''operation''' - For joining a room, will be set to "join"
* '''action''' - For joining a room, will be set to "join"
* '''displayName''' - User-friendly display name for the joining user.
* '''displayName''' - User-friendly display name for the joining user.
* '''clientMaxSize''' - Maximum number of room participants the user's client is capable of supporting.
* '''clientMaxSize''' - Maximum number of room participants the user's client is capable of supporting.
Line 232: Line 397:
     "sessionId": "1_MX40NDY2OTEwMn5-V2VkIEp1bCAxNiAwNjo",
     "sessionId": "1_MX40NDY2OTEwMn5-V2VkIEp1bCAxNiAwNjo",
     "sessionToken": "T1==cGFydG5lcl9pZD00NDY2OTEwMiZzaW",
     "sessionToken": "T1==cGFydG5lcl9pZD00NDY2OTEwMiZzaW",
     "expires": "600"
     "expires": 600
  }
  }


* '''apiKey''' - The provider public api Key
* '''apiKey''' - The TokBox public api Key
* '''sessionId''' - The provider session identifier
* '''sessionId''' - The TokBox session identifier (identifies the room)
* '''sessionToken''' - The provider session token (for the room participant)
* '''sessionToken''' - The TokBox session token (identifies the room participant)
* '''expires''' - The number of seconds within which the client must send another POST to this endpoint to remain a participant in this room. See [[#Room Membership and Soft State]]
* '''expires''' - The number of seconds within which the client must send another POST to this endpoint to remain a participant in this room. See [[#Room Membership and Soft State]]


==== Refreshing Membership in a Room ====
==== Refreshing Membership in a Room ====
* For a desktop client user, the "Authorization" header field is populated with the HAWK token (using a scheme of "Hawk"), just like it is for other requests.
* For the standalone client, the "Authorization" header field is encoded using [http://tools.ietf.org/html/rfc1945#section-11.1 Basic authentication]. The user ID portion is the sessionToken provided to the user when they joined the room, and the password is blank.


  POST /rooms/QzBbvGmIZWU HTTP/1.1
  POST /rooms/QzBbvGmIZWU HTTP/1.1
Line 246: Line 414:
  Accept-Encoding: gzip, deflate
  Accept-Encoding: gzip, deflate
  Content-Type: application/json; charset=utf-8
  Content-Type: application/json; charset=utf-8
Authorization: <stripped>
  Host: localhost:5000
  Host: localhost:5000
   
   
  {
  {
     "operation": "refresh"
     "action": "refresh"
  }
  }


* '''operation''' - For refreshing the soft state relationship, this will be "refresh".
* '''action''' - For refreshing the soft state relationship, this will be "refresh".
* '''sessionToken''' - The session token received when joining the room. Used to identify the session to be refreshed as well as to authorize the user to perform the refresh.


  HTTP/1.1 200 OK
  HTTP/1.1 200 OK
Line 261: Line 431:
   
   
  {
  {
     "expires": "600"
     "expires": 600
  }
  }


Line 267: Line 437:


==== Leaving a Room ====
==== Leaving a Room ====
* For a desktop client user, the "Authorization" header field is populated with the HAWK token (using a scheme of "Hawk"), just like it is for other requests.
* For the standalone client, the "Authorization" header field is encoded using [http://tools.ietf.org/html/rfc1945#section-11.1 Basic authentication]. The user ID portion is the sessionToken provided to the user when they joined the room, and the password is blank.


  POST /rooms/QzBbvGmIZWU HTTP/1.1
  POST /rooms/QzBbvGmIZWU HTTP/1.1
Line 272: Line 445:
  Accept-Encoding: gzip, deflate
  Accept-Encoding: gzip, deflate
  Content-Type: application/json; charset=utf-8
  Content-Type: application/json; charset=utf-8
Authorization: <stripped>
  Host: localhost:5000
  Host: localhost:5000
   
   
  {
  {
     "operation": "leave"
     "action": "leave"
  }
  }


* '''operation''' - For leaving a room, this will be "leave".
* '''action''' - For leaving a room, this will be "leave".
* '''sessionToken''' - The session token received when joining the room. Used to identify the session to be left as well as to authorize the user to perform the leave.


HTTP/1.1 204 No Content
Connection: keep-alive
Server-Authorization: <stripped>
==== Updating Session State ====
In order to track room state for metric purposes, the client will also send a POST message to the "/rooms/{token}" endpoint whenever a state change occurs, according to the following state machine:
[[File:loop-room-state.png|center|Room Session State Diagram]]
<center><graphviz format="png" renderer="dot">
digraph loop_room_state {
  init;
  waiting;
  starting;
  sending;
  receiving;
  sendrecv;
  cleanup;
  init->waiting [label="Sesssion.connectionCreated"]
  waiting->starting [label="Sesssion.connectionCreated"]
  starting->receiving [label="Sesssion.streamCreated"]
  starting->sending [label="Publisher.streamCreated"]
  receiving->sendrecv [label="Publisher.streamCreated"]
  sending->sendrecv [label="Session.streamCreated"]
  sendrecv->receiving [label="Publisher.streamDestroyed\nif send count = 0"]
  sendrecv->sending [label="Session.streamDestroyed\nif recv count = 0"]
  sending->cleanup [label="Publisher.streamDestroyed\nif send count = 0"]
  receiving->cleanup [label="Session.streamDestroyed\nif recv count = 0"]
  cleanup->waiting [label="Session.connectionDestroyed\nif connection count = 1"]
  waiting->init [label="Session.connectionDestroyed"]
}
</graphviz></center>
Note that there are some exceptional transitions that are not shown; in particular, a "session.connectionDestroyed" event from any of the "starting", "sending", "receiving", or "sendrecv" states will cause a transition to "waiting."
In order to run this state machine, the client must (in addition to the current state) keep track of three counters:
# Total connection count
#* Increased when event "Session.connectionCreated" occurs
#* Decreased when event "Session.connectionDestroyed" occurs
# Count of streams currently being received
#* Increased when event "Session.streamCreated" occurs
#* Decreased when event "Session.StreamDestroyed" occurs
# Count of streams currently being sent
#* Increased when event "Publisher.streamCreated" occurs
#* Decreased when event "Publisher.StreamDestroyed" occurs
* For a desktop client user, the "Authorization" header field is populated with the HAWK token (using a scheme of "Hawk"), just like it is for other requests.
* For the standalone client, the "Authorization" header field is encoded using [http://tools.ietf.org/html/rfc1945#section-11.1 Basic authentication]. The user ID portion is the sessionToken provided to the user when they joined the room, and the password is blank.
POST /rooms/QzBbvGmIZWU HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate
Content-Type: application/json; charset=utf-8
Authorization: <stripped>
Host: localhost:5000
{
    "action": "status",
    "event": "Session.connectionCreated",
    "state": "sendrecv",
    "connections": 2,
    "sendStreams": 1,
    "recvStreams": 1
}
* '''action''' - For updating session state, this will be "status".
* '''event''' - The event that cause the state to be updated. These are currently based on the TokBox events. Possible values include:
** ''Session.connectionCreated''
** ''Session.connectionDestroyed''
** ''Session.streamCreated''
** ''Session.streamDestroyed''
** ''Publisher.streamCreated''
** ''Publisher.streamDestroyed''
* '''state''' - From the state diagram above. Will be one of:
** ''init'' - Initial and terminal state
** ''waiting'' - Client is in room, is waiting for another user to show up
** ''starting'' - Other user has joined, and media is being established
** ''sending'' - Outbound media streams are set up, but inbound are not
** ''receiving'' - Inbound media streams are set up, but outbound are not
** ''sendrecv'' - Client is sending and receiving media: the session is set up
** ''cleanup'' - No streams are being sent or received, and the session connection should be torn down momentarily
* '''connections''' - Number of ongoing connections (learned by counting Session.connectionCreated and Session.connectionDestroyed)
* '''sendStreams''' - Number of streams being sent (learned by counting Publisher.streamCreated and Publisher.streamDestroyed)
* '''recvStreams''' - Number of streams being received (learned by counting Session.streamCreated and Session.streamDestroyed)


  HTTP/1.1 204 No Content
  HTTP/1.1 204 No Content
Line 307: Line 571:
         "roomToken": "_nxD4V4FflQ",
         "roomToken": "_nxD4V4FflQ",
         "roomName": "First Room Name",
         "roomName": "First Room Name",
         "maxSize": "2",
         "roomUrl": "http://localhost:3000/rooms/_nxD4V4FflQ",
         "currSize": "0",
         "roomOwner": "Alexis",
         "ctime": "1405517546"
        "maxSize": 2,
        "creationTime": 1405517546,
         "ctime": 1405517546,
        "expiresAt": 1405534180,
        "participants": []
     },
     },
     {
     {
         "roomToken": "QzBbvGmIZWU",
         "roomToken": "QzBbvGmIZWU",
         "roomName": "Second Room Name",
         "roomName": "Second Room Name",
         "maxSize": "2",
         "roomUrl": "http://localhost:3000/rooms/QzBbvGmIZWU",
         "currSize": "0",
        "roomOwner": "Alexis",
         "ctime": "1405517418"
         "maxSize": 2,
        "creationTime": 1405517546,
         "ctime": 1405517546,
        "expiresAt": 1405534180,
        "participants": []
     },
     },
     {   
     {   
         "roomToken": "3jKS_Els9IU",
         "roomToken": "3jKS_Els9IU",
         "roomName": "Third Room Name",
         "roomName": "UX Discussion",
         "maxSize": "3",
         "roomUrl": "http://localhost:3000/rooms/3jKS_Els9IU",
         "clientMaxSize": "2",
        "roomOwner": "Alexis",
         "currSize": "1",
        "maxSize": 2,
         "ctime": "1405518241"
         "clientMaxSize": 2,
        "creationTime": 1405517546,
         "ctime": 1405517818,
        "expiresAt": 1405534180,
         "participants": [
            { "displayName": "Alexis", "account": "alexis@example.com", "roomConnectionId": "2a1787a6-4a73-43b5-ae3e-906ec1e763cb" },
            { "displayName": "Adam", "roomConnectionId": "781f012b-f1ea-4ce1-9105-7cfc36fb4ec7" }
          ]
    },
    { 
        "roomToken": "z9Hg4Bk19_Z",
        "deleted": true
     }
     }
  ]
  ]


* '''roomToken''' - The token that uniquely identifies this room
This body is an array of rooms, each of which is formatted as described in [[#GET_.2Frooms.2F.7Btoken.7D]].
* '''roomName''' - The room-owner-assigned name used to identify this room
 
* '''maxSize''' - The maximum number of users allowed in the room at one time (as configured by the room owner).  
Additionally, if a "?version=<version>" parameter is included, then the list will include the rooms that have been deleted since that version. These entries will simply include the roomToken, plus a field "deleted" with a value of "true". None of the other fields need to be included for deleted rooms.
* '''clientMaxSize''' - The current maximum number of users allowed in the room, as constrained by the clients currently participating in the session. If no client has a supported size smaller than "maxSize", then this will be equal to "maxSize". Under no circumstances can "clientMaxSize" be larger than "maxSize".  
 
* '''currSize''' - The number of users currently in the room
[[Category:Firefox Hello]]
* '''ctime''' - Similar in spirit to the Unix filesystem "ctime" (change time) attribute. The time, in seconds since the Unix epoch, that any of the following happened to the room:
** The room was created
** The owner modified its attributes with "PUT /room-url/{token}"
** A user joined the room
** A user left the room
Confirmed users
632

edits