I am trying to write some code to send a push notification to a pass on an Apple device using C# .NET and HttpClient over HTTP2 with client certificate authentication.
When I run the code, I am seeing the below exception: InnerException = {"Error 12152 calling WinHttpWriteData, 'The server returned an invalid or unrecognized response'."}
I am trying to find out why this code is failing?
Is it possible to debug/troubleshoot this in some way?
Running this code on Windows 10 OS Build version: 21H2 (19044). The application is built on .Net Framework 4.8 and tried using WinHttpHanlder versions 6 and also 7.
Also, tried using other third party open source libraries like PushSharp and DotApns. PushSharp does not seem to support Http/2 and dotApns does not support certificate authentication for .net framework. We have no plans to migrate to .net Core.
Below is my code:
private static string pushToken = "dbc56849<hidden>";
private static string AppleApnServer = "https://api.sandbox.push.apple.com";
public static async Task<PushResult> SendPushNotificationToWalletPass(string notificationContent)
{
byte[] certificateData = LoadCertificate();
X509Certificate2 certificate = new X509Certificate2(certificateData, String.Empty, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
string url = $"{AppleApnServer}:443/3/device/{pushToken}";
StringBuilder payload = new StringBuilder();
payload.Append("{ \"aps\" : ");
if (string.IsNullOrWhiteSpace(notificationContent))
{
payload.Append("\"\" }");
}
else
{
payload.Append(notificationContent);
payload.Append(" }"); // close aps dictionary
}
var handler = new Http2Handler();
handler.ClientCertificates.Add(certificate);
using (var httpClient = new HttpClient(handler))
{
using (var request = new HttpRequestMessage(HttpMethod.Post, url))
{
var messageGuid = Guid.NewGuid().ToString();
request.Content = new StringContent(payload.ToString());
request.Headers.Add("apns-id", messageGuid);
request.Headers.Add("apns-push-type", "alert");
using (var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead))
{
HttpStatusCode statusCode = response.StatusCode;
string reasonPhrase = response.ReasonPhrase;
bool success = response.IsSuccessStatusCode;
Console.WriteLine($"APN {(success ? "Delivered Successfully!" : $"Failed to Send! :: StatusCode [{statusCode}] Reason [{reasonPhrase}]")} :: PushToken [{pushToken}]");
if (!success)
{
switch (statusCode)
{
case HttpStatusCode.Gone:
// The device token is no longer active for the topic.
return PushResult.DeviceNotRegistered;
default:
return PushResult.Failure;
}
}
return PushResult.Success;
}
}
}
}
public enum PushResult
{
Success = 0,
Failure = 100,
DeviceNotRegistered = 200
}
// Apple APNS requires http2 but .Net Framework does not support http2 (only built in support in .net core)
// found this workaround https://stackoverflow.com/questions/32685151/how-to-make-the-net-httpclient-use-http-2-0/43101990#43101990
private class Http2Handler : WinHttpHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
request.Version = new Version("2.0");
return base.SendAsync(request, cancellationToken);
}
}