SSL gRPC 客户端在 C# 中工作正常,但在 C++ 中因不可用 "Empty update" 而失败
SSL gRPC client works fine in C#, but fails with UNAVAILABLE "Empty update" in C++
在带有 VS2022 17.1.2 和 .NET 6 的
10 Pro 21H2 上,我正在将一个简单的 C# gRPC 客户端移植到 C++,但是 C++ 客户端总是无法连接到服务器,尽管我的代码看似做同样的事情,我 运行 不知道为什么。
我的 gRPC 服务器使用 SSL 和 LetsEncrypt 生成的证书(通过 LettuceEncrypt),因此我使用默认 SslCredentials
.
在 C# 中(Grpc.Core
),我使用 gRPC 如下:
// Channel and client creation
var channel = new Channel("my.domain.org:12345", new SslCredentials());
var client = new MyGrpc.MyGrpcClient(channel);
// Sample call
LoginUserReply reply = client.LoginUser(new LoginUserRequest()
{
Username = username
});
我按如下方式将其转换为 C++,主要基于 gRPC 网站上给出的示例:
// Channel and client creation
auto channelCreds = SslCredentials(SslCredentialsOptions());
auto channel = CreateChannel("my.domain.org:12345", channelCreds);
auto stub = MyGrpc::NewStub(channel);
// Sample call
ClientContext context;
LoginUserRequest request;
request.set_username(username);
LoginUserReply reply;
Status status = m_stub->LoginUser(&context, request, &reply);
但是,在 C++ 中使用这样的代码,status
总是报告失败 UNAVAILABLE (14) "Empty update".
为什么会这样,我该如何解决?
到目前为止我所做的调查:
仅使用 InsecureChannelCredentials()
会导致 UNAVAILABLE (14)“无法连接到所有地址”.
运行 纯文本的服务器,调用 does 与 OK (0) 仅供测试(雷洋建议)
我设置了 GRPC_TRACE=all
和 GRPC_VERBOSITY=DEBUG
环境变量,但没有看到来自客户端的额外日志记录。
在 C# 服务器中,我将 Grpc 日志记录级别设置为调试,并且只注意到没有像工作/未加密的客户端那样获取“正在阅读消息”日志行。
这是一个 known issue in the Windows C++ implementation of the gRPC client (and apparently macOS too). There is a small note on the gRPC authentication guide 说明:
Non-POSIX-compliant systems (such as Windows) need to specify the root certificates in SslCredentialsOptions, since the defaults are only configured for POSIX filesystems.
因此,要在 Windows 上实现此功能,您可以按如下方式填充 SslCredentialsOptions
:
#include <wincrypt.h>
SslCredentialsOptions getSslOptions()
{
// Fetch root certificate as required on Windows (s. issue 25533).
SslCredentialsOptions result;
// Open root certificate store.
HANDLE hRootCertStore = CertOpenSystemStoreW(NULL, L"ROOT");
if (!hRootCertStore)
return result;
// Get all root certificates.
PCCERT_CONTEXT pCert = NULL;
while ((pCert = CertEnumCertificatesInStore(hRootCertStore, pCert)) != NULL)
{
// Append this certificate in PEM formatted data.
DWORD size = 0;
CryptBinaryToStringW(pCert->pbCertEncoded, pCert->cbCertEncoded,
CRYPT_STRING_BASE64HEADER, NULL, &size);
std::vector<WCHAR> pem(size);
CryptBinaryToStringW(pCert->pbCertEncoded, pCert->cbCertEncoded,
CRYPT_STRING_BASE64HEADER, pem.data(), &size);
result.pem_root_certs += utf8Encode(pem.data());
}
CertCloseStore(hRootCertStore, 0);
return result;
}
如果您也需要 utf8Encode
方法:
std::string utf8Encode(const std::wstring& wstr)
{
if (wstr.empty())
return string();
int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(),
NULL, 0, NULL, NULL);
std::string strTo(sizeNeeded, 0);
WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(),
&strTo[0], sizeNeeded, NULL, NULL);
return strTo;
}
在 gRPC 存储库上有一个(遗憾的是不断陈旧)feature request 以在 Windows 上添加 out-of-the-box 对此的支持以及使行为成为默认行为 SslCredentials()
在所有系统上都一样。
在带有 VS2022 17.1.2 和 .NET 6 的
10 Pro 21H2 上,我正在将一个简单的 C# gRPC 客户端移植到 C++,但是 C++ 客户端总是无法连接到服务器,尽管我的代码看似做同样的事情,我 运行 不知道为什么。
我的 gRPC 服务器使用 SSL 和 LetsEncrypt 生成的证书(通过 LettuceEncrypt),因此我使用默认 SslCredentials
.
在 C# 中(Grpc.Core
),我使用 gRPC 如下:
// Channel and client creation
var channel = new Channel("my.domain.org:12345", new SslCredentials());
var client = new MyGrpc.MyGrpcClient(channel);
// Sample call
LoginUserReply reply = client.LoginUser(new LoginUserRequest()
{
Username = username
});
我按如下方式将其转换为 C++,主要基于 gRPC 网站上给出的示例:
// Channel and client creation
auto channelCreds = SslCredentials(SslCredentialsOptions());
auto channel = CreateChannel("my.domain.org:12345", channelCreds);
auto stub = MyGrpc::NewStub(channel);
// Sample call
ClientContext context;
LoginUserRequest request;
request.set_username(username);
LoginUserReply reply;
Status status = m_stub->LoginUser(&context, request, &reply);
但是,在 C++ 中使用这样的代码,status
总是报告失败 UNAVAILABLE (14) "Empty update".
为什么会这样,我该如何解决?
到目前为止我所做的调查:
仅使用
InsecureChannelCredentials()
会导致 UNAVAILABLE (14)“无法连接到所有地址”.运行 纯文本的服务器,调用 does 与 OK (0) 仅供测试(雷洋建议)
我设置了
GRPC_TRACE=all
和GRPC_VERBOSITY=DEBUG
环境变量,但没有看到来自客户端的额外日志记录。在 C# 服务器中,我将 Grpc 日志记录级别设置为调试,并且只注意到没有像工作/未加密的客户端那样获取“正在阅读消息”日志行。
这是一个 known issue in the Windows C++ implementation of the gRPC client (and apparently macOS too). There is a small note on the gRPC authentication guide 说明:
Non-POSIX-compliant systems (such as Windows) need to specify the root certificates in SslCredentialsOptions, since the defaults are only configured for POSIX filesystems.
因此,要在 Windows 上实现此功能,您可以按如下方式填充 SslCredentialsOptions
:
#include <wincrypt.h>
SslCredentialsOptions getSslOptions()
{
// Fetch root certificate as required on Windows (s. issue 25533).
SslCredentialsOptions result;
// Open root certificate store.
HANDLE hRootCertStore = CertOpenSystemStoreW(NULL, L"ROOT");
if (!hRootCertStore)
return result;
// Get all root certificates.
PCCERT_CONTEXT pCert = NULL;
while ((pCert = CertEnumCertificatesInStore(hRootCertStore, pCert)) != NULL)
{
// Append this certificate in PEM formatted data.
DWORD size = 0;
CryptBinaryToStringW(pCert->pbCertEncoded, pCert->cbCertEncoded,
CRYPT_STRING_BASE64HEADER, NULL, &size);
std::vector<WCHAR> pem(size);
CryptBinaryToStringW(pCert->pbCertEncoded, pCert->cbCertEncoded,
CRYPT_STRING_BASE64HEADER, pem.data(), &size);
result.pem_root_certs += utf8Encode(pem.data());
}
CertCloseStore(hRootCertStore, 0);
return result;
}
如果您也需要 utf8Encode
方法:
std::string utf8Encode(const std::wstring& wstr)
{
if (wstr.empty())
return string();
int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(),
NULL, 0, NULL, NULL);
std::string strTo(sizeNeeded, 0);
WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(),
&strTo[0], sizeNeeded, NULL, NULL);
return strTo;
}
在 gRPC 存储库上有一个(遗憾的是不断陈旧)feature request 以在 Windows 上添加 out-of-the-box 对此的支持以及使行为成为默认行为 SslCredentials()
在所有系统上都一样。