Skip to content

Commit 77b04bd

Browse files
Merge pull request #83 from GurGaller/master
Friendly exception for invalid keys
2 parents 4ce0be8 + da64e64 commit 77b04bd

File tree

3 files changed

+61
-18
lines changed

3 files changed

+61
-18
lines changed

WebPush.Test/WebPushClientTest.cs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Linq;
66
using System.Net;
77
using System.Net.Http;
8+
using WebPush.Model;
89

910
namespace WebPush.Test
1011
{
@@ -73,7 +74,8 @@ public void TestSetGcmApiKey()
7374
[TestMethod]
7475
public void TestSetGCMAPIKeyEmptyString()
7576
{
76-
Assert.ThrowsException<ArgumentException>(delegate { client.SetGcmApiKey(""); });
77+
Assert.ThrowsException<ArgumentException>(delegate
78+
{ client.SetGcmApiKey(""); });
7779
}
7880

7981
[TestMethod]
@@ -85,8 +87,7 @@ public void TestSetGcmApiKeyNonGcmPushService()
8587
var subscription = new PushSubscription(TestFirefoxEndpoint, TestPublicKey, TestPrivateKey);
8688
var message = client.GenerateRequestDetails(subscription, @"test payload");
8789

88-
IEnumerable<string> values;
89-
Assert.IsFalse(message.Headers.TryGetValues(@"Authorization", out values));
90+
Assert.IsFalse(message.Headers.TryGetValues(@"Authorization", out var values));
9091
}
9192

9293
[TestMethod]
@@ -98,15 +99,14 @@ public void TestSetGcmApiKeyNull()
9899
var subscription = new PushSubscription(TestGcmEndpoint, TestPublicKey, TestPrivateKey);
99100
var message = client.GenerateRequestDetails(subscription, @"test payload");
100101

101-
IEnumerable<string> values;
102-
Assert.IsFalse(message.Headers.TryGetValues("Authorization", out values));
102+
Assert.IsFalse(message.Headers.TryGetValues("Authorization", out var values));
103103
}
104104

105105
[TestMethod]
106106
public void TestSetVapidDetails()
107107
{
108108
client.SetVapidDetails(TestSubject, TestPublicKey, TestPrivateKey);
109-
109+
110110
var subscription = new PushSubscription(TestFirefoxEndpoint, TestPublicKey, TestPrivateKey);
111111
var message = client.GenerateRequestDetails(subscription, @"test payload");
112112
var authorizationHeader = message.Headers.GetValues(@"Authorization").First();
@@ -161,9 +161,21 @@ public void TestHandlingFailureMessages(HttpStatusCode status, string response,
161161
Assert.AreEqual(expectedMessage, actual.Message);
162162
}
163163

164-
private void TestSendNotification(HttpStatusCode status, string response=null)
164+
[TestMethod]
165+
[DataRow(1)]
166+
[DataRow(5)]
167+
[DataRow(10)]
168+
[DataRow(50)]
169+
public void TestHandleInvalidPublicKeys(int charactersToDrop)
165170
{
166-
var subscription = new PushSubscription(TestFcmEndpoint, TestPublicKey, TestPrivateKey);
171+
var invalidKey = TestPublicKey.Substring(0, TestPublicKey.Length - charactersToDrop);
172+
173+
Assert.ThrowsException<InvalidEncryptionDetailsException>(() => TestSendNotification(HttpStatusCode.OK, response: null, invalidKey));
174+
}
175+
176+
private void TestSendNotification(HttpStatusCode status, string response = null, string publicKey = TestPublicKey)
177+
{
178+
var subscription = new PushSubscription(TestFcmEndpoint, publicKey, TestPrivateKey);
167179
var httpContent = response == null ? null : new StringContent(response);
168180
httpMessageHandlerMock.When(TestFcmEndpoint).Respond(req => new HttpResponseMessage { StatusCode = status, Content = httpContent });
169181
client.SetVapidDetails(TestSubject, TestPublicKey, TestPrivateKey);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
3+
namespace WebPush.Model
4+
{
5+
public class InvalidEncryptionDetailsException : Exception
6+
{
7+
public InvalidEncryptionDetailsException(string message, PushSubscription pushSubscription)
8+
: base(message)
9+
{
10+
PushSubscription = pushSubscription;
11+
}
12+
13+
public PushSubscription PushSubscription { get; }
14+
}
15+
}

WebPush/WebPushClient.cs

100755100644
Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Runtime.CompilerServices;
77
using System.Threading;
88
using System.Threading.Tasks;
9+
using WebPush.Model;
910
using WebPush.Util;
1011

1112
[assembly: InternalsVisibleTo("WebPush.Test")]
@@ -202,7 +203,7 @@ public HttpRequestMessage GenerateRequestDetails(PushSubscription subscription,
202203
@"Unable to send a message with payload to this subscription since it doesn't have the required encryption key");
203204
}
204205

205-
var encryptedPayload = Encryptor.Encrypt(subscription.P256DH, subscription.Auth, payload);
206+
var encryptedPayload = EncryptPayload(subscription, payload);
206207

207208
request.Content = new ByteArrayContent(encryptedPayload.Payload);
208209
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
@@ -243,7 +244,8 @@ public HttpRequestMessage GenerateRequestDetails(PushSubscription subscription,
243244
{
244245
cryptoKeyHeader += @";" + vapidHeaders["Crypto-Key"];
245246
}
246-
} else if (isFcm && !string.IsNullOrEmpty(currentGcmApiKey))
247+
}
248+
else if (isFcm && !string.IsNullOrEmpty(currentGcmApiKey))
247249
{
248250
request.Headers.TryAddWithoutValidation("Authorization", "key=" + currentGcmApiKey);
249251
}
@@ -252,6 +254,23 @@ public HttpRequestMessage GenerateRequestDetails(PushSubscription subscription,
252254
return request;
253255
}
254256

