Fix Android Connection Swap Issue (Server Side)
Summary: - Take device id retrieved by matching CSR to the given CSR destination - Otherwise, use the previous way Note: - Backward compatibility will be fixed next diff Reviewed By: jknoxville Differential Revision: D17346422 fbshipit-source-id: 59b2fb9849373db1ba930dde702194c5fb201678
This commit is contained in:
committed by
Facebook Github Bot
parent
88197a7076
commit
2239ca65a5
@@ -112,7 +112,6 @@ export default class Client extends EventEmitter {
|
|||||||
lessPlugins: Plugins | undefined;
|
lessPlugins: Plugins | undefined;
|
||||||
showAllPlugins: boolean;
|
showAllPlugins: boolean;
|
||||||
connection: RSocketClientSocket<any, any> | null | undefined;
|
connection: RSocketClientSocket<any, any> | null | undefined;
|
||||||
responder: Partial<Responder<string, any>>;
|
|
||||||
store: Store;
|
store: Store;
|
||||||
activePlugins: Set<string>;
|
activePlugins: Set<string>;
|
||||||
device: Promise<BaseDevice>;
|
device: Promise<BaseDevice>;
|
||||||
@@ -121,6 +120,7 @@ export default class Client extends EventEmitter {
|
|||||||
logger: Logger;
|
logger: Logger;
|
||||||
lastSeenDeviceList: Array<BaseDevice>;
|
lastSeenDeviceList: Array<BaseDevice>;
|
||||||
broadcastCallbacks: Map<string, Map<string, Set<Function>>>;
|
broadcastCallbacks: Map<string, Map<string, Set<Function>>>;
|
||||||
|
rIC: any;
|
||||||
|
|
||||||
requestCallbacks: Map<
|
requestCallbacks: Map<
|
||||||
number,
|
number,
|
||||||
@@ -162,19 +162,12 @@ export default class Client extends EventEmitter {
|
|||||||
|
|
||||||
const client = this;
|
const client = this;
|
||||||
// node.js doesn't support requestIdleCallback
|
// node.js doesn't support requestIdleCallback
|
||||||
const rIC =
|
this.rIC =
|
||||||
typeof window === 'undefined'
|
typeof window === 'undefined'
|
||||||
? (cb: Function, _: any) => {
|
? (cb: Function, _: any) => {
|
||||||
cb();
|
cb();
|
||||||
}
|
}
|
||||||
: window.requestIdleCallback;
|
: window.requestIdleCallback.bind(window);
|
||||||
|
|
||||||
this.responder = {
|
|
||||||
fireAndForget: (payload: {data: string}) =>
|
|
||||||
rIC(() => client.onMessage(payload.data), {
|
|
||||||
timeout: 500,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (conn) {
|
if (conn) {
|
||||||
conn.connectionStatus().subscribe({
|
conn.connectionStatus().subscribe({
|
||||||
|
|||||||
113
src/server.tsx
113
src/server.tsx
@@ -28,6 +28,11 @@ type ClientInfo = {
|
|||||||
client: Client;
|
client: Client;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ClientCsrQuery = {
|
||||||
|
csr?: string | undefined;
|
||||||
|
csr_path?: string | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
declare interface Server {
|
declare interface Server {
|
||||||
on(event: 'new-client', callback: (client: Client) => void): this;
|
on(event: 'new-client', callback: (client: Client) => void): this;
|
||||||
on(event: 'error', callback: (err: Error) => void): this;
|
on(event: 'error', callback: (err: Error) => void): this;
|
||||||
@@ -124,16 +129,24 @@ class Server extends EventEmitter {
|
|||||||
if (!payload.data) {
|
if (!payload.data) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const clientData: ClientQuery = JSON.parse(payload.data);
|
const clientData: ClientQuery & ClientCsrQuery = JSON.parse(payload.data);
|
||||||
this.connectionTracker.logConnectionAttempt(clientData);
|
this.connectionTracker.logConnectionAttempt(clientData);
|
||||||
|
|
||||||
const client = this.addConnection(socket, clientData);
|
const {app, os, device, device_id, sdk_version, csr, csr_path} = clientData;
|
||||||
|
|
||||||
|
const client: Promise<Client> = this.addConnection(
|
||||||
|
socket,
|
||||||
|
{app, os, device, device_id, sdk_version},
|
||||||
|
{csr, csr_path},
|
||||||
|
);
|
||||||
|
|
||||||
socket.connectionStatus().subscribe({
|
socket.connectionStatus().subscribe({
|
||||||
onNext(payload) {
|
onNext(payload) {
|
||||||
if (payload.kind == 'ERROR' || payload.kind == 'CLOSED') {
|
if (payload.kind == 'ERROR' || payload.kind == 'CLOSED') {
|
||||||
console.debug(`Device disconnected ${client.id}`, 'server');
|
client.then(client => {
|
||||||
server.removeConnection(client.id);
|
console.debug(`Device disconnected ${client.id}`, 'server');
|
||||||
|
server.removeConnection(client.id);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSubscribe(subscription) {
|
onSubscribe(subscription) {
|
||||||
@@ -141,7 +154,14 @@ class Server extends EventEmitter {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return client.responder;
|
return {
|
||||||
|
fireAndForget: (payload: {data: string}) =>
|
||||||
|
client.then(client => {
|
||||||
|
client.rIC(() => client.onMessage(payload.data), {
|
||||||
|
timeout: 500,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
_untrustedRequestHandler = (
|
_untrustedRequestHandler = (
|
||||||
@@ -272,49 +292,66 @@ class Server extends EventEmitter {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
addConnection(
|
async addConnection(
|
||||||
conn: RSocketClientSocket<any, any>,
|
conn: RSocketClientSocket<any, any>,
|
||||||
query: ClientQuery,
|
query: ClientQuery,
|
||||||
): Client {
|
csrQuery: ClientCsrQuery,
|
||||||
|
): Promise<Client> {
|
||||||
invariant(query, 'expected query');
|
invariant(query, 'expected query');
|
||||||
|
|
||||||
const id = `${query.app}#${query.os}#${query.device}#${query.device_id}`;
|
// try to get id by comparing giving `csr` to file from `csr_path`
|
||||||
console.debug(`Device connected: ${id}`, 'server');
|
// otherwise, use given device_id
|
||||||
|
const {csr_path, csr} = csrQuery;
|
||||||
|
return (csr_path && csr
|
||||||
|
? this.certificateProvider.extractAppNameFromCSR(csr).then(appName => {
|
||||||
|
return this.certificateProvider.getTargetDeviceId(
|
||||||
|
query.os,
|
||||||
|
appName,
|
||||||
|
csr_path,
|
||||||
|
csr,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: Promise.resolve(query.device_id)
|
||||||
|
).then(csrId => {
|
||||||
|
query.device_id = csrId;
|
||||||
|
const id = `${query.app}#${query.os}#${query.device}#${csrId}`;
|
||||||
|
console.debug(`Device connected: ${id}`, 'server');
|
||||||
|
|
||||||
const client = new Client(id, query, conn, this.logger, this.store);
|
const client = new Client(id, query, conn, this.logger, this.store);
|
||||||
|
|
||||||
const info = {
|
const info = {
|
||||||
client,
|
client,
|
||||||
connection: conn,
|
connection: conn,
|
||||||
};
|
};
|
||||||
|
|
||||||
client.init().then(() => {
|
client.init().then(() => {
|
||||||
console.debug(
|
console.debug(
|
||||||
`Device client initialised: ${id}. Supported plugins: ${client.plugins.join(
|
`Device client initialised: ${id}. Supported plugins: ${client.plugins.join(
|
||||||
', ',
|
', ',
|
||||||
)}`,
|
)}`,
|
||||||
'server',
|
'server',
|
||||||
);
|
);
|
||||||
|
|
||||||
/* If a device gets disconnected without being cleaned up properly,
|
/* If a device gets disconnected without being cleaned up properly,
|
||||||
* Flipper won't be aware until it attempts to reconnect.
|
* Flipper won't be aware until it attempts to reconnect.
|
||||||
* When it does we need to terminate the zombie connection.
|
* When it does we need to terminate the zombie connection.
|
||||||
*/
|
*/
|
||||||
if (this.connections.has(id)) {
|
if (this.connections.has(id)) {
|
||||||
const connectionInfo = this.connections.get(id);
|
const connectionInfo = this.connections.get(id);
|
||||||
connectionInfo &&
|
connectionInfo &&
|
||||||
connectionInfo.connection &&
|
connectionInfo.connection &&
|
||||||
connectionInfo.connection.close();
|
connectionInfo.connection.close();
|
||||||
this.removeConnection(id);
|
this.removeConnection(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.connections.set(id, info);
|
this.connections.set(id, info);
|
||||||
this.emit('new-client', client);
|
this.emit('new-client', client);
|
||||||
this.emit('clients-change');
|
this.emit('clients-change');
|
||||||
client.emit('plugins-change');
|
client.emit('plugins-change');
|
||||||
|
});
|
||||||
|
|
||||||
|
return client;
|
||||||
});
|
});
|
||||||
|
|
||||||
return client;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attachFakeClient(client: Client) {
|
attachFakeClient(client: Client) {
|
||||||
|
|||||||
@@ -346,7 +346,12 @@ export default class CertificateProvider {
|
|||||||
return androidUtil
|
return androidUtil
|
||||||
.pull(deviceId, processName, directory + csrFileName)
|
.pull(deviceId, processName, directory + csrFileName)
|
||||||
.then(deviceCsr => {
|
.then(deviceCsr => {
|
||||||
return this.santitizeString(deviceCsr.toString()) === csr;
|
// Santitize both of the string before comparation
|
||||||
|
// The csr string extraction on client side return string in both way
|
||||||
|
return (
|
||||||
|
this.santitizeString(deviceCsr.toString()) ===
|
||||||
|
this.santitizeString(csr)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user