Loop/Architecture/MVP

From MozillaWiki
Jump to: navigation, search

Note that the changes proposed here are preliminary, and will be updated as the result of conversations with interested parties.

Date Author Changes
Tue Jun 17 11:38:10 EDT 2014 Adam Roach Added call ids to WSS URLs; added GET /call/{token}; added callType to call initiation call
Fri Jun 20 16:34:31 CDT 2014 Adam Roach Added urlDate to GET /calls?version=<version>
Tue Jun 24 12:08:55 CDT 2014 Adam Roach Added 'state' to GET /calls?version=<version>; Added 'calleeId' to GET /call/{token}
Thu Jun 26 15:57:55 UTC+1 2014 Alexis Métaireau Changed WSS URLs so they are just domains + ports.
Mon Jun 30 18:32:31 UTC+1 2014 Rémy Hubscher Add missing calleeId information.
Thu Aug 01 14:52:00 UTC+1 2014 Alexis Métaireau replace POST by PUT for /call-url/{token}
Wed Jul 2 15:20:40 CDT 2014 Adam Roach Added "channel" parameter to POST /calls (see bug 1033573)
Mon Jul 14 11:58:01 CDT 2014 Adam Roach Increased connecting timer from 5 seconds to 10 seconds (see bug 1037979)
Wed Jul 16 14:57:15 CDT 2014 Adam Roach Added two new channel types for mobile and standalone clients
Fri Jul 18 12:01:12 CDT 2014 Rémy Hubscher Change the Transition paragraph with last decisions.

Loop Server API Changes

Modified API Calls

The following existing API calls need to be enhanced to accommodate the new functionality required for MVP. Note that these changes are intended to be strictly backwards compatible with the existing 1.0 API: all changes to existing request formats are in the form of new optional fields, and all changes to existing response formats are new fields in existing structures. These new fields are indicated in bold below.

These changes assume that the current API documentation is an accurate as-built description. In particular, we assume that the following statement is true: "You can currently authenticate by sending a valid Firefox Accounts assertion or a valid Hawk session," where "valid Firefox Accounts assertion" includes the MSISDN-based assertions generated by the MSISDN verification server.

POST /call-url

To facilitate the display of a "friendly name" to the calling user, the link generator needs to be able to include this information to be associated with the link. See bug 1025779

POST /call-url HTTP/1.1
Accept: application/json
Content-Type: application/json; charset=utf-8
Authorization: <authentication information>
{
    "callerId": "alexis@example.com",
    "expiresIn": 5,
    "issuer": "Adam Roach"
}

New Parameters:

  • issuer: Friendly name, if any, configured by person who generated the URL. If no name is configured, this field will be omitted.

POST /calls/{token}

Both call initiator and call recipient need to receive call setup progress information. See "WebSockets Connection for Call Progress", below for associated bugs.

POST /calls/FfzMMm2hSlP HTTP/1.1
Content-Type: application/json; charset=utf-8
Accept: application/json

{
  "callType": "audio-video"
}

New Parameters:

  • callType: Specifies the type of media the remote party intends to send. Valid values are "audio" or "audio-video".
HTTP/1.1 200 OK
Access-Control-Allow-Methods: GET,POST
Access-Control-Allow-Origin: https://localhost:3000
Content-Type: application/json; charset=utf-8

{
    "apiKey": "44700952",
    "sessionId": "2_MX40NDcwMDk1Mn5-V2VkIE1hciA",
    "sessionToken": "T1==cGFydG5lcl9pZD00NDcwMD",
    "callId": "1afeb4340d995938248ce7b3e953fe80",
    "progressURL": "wss://loop214.services.mozilla.com:443",
    "calleeId": "Adam Roach"
}

New parameters:

  • progressURL: WebSockets URL the client needs to connect to in order to receive call setup progress information and control call setup.
  • calleeId: The name of the URL issuer that the caller is calling.

GET /calls?version=<version>

Changes are required to support three features:

  1. Incoming calls need to display the name of the calling user (See UX design and bug 1025881)
  2. Incoming calls need to display whether the calling user is going to send audio and video, or audio only. (See UX design and bug 1025883)
  3. Both call initiator and call recipient need to receive call setup progress information. See "WebSockets Connection for Call Progress", below for associated bugs.
  4. The called party needs to be able to reject an incoming call and revoke the associated URL; see UX design and bug 1026494.
