odelot / aws-mqtt-websockets

Implementation of a middleware to use AWS MQTT service through websockets, aiming the ESP8266 plataform

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Error 400 on connection upgrade

dotCID opened this issue · comments

Since I've not been able to find sources on how to proceed, here's my current issue in hopes of resolve.
Upon connecting to AWS IoT I receive a "400: Bad Request" from the IoT server. This happens as a response to the connection upgrade header sent out by the example. I have attempted to solve this by adjusting the AWS IoT policies, but had no success; editing the (public) key only resolved in a 403, which to me indicates that the verification is not the issue. It seems from the console output that it is related to the following lines in the response:

[WS-Client][handleHeader] RX: x-amzn-ErrorType: InvalidRequestException:
[WS-Client][handleHeader] RX: access-control-allow-origin: *

Any advice on how to process would be much appreciated!

The debug console output is as follows:

connecting to wifi

connected to wifi
Mon Jan 30 16:55:45 2017

25597 - conn: 1 - (27744)
[WS-Client] connect wss...
[WS-Client] connected to a229lcxkod94gz.iot.eu-central-1.amazonaws.com:443.
[WS-Client][sendHeader] sending header...
[WS-Client][sendHeader] handshake GET /mqtt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgVTqFkxdtijfCqb6cbLl
xzJucnT0cBv7q%2Fw7mQ3SySunjlKL2bAT5CzLMFhkxRcsJo29OwW9OXFsAeo1PHqz
n3QrY6Lg395PuWrUqu8VB6kglaxrx%2FKFnHUh0kGBe4K3XCOog06JMJu%2FJtE0SInZ
YlsWmqTIYcjf9CFbTZu2J6FYa99CKbhqeBwtnNUWW4s%2FZmAFu8o%2FZLZnh3+zTZGT
OLrJRypgLzRQrodBG+vzrKGkymnvUBItyV3UDOnEN6Nxz2U9vD7QajQCN02KAq60
zjjmEqzscZxGuHydBJy85Cd0tocP3mzreSWdgZ1FEaSXATabspnfFr1z6nRVZxLt
wQIDAQAB
-----END PUBLIC KEY-----%2F20170130%2Feu-central-1%2Fiotdevicegateway%2Faws4_request&X-Amz-Date=20170130T135547Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=39e2a13082057a7762a97a81905552679700b0bdbf13dbb1b58272543fce71c5 HTTP/1.1
Host: a229lcxkod94gz.iot.eu-central-1.amazonaws.com:443
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: XSN17QH9Jg0EH8cps+F9HQ==
Sec-WebSocket-Protocol: mqtt
Origin: file://
User-Agent: arduino-WebSocket-Client

[WS-Client][sendHeader] sending header... Done (145065us).
[WS-Client][handleHeader] RX: HTTP/1.1 400 Bad Request
[WS-Client][handleHeader] RX: content-type: application/json
[WS-Client][handleHeader] RX: content-length: 102
[WS-Client][handleHeader] RX: date: Mon, 30 Jan 2017 13:55:49 GMT
[WS-Client][handleHeader] RX: x-amzn-RequestId: f9b962c1-52f2-965d-9672-d3f7215840cc
[WS-Client][handleHeader] RX: connection: Keep-Alive
[WS-Client][handleHeader] RX: x-amzn-ErrorType: InvalidRequestException:
[WS-Client][handleHeader] RX: access-control-allow-origin: *
[WS-Client][handleHeader] Header read fin.
[WS-Client][handleHeader] Client settings:
[WS-Client][handleHeader]  - cURL: /mqtt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgVTqFkxdtijfCqb6cbLl
xzJucnT0cBv7q%2Fw7mQ3SySunjlKL2bAT5CzLMFhkxRcsJo29OwW9OXFsAeo1PHqz
n3QrY6Lg395PuWrUqu8VB6kglaxrx%2FKFnHUh0kGBe4K3XCOog06JMJu%2FJtE0SInZ
YlsWmqTIYcjf9CFbTZu2J6FYa99CKbhqeBwtnNUWW4s%2FZmAFu8o%2FZLZnh3+zTZGT
OLrJRypgLzRQrodBG+vzrKGkymnvUBItyV3UDOnEN6Nxz2U9vD7QajQCN02KAq60
zjjmEqzscZxGuHydBJy85Cd0tocP3mzreSWdgZ1FEaSXATabspnfFr1z6nRVZxLt
wQIDAQAB
-----END PUBLIC KEY-----%2F20170130%2Feu-central-1%2Fiotdevicegateway%2Faws4_request&X-Amz-Date=20170130T135547Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=39e2a13082057a7762a97a81905552679700b0bdbf13dbb1b58272543fce71c5
[WS-Client][handleHeader]  - cKey: XSN17QH9Jg0EH8cps+F9HQ==
[WS-Client][handleHeader] Server header:
[WS-Client][handleHeader]  - cCode: 400
[WS-Client][handleHeader]  - cIsUpgrade: 0
[WS-Client][handleHeader]  - cIsWebsocket: 1
[WS-Client][handleHeader]  - cAccept: 
[WS-Client][handleHeader]  - cProtocol: mqtt
[WS-Client][handleHeader]  - cExtensions: 
[WS-Client][handleHeader]  - cVersion: 0
[WS-Client][handleHeader]  - cSessionId: 
[WS-Client][handleHeader] no Websocket connection close.
[WS-Client] client disconnected.
[AWSc] Disconnected!

