Skip to content

Commit

Permalink
alexa: fix race condition in sendResponse
Browse files Browse the repository at this point in the history
  • Loading branch information
koush committed Nov 23, 2023
1 parent fc7d1ea commit b04aa75
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 32 deletions.
35 changes: 29 additions & 6 deletions plugins/alexa/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions plugins/alexa/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@scrypted/alexa",
"version": "0.2.8",
"version": "0.2.9",
"scripts": {
"scrypted-setup-project": "scrypted-setup-project",
"prescrypted-setup-project": "scrypted-package-json",
Expand Down Expand Up @@ -39,6 +39,7 @@
},
"devDependencies": {
"@types/node": "^18.4.2",
"@scrypted/sdk": "../../sdk"
"@scrypted/sdk": "../../sdk",
"@scrypted/common": "../../common"
}
}
9 changes: 6 additions & 3 deletions plugins/alexa/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -632,8 +632,10 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
debug("received directive from alexa", mapName, body);

const handler = alexaHandlers.get(mapName);
if (handler)
return handler.apply(this, [request, response, directive]);
if (handler) {
await handler.apply(this, [request, response, directive]);
return;
}

const deviceHandler = alexaDeviceHandlers.get(mapName);

Expand All @@ -644,7 +646,8 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
return;
}

return deviceHandler.apply(this, [request, response, directive, device]);
await deviceHandler.apply(this, [request, response, directive, device]);
return;
} else {
this.console.error(`no handler for: ${mapName}`);
}
Expand Down
52 changes: 31 additions & 21 deletions plugins/alexa/src/types/camera/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { v4 as createMessageId } from 'uuid';
import { AlexaHttpResponse, sendDeviceResponse } from "../../common";
import { alexaDeviceHandlers } from "../../handlers";
import { Response, WebRTCAnswerGeneratedForSessionEvent, WebRTCSessionConnectedEvent, WebRTCSessionDisconnectedEvent } from '../../alexa'
import { Deferred } from '@scrypted/common/src/deferred';

export class AlexaSignalingSession implements RTCSignalingSession {
constructor(public response: AlexaHttpResponse, public directive: any) {
Expand All @@ -13,7 +14,8 @@ export class AlexaSignalingSession implements RTCSignalingSession {

__proxy_props: { options: RTCSignalingOptions; };
options: RTCSignalingOptions;

remoteDescription = new Deferred<void>();

async getOptions(): Promise<RTCSignalingOptions> {
return this.options;
}
Expand All @@ -39,11 +41,17 @@ export class AlexaSignalingSession implements RTCSignalingSession {
}

async createLocalDescription(type: "offer" | "answer", setup: RTCAVSignalingSetup, sendIceCandidate: RTCSignalingSendIceCandidate): Promise<RTCSessionDescriptionInit> {
if (type !== 'offer')
throw new Error('Alexa only supports RTC offer');
if (type !== 'offer') {
const e = new Error('Alexa only supports RTC offer');
this.remoteDescription.reject(e);
throw e;
}

if (sendIceCandidate)
throw new Error("Alexa does not support trickle ICE");
if (sendIceCandidate) {
const e = new Error("Alexa does not support trickle ICE");
this.remoteDescription.reject(e);
throw e;
}

return {
type: type,
Expand All @@ -67,15 +75,16 @@ export class AlexaSignalingSession implements RTCSignalingSession {
},
context: undefined
};

data.event.header.name = "AnswerGeneratedForSession";
data.event.header.messageId = createMessageId();

data.event.payload.answer = {
format: 'SDP',
value: description.sdp,
};

this.remoteDescription.resolve();
this.response.send(data);
}
}
Expand All @@ -85,13 +94,14 @@ const sessionCache = new Map<string, RTCSessionControl>();
alexaDeviceHandlers.set('Alexa.RTCSessionController/InitiateSessionWithOffer', async (request, response, directive: any, device: ScryptedDevice & RTCSignalingChannel) => {
const { header, endpoint, payload } = directive;
const { sessionId } = payload;

const session = new AlexaSignalingSession(response, directive);
const control = await device.startRTCSignalingSession(session);
control.setPlayback({
audio: true,
video: false,
})
});
await session.remoteDescription.promise;

sessionCache.set(sessionId, control);
});
Expand All @@ -115,13 +125,13 @@ alexaDeviceHandlers.set('Alexa.RTCSessionController/SessionConnected', async (re
alexaDeviceHandlers.set('Alexa.RTCSessionController/SessionDisconnected', async (request, response, directive: any, device: ScryptedDevice) => {
const { header, endpoint, payload } = directive;
const { sessionId } = payload;

const session = sessionCache.get(sessionId);
if (session) {
sessionCache.delete(sessionId);
await session.endSession();
}

const data: WebRTCSessionDisconnectedEvent = {
"event": {
header,
Expand All @@ -130,9 +140,9 @@ alexaDeviceHandlers.set('Alexa.RTCSessionController/SessionDisconnected', async
},
context: undefined
};

data.event.header.messageId = createMessageId();

response.send(data);
});

Expand All @@ -152,14 +162,14 @@ alexaDeviceHandlers.set('Alexa.SmartVision.ObjectDetectionSensor/SetObjectDetect
},
"context": {
"properties": [{
"namespace": "Alexa.SmartVision.ObjectDetectionSensor",
"name": "objectDetectionClasses",
"value": detectionTypes.classes.map(type => ({
"imageNetClass": type
})),
timeOfSample: new Date().toISOString(),
uncertaintyInMilliseconds: 0
}]
"namespace": "Alexa.SmartVision.ObjectDetectionSensor",
"name": "objectDetectionClasses",
"value": detectionTypes.classes.map(type => ({
"imageNetClass": type
})),
timeOfSample: new Date().toISOString(),
uncertaintyInMilliseconds: 0
}]
}
};

Expand Down

0 comments on commit b04aa75

Please sign in to comment.