Skip to content

Commit

Permalink
Return http error when receiving duplicate values in /characteristics…
Browse files Browse the repository at this point in the history
… set/get requests
  • Loading branch information
Supereg committed Oct 8, 2020
1 parent 0ac698f commit 5f8a827
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 20 deletions.
41 changes: 26 additions & 15 deletions src/lib/Accessory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ import { ButtonEvent } from "./gen/HomeKit-Remote";
import {
AccessoriesCallback,
AddPairingCallback,
TLVErrorCode,
HAPHTTPCode,
HAPServer,
HAPServerEventTypes,
Expand All @@ -66,6 +65,7 @@ import {
ReadCharacteristicsCallback,
RemovePairingCallback,
ResourceRequestCallback,
TLVErrorCode,
WriteCharacteristicsCallback
} from './HAPServer';
import { AccessoryInfo, PermissionTypes } from './model/AccessoryInfo';
Expand Down Expand Up @@ -1279,7 +1279,13 @@ export class Accessory extends EventEmitter {
const characteristics: CharacteristicReadData[] = [];
const response: CharacteristicsReadResponse = { characteristics: characteristics };

const missingCharacteristics: Set<EventName> = new Set();
const missingCharacteristics: Set<EventName> = new Set(request.ids.map(id => id.aid + "." + id.iid));
if (missingCharacteristics.size !== request.ids.length) {
// if those sizes differ, we have duplicates and can't properly handle that
callback({ httpCode: HAPHTTPCode.UNPROCESSABLE_ENTITY, status: HAPStatus.INVALID_VALUE_IN_REQUEST });
return;
}

let timeout: Timeout | undefined = setTimeout(() => {
for (const id of missingCharacteristics) {
const split = id.split(".");
Expand Down Expand Up @@ -1319,24 +1325,22 @@ export class Accessory extends EventEmitter {
}
missingCharacteristics.clear();

callback(response);
callback(undefined, response);
}, 7000);
timeout.unref();
}, 3000);
timeout.unref();

for (const id of request.ids) {
const name = id.aid + "." + id.iid;
missingCharacteristics.add(name);

this.handleCharacteristicRead(connection, id, request).then(value => {
return {
aid: id.aid,
iid: id.iid,
...value,
};
}, reason => { // this error block is only called if hap-nodejs itself messed up
console.error(`[${this.displayName}] Read request for characteristic ${id} encountered an error: ${reason.stack}`)
console.error(`[${this.displayName}] Read request for characteristic ${name} encountered an error: ${reason.stack}`)

return {
aid: id.aid,
Expand All @@ -1356,7 +1360,7 @@ export class Accessory extends EventEmitter {
clearTimeout(timeout);
timeout = undefined;
}
callback(response);
callback(undefined, response);
}
});
}
Expand Down Expand Up @@ -1441,7 +1445,16 @@ export class Accessory extends EventEmitter {
const characteristics: CharacteristicWriteData[] = [];
const response: CharacteristicsWriteResponse = { characteristics: characteristics };

const missingCharacteristics: Set<EventName> = new Set();
const missingCharacteristics: Set<EventName> = new Set(
writeRequest.characteristics
.map(characteristic => characteristic.aid + "." + characteristic.iid)
);
if (missingCharacteristics.size !== writeRequest.characteristics.length) {
// if those sizes differ, we have duplicates and can't properly handle that
callback({ httpCode: HAPHTTPCode.UNPROCESSABLE_ENTITY, status: HAPStatus.INVALID_VALUE_IN_REQUEST });
return;
}

let timeout: Timeout | undefined = setTimeout(() => {
for (const id of missingCharacteristics) {
const split = id.split(".");
Expand Down Expand Up @@ -1481,24 +1494,22 @@ export class Accessory extends EventEmitter {
}
missingCharacteristics.clear();

callback(response);
callback(undefined, response);
}, 7000);
timeout.unref();
}, 3000);
timeout.unref();

for (const data of writeRequest.characteristics) {
const id = data.aid + "." + data.iid;
missingCharacteristics.add(id);

const name = data.aid + "." + data.iid;
this.handleCharacteristicWrite(connection, data, writeState).then(value => {
return {
aid: data.aid,
iid: data.iid,
...value,
};
}, reason => { // this error block is only called if hap-nodejs itself messed up
console.error(`[${this.displayName}] Write request for characteristic ${id} encountered an error: ${reason.stack}`)
console.error(`[${this.displayName}] Write request for characteristic ${name} encountered an error: ${reason.stack}`)

return {
aid: data.aid,
Expand All @@ -1510,15 +1521,15 @@ export class Accessory extends EventEmitter {
return; // if timeout is undefined, response was already sent out
}

missingCharacteristics.delete(id);
missingCharacteristics.delete(name);
characteristics.push(value);

if (missingCharacteristics.size === 0) { // if everything returned send the response
if (timeout) {
clearTimeout(timeout);
timeout = undefined;
}
callback(response);
callback(undefined, response);
}
})
}
Expand Down
22 changes: 17 additions & 5 deletions src/lib/HAPServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export const enum HAPHTTPCode {
// client error
BAD_REQUEST = 400, // e.g. malformed request
NOT_FOUND = 404,
UNPROCESSABLE_ENTITY = 422, // for well-formed requests tha contain invalid http parameters
UNPROCESSABLE_ENTITY = 422, // for well-formed requests tha contain invalid http parameters (semantics are wrong and not syntax)

// server error
INTERNAL_SERVER_ERROR = 500,
Expand Down Expand Up @@ -175,8 +175,8 @@ export type RemovePairingCallback = PairingsCallback;
export type ListPairingsCallback = PairingsCallback<PairingInformation[]>;
export type PairCallback = VoidCallback;
export type AccessoriesCallback = (error: HAPHttpError | undefined, result?: AccessoriesResponse) => void;
export type ReadCharacteristicsCallback = (response: CharacteristicsReadResponse) => void;
export type WriteCharacteristicsCallback = (response: CharacteristicsWriteResponse) => void;
export type ReadCharacteristicsCallback = (error: HAPHttpError | undefined, response?: CharacteristicsReadResponse) => void;
export type WriteCharacteristicsCallback = (error: HAPHttpError | undefined, response?: CharacteristicsWriteResponse) => void;
export type ResourceRequestCallback = (error: HAPHttpError | undefined, resource?: Buffer) => void;

export const enum HAPServerEventTypes {
Expand Down Expand Up @@ -799,7 +799,13 @@ export class HAPServer extends EventEmitter {
includeEvent: consideredTrue(searchParams.get("ev")),
};

this.emit(HAPServerEventTypes.GET_CHARACTERISTICS, connection, readRequest, once((readResponse: CharacteristicsReadResponse) => {
this.emit(HAPServerEventTypes.GET_CHARACTERISTICS, connection, readRequest, once((error: HAPHttpError | undefined, readResponse: CharacteristicsReadResponse) => {
if (error) {
response.writeHead(error.httpCode, {"Content-Type": "application/hap+json"});
response.end(JSON.stringify({ status: error.status }));
return;
}

const characteristics = readResponse.characteristics;

let errorOccurred = false; // determine if we send a 207 Multi-Status
Expand Down Expand Up @@ -838,7 +844,13 @@ export class HAPServer extends EventEmitter {

const writeRequest = JSON.parse(data.toString("utf8")) as CharacteristicsWriteRequest;

this.emit(HAPServerEventTypes.SET_CHARACTERISTICS, connection, writeRequest, once((writeResponse: CharacteristicsWriteResponse) => {
this.emit(HAPServerEventTypes.SET_CHARACTERISTICS, connection, writeRequest, once((error: HAPHttpError | undefined, writeResponse: CharacteristicsWriteResponse) => {
if (error) {
response.writeHead(error.httpCode, {"Content-Type": "application/hap+json"});
response.end(JSON.stringify({ status: error.status }));
return;
}

const characteristics = writeResponse.characteristics;

let multiStatus = false;
Expand Down

0 comments on commit 5f8a827

Please sign in to comment.