aws-iot-device-sdk-v2
Version:
NodeJS API for the AWS IoT service
140 lines (120 loc) • 5.45 kB
text/typescript
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
import { mqtt, mqtt5, CrtError } from 'aws-crt';
/**
* Internal interface for the low-level MQTT service clients that lets them use either an MQTT311 client or an MQTT5
* client. Essentially a copy of the part of the MQTT311 client's public API that is used by the original
* MQTT service clients.
*
* @internal
*/
export interface IServiceClientMqttAdapter {
/**
* MQTT publish API sufficient for low-level MQTT service client usage.
*
* @param topic topic to publish to
* @param payload payload of the message to publish
* @param qos MQTT quality of service to deliver the message with
*/
publish(topic: string, payload: mqtt.Payload, qos: mqtt.QoS) : Promise<mqtt.MqttRequest>;
/**
* MQTT subscribe API sufficient for low-level MQTT service client usage.
*
* @param topic topic to subscribe to
* @param qos requested maximum quality of service the subscription should allow
* @param on_message
*/
subscribe(topic: string, qos: mqtt.QoS, on_message: mqtt.OnMessageCallback) : Promise<mqtt.MqttSubscribeRequest>;
}
/**
* Simple adapter for low-level MQTT service clients to use the MQTT311 client. Since the interface is just the
* MQTT311 client's interface, all we need to do is forward.
*
* @internal
*/
export class ServiceClientMqtt311Adapter implements IServiceClientMqttAdapter {
constructor(private connection: mqtt.MqttClientConnection) {
}
public async publish(topic: string, payload: mqtt.Payload, qos: mqtt.QoS) : Promise<mqtt.MqttRequest> {
return this.connection.publish(topic, payload, qos);
}
public async subscribe(topic: string, qos: mqtt.QoS, on_message: mqtt.OnMessageCallback) : Promise<mqtt.MqttSubscribeRequest> {
return this.connection.subscribe(topic, qos, on_message);
}
}
/**
* Adapter for low-level MQTT service clients to use the MQTT5 client.
*
* In addition to converting between the API contracts, adaptation requires constrained simulation of certain
* features of the MQTT311 client (per subscription callbacks). Full emulation of the topic tried is not needed
* because all subscriptions are done with individual, non-wildcarded topics.
*
* @internal
*/
export class ServiceClientMqtt5Adapter implements IServiceClientMqttAdapter {
subscriptionHandlers: Map<string, mqtt.OnMessageCallback>;
private onMessageReceivedHandler(eventData: mqtt5.MessageReceivedEvent) : void {
let publish : mqtt5.PublishPacket = eventData.message;
let topic: string = publish.topicName;
let handler : mqtt.OnMessageCallback | undefined = this.subscriptionHandlers.get(topic);
if (handler) {
/* The payload field of a PublishPacket is always an ArrayBuffer */
handler(topic, publish.payload as ArrayBuffer, false, publish.qos, publish.retain ?? false);
}
}
constructor(private client: mqtt5.Mqtt5Client) {
this.subscriptionHandlers = new Map<string, mqtt.OnMessageCallback>();
client.on('messageReceived', this.onMessageReceivedHandler.bind(this));
}
public async publish(topic: string, payload: mqtt.Payload, qos: mqtt.QoS) : Promise<mqtt.MqttRequest> {
return new Promise<mqtt.MqttRequest>(async (resolve, reject) => {
try {
let result: mqtt5.PublishCompletionResult = await this.client.publish({
topicName: topic,
payload: payload,
qos: (qos as number) as mqtt5.QoS
});
if (result === undefined) {
if (qos == mqtt.QoS.AtMostOnce) {
resolve({});
} else {
reject("Publish failed due to internal error");
}
return;
}
let puback: mqtt5.PubackPacket = result;
if (mqtt5.isSuccessfulPubackReasonCode(puback.reasonCode)) {
resolve({});
} else {
reject(new CrtError("Publish failed with reason code: " + puback.reasonCode));
}
} catch (e) {
reject(e);
}
});
}
public async subscribe(topic: string, qos: mqtt.QoS, on_message: mqtt.OnMessageCallback) : Promise<mqtt.MqttSubscribeRequest> {
return new Promise<mqtt.MqttSubscribeRequest>(async (resolve, reject) => {
try {
this.subscriptionHandlers.set(topic, on_message);
let result: mqtt5.SubackPacket = await this.client.subscribe({
subscriptions: [{topicFilter: topic, qos: (qos as number) as mqtt5.QoS}]
});
let reasonCode: mqtt5.SubackReasonCode = result.reasonCodes[0];
if (mqtt5.isSuccessfulSubackReasonCode(reasonCode)) {
resolve({
topic: topic,
qos: (reasonCode as number) as mqtt.QoS
});
} else {
throw new CrtError("Subscribe failed with reason code: " + reasonCode);
}
} catch (e) {
this.subscriptionHandlers.delete(topic);
reject(e);
}
});
}
}