These messages repeat in the console indefinitely.

My sketch is based upon the example that comes with this library. My only alterations are the debugging instructions seen here, some Serial.prints and an altered MQTT message.

I am currently using the following software versions and libraries:
Arduino IDE: 1.6.11
esp8266 by ESPCommunity: 2.3.0
And the most recent versions of the dependencies as listed in the readme.md of this project.

As for hardware, I'm using an Adafruit Feather Huzzah, which is based upon an ESP8266-12S.

Below is the sketch with the WiFi and Amazon keys redacted:

#include <Arduino.h>
#include <Stream.h>

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>

//AWS
#include "sha256.h"
#include "Utils.h"
#include "AWSClient2.h"

//WEBSockets
#include <Hash.h>
#include <WebSocketsClient.h>

//MQTT PAHO
#include <SPI.h>
#include <IPStack.h>
#include <Countdown.h>
#include <MQTTClient.h>



//AWS MQTT Websocket
#include "Client.h"
#include "AWSWebSocketClient.h"
#include "CircularByteBuffer.h"

//AWS IOT config, change these:
char wifi_ssid[]       = "**";
char wifi_password[]   = "**";
char aws_endpoint[]    = "a229lcxkod94gz.iot.eu-central-1.amazonaws.com";
char aws_key[]         = 
"-----BEGIN PUBLIC KEY-----\n"
"-----END PUBLIC KEY-----";

char aws_secret[]      = 
"-----BEGIN RSA PRIVATE KEY-----\n"
"-----END RSA PRIVATE KEY-----";
char aws_region[]      = "eu-central-1";
const char* aws_topic  = "$aws/things/RESTtest/shadow/update";
int port = 443;

//MQTT config
const int maxMQTTpackageSize = 512;
const int maxMQTTMessageHandlers = 1;

ESP8266WiFiMulti WiFiMulti;

AWSWebSocketClient awsWSclient(1000);

IPStack ipstack(awsWSclient);
MQTT::Client<IPStack, Countdown, maxMQTTpackageSize, maxMQTTMessageHandlers> *client = NULL;

//# of connections
long connection = 0;

//generate random mqtt clientID
char* generateClientID () {
  char* cID = new char[23]();
  for (int i=0; i<22; i+=1)
    cID[i]=(char)random(1, 256);
  return cID;
}

//count messages arrived
int arrivedcount = 0;

//callback to handle mqtt messages
void messageArrived(MQTT::MessageData& md)
{
  MQTT::Message &message = md.message;

  Serial.print("Message ");
  Serial.print(++arrivedcount);
  Serial.print(" arrived: qos ");
  Serial.print(message.qos);
  Serial.print(", retained ");
  Serial.print(message.retained);
  Serial.print(", dup ");
  Serial.print(message.dup);
  Serial.print(", packetid ");
  Serial.println(message.id);
  Serial.print("Payload ");
  char* msg = new char[message.payloadlen+1]();
  memcpy (msg,message.payload,message.payloadlen);
  Serial.println(msg);
  delete msg;
}

