使用 HttpClient、客户端证书和 WinHttpHandler 向 Apple 推送通知服务发送 HTTP/2 请求引发 HttpRequestException
Sending HTTP/2 request to Apple Push Notification Service using HttpClient, Client Certificate and WinHttpHandler raises HttpRequestException
我正在尝试编写一些代码,使用 C# .NET 和 HttpClient 通过客户端证书身份验证通过 HTTP 向 iPhone 发送推送通知。
代码编译但总是产生:
带有消息“将内容复制到流时出错”的 HttpRequestException。
InnerException IOException 消息“写入操作失败,请参阅内部异常。”
InnerException WinHttpException 消息“{”调用 WinHttpWriteData 时出现错误 12152,'The server returned an invalid or unrecognized response'。”}”
我在寻找代码失败的原因?
是否可以debu/troubleshoot以某种方式做到这一点?
这是我的代码
class Program {
private const string cert = "MIINKwI....<hidden>";
private const string certpw = "password";
private const string devicetoken = "921c95b6494828f973512f2327478b0<hidden>";
private const string bundleid = "se.jensolsson.testapp";
private static HttpClient client = null;
static async Task Main(string[] args) {
try {
byte[] certificateData = Convert.FromBase64String(cert);
X509Certificate2 certificate = new X509Certificate2(certificateData, certpw, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
string url = "https://api.push.apple.com:443/3/device/";
string payload = @"{""aps"":{""alert"":""Test""}}";
var handler = new WinHttpHandler();
handler.ClientCertificates.Add(certificate);
if (client == null)
client = new HttpClient(handler);
using (var request = new HttpRequestMessage(HttpMethod.Post, url + devicetoken)) {
var messageGuid = Guid.NewGuid().ToString();
request.Version = new Version("2.0");
request.Content = new StringContent(payload);
request.Headers.Add("apns-id", messageGuid);
request.Headers.Add("apns-push-type", "alert");
request.Headers.Add("apns-priority", "10");
request.Headers.Add("apns-topic", bundleid);
using (var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)) {
HttpStatusCode statusCode = response.StatusCode;
string reasonPhrase = response.ReasonPhrase;
bool success = response.IsSuccessStatusCode;
}
}
}
catch (ArgumentNullException anu) { }
catch (InvalidOperationException ioe) { }
catch (HttpRequestException hre) { }
catch (TaskCanceledException tce) { }
catch (Exception e) { }
}
}
编辑 @jdweng 实际上尝试过 Wireshark,但由于它是通过 SSL 和加密的,我希望它无法从 Wireshark 读取?我可以看到连接是从我们的客户端重置的,必须由底层框架完成。
编辑 2:
进一步调查。尝试连接到支持 HTTP2 的开放式 Web 服务器。我以 https://nghttp2.org/ 为例。
- 将 url 行替换为
string url = "https://nghttp2.org/";
- 将请求行替换为
using (var request = new HttpRequestMessage(HttpMethod.Get, url)) {
。
一个非常有趣的发现是,即使我发送了一个 HTTP/2 请求并且它似乎给出了回复,查看响应对象,版本设置为 1.1.
如果我也删除 handler.ClientCertificates.Add(certificate);
行并重试,查看响应对象,版本已正确设置为 2.0。
因此,一旦将客户端证书添加到处理程序,WinHttpHandler 似乎就会放弃 HTTP/2 支持。
编辑 3:
我现在假设这是框架中的一个错误,我在这里发布了一个错误报告:https://github.com/dotnet/runtime/issues/40794
原来不支持这个。
主要是因为 Windows 版本中缺少功能。根据 corefx 团队 github 中的评论,这应该适用于 Windows 版本 2004,构建 19573+ github discussion。这个版本的问题是还没有发布,也没有计划什么时候发布。
我正在尝试编写一些代码,使用 C# .NET 和 HttpClient 通过客户端证书身份验证通过 HTTP 向 iPhone 发送推送通知。
代码编译但总是产生:
带有消息“将内容复制到流时出错”的 HttpRequestException。
InnerException IOException 消息“写入操作失败,请参阅内部异常。”
InnerException WinHttpException 消息“{”调用 WinHttpWriteData 时出现错误 12152,'The server returned an invalid or unrecognized response'。”}”
我在寻找代码失败的原因?
是否可以debu/troubleshoot以某种方式做到这一点?
这是我的代码
class Program {
private const string cert = "MIINKwI....<hidden>";
private const string certpw = "password";
private const string devicetoken = "921c95b6494828f973512f2327478b0<hidden>";
private const string bundleid = "se.jensolsson.testapp";
private static HttpClient client = null;
static async Task Main(string[] args) {
try {
byte[] certificateData = Convert.FromBase64String(cert);
X509Certificate2 certificate = new X509Certificate2(certificateData, certpw, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
string url = "https://api.push.apple.com:443/3/device/";
string payload = @"{""aps"":{""alert"":""Test""}}";
var handler = new WinHttpHandler();
handler.ClientCertificates.Add(certificate);
if (client == null)
client = new HttpClient(handler);
using (var request = new HttpRequestMessage(HttpMethod.Post, url + devicetoken)) {
var messageGuid = Guid.NewGuid().ToString();
request.Version = new Version("2.0");
request.Content = new StringContent(payload);
request.Headers.Add("apns-id", messageGuid);
request.Headers.Add("apns-push-type", "alert");
request.Headers.Add("apns-priority", "10");
request.Headers.Add("apns-topic", bundleid);
using (var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)) {
HttpStatusCode statusCode = response.StatusCode;
string reasonPhrase = response.ReasonPhrase;
bool success = response.IsSuccessStatusCode;
}
}
}
catch (ArgumentNullException anu) { }
catch (InvalidOperationException ioe) { }
catch (HttpRequestException hre) { }
catch (TaskCanceledException tce) { }
catch (Exception e) { }
}
}
编辑 @jdweng 实际上尝试过 Wireshark,但由于它是通过 SSL 和加密的,我希望它无法从 Wireshark 读取?我可以看到连接是从我们的客户端重置的,必须由底层框架完成。
编辑 2: 进一步调查。尝试连接到支持 HTTP2 的开放式 Web 服务器。我以 https://nghttp2.org/ 为例。
- 将 url 行替换为
string url = "https://nghttp2.org/";
- 将请求行替换为
using (var request = new HttpRequestMessage(HttpMethod.Get, url)) {
。
一个非常有趣的发现是,即使我发送了一个 HTTP/2 请求并且它似乎给出了回复,查看响应对象,版本设置为 1.1.
如果我也删除 handler.ClientCertificates.Add(certificate);
行并重试,查看响应对象,版本已正确设置为 2.0。
因此,一旦将客户端证书添加到处理程序,WinHttpHandler 似乎就会放弃 HTTP/2 支持。
编辑 3: 我现在假设这是框架中的一个错误,我在这里发布了一个错误报告:https://github.com/dotnet/runtime/issues/40794
原来不支持这个。 主要是因为 Windows 版本中缺少功能。根据 corefx 团队 github 中的评论,这应该适用于 Windows 版本 2004,构建 19573+ github discussion。这个版本的问题是还没有发布,也没有计划什么时候发布。