将 AWS Lex v2 流与 C++ 结合使用:签名不匹配
Using AWS Lex v2 streaming with C++: Signature does not match
我正在尝试使我 PC 上的 C++ 应用程序使用流式 API 与 AWS Lex v2 通信。但是哪里都绝对没有示例代码,逼着我一路猜测流程。我设法将一些东西缝合在一起,但没有用。我收到以下错误:
在请求处理程序中: CRC 不匹配。 prelude_crc 为 0x0865223A22,但计算为 0x08A98FE9C5
在响应处理程序中:我们计算的请求签名与您提供的签名不匹配。检查您的 AWS 秘密访问密钥和签名方法。有关详细信息,请参阅服务文档。
秘密访问密钥没有任何问题,因为我可以使用 aws lexv2-runtime recognize-text 命令从同一台 PC 从 AWS CLI 调用同一个机器人。
是否需要先使用签名为流媒体播种?
AWS 日志说明如下:
[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
我的整个测试代码如下,我漏了什么?既然我可以确认 key 和 secret 没问题,那么如何确保它被正确签名呢? BOT 区域是 us-east-1,但我正在从另一个区域发出请求。这重要吗?
#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 <aws/lex/LexRuntimeServiceClient.h>
#include <aws/lex/LexRuntimeServiceRequest.h>
#include <aws/lex/model/PostContentRequest.h>
#include <aws/lex/model/PostContentResult.h>
#include <iterator>
#include <nlohmann_json.hpp>
#include <fstream>
#include <chrono>
using namespace Aws::LexRuntimeV2;
int SessionCount = 0;
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::Info;
Aws::InitAPI(options);
Aws::Client::ClientConfiguration config;
config.region = jsonObj["lex_region"];
auto lexClient = Aws::MakeUnique<LexRuntimeV2Client>("MyClient", Aws::Auth::AWSCredentials(lexKey.c_str(), lexSecret.c_str()), config);
Model::StartConversationRequest LexRequest;
Model::StartConversationHandler requestHandler;
requestHandler.SetTranscriptEventCallback([](const Model::TranscriptEvent& ev)
{
std::cout << ev.GetTranscript() << std::endl;
});
requestHandler.SetOnErrorCallback([](const Aws::Client::AWSError<LexRuntimeV2Errors>& error)
{
std::cout << "Request handler: " << error.GetMessage() << std::endl;
});
LexRequest.WithLocaleId(localeId).WithBotId(botId.c_str()).WithBotAliasId(botAliasId.c_str()).WithSessionId("Blah")
.WithConversationMode(Model::ConversationMode::AUDIO).WithEventStreamHandler(requestHandler);
Aws::Utils::Threading::Semaphore signaling(0 /*initialCount*/, 1 /*maxCount*/);
auto OnResponseCallback = [&signaling](const LexRuntimeV2Client*,const Model::StartConversationRequest&,
const Model::StartConversationOutcome& outcome, const std::shared_ptr<const Aws::Client::AsyncCallerContext>&)
{
std::cout << "Response handler: " << outcome.GetError().GetMessage();
signaling.Release();
};
Model::StartConversationRequestEventStream* pStream = nullptr;
Aws::Utils::Threading::Semaphore starting(0 /*initialCount*/, 1 /*maxCount*/);
auto OnStreamReady = [&starting,&pStream](Model::StartConversationRequestEventStream& stream)
{
pStream = &stream;
pStream->SetSignatureSeed("blah");
starting.Release();
};
lexClient->StartConversationAsync(LexRequest, OnStreamReady, OnResponseCallback, nullptr);
starting.WaitOne();
std::ifstream audioFile(argv[2], std::ios_base::binary);
if (!audioFile)
{
std::cout << "Cannot open audio file " << argv[2] << std::endl;
return 0;
}
while (!audioFile.eof())
{
unsigned char buf[320 + 1];
audioFile.read((char*)buf, 320);
Aws::Utils::ByteBuffer bytes(buf, audioFile.gcount());
Model::AudioInputEvent input;
auto millisec_since_epoch = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock().now().time_since_epoch()).count();
input.SetClientTimestampMillis(millisec_since_epoch);
input.SetAudioChunk(bytes);
input.SetContentType("audio/lpcm; sample-rate=8000; sample-size-bits=16; channel-count=1; is-big-endian=false");
pStream->WriteAudioInputEvent(input);
_sleep(20);
}
signaling.WaitOne(); // prevent the application from exiting until we're done
Aws::ShutdownAPI(options);
return 0;
}
原来这是 Windows 上 WinHttp/WinINet 的限制。它还不完全支持 HTTP/2 多路复用。所以我切换到 CURL,它起作用了。
由于 AWS streaming lex/transcribe 功能需要 HTTP/2 多路复用,请始终将其与 CURL 一起使用。
我正在尝试使我 PC 上的 C++ 应用程序使用流式 API 与 AWS Lex v2 通信。但是哪里都绝对没有示例代码,逼着我一路猜测流程。我设法将一些东西缝合在一起,但没有用。我收到以下错误:
在请求处理程序中: CRC 不匹配。 prelude_crc 为 0x0865223A22,但计算为 0x08A98FE9C5 在响应处理程序中:我们计算的请求签名与您提供的签名不匹配。检查您的 AWS 秘密访问密钥和签名方法。有关详细信息,请参阅服务文档。
秘密访问密钥没有任何问题,因为我可以使用 aws lexv2-runtime recognize-text 命令从同一台 PC 从 AWS CLI 调用同一个机器人。
是否需要先使用签名为流媒体播种?
AWS 日志说明如下:
[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
我的整个测试代码如下,我漏了什么?既然我可以确认 key 和 secret 没问题,那么如何确保它被正确签名呢? BOT 区域是 us-east-1,但我正在从另一个区域发出请求。这重要吗?
#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 <aws/lex/LexRuntimeServiceClient.h>
#include <aws/lex/LexRuntimeServiceRequest.h>
#include <aws/lex/model/PostContentRequest.h>
#include <aws/lex/model/PostContentResult.h>
#include <iterator>
#include <nlohmann_json.hpp>
#include <fstream>
#include <chrono>
using namespace Aws::LexRuntimeV2;
int SessionCount = 0;
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::Info;
Aws::InitAPI(options);
Aws::Client::ClientConfiguration config;
config.region = jsonObj["lex_region"];
auto lexClient = Aws::MakeUnique<LexRuntimeV2Client>("MyClient", Aws::Auth::AWSCredentials(lexKey.c_str(), lexSecret.c_str()), config);
Model::StartConversationRequest LexRequest;
Model::StartConversationHandler requestHandler;
requestHandler.SetTranscriptEventCallback([](const Model::TranscriptEvent& ev)
{
std::cout << ev.GetTranscript() << std::endl;
});
requestHandler.SetOnErrorCallback([](const Aws::Client::AWSError<LexRuntimeV2Errors>& error)
{
std::cout << "Request handler: " << error.GetMessage() << std::endl;
});
LexRequest.WithLocaleId(localeId).WithBotId(botId.c_str()).WithBotAliasId(botAliasId.c_str()).WithSessionId("Blah")
.WithConversationMode(Model::ConversationMode::AUDIO).WithEventStreamHandler(requestHandler);
Aws::Utils::Threading::Semaphore signaling(0 /*initialCount*/, 1 /*maxCount*/);
auto OnResponseCallback = [&signaling](const LexRuntimeV2Client*,const Model::StartConversationRequest&,
const Model::StartConversationOutcome& outcome, const std::shared_ptr<const Aws::Client::AsyncCallerContext>&)
{
std::cout << "Response handler: " << outcome.GetError().GetMessage();
signaling.Release();
};
Model::StartConversationRequestEventStream* pStream = nullptr;
Aws::Utils::Threading::Semaphore starting(0 /*initialCount*/, 1 /*maxCount*/);
auto OnStreamReady = [&starting,&pStream](Model::StartConversationRequestEventStream& stream)
{
pStream = &stream;
pStream->SetSignatureSeed("blah");
starting.Release();
};
lexClient->StartConversationAsync(LexRequest, OnStreamReady, OnResponseCallback, nullptr);
starting.WaitOne();
std::ifstream audioFile(argv[2], std::ios_base::binary);
if (!audioFile)
{
std::cout << "Cannot open audio file " << argv[2] << std::endl;
return 0;
}
while (!audioFile.eof())
{
unsigned char buf[320 + 1];
audioFile.read((char*)buf, 320);
Aws::Utils::ByteBuffer bytes(buf, audioFile.gcount());
Model::AudioInputEvent input;
auto millisec_since_epoch = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock().now().time_since_epoch()).count();
input.SetClientTimestampMillis(millisec_since_epoch);
input.SetAudioChunk(bytes);
input.SetContentType("audio/lpcm; sample-rate=8000; sample-size-bits=16; channel-count=1; is-big-endian=false");
pStream->WriteAudioInputEvent(input);
_sleep(20);
}
signaling.WaitOne(); // prevent the application from exiting until we're done
Aws::ShutdownAPI(options);
return 0;
}
原来这是 Windows 上 WinHttp/WinINet 的限制。它还不完全支持 HTTP/2 多路复用。所以我切换到 CURL,它起作用了。
由于 AWS streaming lex/transcribe 功能需要 HTTP/2 多路复用,请始终将其与 CURL 一起使用。