PageRenderTime 41ms CodeModel.GetById 13ms app.highlight 15ms RepoModel.GetById 10ms app.codeStats 0ms

/PushSharp.Android/Gcm/GcmMessageTransportAsync.cs

https://github.com/mustafagenc/PushSharp
C# | 331 lines | 272 code | 53 blank | 6 comment | 34 complexity | 7a632fd37e5485d594e9fa2b8ee02fc4 MD5 | raw file
  1using System;
  2using System.Collections.Generic;
  3using System.Linq;
  4using System.Text;
  5using System.Threading;
  6using System.Net;
  7using System.IO;
  8using System.Security.Cryptography.X509Certificates;
  9using System.Security;
 10using System.Net.Security;
 11using Newtonsoft.Json.Linq;
 12
 13namespace PushSharp.Android
 14{
 15	internal class GcmMessageTransportAsync
 16	{
 17		public event Action<string> UpdateGoogleClientAuthToken;
 18		public event Action<GcmMessageTransportResponse> MessageResponseReceived;
 19		public event Action<GcmNotification, Exception> UnhandledException;
 20
 21		static GcmMessageTransportAsync()
 22		{
 23			ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, policyErrs) => { return true; };
 24		}
 25
 26		private const string GCM_SEND_URL = "https://android.googleapis.com/gcm/send";
 27
 28		public void Send(GcmNotification msg, string senderAuthToken, string senderID, string applicationID)
 29		{
 30			try
 31			{
 32				send(msg, senderAuthToken, senderID, applicationID);
 33			}
 34			catch (Exception ex)
 35			{
 36				if (UnhandledException != null)
 37					UnhandledException(msg, ex);
 38				else
 39					throw ex;
 40			}
 41		}
 42
 43		void send(GcmNotification msg, string senderAuthToken, string senderID, string applicationID)
 44		{
 45			var result = new GcmMessageTransportResponse();
 46			result.Message = msg;
 47						
 48			var postData = msg.GetJson();
 49
 50			var webReq = (HttpWebRequest)WebRequest.Create(GCM_SEND_URL);
 51			//webReq.ContentLength = postData.Length;
 52			webReq.Method = "POST";
 53			webReq.ContentType = "application/json";
 54			//webReq.ContentType = "application/x-www-form-urlencoded;charset=UTF-8   can be used for plaintext bodies
 55			webReq.UserAgent = "PushSharp (version: 1.0)";
 56			webReq.Headers.Add("Authorization: key=" + senderAuthToken);
 57
 58			webReq.BeginGetRequestStream(new AsyncCallback(requestStreamCallback), new GcmAsyncParameters()
 59			{
 60				WebRequest = webReq,
 61				WebResponse = null,
 62				Message = msg,
 63				SenderAuthToken = senderAuthToken,
 64				SenderId = senderID,
 65				ApplicationId = applicationID
 66			});
 67		}
 68
 69		void requestStreamCallback(IAsyncResult result)
 70		{
 71			var msg = new GcmNotification();
 72
 73			try
 74			{
 75				var asyncParam = result.AsyncState as GcmAsyncParameters;
 76				msg = asyncParam.Message;
 77
 78				if (asyncParam != null)
 79				{
 80					var wrStream = asyncParam.WebRequest.EndGetRequestStream(result);
 81
 82					using (var webReqStream = new StreamWriter(wrStream))
 83					{
 84						var data = asyncParam.Message.GetJson();
 85						webReqStream.Write(data);
 86						webReqStream.Close();
 87					}
 88
 89					try
 90					{
 91						asyncParam.WebRequest.BeginGetResponse(new AsyncCallback(responseCallback), asyncParam);
 92					}
 93					catch (WebException wex)
 94					{
 95						asyncParam.WebResponse = wex.Response as HttpWebResponse;
 96						processResponseError(asyncParam);
 97					}
 98				}
 99			}
100			catch (Exception ex)
101			{
102				if (UnhandledException != null)
103					UnhandledException(msg, ex);
104				else
105					throw ex;
106			}
107		}
108
109		void responseCallback(IAsyncResult result)
110		{
111			var msg = new GcmNotification();
112
113			try
114			{
115				var asyncParam = result.AsyncState as GcmAsyncParameters;
116				msg = asyncParam.Message;
117
118				try
119				{
120					asyncParam.WebResponse = asyncParam.WebRequest.EndGetResponse(result) as HttpWebResponse;
121					processResponseOk(asyncParam);
122				}
123				catch (WebException wex)
124				{
125					asyncParam.WebResponse = wex.Response as HttpWebResponse;
126					processResponseError(asyncParam);
127				}
128			}
129			catch (Exception ex)
130			{
131				if (UnhandledException != null)
132					UnhandledException(msg, ex);
133				else
134					throw ex;
135			}
136		}
137
138		void processResponseOk(GcmAsyncParameters asyncParam)
139		{
140			var result = new GcmMessageTransportResponse()
141			{
142				ResponseCode = GcmMessageTransportResponseCode.Ok,
143				Message = asyncParam.Message
144			};
145
146			var updateClientAuth = asyncParam.WebResponse.GetResponseHeader("Update-Client-Auth");
147
148			if (!string.IsNullOrEmpty(updateClientAuth) && UpdateGoogleClientAuthToken != null)
149				UpdateGoogleClientAuthToken(updateClientAuth);
150
151			//Get the response body
152			var json = new JObject();
153
154			try { json = JObject.Parse((new StreamReader(asyncParam.WebResponse.GetResponseStream())).ReadToEnd()); }
155			catch { }
156
157
158			result.NumberOfCanonicalIds = json.Value<long>("canonical_ids");
159			result.NumberOfFailures = json.Value<long>("failure");
160			result.NumberOfSuccesses = json.Value<long>("success");
161
162		
163			var jsonResults = json["results"] as JArray;
164
165			if (jsonResults == null)
166				jsonResults = new JArray();
167
168			foreach (var r in json["results"])
169			{
170				var msgResult = new GcmMessageResult();
171								
172				msgResult.MessageId = r.Value<string>("message_id");
173				msgResult.CanonicalRegistrationId = r.Value<string>("registration_id");
174
175				if (!string.IsNullOrEmpty(msgResult.CanonicalRegistrationId))
176				{
177					msgResult.ResponseStatus = GcmMessageTransportResponseStatus.CanonicalRegistrationId;
178				}
179				else if (r["error"] != null)
180				{
181					var err = r.Value<string>("error") ?? "";
182
183					switch (err.ToLower().Trim())
184					{
185						case "missingregistration":
186							msgResult.ResponseStatus = GcmMessageTransportResponseStatus.MissingRegistrationId;
187							break;
188						case "unavailable":
189							msgResult.ResponseStatus = GcmMessageTransportResponseStatus.Unavailable;
190							break;
191						case "notregistered":
192							msgResult.ResponseStatus = GcmMessageTransportResponseStatus.NotRegistered;
193							break;
194						case "invalidregistration":
195							msgResult.ResponseStatus = GcmMessageTransportResponseStatus.InvalidRegistration;
196							break;
197						case "mismatchsenderid":
198							msgResult.ResponseStatus = GcmMessageTransportResponseStatus.MismatchSenderId;
199							break;
200						case "messagetoobig":
201							msgResult.ResponseStatus = GcmMessageTransportResponseStatus.MessageTooBig;
202							break;
203                        case "invaliddatakey":
204                            msgResult.ResponseStatus = GcmMessageTransportResponseStatus.InvalidDataKey;
205                            break;
206                        case "invalidttl":
207                            msgResult.ResponseStatus = GcmMessageTransportResponseStatus.InvalidTtl;
208                            break;
209                        case "internalservererror":
210                            msgResult.ResponseStatus = GcmMessageTransportResponseStatus.InternalServerError;
211                            break;
212						default:
213							msgResult.ResponseStatus = GcmMessageTransportResponseStatus.Error;
214							break;
215					}
216				}
217
218				result.Results.Add(msgResult);
219								
220			}
221
222			asyncParam.WebResponse.Close();
223
224			var evtmrr = MessageResponseReceived;
225
226			if (evtmrr != null)
227				evtmrr(result);
228		}
229
230		void processResponseError(GcmAsyncParameters asyncParam)
231		{
232			var result = new GcmMessageTransportResponse();
233			result.ResponseCode = GcmMessageTransportResponseCode.Error;
234
235			if (asyncParam.WebResponse.StatusCode == HttpStatusCode.Unauthorized)
236			{
237				//401 bad auth token
238				result.ResponseCode = GcmMessageTransportResponseCode.InvalidAuthToken;
239				throw new GcmAuthenticationErrorTransportException(result);
240			}
241			else if (asyncParam.WebResponse.StatusCode == HttpStatusCode.BadRequest)
242			{
243				result.ResponseCode = GcmMessageTransportResponseCode.BadRequest;
244				throw new GcmBadRequestTransportException(result);
245			}
246            else if (asyncParam.WebResponse.StatusCode == HttpStatusCode.InternalServerError)
247            {
248                result.ResponseCode = GcmMessageTransportResponseCode.InternalServiceError;
249                throw new GcmMessageTransportException("Internal Service Error", result);
250            }
251            else if (asyncParam.WebResponse.StatusCode == HttpStatusCode.ServiceUnavailable)
252            {
253                //First try grabbing the retry-after header and parsing it.
254                TimeSpan retryAfter = new TimeSpan(0, 0, 120);
255
256                var wrRetryAfter = asyncParam.WebResponse.GetResponseHeader("Retry-After");
257
258                if (!string.IsNullOrEmpty(wrRetryAfter))
259                {
260                    DateTime wrRetryAfterDate = DateTime.UtcNow;
261
262                    if (DateTime.TryParse(wrRetryAfter, out wrRetryAfterDate))
263                        retryAfter = wrRetryAfterDate - DateTime.UtcNow;
264                    else
265                    {
266                        int wrRetryAfterSeconds = 120;
267                        if (int.TryParse(wrRetryAfter, out wrRetryAfterSeconds))
268                            retryAfter = new TimeSpan(0, 0, wrRetryAfterSeconds);
269                    }
270                }
271
272                //503 exponential backoff, get retry-after header
273                result.ResponseCode = GcmMessageTransportResponseCode.ServiceUnavailable;
274
275                throw new GcmServiceUnavailableTransportException(retryAfter, result);
276            }
277
278			asyncParam.WebResponse.Close();
279
280			throw new GcmMessageTransportException("Unknown Transport Error", result);
281		}
282
283		class GcmAsyncParameters
284		{
285			public GcmNotification Message
286			{
287				get;
288				set;
289			}
290
291			public HttpWebRequest WebRequest
292			{
293				get;
294				set;
295			}
296
297			public HttpWebResponse WebResponse
298			{
299				get;
300				set;
301			}
302
303			public string SenderAuthToken
304			{
305				get;
306				set;
307			}
308
309			public string SenderId
310			{
311				get;
312				set;
313			}
314
315			public string ApplicationId
316			{
317				get;
318				set;
319			}
320		}
321	}
322
323	public class GcmMessageResult
324	{
325		public string MessageId { get; set; }
326
327		public string CanonicalRegistrationId {	get; set; }
328
329		public GcmMessageTransportResponseStatus ResponseStatus { get; set; }
330	}
331}