257+
private static EncryptionResult EncryptPayload(PushSubscription subscription, string payload)
258+
{
259+
try
260+
{
261+
return Encryptor.Encrypt(subscription.P256DH, subscription.Auth, payload);
262+
}
263+
catch (Exception ex)
264+
{
265+
if (ex is FormatException || ex is ArgumentException)
266+
{
267+
throw new InvalidEncryptionDetailsException("Unable to encrypt the payload with the encryption key of this subscription.", subscription);
268+
}
269+
270+
throw;
271+
}
272+
}
273+
255274
/// <summary>
256275
/// To send a push notification call this method with a subscription, optional payload and any options
257276
/// Will exception if unsuccessful
@@ -265,11 +284,8 @@ public HttpRequestMessage GenerateRequestDetails(PushSubscription subscription,
265284
public void SendNotification(PushSubscription subscription, string payload = null,
266285
Dictionary<string, object> options = null)
267286
{
268-
var request = GenerateRequestDetails(subscription, payload, options);
269-
var response = HttpClient.SendAsync(request).Result;
270-
HandleResponse(response, subscription);
287+
SendNotificationAsync(subscription, payload, options).GetAwaiter().GetResult();
271288
}
272-
273289

274290
/// <summary>
275291
/// To send a push notification call this method with a subscription, optional payload and any options
@@ -296,7 +312,7 @@ public void SendNotification(PushSubscription subscription, string payload, stri
296312
var options = new Dictionary<string, object> { ["gcmAPIKey"] = gcmApiKey };
297313
SendNotification(subscription, payload, options);
298314
}
299-
315+
300316

301317
/// <summary>
302318
/// To send a push notification asynchronous call this method with a subscription, optional payload and any options
@@ -310,7 +326,7 @@ public void SendNotification(PushSubscription subscription, string payload, stri
310326
/// </param>
311327
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
312328
public async Task SendNotificationAsync(PushSubscription subscription, string payload = null,
313-
Dictionary<string, object> options = null, CancellationToken cancellationToken=default)
329+
Dictionary<string, object> options = null, CancellationToken cancellationToken = default)
314330
{
315331
var request = GenerateRequestDetails(subscription, payload, options);
316332
var response = await HttpClient.SendAsync(request, cancellationToken);
@@ -327,7 +343,7 @@ public async Task SendNotificationAsync(PushSubscription subscription, string pa
327343
/// <param name="vapidDetails">The vapid details for the notification.</param>
328344
/// <param name="cancellationToken"></param>
329345
public async Task SendNotificationAsync(PushSubscription subscription, string payload,
330-
VapidDetails vapidDetails, CancellationToken cancellationToken=default)
346+
VapidDetails vapidDetails, CancellationToken cancellationToken = default)
331347
{
332348
var options = new Dictionary<string, object> { ["vapidDetails"] = vapidDetails };
333349
await SendNotificationAsync(subscription, payload, options, cancellationToken);
@@ -341,7 +357,7 @@ public async Task SendNotificationAsync(PushSubscription subscription, string pa
341357
/// <param name="payload">The payload you wish to send to the user</param>
342358
/// <param name="gcmApiKey">The GCM API key</param>
343359
/// <param name="cancellationToken"></param>
344-
public async Task SendNotificationAsync(PushSubscription subscription, string payload, string gcmApiKey, CancellationToken cancellationToken=default)
360+
public async Task SendNotificationAsync(PushSubscription subscription, string payload, string gcmApiKey, CancellationToken cancellationToken = default)
345361
{
346362
var options = new Dictionary<string, object> { ["gcmAPIKey"] = gcmApiKey };
347363
await SendNotificationAsync(subscription, payload, options, cancellationToken);

0 commit comments

Comments
 (0)