背景
AWSのIoT Coreにブラウザから接続するのにクラスメソッドさんのAWS IoTのMQTT over WebSocketにHTMLから接続してみたが非常に参考になるのですが、Paho mqtt clientのバージョンが古いため、最新版(v1.1.0)で試しました。
最新版にするメリットは、reconnectの処理が実装されているらしいということです。
本記事はpaho-mqttを使用してみたところ、色々とハマりどころがありましたので、そのまとめです。
最初に結果
reconnectの試験をするために、次の2種類を試しましたが、結果は次のとおりでした。
- PCのWiFiをOFFする ⇒ 再接続してくれる
- PCをスリープさせる ⇒ 再接続に失敗する
- reconnectが成功したときもsubscribeしてくれないので、改めてsubscribeする必要があります。
最新版(v1.1.0)を入手する
最新版はgithubで管理されています。
https://github.com/eclipse/paho.mqtt.javascript
使用方法
JavaScriptからrequireして利用したかったので、このようにしました。ここでのポイントは2つ。
-
Paho.MQTT.client
ではなくなっていて、Paho.client
にする必要がありました。
※動いたコードから抜き出したコードなので動かないかも...
const Paho = require("paho-mqtt/paho-mqtt-min.js");
const CryptoJS = require("crypto-js");
const moment = require("moment");
let connected = false;
let endpoint = createEndpoint(
'ap-northeast-1', // Your Region
'yourendpoint.iot.ap-northeast-1.amazonaws.com', // Require 'lowercamelcase'!!
'YOUR_AWS_ACCESS_KEY',
'YOUR_AWS_SECRET_ACCESS_KEY')
);
let clientId = 'your client id';
let client = new Paho.Client(endpoint,clientId);
client.onConnectionLost = onConnectionLost;
client.onMessageArrived = onMessageArrived;
let connectOptions = {
useSSL: true,
timeout: 3,
mqttVersion: 4,
reconnect: true,
onSuccess: function() {
if(connected !== true) {
// 最初の接続時の処理
console.log("onConnect");
client.subscribe("/World");
} else {
// 再接続時の処理
console.log("reconnect");
client.subscribe("/World");
}
onFailure: function(e) {
callback(e);
}
};
client.connect(connectOptions);
function onConnectionLost(responseObject) {
if (responseObject.errorCode !== 0)
console.log("onConnectionLost:"+responseObject.errorMessage);
};
function onMessageArrived(message) {
console.log("onMessageArrived:"+message.payloadString);
client.disconnect();
};
function SigV4Utils(){}
SigV4Utils.sign = function(key, msg) {
var hash = CryptoJS.HmacSHA256(msg, key);
return hash.toString(CryptoJS.enc.Hex);
};
SigV4Utils.sha256 = function(msg) {
var hash = CryptoJS.SHA256(msg);
return hash.toString(CryptoJS.enc.Hex);
};
SigV4Utils.getSignatureKey = function(key, dateStamp, regionName, serviceName) {
var kDate = CryptoJS.HmacSHA256(dateStamp, 'AWS4' + key);
var kRegion = CryptoJS.HmacSHA256(regionName, kDate);
var kService = CryptoJS.HmacSHA256(serviceName, kRegion);
var kSigning = CryptoJS.HmacSHA256('aws4_request', kService);
return kSigning;
};
function createEndpoint(regionName, awsIotEndpoint, accessKey, secretKey) {
var time = moment.utc();
var dateStamp = time.format('YYYYMMDD');
var amzdate = dateStamp + 'T' + time.format('HHmmss') + 'Z';
var service = 'iotdevicegateway';
var region = regionName;
var secretKey = secretKey;
var accessKey = accessKey;
var algorithm = 'AWS4-HMAC-SHA256';
var method = 'GET';
var canonicalUri = '/mqtt';
var host = awsIotEndpoint;
var credentialScope = dateStamp + '/' + region + '/' + service + '/' + 'aws4_request';
var canonicalQuerystring = 'X-Amz-Algorithm=AWS4-HMAC-SHA256';
canonicalQuerystring += '&X-Amz-Credential=' + encodeURIComponent(accessKey + '/' + credentialScope);
canonicalQuerystring += '&X-Amz-Date=' + amzdate;
canonicalQuerystring += '&X-Amz-SignedHeaders=host';
var canonicalHeaders = 'host:' + host + '\n';
var payloadHash = SigV4Utils.sha256('');
var canonicalRequest = method + '\n' + canonicalUri + '\n' + canonicalQuerystring + '\n' + canonicalHeaders + '\nhost\n' + payloadHash;
var stringToSign = algorithm + '\n' + amzdate + '\n' + credentialScope + '\n' + SigV4Utils.sha256(canonicalRequest);
var signingKey = SigV4Utils.getSignatureKey(secretKey, dateStamp, region, service);
var signature = SigV4Utils.sign(signingKey, stringToSign);
canonicalQuerystring += '&X-Amz-Signature=' + signature;
return 'wss://' + host + canonicalUri + '?' + canonicalQuerystring;
}