//connects to websocket layer and mqtt layer
bool connect () {

    if (client == NULL) {
      client = new MQTT::Client<IPStack, Countdown, maxMQTTpackageSize, maxMQTTMessageHandlers>(ipstack);
    } else {

      if (client->isConnected ()) {    
        client->disconnect ();
      }  
      delete client;
      client = new MQTT::Client<IPStack, Countdown, maxMQTTpackageSize, maxMQTTMessageHandlers>(ipstack);
    }


    //delay is not necessary... it just help us to get a "trustful" heap space value
    delay (1000);
    Serial.print (millis ());
    Serial.print (" - conn: ");
    Serial.print (++connection);
    Serial.print (" - (");
    Serial.print (ESP.getFreeHeap ());
    Serial.println (")");




   int rc = ipstack.connect(aws_endpoint, port);
    if (rc != 1)
    {
      Serial.println("error connection to the websocket server");
      return false;
    } else {
      Serial.println("websocket layer connected");
    }


    Serial.println("MQTT connecting");
    MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
    data.MQTTVersion = 3;
    char* clientID = generateClientID ();
    data.clientID.cstring = clientID;
    rc = client->connect(data);
    delete[] clientID;
    if (rc != 0)
    {
      Serial.print("error connection to MQTT server");
      Serial.print("rc = ");
      Serial.println(rc);
      return false;
    }
    Serial.println("MQTT connected");
    return true;
}

//subscribe to a mqtt topic
void subscribe () {
   //subscript to a topic
    int rc = client->subscribe(aws_topic, MQTT::QOS0, messageArrived);
    if (rc != 0) {
      Serial.print("rc from MQTT subscribe is ");
      Serial.println(rc);
      return;
    }
    Serial.println("MQTT subscribed");
}

//send a message to a mqtt topic
void sendmessage () {
    //send a message
    MQTT::Message message;
    char buf[100];
    strcpy(buf, "{\"desired\":{\"value\": \"HelloWorld\",\"esp_uptime\": 5},\"reported\": {\"value\": \"HelloWorld\",\"esp_uptime\": 5}}");
    
    message.qos = MQTT::QOS0;
    message.retained = false;
    message.dup = false;
    message.payload = (void*)buf;
    message.payloadlen = strlen(buf)+1;
    int rc = client->publish(aws_topic, message); 
}


void setup() {
    Serial.begin (115200);
    delay (2000);
    Serial.setDebugOutput(1);

    //fill with ssid and wifi password
    WiFiMulti.addAP(wifi_ssid, wifi_password);
    Serial.println ("connecting to wifi");
    while(WiFiMulti.run() != WL_CONNECTED) {
        delay(100);
        Serial.print (".");
    }
    Serial.println ("\nconnected to wifi");

    // config the time to prevent "please start sntp first!" message/error
    configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov");
    
    //fill AWS parameters    
    awsWSclient.setAWSRegion(aws_region);
    awsWSclient.setAWSDomain(aws_endpoint);
    awsWSclient.setAWSKeyID(aws_key);
    awsWSclient.setAWSSecretKey(aws_secret);
    awsWSclient.setUseSSL(true);

    if (connect ()){
      Serial.println(F("Sending first message over MQTT"));
      subscribe ();
      sendmessage ();
    }else{
      Serial.println(F("Could not connect to MQTT"));
    }

    // Show that the device is on
    pinMode(0, OUTPUT);
    digitalWrite(0, HIGH);
}

void loop() {
  //keep the mqtt up and running
  if (awsWSclient.connected ()) {    
      Serial.println(F("Connected to MQTT"));
      client->yield();
  } else {
    //handle reconnection
    if (connect ()){
      Serial.println(F("Connected, subscribing.."));
      subscribe ();      
    }else{
      Serial.println(F("Could not reconnect"));
    }
  }

}

You are using the certificate credential instead of AWS key access, that is needed by SigV4 way of authentication used by the MQTT over websockets protocol.

Create a AWS KEY and AWS SECRET for your user and use it. http://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys