public override async Task SendNotificationAsync(Notification notification) { try { byte[] body; var timeToLive = (int)((notification.ExpireTime - DateTime.UtcNow).Ticks / TimeSpan.TicksPerSecond); if (timeToLive < 60) { throw new Exception("Notification expired."); } using (StringWriter stringWriter = new StringWriter()) { using (JsonWriter jsonWriter = new JsonWriter(stringWriter)) { jsonWriter.WriteStartObject(); jsonWriter.WriteNameValue("to", notification.DeviceAddress); jsonWriter.WriteNameValue("time_to_live", timeToLive); if (notification.HighPriority) { jsonWriter.WriteNameValue("priority", "high"); } jsonWriter.WriteStartObject("data"); foreach (var p in notification.Payload) { jsonWriter.WriteNameValue(p.Key, p.Value); } jsonWriter.WriteEndObject(); jsonWriter.WriteEndObject(); } var sbody = stringWriter.ToString(); #if DEBUG System.Diagnostics.Debug.WriteLine("https://fcm.googleapis.com/fcm/send: POST " + sbody); #endif body = System.Text.Encoding.UTF8.GetBytes(sbody); } HttpWebRequest webreq = (HttpWebRequest)WebRequest.Create("https://fcm.googleapis.com/fcm/send"); webreq.Method = "POST"; webreq.ContentType = "application/json; charset=UTF-8"; webreq.Headers.Add("Authorization: key=" + Config.AuthorizationKey); webreq.Headers.Add("Sender: id=" + Config.SenderId); webreq.ContentLength = body.Length; string response; using (Stream dataStream = await webreq.GetRequestStreamAsync()) { dataStream.Write(body, 0, body.Length); using (HttpWebResponse webresp = (HttpWebResponse)await webreq.GetResponseAsync()) { if (webresp.StatusCode != HttpStatusCode.OK) { throw new Exception("FCM server returns: " + webresp.StatusCode); } if (!webresp.ContentType.StartsWith("application/json;", StringComparison.Ordinal)) { throw new Exception("FCM server returns invalid contenttype: " + webresp.ContentType); } using (Stream responseReader = webresp.GetResponseStream()) { using (StreamReader reader = new StreamReader(responseReader, System.Text.Encoding.GetEncoding(webresp.CharacterSet))) { response = await reader.ReadToEndAsync(); } } } } JsonObject jsonResponse = (JsonObject)JsonReader.ParseString(response); if (jsonResponse.GetValueInt("failure") != 0 || jsonResponse.GetValueInt("canonical_ids") != 0) { var results = jsonResponse["results"]; if (results is JsonArray && ((JsonArray)results).Count == 1) { var result = ((JsonArray)results)[0]; if (result is JsonObject) { var error = ((JsonObject)result)["error"]; if (error is string) { Service.Error((string)error == "NotRegistered" || (string)error == "MismatchSenderId" ? new PushNotificationInvalidDeviceException(notification) : new PushNotificationException(notification, "Submit notification to '" + notification.DeviceAddress + "' failed error '" + error + "'.")); return; } } } Service.Error(new PushNotificationException(notification, "Submit notification to '" + notification.DeviceAddress + "' failed error '" + response + "'.")); } } catch (Exception err) { Service.Error(new PushNotificationException(notification, "Failed to submit notification to '" + notification.DeviceAddress + "'.", err)); } }
public override async Task SendNotificationAsync(Notification notification) { byte[] msg = new byte[2102]; int pos = 1 + 4; try { // 3 NotificationIdentifier var notificationIdentifier = _notificationIdentifier; { msg[pos++] = 0x03; msg[pos++] = 0; msg[pos++] = 4; msg[pos++] = (byte)((notificationIdentifier >> 24) & 0xFF); msg[pos++] = (byte)((notificationIdentifier >> 16) & 0xFF); msg[pos++] = (byte)((notificationIdentifier >> 8) & 0xFF); msg[pos++] = (byte)((notificationIdentifier) & 0xFF); } if (notification != null) { // 1 DeviceToken { var deviceToken = notification.DeviceAddress; if (deviceToken.Length != 64) { throw new FormatException("Invalid DeviceToken."); } msg[pos++] = 0x01; msg[pos++] = 0; msg[pos++] = 32; for (int i = 0; i < deviceToken.Length; i += 2) { msg[pos++] = (byte)(Library.HexToNibble(deviceToken[i]) << 4 | Library.HexToNibble(deviceToken[i + 1])); } } // 2 Payload { byte[] bpayload; using (var x = new StringWriter()) { (new JsonWriter(x)).WriteValue(notification.Payload); bpayload = Encoding.UTF8.GetBytes(x.ToString()); } if (bpayload.Length > 2048) { throw new FormatException("Payload to big."); } msg[pos++] = 0x02; msg[pos++] = (byte)((bpayload.Length >> 8) & 0xFF); msg[pos++] = (byte)((bpayload.Length) & 0xFF); Array.Copy(bpayload, 0, msg, pos, bpayload.Length); pos += bpayload.Length; } // 4 ExpirationDate { var expireTime = notification.ExpireTime; if (expireTime.Ticks < DateTime.UtcNow.Ticks + TimeSpan.TicksPerMinute) { throw new FormatException("Notification expired."); } var v = (Int32)((expireTime - Library.UnixEPoch).Ticks / TimeSpan.TicksPerSecond); msg[pos++] = 0x04; msg[pos++] = 0; msg[pos++] = 4; msg[pos++] = (byte)((v >> 24) & 0xFF); msg[pos++] = (byte)((v >> 16) & 0xFF); msg[pos++] = (byte)((v >> 8) & 0xFF); msg[pos++] = (byte)((v) & 0xFF); } // 5 Priority if (notification.HighPriority) { msg[pos++] = 0x05; msg[pos++] = 0; msg[pos++] = 1; msg[pos++] = 0x05; } } int sz = pos - 5; msg[0] = 0x02; msg[1] = (byte)((sz >> 24) & 0xFF); msg[2] = (byte)((sz >> 16) & 0xFF); msg[3] = (byte)((sz >> 8) & 0xFF); msg[4] = (byte)((sz) & 0xFF); var queued = false; lock (_lockObject) { if (_notifications != null) { _notifications.Insert(_notificationIdentifier++, notification); queued = true; } } if (!queued) { if (notification != null) { Service.SendNotification(notification); } return; } } catch (Exception err) { if (notification != null) { Service.Error(new PushNotificationException(notification, "Notification to '" + notification.DeviceAddress + "' failed. Invalid notification format.", err)); } } try { await _connection.Send(msg, pos); } catch (Exception err) { Dispose(); Service.Error(new PushNotificationServiceException("Sending request to APSN failed.", err)); } }