Using AWS Lex v2 streaming with C++: Signature does not match

  amazon-lex, aws-sdk-cpp, c++

I am trying to make the C++ application on my PC talk to AWS Lex v2 using streaming APIs. But there is absolutely no sample code anywhere, forcing me to guess the flow all the way. I managed to stitch something together, but is not working. I get the following error:

The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method.

There is nothing wrong with the secret access key since I am able call the same bot from AWS CLI from the same PC, using aws lexv2-runtime recognize-text command.

The AWS log says the following:

[ERROR] 2021 - 08 - 19 18:08 : 19.427 AWSClient[24956] HTTP response code : -1
Resolved remote host IP address :
Request ID :
Exception name :
Error message : Encountered network error when sending http request
0 response headers :
[WARN] 2021 - 08 - 19 18:08 : 19.427 AWSClient[24956] If the signature check failed.This could be because of a time skew.Attempting to adjust the signer.
[WARN] 2021 - 08 - 19 18:08 : 19.427 AWSClient[24956] Request failed, now waiting 50 ms before attempting again.
[WARN] 2021 - 08 - 19 18:08 : 19.489 WinHttpSyncHttpClient[24956] Failed setting TCP keep - alive interval with error code : 12018
[WARN] 2021 - 08 - 19 18:08 : 19.717 AWSErrorMarshaller[24956] Encountered AWSError 'InvalidSignatureException' : The request signature we calculated does not match the signature you provided.Check your AWS Secret Access Key and signing method.Consult the service documentation for details.
[ERROR] 2021 - 08 - 19 18:08 : 19.717 AWSClient[24956] HTTP response code : 403

My entire test code is as follows, what am I missing? Since I can confirm key and secret are fine, how to ensure it is signed correctly? The BOT region is us-east-1, but I am making the request from another region. Does that matter?

#include <aws/core/Aws.h>
#include <aws/core/client/AsyncCallerContext.h>
#include <aws/core/utils/Outcome.h>
#include <aws/core/auth/AWSCredentialsProvider.h>
#include <aws/lexv2-runtime/LexRuntimeV2Client.h>
#include <aws/lexv2-runtime/model/AudioInputEvent.h>
#include <aws/lexv2-runtime/model/RecognizeUtteranceRequest.h>
#include <aws/lexv2-runtime/model/RecognizeUtteranceResult.h>
#include <aws/lexv2-runtime/model/StartConversationHandler.h>
#include <aws/lexv2-runtime/model/StartConversationRequest.h>
#include <aws/lexv2-runtime/model/StartConversationRequestEventStream.h>
#include <nlohmann_json.hpp>
#include <fstream>

using namespace Aws::LexRuntimeV2;
int SessionCount = 0;

class MyLexSession : public Aws::Client::AsyncCallerContext
{
    Model::StartConversationRequestEventStream* _lexInputStream = nullptr;
public:
    std::string _id;

    MyLexSession()
    {
        char buf[20];
        itoa(++SessionCount, buf, 10);
        _id = buf;
    }

    static void ReadyHandler(std::shared_ptr<MyLexSession> session, Model::StartConversationRequestEventStream& s)
    {
        session->_lexInputStream = &s;
    }

    static void ResponseHandler(const LexRuntimeV2Client*, 
        const Model::StartConversationRequest&,
        const Model::StartConversationOutcome& outcome, 
        const std::shared_ptr<const Aws::Client::AsyncCallerContext>&)
    {
        if (outcome.IsSuccess())
        {
            // what do to with this? What Result class I can use here?
            auto result = outcome.GetResult();
        }
        else
        {
            std::cout << outcome.GetError().GetMessage();
        }
    }

    void writeAudio(Model::AudioInputEvent& input)
    {
        if (_lexInputStream)
        {
            _lexInputStream->WriteAudioInputEvent(input);
        }
    }
};

Aws::UniquePtr<LexRuntimeV2Client> LexClient;
Model::StartConversationRequest LexRequest;

int main(int argc, char* argv[])
{
    if (argc != 3)
    {
        std::cout << "Usage: LexV2Test pcm_audio.raw bot_name_cred.json" << std::endl;
        return -1;
    }

    // read json file and populate the fields
    std::ifstream jsonFile(argv[1]);
    if (!jsonFile)
    {
        std::cout << "Cannot open license file " << argv[1] << std::endl;
        return -1;
    }

    nlohmann::json jsonObj = nlohmann::json::parse(std::istreambuf_iterator<char>(jsonFile), std::istreambuf_iterator<char>());
    jsonFile.close();

    std::string lexKey, lexSecret;
    std::string botId, botAliasId, localeId, sessionId, regionId;

    botId = jsonObj["bot_id"];
    botAliasId = jsonObj["bot_alias_id"];
    lexKey = jsonObj["lex_key"];
    lexSecret = jsonObj["lex_secret"];
    localeId = jsonObj["lex_locale"];

    Aws::SDKOptions options;
    options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Warn;
    Aws::InitAPI(options);
    Aws::Client::ClientConfiguration config;
    config.region = jsonObj["lex_region"];
    LexClient = Aws::MakeUnique<LexRuntimeV2Client>("MyClient", Aws::Auth::AWSCredentials(lexKey.c_str(), lexSecret.c_str()), config);

    std::shared_ptr<MyLexSession> asyncSession = Aws::MakeShared<MyLexSession>("MySession");
    StartConversationStreamReadyHandler readyHandler = std::bind(MyLexSession::ReadyHandler, asyncSession, std::placeholders::_1);
    LexRequest.WithBotId(botId.c_str()).WithBotAliasId(botAliasId.c_str()).WithSessionId(asyncSession->_id)
        .WithConversationMode(Model::ConversationMode::AUDIO).WithLocaleId(localeId);

    LexClient->StartConversationAsync(LexRequest, readyHandler, MyLexSession::ResponseHandler, asyncSession);

    std::ifstream audioFile(argv[2], std::ios_base::binary);
    if (!audioFile)
    {
        std::cout << "Cannot open audio file " << argv[2] << std::endl;
        return -1;
    }

    while (!audioFile.eof())
    {
        unsigned char buf[320+1];
        audioFile.read((char*)buf, 320);
        Aws::Utils::ByteBuffer bytes(buf, audioFile.gcount());
        Model::AudioInputEvent input;
        input.SetAudioChunk(bytes);
        input.SetContentType("audio/lpcm; sample-rate=8000; sample-size-bits=16; channel-count=1; is-big-endian=false");
        asyncSession->writeAudio(input);
    }

    _sleep(5000);

    Aws::ShutdownAPI(options);
    return 0;
}

Source: Windows Questions C++

LEAVE A COMMENT