-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
I've successfully been able to connect to APNS (Apple Push Notification Service) using HTTP2 and .NET Core 3.0 Preview 7.
According to their documentation they recommend keeping the connection open for basically as long as possible and that they treat "rapid connection and disconnection as a denial-of-service attack". See the section title "Best Practices for Managing Connections".
In order to cater for this I have tried to create a HttpClient
that effectively lives for the duration of the app, expecting it to keep the connection open for that lifetime. Unfortunately it appears that the connection only stays open for approximately 2 minutes. Tested using the following terminal command on macOS Mojave 10.14.6 (18G84):
lsof -i -n | sort | grep "17\."
17.
being the first octet of Apple's assigned IP block.
A new connection is opened when another request is sent using the same HttpClient
but it seems like the connection shouldn't have been dropped in the first place. The only issue I can find that seems obviously related is https://github.com/dotnet/corefx/issues/31294.
Here is some code to reproduce the issue, implemented as a Hosted Service
public class Startup : IHostedService
{
private HttpClient Client { get; set; }
public static Task Main(string[] args)
{
return new HostBuilder()
.ConfigureServices(s => s.AddHostedService<Startup>())
.RunConsoleAsync();
}
public Startup()
{
var handler = new HttpClientHandler();
handler.SslProtocols = SslProtocols.Tls12;
Client = new HttpClient(handler, true);
Client.BaseAddress = new Uri("https://api.push.apple.com/3/device/");
Client.DefaultRequestVersion = new Version(2, 0);
Client.DefaultRequestHeaders.Connection.Add("keep-alive");
}
public Task StartAsync(CancellationToken cancellationToken)
{
var deviceToken = "ABC";
Console.WriteLine($"{DateTime.Now} Sending...");
var response = await Client.PostAsync(deviceToken, new StringContent(""));
Console.WriteLine(response);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}