GET /calls?version=1234 HTTP/1.1
Accept: application/json
Authorization: <authentication information>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
    "calls": [
        {
            "apiKey": "13245678",
            "sessionId": "2_MX40NDcwMDk1Mn5",
            "sessionToken": "T1==cGFydG5lcl",
            "callId": "1afeb4340d995938248ce7b3e953fe80",
            "callType": "audio-video",
            "callerId": "alexis@example.com",
            "progressURL": "wss://loop214.services.mozilla.com:443",
            "state": "init"
            "callUrl": "http://localhost:5000/calls/FfzMMm2hSP" // optional
            "urlCreationDate": "1403300282" //optional
        }
    ]
}

New Parameters:

  • callType: Specifies the type of media the remote party intends to send. Valid values are "audio" or "audio-video".
  • callerId: Identity of calling party. Can be email address, E.164-format phone number (must start with a "+" character), or memento indicated by user at link generation time.
  • progressURL: WebSockets URL the client needs to connect to in order to receive call setup progress information and control call setup.
  • state: Call setup state for the incoming call (see #WebSockets Connection for Call Progress, below).
  • callUrl: For link-clicker calls, the URL that was used to initiate this call. This is used to revoke the URL at call alerting time, if necessary.
  • urlCreationDate: For link-clicker calls, the date on which the link was generated.

New API Calls

In addition to the changes detailed above, we need the following API methods to be added so as to facilitate the functionality required for MVP.

GET /call/{token}

This is used for outgoing calls initiated by the standalone client only.

To facilitate the display of a "friendly name" to the calling user, the link clicker needs to receive this information before committing to the call. See bug 1025779. This is done by retrieving the called-party name from the Loop server using a GET request.

GET /call/FfzMMm2hSlP
Accept: application/json
HTTP/1.1 200 OK
Access-Control-Allow-Methods: GET,POST
Access-Control-Allow-Origin: http://localhost:3000
Content-Type: application/json; charset=utf-8

{
   calleeName: "Adam Roach"
   calleeId: "adam@nostrum.com" // optional, only present if link generator is logged in
}

For security purposes, it is important that these GET requests honor URL revocation -- if a URL has been revoked, attempts to get the called party name must fail.

POST /calls

New Operation -- see bug 1015085 and bug 1033573

POST /calls HTTP/1.1
Accept: application/json
Authorization: <authentication information>

{
   "calleeId": [
     "adam@example.com",
     "+12145551234",
     {"phoneNumber": "(817) 569-8900", "mcc": "310"}
   ],
   "callType": "audio-video",
   "channel": "nightly"
}
  • calleeId: Array of identities the calling party has for the user they are trying to reach. This is a list of one or more identities of the following types:
    • Phone numbers: fully-qualified (i.e., starting with "+") E.164-format phone number.
    • Email addresses
  • callType: Specifies the type of media the remote party intends to send. Valid values are "audio" or "audio-video".
  • channel: The release channel of the calling client; this is the value from the app.update.channel pref, and can be any one of:
    • release
    • esr
    • beta
    • aurora
    • nightly
    • default -- I believe this value indicates that the browser is not configured for automatic updates.
    • mobile -- used for the Firefox OS Mobile client
    • standalone -- used for the standalone / "link-clicker" client
HTTP/1.1 200 OK
Access-Control-Allow-Methods: GET,POST
Access-Control-Allow-Origin: https://localhost:3000
Content-Type: application/json; charset=utf-8

{
    "apiKey": "44700952",
    "sessionId": "2_MX40NDcwMDk1Mn5-V2VkIE1hciA",
    "sessionToken": "T1==cGFydG5lcl9pZD00NDcwMD",
    "callId": "1afeb4340d995938248ce7b3e953fe80",
    "progressURL": "wss://loop214.services.mozilla.com:443",
    "calleeId": "adam@example.com"
}

The response body is interepreted in the same way as for POST /call/{token}

PUT /call-url/{token}

New Operation -- In discussions with Darrin, it has been assumed that the link generator can update the information associated with the link at a later time.

This operation allows the properties associated with the given token to be modified.

PUT /call-url/FfzMMm2hSlP HTTP/1.1
Accept: application/json
Content-Type: application/json; charset=utf-8
Authorization: <authentication information>
{
    "callerId": "remy@example.com",
    "expiresIn": 250,
    "issuer": "Mark Banner"
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
    "expiresAt": 390388
}

Deprecated API Calls

The following existing operations are deprecated in favor of the WebSockets interface described below. They may be removed in a future version of the API:

  • GET /calls/id/{callId}
  • DELETE /calls/id/{callId}

WebSockets Connection for Call Progress

Several MVP use cases necessitate the ability to communicate information about the state of a call during its setup phase from the server to the clients, as well as a means to convey call setup control information from the clients to the server. These use cases include:

To facilitate these operations in a timely fashion, the client will establish a WebSockets connection to the resource indicated in the "progressURL" when it receives it. The client never closes this connection; that is the responsibility of the server. The times at which the server closes the connection are detailed below. If the server sees the client close the connection, it assumes that the client has failed, and informs the other party of such call failure.

For forward compatibility purposes:

  • Unknown fields in messages are ignored
  • Unknown message types received by the client (indicating an earlier release) result in the client sending an "error" message ({"messageType": "error", "reason": "unknown message"}). The call setup should continue.
  • Unknown message types received by the server result in the server sending an "error" message (as above); however, since this situation can only arise due to a misimplemented client or an out-of-date server, it results in call setup failure. The server closes the connection.


Call Setup States

Call setup goes through the following states:

Call Setup State Diagram

Call Progress Protocol

Initial Connection (hello)

Upon connecting to the server, the client sends an immediate "hello" message, which serves two purposes: it identifies the call that the progress channel corresponds to (using the "callId"), as well as authenticating the connecting user, so that they can be verified to be authorized to view/impact the call setup state.

Note that the callId with which this connection is to be associated is encoded as a component of the WSS URL.

UA -> Server

{
  "messageType": "hello",
  "callId: "<call identifier>",
  "auth": "<authentication information>"
}


  • auth: Information to authenticate the user, so that they can be verified to be authorized to access call setup information.
    • For authenticated users, the value that would ordinarily be included in the "Authorization" header field.
    • For "link clicker" users, this is the token from the "callback" URL. Open Issue: Alternately, we could add a Hawk token to the "POST /call/..." response, if that would be easier to implement

If the hello is valid (the callId is known, the auth information is valid, and the authenticated user is a party to the call), then the server responds with a "hello." This "hello" includes the current call setup state.

Server -> UA

{
  "messageType": "hello",
  "state": "alerting"
  // may contain "reason" field for certain states.
}
  • state: See states in "progress", below.

If the hello is invalid for any reason, then the server sends an "error" message, as follows. It then closes the connection.

Server -> UA

{
  "messageType": "error",
  "reason": "unknown callId"
}
  • reason: The reason the hello was rejected:
    • unknown callId
    • invalid authentication - The auth information was not valid
    • unauthorized - The auth information was valid, but did not match the indicated callId

Call Progress State Change (progress)

The server informs users of the current state of call setup. The state sent to both parties is always the same state. So, for example, when a user rejects a call, he will receive a "progress" message with a state of "terminated" and a reason of "rejected."

Server -> UA

{
  "messageType": "progress",
  "state": "alerting"
  // may contain optional "reason" field for certain events.
}

Defined states are:

  • init: The call is starting, and the remote party is not yet being alerted.
  • alerting: The called party is being alerted (triggered by remote party sending a "hello" message).
  • terminated: The call is no longer being set up. After sending a "terminated" message, the server closes the WebSockets connection. This message will include a "reason" field with one of the reason values described below.
  • connecting: The called party has indicated that he has answered the call, but the media is not yet confirmed
  • half-connected: One of the two parties has indicated successful media set up, but the other has not yet.
  • connected: Both endpoints have reported successfully establishing media. After sending a "connected" message, the server closes the WebSockets connection.

Client Action (action)

During call setup, clients send progress information about their own state so that it can be reflected in the call state.

UA -> Server

{
  "messageType": "action",
  "event": "accept"
  // May contain "reason" field for certain events
}

Defined event types are:

  • accept: Only sent by called party. The user has answered this call. This is sent before the called party attempts to set up the media.
  • media-up: Sent by both parties. Communications have been successfully established.
  • terminate: Sent by both parties. Ends attempt to set up call. Includes a "reason" field with one of values detailed below.

Termination Reasons

The following reasons appear in "action"/"terminate" and "progress"/"terminated" messages. The "√" columns indicate whether the indicated element is permitted to generate the reason. When generated a "terminated" message as the result of receiving a "terminate" action from either client, the server will copy the reason code from the "terminate" action message into all resulting "terminated" progress messages, even if it does not recognize the reason code.

To provide for forwards compatibility, clients must be prepared to process "terminated" progress messages with unknown reason codes. The reaction to this situation should be the display of a generic "call setup failed" message.

If the server receives an action of "terminate" with a reason it does not recognize, it copies that reason into the resulting "terminated" message.

Reason Caller Callee Server Note
reject The called user has declined the call.
busy The user is logged in, but cannot answer the call due to some current state (e.g., DND, in another call)
timeout The call setup has timed out (The called party's client has exceeded the amount of time it is willing to alert the user, or one of the server's timers expired)
cancel The calling party has cancelled a pending call.
media-fail The called user has declined the call
unregistered The called user does not have any registered push server endpoints.
user-unknown The indicated user id does not exist.
connection-failure The other user's WSS connection closed unexpectedly
answered-elsewhere When the called user has more than one device alerting at the same time and accepts on one device, a "terminated" with this reason code is sent to all other devices that had been alerting.

Timer Supervision

Server Timers

The server uses three timers to ensure that the call created by a setup attempt is cleaned up in a timely fashion.

Supervisory Timer

After responding to a POST /call/{token} or POST /call/user message, the server starts a supervisory timer of 10 seconds.

  • If the calling user does not connect and send a "hello" in this time period, the server considers the call to be failed. The called user, if connected, will receive a "progress"/"terminated" message with a reason of "timeout".
  • If the called user does not connect and send a "hello" in this time period, the server considers the call to be failed. The calling user, if connected, will receive a "progress"/"terminated" message with a reason of "timeout".
Ringing Timer

Upon receiving a "hello" from the called user, the server starts a ringing timer of 30 seconds. If the called user does not send an "accept" message in this time period, then both parties will receive a "progress"/"terminated" message with a reason of "timeout".

Connection Timer

Upon receiving an "accept" from the called user, the server starts a connection timer of 10 seconds. If the call setup state does not reach "connected" in this time period, then both parties will receive a "progress"/"terminated" message with a reason of "timeout".

Client Timers

Response Timer

Every client message triggers a response from the server: "hello" results in "hello" or "error"; and "action" will always cause a corresponding "progress" message to be sent. When the client sends a message, it sets a timer for 5 seconds. If the server does not respond in that time period, it disconnects from the server and considers the call failed.

Media Setup Timer

After sending a "media-up" action, the client sets a timer for 5 seconds. If the server does not indicate that the call setup has entered the "connected" state before the timer expires, the client disconnects from the server and considers the call failed.

Alerting Timer

We may wish to let users configure the maximum amount of time the call is allowed to ring (up to 30 seconds) before it considers it unanswered. This timer would start as soon as user alerting begins. If it expires before the call is set up, then the called party sends a "action"/"disconnect" message with a reason of "timeout."

Network Resource Usage

Although there may be some marginal benefits to keeping the WebSockets connection during the course of an ongoing call, the current design does not do so (however, it leaves open the possibility of doing so in the future, if necessary). This trade-off is made out of concern for scalability: at present, the servers on which the Loop Server are being deployed have hard connection limits that are not conducive to connections lasting as long as ordinary conversations do. Note, however, that the decision to distribute the WSS URLs as part of call setup is being done, in part, to facilitate scaling the number of servers up to arbitrarily large cluster sizes.

The server timers, described below, make it impossible for any of these connections to stay open for longer than 45 seconds. In practice, unanswered calls may result in connections for as long as 30 seconds; although these will typically be abandoned by the calling user well before this time. Answered calls will be anywhere in the range from 0 to 30 seconds.

Percentage of Calls Answered or Rejected by Callee 45%
Average Ring Time of Answered or Rejected Calls 8.5 seconds
Percentage of Calls Abandoned by Caller or Timed Out 55%
Average ring time of abandoned calls 10 seconds
Overall average ring time 9.325 seconds
Offered traffic 0.1 Erlang per subscriber
Average call hold time 90 seconds
Average Websockets Connections per Call Setup 2.25 (1 caller + 1 callee + 0.25 "other callee device")

Although the application of circuit-switched techniques to a monolithic VoIP setup is not completely appropriate, it's a good first-order approximation until we have better information about actual calling patterns. To that end, we will be calculating Busy Hour Call Attempts as BHCA = offered traffic * 3600 / avg hold time. This is likely to be far more conservative than actual calling patterns for Loop.

The following projections conservatively assume that short term-usage will approach 100% saturation of Nightly users, while long-term usage will significantly exceed 100% of total Firefox users.

Short-Term Projection

Systemwide Users 100,000
Busy Hour Call Attempts 400,000
Peak call attempts per second 111
Maximum Simultaneous Connections 2,329

Long-Term Projection

Systemwide Users 500,000,000
Busy Hour Call Attempts 2,000,000,000
Peak call attempts per second 555,555
Maximum Simultaneous Connections 11,656,238

URL Generation, Tracking, and Revocation

During the UX discussions for MVP, several issues were raised that necessitate moving away from the model in which all state is encoded in the URLs themselves. In particular:

  • The length of the issued URLs were deemed too cumbersome for users to easily deal with (see bug 1026426).
  • A need to issue a URL with incomplete or incorrect "caller id" information, with the option to complete or correct the information at a later point in time.

Further, there are several use cases which, while not formally requirements yet, are likely to be required in future product releases:

  • "Vanity" URLs, where users are allowed to select the "token" portion of their URL (e.g., "https://call.mozilla.com/adam-roach/")
  • Single-use URLs, implemented by adjusting the expiration time on a URL to be some very short period of time after its first use.
  • Extending or shortening the lifetime of an already-issued URL.

These requirements have the following implications:

  1. The URL creation call will now create state in the server -- equivalent to the state currently being encoded in the URLs -- and issue a short token that is used as a key to look up that information.
  2. We need a new operation that allows updating the information associated with a URL.
  3. We no longer need a revocation table, as URL revocation can be effected by removing the token from the table of issued tokens.
  4. Except for the new operation that allows modification of an existing token, this has no impact the client behavior.

Token Length and Generation

The token portion of "call me" URLs serve as bearer tokens that represent both addressing information for the user as well as authorization to call that user. While making them cryptographically strong against guessing is likely overkill, we do want to make guessing valid tokens to be sufficiently difficult that they cannot be trivially used for the purpose of generating large volumes of unsolicited calls.

Assuming a user base of ~500M users (29 bits), generating one URL a day, each with an expiration of one month (5 bits), we're using 34 bits of information for uniqueness. Pulling a "reasonably large" value out of thin air, it seems that requiring an adversary to attempt over a billion (30 bits) call attempts before successfully guessing a valid token would be a reasonable deterrent.

Given these assumptions, then, 64 bits of information in the token should be sufficient for both addressing and for deterring unwanted calls. If Base64 encoding is used, this yields tokens that are 11 characters long.

Note, however, that the "vanity URLs" mentioned above may well be significantly longer than these tokens -- the underlying data structures should accommodate significantly longer strings.

Transition

MLP and MVP version are not compatible.

Since there is only nightly users on MLP (Loop 0.5.0) and that it only handle call-url, all old call-url will stop working and defined as expired.

During the transition from MLP to MVP all old user will have to recreate call-url in order to get the new format.

The initial plan was to have both URLs working during a month to smooth the transition but it doesn't worth it with regards to the number of existing call-urls.

Advanced Call Handling

This section is not yet completed

  • Multiple incoming calls
  • Call forking

Accounted calling

This section is not yet completed

Identity

This section is not yet completed See http://dev.w3.org/2011/webrtc/editor/webrtc.html#identity