ブラウザからAWSのIoT Coreにつなぐ

# 背景
AWSのIoT Coreにブラウザから接続するのにクラスメソッドさんの[AWS IoTのMQTT over WebSocketにHTMLから接続してみた](https://dev.classmethod.jp/articles/aws-iot-mqtt-over-websocket/)が非常に参考になるのですが、Paho mqtt clientのバージョンが古いため、最新版(v1.1.0)で試しました。

最新版にするメリットは、reconnectの処理が実装されているらしいということです。
本記事はpaho-mqttを使用してみたところ、色々とハマりどころがありましたので、そのまとめです。

# 最初に結果
 reconnectの試験をするために、次の２種類を試しましたが、結果は次のとおりでした。
 - 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`にする必要がありました。


※動いたコードから抜き出したコードなので動かないかも...

```javascript:
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;
}

```