aws-crt
Version:
NodeJS/browser bindings to the aws-c-* libraries
348 lines • 15 kB
JavaScript
;
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MqttClientConnection = exports.MqttClient = void 0;
const binding_1 = __importDefault(require("./binding"));
const native_resource_1 = require("./native_resource");
const event_1 = require("../common/event");
const error_1 = require("./error");
const io = __importStar(require("./io"));
var http_1 = require("./http");
Object.defineProperty(exports, "HttpProxyOptions", { enumerable: true, get: function () { return http_1.HttpProxyOptions; } });
/** @category MQTT */
var mqtt_1 = require("../common/mqtt");
Object.defineProperty(exports, "QoS", { enumerable: true, get: function () { return mqtt_1.QoS; } });
Object.defineProperty(exports, "MqttWill", { enumerable: true, get: function () { return mqtt_1.MqttWill; } });
/**
* MQTT client
*
* @module aws-crt
* @category MQTT
*/
class MqttClient extends native_resource_1.NativeResource {
/**
* @param bootstrap The {@link ClientBootstrap} to use for socket connections
*/
constructor(bootstrap) {
super(binding_1.default.mqtt_client_new(bootstrap.native_handle()));
this.bootstrap = bootstrap;
}
/**
* Creates a new {@link MqttClientConnection}
* @param config Configuration for the connection
* @returns A new connection
*/
new_connection(config) {
return new MqttClientConnection(this, config);
}
}
exports.MqttClient = MqttClient;
/** @internal */
function normalize_payload(payload) {
if (ArrayBuffer.isView(payload)) {
// native can use ArrayBufferView bytes directly
return payload;
}
if (payload instanceof ArrayBuffer) {
// native can use ArrayBuffer bytes directly
return payload;
}
if (typeof payload === 'string') {
// native will convert string to utf-8
return payload;
}
if (typeof payload === 'object') {
// convert object to JSON string (which will be converted to utf-8 in native)
return JSON.stringify(payload);
}
throw new TypeError("payload parameter must be a string, object, or DataView.");
}
/**
* MQTT client connection
*
* @module aws-crt
* @category MQTT
*/
class MqttClientConnection extends native_resource_1.NativeResourceMixin(event_1.BufferedEventEmitter) {
/**
* @param client The client that owns this connection
* @param config The configuration for this connection
*/
constructor(client, config) {
super();
this.client = client;
this.config = config;
// If there is a will, ensure that its payload is normalized to a DataView
const will = config.will ?
{
topic: config.will.topic,
qos: config.will.qos,
payload: normalize_payload(config.will.payload),
retain: config.will.retain
}
: undefined;
this._super(binding_1.default.mqtt_client_connection_new(client.native_handle(), (error_code) => { this._on_connection_interrupted(error_code); }, (return_code, session_present) => { this._on_connection_resumed(return_code, session_present); }, config.tls_ctx ? config.tls_ctx.native_handle() : null, will, config.username, config.password, config.use_websocket, config.proxy_options ? config.proxy_options.create_native_handle() : undefined, config.websocket_handshake_transform));
this.tls_ctx = config.tls_ctx;
binding_1.default.mqtt_client_connection_on_message(this.native_handle(), this._on_any_publish.bind(this));
}
close() {
binding_1.default.mqtt_client_connection_close(this.native_handle());
}
/** @internal */
// Overridden to allow uncorking on ready
on(event, listener) {
super.on(event, listener);
if (event == 'connect') {
process.nextTick(() => {
this.uncork();
});
}
return this;
}
/**
* Open the actual connection to the server (async).
* @returns A Promise which completes whether the connection succeeds or fails.
* If connection fails, the Promise will reject with an exception.
* If connection succeeds, the Promise will return a boolean that is
* true for resuming an existing session, or false if the session is new
*/
connect() {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
reject = this._reject(reject);
const on_connect = (error_code, return_code, session_present) => {
if (error_code == 0 && return_code == 0) {
resolve(session_present);
this.emit('connect', session_present);
}
else if (error_code != 0) {
reject("Failed to connect: " + io.error_code_to_string(error_code));
}
else {
reject("Server rejected connection.");
}
};
try {
binding_1.default.mqtt_client_connection_connect(this.native_handle(), this.config.client_id, this.config.host_name, this.config.port, this.config.socket_options.native_handle(), this.config.keep_alive, this.config.ping_timeout, this.config.protocol_operation_timeout, this.config.clean_session, on_connect);
}
catch (e) {
reject(e);
}
});
});
}
/**
* The connection will automatically reconnect. To cease reconnection attempts, call {@link disconnect}.
* To resume the connection, call {@link connect}.
* @deprecated
*/
reconnect() {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
reject = this._reject(reject);
function on_connect(error_code, return_code, session_present) {
if (error_code == 0 && return_code == 0) {
resolve(session_present);
}
else if (error_code != 0) {
reject("Failed to connect: " + io.error_code_to_string(error_code));
}
else {
reject("Server rejected connection.");
}
}
try {
binding_1.default.mqtt_client_connection_reconnect(this.native_handle(), on_connect);
}
catch (e) {
reject(e);
}
});
});
}
/**
* Publish message (async).
* If the device is offline, the PUBLISH packet will be sent once the connection resumes.
*
* @param topic Topic name
* @param payload Contents of message
* @param qos Quality of Service for delivering this message
* @param retain If true, the server will store the message and its QoS so that it can be
* delivered to future subscribers whose subscriptions match the topic name
* @returns Promise which returns a {@link MqttRequest} which will contain the packet id of
* the PUBLISH packet.
*
* * For QoS 0, completes as soon as the packet is sent.
* * For QoS 1, completes when PUBACK is received.
* * For QoS 2, completes when PUBCOMP is received.
*/
publish(topic, payload, qos, retain = false) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
reject = this._reject(reject);
function on_publish(packet_id, error_code) {
payload = "";
topic = "";
if (error_code == 0) {
resolve({ packet_id });
}
else {
reject("Failed to publish: " + io.error_code_to_string(error_code));
}
}
let payload_data = normalize_payload(payload);
try {
binding_1.default.mqtt_client_connection_publish(this.native_handle(), topic, payload_data, qos, retain, on_publish);
}
catch (e) {
reject(e);
}
});
});
}
/**
* Subscribe to a topic filter (async).
* The client sends a SUBSCRIBE packet and the server responds with a SUBACK.
*
* subscribe() may be called while the device is offline, though the async
* operation cannot complete successfully until the connection resumes.
*
* Once subscribed, `callback` is invoked each time a message matching
* the `topic` is received. It is possible for such messages to arrive before
* the SUBACK is received.
*
* @param topic Subscribe to this topic filter, which may include wildcards
* @param qos Maximum requested QoS that server may use when sending messages to the client.
* The server may grant a lower QoS in the SUBACK
* @param on_message Optional callback invoked when message received.
* @returns Promise which returns a {@link MqttSubscribeRequest} which will contain the
* result of the SUBSCRIBE. The Promise resolves when a SUBACK is returned
* from the server or is rejected when an exception occurs.
*/
subscribe(topic, qos, on_message) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
reject = this._reject(reject);
function on_suback(packet_id, topic, qos, error_code) {
if (error_code == 0) {
resolve({ packet_id, topic, qos, error_code });
}
else {
reject("Failed to subscribe: " + io.error_code_to_string(error_code));
}
}
try {
binding_1.default.mqtt_client_connection_subscribe(this.native_handle(), topic, qos, on_message, on_suback);
}
catch (e) {
reject(e);
}
});
});
}
/**
* Unsubscribe from a topic filter (async).
* The client sends an UNSUBSCRIBE packet, and the server responds with an UNSUBACK.
* @param topic The topic filter to unsubscribe from. May contain wildcards.
* @returns Promise wihch returns a {@link MqttRequest} which will contain the packet id
* of the UNSUBSCRIBE packet being acknowledged. Promise is resolved when an
* UNSUBACK is received from the server or is rejected when an exception occurs.
*/
unsubscribe(topic) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
reject = this._reject(reject);
function on_unsuback(packet_id, error_code) {
if (error_code == 0) {
resolve({ packet_id });
}
else {
reject("Failed to unsubscribe: " + io.error_code_to_string(error_code));
}
}
try {
binding_1.default.mqtt_client_connection_unsubscribe(this.native_handle(), topic, on_unsuback);
}
catch (e) {
reject(e);
}
});
});
}
/**
* Close the connection (async).
* @returns Promise which completes when the connection is closed.
*/
disconnect() {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
reject = this._reject(reject);
const on_disconnect = () => {
resolve();
this.emit('disconnect');
this.close();
};
try {
binding_1.default.mqtt_client_connection_disconnect(this.native_handle(), on_disconnect);
}
catch (e) {
reject(e);
}
});
});
}
// Wrap a promise rejection with a function that will also emit the error as an event
_reject(reject) {
return (reason) => {
reject(reason);
process.nextTick(() => {
this.emit('error', new error_1.CrtError(reason));
});
};
}
_on_connection_interrupted(error_code) {
this.emit('interrupt', new error_1.CrtError(error_code));
}
_on_connection_resumed(return_code, session_present) {
this.emit('resume', return_code, session_present);
}
_on_any_publish(topic, payload, dup, qos, retain) {
this.emit('message', topic, payload, dup, qos, retain);
}
}
exports.MqttClientConnection = MqttClientConnection;
//# sourceMappingURL=mqtt.js.map