UNPKG

thali

Version:
537 lines (490 loc) 27 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: NextGeneration/wifiBasedNativeMock.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: NextGeneration/wifiBasedNativeMock.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>'use strict'; var Promise = require('lie'); var ThaliWifiInfrastructure = require('ThaliWifiInfrastructure'); /** @module WifiBasedNativeMock */ /** * @file * * This is a mock of {@link module:thaliMobileNative}. It is intended to * replicate all the capabilities of {@link module:thaliMobileNative} so that we * can build and test code intended to use {@link module:thaliMobileNative} but * on the desktop. * * We are intentionally replicating the lowest layer of the stack in order to * to be able to test on the desktop all the layers on top of it. This includes * emulating behaviors unique to iOS and Android. * * For testing purposes if callNative or registerToNative do not get all the * parameters they were expecting then a "Bad Arguments" exception MUST be * thrown. */ /** * This is the method that actually handles processing the native requests. In * general this method just records the arguments for later use. * * @param {string} mobileMethodName This is the name of the method that was * passed in on the mobile object * @param {platformChoice} platform * @param {Object} router * @param {WifiBasedNativeMock} wifiBasedNativeMock * @constructor */ function MobileCallInstance(mobileMethodName, platform, router, wifiBasedNativeMock) { this.mobileMethodName = mobileMethodName; this.platform = platform; this.router = router; this.wifiBasedNativeMock = wifiBasedNativeMock; } MobileCallInstance.prototype.wifiBasedNativeMock = null; MobileCallInstance.prototype.mobileMethodName = null; MobileCallInstance.prototype.platform = null; MobileCallInstance.prototype.router = null; /** * In effect this listens for SSDP:alive and SSDP:byebye messages along with * the use of SSDP queries to find out who is around. These will be translated * to peer availability callbacks as specified below. This code MUST meet the * same requirements for using a unique SSDP port, syntax for requests, etc. as * {@link module:ThaliWifiInfrastructure}. * * Other requirements for this method MUST match those of {@link * external:"Mobile('startListeningForAdvertisements')".callNative} in terms of * idempotency. This also means we MUST return "Radio Turned Off" if we are * emulating Bluetooth as being off. * * @public * @param {module:thaliMobileNative~ThaliMobileCallback} callBack */ MobileCallInstance.prototype.startListeningForAdvertisements = function(callBack) { }; /** * This shuts down the SSDP listener/query code. It MUST otherwise behave as * given for {@link * external:"Mobile('stopListeningForAdvertisements')".callNative}. * * @public * @param {module:thaliMobileNative~ThaliMobileCallback} callBack */ MobileCallInstance.prototype.stopListeningForAdvertisements = function(callBack) { }; /** * This method tells the system to both start advertising and to accept * incoming connections. In both cases we need to accept incoming connections. * The main challenge is simulating what happens when stop is called. This is * supposed to shut down all incoming connections. So we can't just advertise * our 127.0.0.1 port and let the other mocks running on the same machine * connect since stop wouldn't behave properly. To handle the stop behavior, * that is to disconnect all incoming connections, we have to introduce a TCP * level proxy. The reason we need a TCP proxy is that we are using direct SSL * connections in a way that may or may not properly work through a HTTPS proxy. * So it's simpler to just introduce the TCP proxy. We will advertise the TCP * proxy's listener port in SSDP and when someone connects we will create a TCP * client connection to portNumber and then pipe the two connections together. * * __Open Issue:__ If we directly pipe the TCP listener socket (from connect) * and the TCP client socket (that we created) then will the system * automatically kill the pipe if either socket is killed? We need to test this. * If it doesn't then we just need to hook the close event and close the other * side of the pipe. * * __Note:__ For now we are going to not simulate the Bluetooth handshake for * Android. This covers the scenario where device A doesn't discover device B * over BLE but device B discovered device A over BLE and then connected over * Bluetooth. The handshake would create a simulated discovery event but we are * going to assume that the SSDP discovery will arrive in a timely manner and so * the behavior should be the same. * * For advertising we will use SSDP both to make SSDP:alive as well as to * answer queries as given in {@link module:ThaliWifiInfrastructure}. * * For incoming connections we will, as described above, just rely on everyone * running on 127.0.0.1. * * Otherwise the behavior MUST be the same as defined for (@link * external:"Mobile('startUpdateAdvertisingAndListening')".ca * llNative}. That includes returning the "Call Start!" error as appropriate as * well as returning "Radio Turned Off" if we are emulating Bluetooth as being * off. * * @param {number} portNumber * @param {module:thaliMobileNative~ThaliMobileCallback} callBack */ MobileCallInstance.prototype.startUpdateAdvertisingAndListening = function(portNumber, callBack) { }; /** * This function MUST behave like {@link module:ThaliWifiInfrastructure} and * send a proper SSDP:byebye and then stop responding to queries or sending * SSDP:alive messages. Otherwise it MUST act like * (@link external:"Mobile('stopAdvertisingAndListening')".callNative} * including terminating the TCP proxy and all of its connections to simulate * killing all incoming connections. * * @param {module:thaliMobileNative~ThaliMobileCallback} callBack */ MobileCallInstance.prototype.stopAdvertisingAndListening = function (callBack) { }; /** * All the usual restrictions on connect apply including throwing errors if * start listening isn't active, handling consecutive calls, etc. Please see the * details in {@link external:"Mobile('connect')".callNative}. In this case the * mock MUST keep track of the advertised IP and port for each peerIdentifier * and then be able to establish a TCP/IP listener on 127.0.0.1 and use a TCP * proxy to relay any connections to the 127.0.0.1 port to the IP address and * port that was advertised over SSDP. The point of all this redirection is to * fully simulate the native layer so we can run tests of the Wrapper and above * with full fidelity. This lets us do fun things like simulate turning off * radios as well as properly enforce behaviors such as those below that let our * local listener only accept one connection and simulating time outs on a * single peer correctly (e.g. the other side is still available but we had no * activity locally and so need to tear down). If setting up the outgoing TCP * proxy is a big enough pain we could probably figure a way around it but I'm * guessing that since we need it anyway for incoming connections it shouldn't * be a big deal. * * In the case of simulating Android we just have to make sure that at any * time we have exactly one outgoing connection to any peerIdentifier. So if we * get a second connect for the same peerIdentifier then we have to return the * port for the existing TCP listener we are using, even if it is connected. We * also need the right tear down behavior so that if the local app connection to * the local TCP listener (that will then relay to the remote peer's port) is * torn down then we tear down the connection to the remote peer and vice versa. * * On iOS we need the same behavior as Android plus we have to deal with the * MCSession problem. This means we have to look at the peerIdentifier, compare * it to the peerIdentifier that we generated at the SSDP layer and do a lexical * comparison. If we are lexically smaller then we have to simulate the trick * that iOS uses where we create a MCSession but don't establish any connections * over it. The MCSession is just used as a signaling mechanism to let the * lexically larger peer know that the lexically smaller peer wants to connect. * See the sections below on /ConnectToMeForMock and /IConnectedMock for * details. * * ## Making requests to /ConnectToMeForMock * * After we receive a connect when we are simulating iOS and the requester is * lexically smaller than the target peerIdentifier then we MUST make a GET * request to the target peer's /ConnectToMeForMock endpoint with a query * argument of the form "?port=x&amp;peerIdentifier=y". The port is the port the * current peer wishes the target peer to connect over and the peerIdentifier is * the current peer's peerIdentifier. * * If we get a 400 response then we MUST return the "Connection could not be * established" error. * * If we get a 200 OK then we just have to wait for a /IConnectedMock request * to come in telling us that the remote peer has established a connection. See * the section below on how we handle this. Note that the usual timeout rules * apply so if the /IConnectedMock request does not come within the timeout * period the we MUST issue a "Connection wait time out" error. * * We do not include IP addresses in the request or response because we are * only running the mock amongst instances that are all hosted on the same box * and talking over 127.0.0.1. * * ## Sending responses to /ConnectToMeForMock * * If we are not currently simulating an iOS device then we MUST return a 500 * Server Error because something really bad has happened. We do not currently * support simulating mixed scenarios, everyone in the test run needs to be * either simulating iOS or Android. * * If we are not currently listening for incoming connections then we MUST * return a 400 Bad Request. But we MUST also log the fact that this happened * since baring some nasty race conditions we really shouldn't get a call to * this endpoint unless we are listening. * * If we are listening then we MUST issue a peerAvailabilityChanged callback * and set the peerIdentifier to the value in the query argument, peerAvailable * to true and pleaseConnect to true. We MUST also record the port in the query * argument so that if we get a connect request we know what port to submit. * * In theory it's possible for us to get into a situation where we get one * port for a peerIdentifier in the /ConnectToMeForMock request and a different * port in a SSDP request. We should just publish the PeerAvailablityChanged * event as they come in and for internal mapping of peerIdentifier to port we * should just record whatever came in last. And yes, this can lead to fun race * conditions which is the situation in the real world too. * * ## Making requests to /IConnectedMock * * If we are simulating iOS and if we are establishing a TCP connection to a * remote peer then by definition we are the lexically larger peer. However the * iOS protocol shares our peerIdentifier with the remote peer, TCP does not. To * work around this anytime we are simulating iOS and have successfully * established a TCP connection to a remote peer we MUST issue a GET request to * the /IConnectedMock endpoint of the remote peer with the query string * "?clientPort=x&amp;serverPort=z&amp;peerIdentifier=y". The clientPort and serverPort * are the client port and server port values from the TCP connection that * caused us to send this request in the first place. The peerIdentifier is our * peerIdentifier. If we get a 400 response back then we MUST log this event as * it really should not have happened. * * ## Sending response to /IConnectedMock * * If we are not currently simulating an iOS device then we MUST return a 500 * Server Error because something really bad has happened. We do not currently * support simulating mixed scenarios, everyone in the test run needs to be * either simulating iOS or Android. * * If we are not currently listening for incoming connections then we MUST * return a 400 Bad Request. But we MUST also log the fact that this happened * since baring some nasty race conditions we really shouldn't have been able to * set up the TCP connection in the first place. * * Otherwise we MUST return a 200 OK. * * When we return a 200 OK we MUST issue a peerAvailabilityChanged callback * with peerIdentifier set to the submitted peerIdentifier, peerAvailable set to * true and pleaseConnect set to false. If we have an outstanding connect * request to the specified peerIdentifier then we MUST look up the specified * clientPort/serverPort and see if we can match it to any of the incoming * connections to the TCP proxy. If we can then we MUST return the * clientPort/serverPort being used by the TCP proxy as the connect response * with listeningPort set to null and clientPort/serverPort set to the values * the TCP proxy is using. If we cannot match the connection via the TCP proxy * then this means that the connection might have died or been killed while this * request to /IConnectedMock was being sent. In that case we should send bogus * values in the connect response to simulate a situation where a peer connects * but then the connection dies before the connect callback is returned. * * @param {string} peerIdentifier * @param {module:thaliMobileNative~ConnectCallback} callback */ MobileCallInstance.prototype.connect = function(peerIdentifier, callback) { }; /** * If we aren't emulating iOS then this method has to return the "Not * Supported" error. If we are emulating iOS then we have to kill all the TCP * listeners we are using to handling outgoing connections and the TCP proxy we * are using to handle incoming connections. * * @public * @param {module:thaliMobileNative~ThaliMobileCallback} callback */ MobileCallInstance.prototype.killConnections = function (callback) { }; /** * Handles processing callNative requests. The actual params differ based on * the particular Mobile method that is being called. */ MobileCallInstance.prototype.callNative = function () { switch (this.mobileMethodName) { case 'startListeningForAdvertisements': { return this.startListeningForAdvertisements(arguments[0]); } case 'stopListeningForAdvertisements': { return this.stopListeningForAdvertisements(arguments[0]); } case 'startUpdateAdvertisingAndListening': { return this.startUpdateAdvertisingAndListening( arguments[0], arguments[1]); } case 'stopAdvertisingAndListening': { return this.stopAdvertisingAndListening( arguments[0]); } case 'connect': { return this.connect(arguments[0], arguments[1]); } case 'killConnections': { return this.killConnections(arguments[0]); } default: { throw new Error('The supplied mobileName does not have a matching ' + 'callNative method: ' + this.mobileMethodName); } } }; /** * Anytime we are looking for advertising and we receive a SSDP:alive, * SSDP:byebye or a response to one of our periodic queries we should use it to * create a peerAvailabilityChanged call back. In practice we don't really need * to batch these messages so we can just fire them as we get them. The * peerIdentifier is the USN from the SSDP message, peerAvailable is true or * false based on the SSDP response and pleaseConnect is false except for the * situation described above for /ConnectToMeforMock. * * @param {module:thaliMobileNative~peerAvailabilityChangedCallback} callback */ MobileCallInstance.prototype.peerAvailabilityChanged = function (callback) { }; /** * Any time there is a call to start and stop or if Bluetooth is turned off on * Android (which also MUST mean that we have disabled both advertising and * discovery) then we MUST fire this event. * * @public * @param {module:thaliMobileNative~discoveryAdvertisingStateUpdateNonTCPCallback} callback */ MobileCallInstance.prototype.discoveryAdvertisingStateUpdateNonTCP = function (callback) { }; /** * At this point this event would only fire because we called toggleBluetooth * or toggleWifi. For the moment we will treat toggleBluetooth and turning * on/off both blueToothLowEnergy and blueTooth. * * __Open Issue:__ Near as I can tell both Android and iOS have a single * Bluetooth switch that activates and de-activates Bluetooth and BLE. Note * however that in theory it's possible to still have one available and not the * other to a particular application because of app level permissions but that * isn't an issue for the mock. * * @public * @param {module:thaliMobileNative~networkChangedCallback} callback */ MobileCallInstance.prototype.networkChanged = function (callback) { }; /** * This is used anytime the TCP proxy for incoming connections cannot connect * to the portNumber set in * {@link module:WifiBasedNativeMock~MobileCallInstance.startUpdateAdvertisingAndListening}. * * @public * @param {module:thaliMobileNative~incomingConnectionToPortNumberFailedCallback} callback */ MobileCallInstance.prototype.incomingConnectionToPortNumberFailed = function (callback) { }; MobileCallInstance.prototype.registerToNative = function () { switch (this.mobileMethodName) { case 'peerAvailabilityChanged': { return this.peerAvailabilityChanged(arguments[0]); } case 'discoveryAdvertisingStateUpdateNonTCP': { return this.discoveryAdvertisingStateUpdateNonTCP(arguments[0]); } case 'networkChanged': { return this.networkChanged(arguments[0]); } case 'incomingConnectionToPortNumberFailed': { return this.incomingConnectionToPortNumberFailed(arguments[0]); } default: { throw new Error('The supplied mobileName does not have a matching ' + 'registerToNative method: ' + this.mobileMethodName); } } }; /** * Enum to describe the platforms we can simulate, this mostly controls how we * handle connect * * @public * @readonly * @enum {string} */ var platformChoice = { ANDROID: 'Android', IOS: 'iOS' }; /** * This simulates turning Bluetooth on and off. * * If we are emulating Android then we MUST start with Bluetooth and WiFi * turned off. * * __Open Issue:__ I believe that JXCore will treat this as a NOOP if called * on iOS. We need to check and emulate their behavior. * * @param {platformChoice} platform * @param {ThaliWifiInfrastructure} wifiBasedNativeMock * @returns {Function} */ function toggleBluetooth (platform, wifiBasedNativeMock) { return function (setting, callback) { return null; }; } /** * If we are on Android then then is a NOOP since we don't care (although to * be good little programmers we should still fire a network changed event). We * won't be using Wifi for discovery or connectivity in the near future. * * __Open Issue:__ I believe that JXCore will treat this as a NOOP if called * on iOS. We need to check and emulate their behavior. * * @param {platformChoice} platform * @param {ThaliWifiInfrastructure} wifiBasedNativeMock * @returns {Function} */ function toggleWiFi(platform, wifiBasedNativeMock) { return function (setting, callback) { return null; }; } /** * To use this mock save the current global object Mobile (if it exists) and * replace it with this object. In general this object won't exist on the * desktop. * * If we are simulating iOS then we MUST add the /ConnectToMeForMock and * /IConnectedMock endpoints as described above to the router object. * * @public * @constructor * @param {platformChoice} platform * @param {Object} router This is the express router being used up in the * stack. We need it here so we can add a router to simulate the iOS case where * we need to let the other peer know we want a connection. */ function WifiBasedNativeMock(platform, router) { var thaliWifiInfrastructure = new ThaliWifiInfrastructure(); var mobileHandler = function (mobileMethodName) { return new MobileCallInstance(mobileMethodName, platform, router, thaliWifiInfrastructure); }; mobileHandler.toggleBluetooth = toggleBluetooth(thaliWifiInfrastructure); mobileHandler.toggleWiFi = toggleWiFi(thaliWifiInfrastructure); return mobileHandler; } module.exports = WifiBasedNativeMock; </code></pre> </article> </section> </div> <nav> <h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-TCPServersManager.html">TCPServersManager</a></li><li><a href="module-thaliMobile.html">thaliMobile</a></li><li><a href="module-thaliMobileNative.html">thaliMobileNative</a></li><li><a href="module-thaliMobileNativeWrapper.html">thaliMobileNativeWrapper</a></li><li><a href="module-thaliNotificationAction.html">thaliNotificationAction</a></li><li><a href="module-thaliNotificationBeacons.html">thaliNotificationBeacons</a></li><li><a href="module-thaliNotificationClient.html">thaliNotificationClient</a></li><li><a href="module-thaliNotificationServer.html">thaliNotificationServer</a></li><li><a href="module-thaliPeerAction.html">thaliPeerAction</a></li><li><a href="module-thaliPeerDictionary.html">thaliPeerDictionary</a></li><li><a href="module-thaliPeerPoolInterface.html">thaliPeerPoolInterface</a></li><li><a href="module-ThaliWifiInfrastructure.html">ThaliWifiInfrastructure</a></li><li><a href="module-WifiBasedNativeMock.html">WifiBasedNativeMock</a></li></ul><h3>Externals</h3><ul><li><a href="external-_Mobile(_connect_)_.html">Mobile('connect')</a></li><li><a href="external-_Mobile(_discoveryAdvertisingStateUpdateNonTCP_)_.html">Mobile('discoveryAdvertisingStateUpdateNonTCP')</a></li><li><a href="external-_Mobile(_incomingConnectionToPortNumberFailed_)_.html">Mobile('incomingConnectionToPortNumberFailed')</a></li><li><a href="external-_Mobile(_killConnections_)_.html">Mobile('killConnections')</a></li><li><a href="external-_Mobile(_networkChanged_)_.html">Mobile('networkChanged')</a></li><li><a href="external-_Mobile(_peerAvailabilityChanged_)_.html">Mobile('peerAvailabilityChanged')</a></li><li><a href="external-_Mobile(_startListeningForAdvertisements_)_.html">Mobile('startListeningForAdvertisements')</a></li><li><a href="external-_Mobile(_startUpdateAdvertisingAndListening_)_.html">Mobile('startUpdateAdvertisingAndListening')</a></li><li><a href="external-_Mobile(_stopAdvertisingAndListening_)_.html">Mobile('stopAdvertisingAndListening')</a></li><li><a href="external-_Mobile(_stopListeningForAdvertisements_)_.html">Mobile('stopListeningForAdvertisements')</a></li></ul><h3>Classes</h3><ul><li><a href="ConnectionTable.html">ConnectionTable</a></li><li><a href="module-TCPServersManager-TCPServersManager.html">TCPServersManager</a></li><li><a href="module-thaliNotificationAction-NotificationAction.html">NotificationAction</a></li><li><a href="module-thaliNotificationBeacons-ParseBeaconsResponse.html">ParseBeaconsResponse</a></li><li><a href="module-thaliNotificationClient-ThaliNotificationClient.html">ThaliNotificationClient</a></li><li><a href="module-thaliNotificationServer-ThaliNotificationServer.html">ThaliNotificationServer</a></li><li><a href="module-thaliPeerAction-PeerAction.html">PeerAction</a></li><li><a href="module-thaliPeerDictionary-NotificationPeerDictionaryEntry.html">NotificationPeerDictionaryEntry</a></li><li><a href="module-thaliPeerDictionary-PeerConnectionInformation.html">PeerConnectionInformation</a></li><li><a href="module-thaliPeerDictionary-PeerDictionary.html">PeerDictionary</a></li><li><a href="module-thaliPeerPoolInterface-ThaliPeerPoolInterface.html">ThaliPeerPoolInterface</a></li><li><a href="module-ThaliWifiInfrastructure-ThaliWifiInfrastructure.html">ThaliWifiInfrastructure</a></li><li><a href="module-WifiBasedNativeMock-MobileCallInstance.html">MobileCallInstance</a></li><li><a href="module-WifiBasedNativeMock-WifiBasedNativeMock.html">WifiBasedNativeMock</a></li></ul><h3>Events</h3><ul><li><a href="module-thaliMobileNativeWrapper.html#~event:discoveryAdvertisingStateUpdateNonTCPEvent">discoveryAdvertisingStateUpdateNonTCPEvent</a></li><li><a href="module-ThaliWifiInfrastructure.html#~event:discoveryAdvertisingStateUpdateWifiEvent">discoveryAdvertisingStateUpdateWifiEvent</a></li><li><a href="module-TCPServersManager.html#~event:failedConnection">failedConnection</a></li><li><a href="module-thaliMobileNativeWrapper.html#~event:incomingConnectionToPortNumberFailed">incomingConnectionToPortNumberFailed</a></li><li><a href="module-thaliMobileNativeWrapper.html#~event:networkChangedNonTCP">networkChangedNonTCP</a></li><li><a href="module-ThaliWifiInfrastructure.html#~event:networkChangedWifi">networkChangedWifi</a></li><li><a href="module-thaliMobileNativeWrapper.html#~event:nonTCPPeerAvailabilityChangedEvent">nonTCPPeerAvailabilityChangedEvent</a></li><li><a href="module-TCPServersManager.html#~event:routerPortConnectionFailed">routerPortConnectionFailed</a></li><li><a href="module-ThaliWifiInfrastructure.html#~event:wifiPeerAvailabilityChanged">wifiPeerAvailabilityChanged</a></li><li><a href="module-thaliMobile.html#.event:event:discoveryAdvertisingStateUpdate">discoveryAdvertisingStateUpdate</a></li><li><a href="module-thaliMobile.html#.event:event:networkChanged">networkChanged</a></li><li><a href="module-thaliMobile.html#.event:event:peerAvailabilityChanged">peerAvailabilityChanged</a></li><li><a href="module-thaliNotificationAction-NotificationAction.html#.event:event:Resolved">Resolved</a></li><li><a href="module-thaliNotificationClient.html#.event:event:peerAdvertisesDataForUs">peerAdvertisesDataForUs</a></li></ul><h3>Global</h3><ul><li><a href="global.html#getPKCS12Content">getPKCS12Content</a></li><li><a href="global.html#getPublicKeyHash">getPublicKeyHash</a></li><li><a href="global.html#stopThaliReplicationManager">stopThaliReplicationManager</a></li><li><a href="global.html#ThaliEmitter">ThaliEmitter</a></li></ul> </nav> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Mon Jan 18 2016 11:19:31 GMT+0200 (EET) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>