From f44c222bd53f82ef5787a2e20cec0d8a7797a156 Mon Sep 17 00:00:00 2001 From: Javier Sanchez Date: Thu, 17 Nov 2016 19:38:09 +0100 Subject: [PATCH 01/16] Update PushShart using a Proxy when connecting to Google Cloud Messaging (GCM) --- PushSharp.Google/GcmConfiguration.cs | 24 ++++++++++++++++ PushSharp.Google/GcmServiceConnection.cs | 36 ++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/PushSharp.Google/GcmConfiguration.cs b/PushSharp.Google/GcmConfiguration.cs index 91f320ff..4f8d5d4d 100644 --- a/PushSharp.Google/GcmConfiguration.cs +++ b/PushSharp.Google/GcmConfiguration.cs @@ -24,6 +24,30 @@ public GcmConfiguration (string optionalSenderID, string senderAuthToken, string this.ValidateServerCertificate = false; } + public void SetProxy(string host, int port) + { + UseProxy = true; + ProxyHost = host; + ProxyPort = port; + ProxyCredentials = CredentialCache.DefaultNetworkCredentials; + } + + public void SetProxy(string host, int port, string username, string pass, string domain) + { + UseProxy = true; + ProxyHost = host; + ProxyPort = port; + ProxyCredentials = new NetworkCredential(username, pass, domain); + } + + public bool UseProxy { get; private set; } + + public string ProxyHost { get; private set; } + + public int ProxyPort { get; private set; } + + public NetworkCredential ProxyCredentials { get; private set; } + public string SenderID { get; private set; } public string SenderAuthToken { get; private set; } diff --git a/PushSharp.Google/GcmServiceConnection.cs b/PushSharp.Google/GcmServiceConnection.cs index b3b539c5..1e1f4e4f 100644 --- a/PushSharp.Google/GcmServiceConnection.cs +++ b/PushSharp.Google/GcmServiceConnection.cs @@ -38,8 +38,14 @@ public class GcmServiceConnection : IServiceConnection public GcmServiceConnection (GcmConfiguration configuration) { Configuration = configuration; - http = new HttpClient (); - + if (Configuration.UseProxy) + { + http = GetClientHTTPProxy(Configuration.ProxyHost, Configuration.ProxyPort, Configuration.ProxyCredentials); + } + else + { + http = new HttpClient(); + } http.DefaultRequestHeaders.UserAgent.Clear (); http.DefaultRequestHeaders.UserAgent.Add (new ProductInfoHeaderValue ("PushSharp", "3.0")); http.DefaultRequestHeaders.TryAddWithoutValidation ("Authorization", "key=" + Configuration.SenderAuthToken); @@ -212,5 +218,31 @@ static GcmResponseStatus GetGcmResponseStatus (string str) //Default return GcmResponseStatus.Error; } + + private HttpClient GetClientHTTPProxy(string host, int port, NetworkCredential credentials) + { + HttpClient client = null; + try + { + string proxyUri = string.Format("{0}:{1}", host, port); + WebProxy proxy = new WebProxy(proxyUri, false) + { + Credentials = credentials + }; + HttpClientHandler httpClientHandler = new HttpClientHandler() + { + Proxy = proxy, + PreAuthenticate = false + }; + client = new HttpClient(httpClientHandler); + } + catch (Exception ex) + { + client = null; + Log.Error(ex.Message); + throw; + } + return client; + } } } From f2adb2c7b189077683321e32d2583f93ef7c49b9 Mon Sep 17 00:00:00 2001 From: FlyMontag Date: Thu, 17 Mar 2016 15:47:30 +0100 Subject: [PATCH 02/16] Options for Proxy Usage Adds properties and methods to use an HTTP-proxy in your connection, either with default network credentials or specified credentials. --- PushSharp.Apple/ApnsConfiguration.cs | 39 +++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/PushSharp.Apple/ApnsConfiguration.cs b/PushSharp.Apple/ApnsConfiguration.cs index 464a5e62..1ad67376 100644 --- a/PushSharp.Apple/ApnsConfiguration.cs +++ b/PushSharp.Apple/ApnsConfiguration.cs @@ -1,5 +1,6 @@ -using System; +using System; using System.Collections.Generic; +using System.Net; using System.Security.Cryptography.X509Certificates; using System.Text.RegularExpressions; @@ -108,19 +109,19 @@ void CheckIsApnsCertificate () var commonName = Certificate.SubjectName.Name; if (!issuerName.Contains ("Apple")) - throw new ArgumentOutOfRangeException ("Your Certificate does not appear to be issued by Apple! Please check to ensure you have the correct certificate!"); + throw new ApnsConnectionException ("Your Certificate does not appear to be issued by Apple! Please check to ensure you have the correct certificate!"); if (!Regex.IsMatch (commonName, "Apple.*?Push Services") && !commonName.Contains ("Website Push ID:")) - throw new ArgumentOutOfRangeException ("Your Certificate is not a valid certificate for connecting to Apple's APNS servers"); + throw new ApnsConnectionException ("Your Certificate is not a valid certificate for connecting to Apple's APNS servers"); if (commonName.Contains ("Development") && ServerEnvironment != ApnsServerEnvironment.Sandbox) - throw new ArgumentOutOfRangeException ("You are using a certificate created for connecting only to the Sandbox APNS server but have selected a different server environment to connect to."); + throw new ApnsConnectionException ("You are using a certificate created for connecting only to the Sandbox APNS server but have selected a different server environment to connect to."); if (commonName.Contains ("Production") && ServerEnvironment != ApnsServerEnvironment.Production) - throw new ArgumentOutOfRangeException ("You are using a certificate created for connecting only to the Production APNS server but have selected a different server environment to connect to."); + throw new ApnsConnectionException ("You are using a certificate created for connecting only to the Production APNS server but have selected a different server environment to connect to."); } else { - throw new ArgumentOutOfRangeException ("You must provide a Certificate to connect to APNS with!"); + throw new ApnsConnectionException ("You must provide a Certificate to connect to APNS with!"); } } @@ -136,6 +137,22 @@ public void OverrideFeedbackServer (string host, int port) FeedbackPort = port; } + public void SetProxy(string proxyHost, int proxyPort) + { + UseProxy = true; + ProxyHost = proxyHost; + ProxyPort = proxyPort; + ProxyCredentials = CredentialCache.DefaultNetworkCredentials; + } + + public void SetProxy(string proxyHost, int proxyPort, string userName, string password, string domain) + { + UseProxy = true; + ProxyHost = proxyHost; + ProxyPort = proxyPort; + ProxyCredentials = new NetworkCredential(userName, password, domain); + } + public string Host { get; private set; } public int Port { get; private set; } @@ -144,6 +161,14 @@ public void OverrideFeedbackServer (string host, int port) public int FeedbackPort { get; private set; } + public bool UseProxy { get; private set; } + + public string ProxyHost { get; private set; } + + public int ProxyPort { get; private set; } + + public NetworkCredential ProxyCredentials { get; private set; } + public X509Certificate2 Certificate { get; private set; } public List AdditionalCertificates { get; private set; } @@ -205,4 +230,4 @@ public enum ApnsServerEnvironment { Production } } -} \ No newline at end of file +} From fa422d579327520282db53ce2765534ffd265285 Mon Sep 17 00:00:00 2001 From: FlyMontag Date: Thu, 17 Mar 2016 16:03:46 +0100 Subject: [PATCH 03/16] Connection via proxy A method to tunnel the TCP-connection via a pre-configured HTTP-Proxy has been added. If Configuration.UseProxy is set to true, a CONNECT-request is sent using the proxy adress specified. The socket is then extracted from the stream of this request's response (using the System.Reflection-Library) and used to create a TcpClient-object. All traffic sent through this TcpClient-object will pass through the proxy used to send the initial CONNECT-Request. NOTE: The method 'getClientViaHTTPProxy' uses code that was not written by the author of this commit. The original code can be found here: https://web.archive.org/web/20160317134733/https://nitormobiledevelopment.wordpress.com/2013/08/13/push-sharp-using-proxy/ --- PushSharp.Apple/ApnsConnection.cs | 75 ++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/PushSharp.Apple/ApnsConnection.cs b/PushSharp.Apple/ApnsConnection.cs index 0908b166..c771922f 100644 --- a/PushSharp.Apple/ApnsConnection.cs +++ b/PushSharp.Apple/ApnsConnection.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Net.Sockets; using System.Net.Security; @@ -8,6 +8,7 @@ using System.Threading; using System.Net; using PushSharp.Core; +using System.Diagnostics; namespace PushSharp.Apple { @@ -160,7 +161,7 @@ async Task SendBatch () } catch (Exception ex) { Log.Error ("APNS-CLIENT[{0}]: Send Batch Error: Batch ID={1}, Error={2}", id, batchId, ex); foreach (var n in toSend) - n.CompleteFailed (new ApnsNotificationException (ApnsNotificationErrorStatusCode.ConnectionError, n.Notification, ex)); + n.CompleteFailed (ex); } Log.Info ("APNS-Client[{0}]: Sent Batch, waiting for possible response...", id); @@ -239,7 +240,7 @@ async Task Reader () sent.Clear (); return; } - + // If we make it here, we did get data back, so we have errors Log.Info ("APNS-Client[{0}]: Batch (ID={1}) completed with error response...", id, batchId); @@ -314,6 +315,55 @@ bool socketCanWrite () return p; } + + /// + /// Source: + /// https://web.archive.org/web/20160317134733/https://nitormobiledevelopment.wordpress.com/2013/08/13/push-sharp-using-proxy/ + /// + /// + /// + /// + /// + /// + private TcpClient getClientViaHTTPProxy(string targetHost, int targetPort, string httpProxyHost, int httpProxyPort) + { + try + { + var uriBuilder = new UriBuilder + { + Scheme = Uri.UriSchemeHttp, + Host = httpProxyHost, + Port = httpProxyPort + }; + var proxyUri = uriBuilder.Uri; + var request = WebRequest.Create("http://" + targetHost + ":" + targetPort); + var webProxy = new WebProxy(proxyUri); + request.Proxy = webProxy; + request.Method = "CONNECT"; + webProxy.Credentials = Configuration.ProxyCredentials; + var response = request.GetResponse(); + var responseStream = response.GetResponseStream(); + Debug.Assert(responseStream != null); + const System.Reflection.BindingFlags Flags = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance; + var rsType = responseStream.GetType(); + var connectionProperty = rsType.GetProperty("Connection", Flags); + var connection = connectionProperty.GetValue(responseStream, null); + var connectionType = connection.GetType(); + var networkStreamProperty = connectionType.GetProperty("NetworkStream", Flags); + Stream networkStream1 = (Stream)networkStreamProperty.GetValue(connection, null); + var nsType = networkStream1.GetType(); + var socketProperty = nsType.GetProperty("Socket", Flags); + var socket = (Socket)socketProperty.GetValue(networkStream1, null); + return new TcpClient { Client = socket }; + } + catch (Exception ex) + { + Log.Error(ex.Message); + throw; + } + } + + async Task connect () { if (client != null) @@ -321,10 +371,21 @@ async Task connect () Log.Info ("APNS-Client[{0}]: Connecting (Batch ID={1})", id, batchId); - client = new TcpClient (); + if (Configuration.UseProxy) + { + client = getClientViaHTTPProxy(Configuration.Host, Configuration.Port, Configuration.ProxyHost, Configuration.ProxyPort); + } + else + { + client = new TcpClient(); + } - try { - await client.ConnectAsync (Configuration.Host, Configuration.Port).ConfigureAwait (false); + try + { + if (!Configuration.UseProxy) + { + await client.ConnectAsync (Configuration.Host, Configuration.Port).ConfigureAwait (false); + } //Set keep alive on the socket may help maintain our APNS connection try { @@ -349,7 +410,7 @@ async Task connect () // Create our ssl stream stream = new SslStream (client.GetStream (), - false, + true, ValidateRemoteCertificate, (sender, targetHost, localCerts, remoteCert, acceptableIssuers) => certificate); From fde875f55d157c3d72d554ed9b0a5b6ad3496e05 Mon Sep 17 00:00:00 2001 From: FlyMontag Date: Thu, 17 Mar 2016 16:11:02 +0100 Subject: [PATCH 04/16] Update ApnsConnection.cs --- PushSharp.Apple/ApnsConnection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PushSharp.Apple/ApnsConnection.cs b/PushSharp.Apple/ApnsConnection.cs index c771922f..2e60a321 100644 --- a/PushSharp.Apple/ApnsConnection.cs +++ b/PushSharp.Apple/ApnsConnection.cs @@ -161,7 +161,7 @@ async Task SendBatch () } catch (Exception ex) { Log.Error ("APNS-CLIENT[{0}]: Send Batch Error: Batch ID={1}, Error={2}", id, batchId, ex); foreach (var n in toSend) - n.CompleteFailed (ex); + n.CompleteFailed (new ApnsNotificationException (ApnsNotificationErrorStatusCode.ConnectionError, n.Notification, ex)); } Log.Info ("APNS-Client[{0}]: Sent Batch, waiting for possible response...", id); From 64c28d125d94ad80de9462396501bebc4c6ae508 Mon Sep 17 00:00:00 2001 From: FlyMontag Date: Thu, 17 Mar 2016 16:14:15 +0100 Subject: [PATCH 05/16] Update ApnsConfiguration.cs --- PushSharp.Apple/ApnsConfiguration.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/PushSharp.Apple/ApnsConfiguration.cs b/PushSharp.Apple/ApnsConfiguration.cs index 1ad67376..c4693559 100644 --- a/PushSharp.Apple/ApnsConfiguration.cs +++ b/PushSharp.Apple/ApnsConfiguration.cs @@ -109,19 +109,19 @@ void CheckIsApnsCertificate () var commonName = Certificate.SubjectName.Name; if (!issuerName.Contains ("Apple")) - throw new ApnsConnectionException ("Your Certificate does not appear to be issued by Apple! Please check to ensure you have the correct certificate!"); + throw new ArgumentOutOfRangeException ("Your Certificate does not appear to be issued by Apple! Please check to ensure you have the correct certificate!"); if (!Regex.IsMatch (commonName, "Apple.*?Push Services") && !commonName.Contains ("Website Push ID:")) - throw new ApnsConnectionException ("Your Certificate is not a valid certificate for connecting to Apple's APNS servers"); + throw new ArgumentOutOfRangeException ("Your Certificate is not a valid certificate for connecting to Apple's APNS servers"); if (commonName.Contains ("Development") && ServerEnvironment != ApnsServerEnvironment.Sandbox) - throw new ApnsConnectionException ("You are using a certificate created for connecting only to the Sandbox APNS server but have selected a different server environment to connect to."); + throw new ArgumentOutOfRangeException ("You are using a certificate created for connecting only to the Sandbox APNS server but have selected a different server environment to connect to."); if (commonName.Contains ("Production") && ServerEnvironment != ApnsServerEnvironment.Production) - throw new ApnsConnectionException ("You are using a certificate created for connecting only to the Production APNS server but have selected a different server environment to connect to."); + throw new ArgumentOutOfRangeException ("You are using a certificate created for connecting only to the Production APNS server but have selected a different server environment to connect to."); } else { - throw new ApnsConnectionException ("You must provide a Certificate to connect to APNS with!"); + throw new ArgumentOutOfRangeException ("You must provide a Certificate to connect to APNS with!"); } } From 0f0aa0e890d9e158b1676a71d78542d5492752e2 Mon Sep 17 00:00:00 2001 From: Rafael Araujo Date: Wed, 14 Dec 2016 16:27:28 -0200 Subject: [PATCH 06/16] apns wit proxy withou socket hijack --- PushSharp.Apple/ApnsConnection.cs | 81 ++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 18 deletions(-) diff --git a/PushSharp.Apple/ApnsConnection.cs b/PushSharp.Apple/ApnsConnection.cs index 2e60a321..63faa9f9 100644 --- a/PushSharp.Apple/ApnsConnection.cs +++ b/PushSharp.Apple/ApnsConnection.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Net.Sockets; using System.Net.Security; @@ -7,6 +7,9 @@ using System.Collections.Generic; using System.Threading; using System.Net; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; using PushSharp.Core; using System.Diagnostics; @@ -128,28 +131,37 @@ async Task SendBatch () Log.Info ("APNS-Client[{0}]: Sending Batch ID={1}, Count={2}", id, batchId, toSend.Count); - try { + try + { var data = createBatch (toSend); - if (data != null && data.Length > 0) { + if (data != null && data.Length > 0) + { - for (var i = 0; i <= Configuration.InternalBatchFailureRetryCount; i++) { + for (var i = 0; i <= Configuration.InternalBatchFailureRetryCount; i++) + { await connectingSemaphore.WaitAsync (); - try { + try + { // See if we need to connect if (!socketCanWrite () || i > 0) await connect (); - } finally { + } + finally + { connectingSemaphore.Release (); } - try { + try + { await networkStream.WriteAsync(data, 0, data.Length).ConfigureAwait(false); break; - } catch (Exception ex) when (i != Configuration.InternalBatchFailureRetryCount) { + } + catch (Exception ex) when (i != Configuration.InternalBatchFailureRetryCount) + { Log.Info("APNS-CLIENT[{0}]: Retrying Batch: Batch ID={1}, Error={2}", id, batchId, ex); } } @@ -157,11 +169,18 @@ async Task SendBatch () foreach (var n in toSend) sent.Add (new SentNotification (n)); } - - } catch (Exception ex) { + } + catch (WebException ex) + { Log.Error ("APNS-CLIENT[{0}]: Send Batch Error: Batch ID={1}, Error={2}", id, batchId, ex); foreach (var n in toSend) n.CompleteFailed (new ApnsNotificationException (ApnsNotificationErrorStatusCode.ConnectionError, n.Notification, ex)); + return; + } + catch (Exception ex) { + Log.Error ("APNS-CLIENT[{0}]: Send Batch Error: Batch ID={1}, Error={2}", id, batchId, ex); + foreach (var n in toSend) + n.CompleteFailed(new ApnsNotificationException(ApnsNotificationErrorStatusCode.ConnectionError, n.Notification, ex)); } Log.Info ("APNS-Client[{0}]: Sent Batch, waiting for possible response...", id); @@ -315,6 +334,28 @@ bool socketCanWrite () return p; } + async Task connectHTTPProxy(TcpClient client) + { + Log.Info("APNS-Client[{0}]: Connecting Proxy (Batch ID={1})", id, batchId); + await client.ConnectAsync(Configuration.ProxyHost, Configuration.ProxyPort).ConfigureAwait(false); + var stream = client.GetStream(); + var buffer = Encoding.UTF8.GetBytes(string.Format("CONNECT {0}:{1} HTTP/1.1\r\nHost: {0}:{1}\r\n\r\n", Configuration.Host, Configuration.Port)); + await stream.WriteAsync(buffer, 0, buffer.Length); + await stream.FlushAsync(); + buffer = new byte[client.Client.ReceiveBufferSize]; + using(var resp = new MemoryStream()) + { + do + { + var len = await stream.ReadAsync(buffer, 0, buffer.Length); + resp.Write(buffer, 0, len); + } + while (client.Client.Available > 0); + buffer = resp.ToArray(); + } + var content = Encoding.UTF8.GetString(buffer); + Log.Info("APNS-Client[{0}]: Proxy Connected (Batch ID={1}) : {2}", id, batchId, content); + } /// /// Source: @@ -371,14 +412,14 @@ async Task connect () Log.Info ("APNS-Client[{0}]: Connecting (Batch ID={1})", id, batchId); - if (Configuration.UseProxy) - { - client = getClientViaHTTPProxy(Configuration.Host, Configuration.Port, Configuration.ProxyHost, Configuration.ProxyPort); - } - else - { + //if (Configuration.UseProxy) + //{ + // client = getClientViaHTTPProxy(Configuration.Host, Configuration.Port, Configuration.ProxyHost, Configuration.ProxyPort); + //} + //else + //{ client = new TcpClient(); - } + //} try { @@ -386,6 +427,10 @@ async Task connect () { await client.ConnectAsync (Configuration.Host, Configuration.Port).ConfigureAwait (false); } + else + { + await connectHTTPProxy(client).ConfigureAwait(false); + } //Set keep alive on the socket may help maintain our APNS connection try { @@ -410,7 +455,7 @@ async Task connect () // Create our ssl stream stream = new SslStream (client.GetStream (), - true, + false, ValidateRemoteCertificate, (sender, targetHost, localCerts, remoteCert, acceptableIssuers) => certificate); From dd9a833ae42fbb2673fc932b81cdb817d98d6737 Mon Sep 17 00:00:00 2001 From: Rafael Araujo Date: Wed, 14 Dec 2016 17:24:10 -0200 Subject: [PATCH 07/16] Handling proxy connection errors on APNS --- PushSharp.Apple/ApnsConnection.cs | 54 ++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/PushSharp.Apple/ApnsConnection.cs b/PushSharp.Apple/ApnsConnection.cs index 63faa9f9..018418be 100644 --- a/PushSharp.Apple/ApnsConnection.cs +++ b/PushSharp.Apple/ApnsConnection.cs @@ -170,7 +170,7 @@ async Task SendBatch () sent.Add (new SentNotification (n)); } } - catch (WebException ex) + catch (ApnsConnectionException ex) { Log.Error ("APNS-CLIENT[{0}]: Send Batch Error: Batch ID={1}, Error={2}", id, batchId, ex); foreach (var n in toSend) @@ -339,7 +339,7 @@ async Task connectHTTPProxy(TcpClient client) Log.Info("APNS-Client[{0}]: Connecting Proxy (Batch ID={1})", id, batchId); await client.ConnectAsync(Configuration.ProxyHost, Configuration.ProxyPort).ConfigureAwait(false); var stream = client.GetStream(); - var buffer = Encoding.UTF8.GetBytes(string.Format("CONNECT {0}:{1} HTTP/1.1\r\nHost: {0}:{1}\r\n\r\n", Configuration.Host, Configuration.Port)); + var buffer = Encoding.UTF8.GetBytes(string.Format("CONNECT {0}:{1} HTTP/1.1\r\nHost: {0}:{1}\r\nProxy-Connection: keep-alive\r\n\r\n", Configuration.Host, Configuration.Port)); await stream.WriteAsync(buffer, 0, buffer.Length); await stream.FlushAsync(); buffer = new byte[client.Client.ReceiveBufferSize]; @@ -354,7 +354,27 @@ async Task connectHTTPProxy(TcpClient client) buffer = resp.ToArray(); } var content = Encoding.UTF8.GetString(buffer); - Log.Info("APNS-Client[{0}]: Proxy Connected (Batch ID={1}) : {2}", id, batchId, content); + var i = content.IndexOf('\r'); + string statusCode; + if (i > 9) + { + statusCode = content.Substring(9, i - 9); + } + else + { + var max = content.Length; + if (max > 50) + { + max = 50; + } + var append = max == 50 ? "..." : string.Empty; + statusCode = content.Substring(0, max) + append; + } + Log.Info("APNS-Client[{0}]: Proxy Connected (Batch ID={1}) : {2}", id, batchId, statusCode); + if (!statusCode.StartsWith("200")) + { + throw new ApnsConnectionException($"Proxy returned {statusCode}. Check proxy settings and if it allows ssl through ports different of 443."); + } } /// @@ -433,19 +453,31 @@ async Task connect () } //Set keep alive on the socket may help maintain our APNS connection - try { - client.Client.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); - } catch { + try + { + client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); + } + catch + { } //Really not sure if this will work on MONO.... // This may help windows azure users - try { - SetSocketKeepAliveValues (client.Client, (int)Configuration.KeepAlivePeriod.TotalMilliseconds, (int)Configuration.KeepAliveRetryPeriod.TotalMilliseconds); - } catch { + try + { + SetSocketKeepAliveValues(client.Client, (int)Configuration.KeepAlivePeriod.TotalMilliseconds, (int)Configuration.KeepAliveRetryPeriod.TotalMilliseconds); } - } catch (Exception ex) { - throw new ApnsConnectionException ("Failed to Connect, check your firewall settings!", ex); + catch + { + } + } + catch (ApnsConnectionException) + { + throw; + } + catch (Exception ex) + { + throw new ApnsConnectionException("Failed to Connect, check your firewall settings!", ex); } // We can configure skipping ssl all together, ie: if we want to hit a test server From 27d8fa0bc03f8a08ca524747ef0130ee6742d891 Mon Sep 17 00:00:00 2001 From: Rafael Araujo Date: Wed, 14 Dec 2016 18:43:41 -0200 Subject: [PATCH 08/16] Using System.Net missing in GcmConfiguration --- PushSharp.Google/GcmConfiguration.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/PushSharp.Google/GcmConfiguration.cs b/PushSharp.Google/GcmConfiguration.cs index 4f8d5d4d..f0939105 100644 --- a/PushSharp.Google/GcmConfiguration.cs +++ b/PushSharp.Google/GcmConfiguration.cs @@ -1,4 +1,5 @@ using System; +using System.Net; namespace PushSharp.Google { From 785133f4a32bc0ef44ec1260eb7441f91b168adb Mon Sep 17 00:00:00 2001 From: Rafael Araujo Date: Wed, 14 Dec 2016 18:45:34 -0200 Subject: [PATCH 09/16] Http Proxy with Basic authentication without WebRequest in APNS --- PushSharp.Apple/ApnsConnection.cs | 58 +++++-------------------------- 1 file changed, 9 insertions(+), 49 deletions(-) diff --git a/PushSharp.Apple/ApnsConnection.cs b/PushSharp.Apple/ApnsConnection.cs index 018418be..cd9ef25d 100644 --- a/PushSharp.Apple/ApnsConnection.cs +++ b/PushSharp.Apple/ApnsConnection.cs @@ -339,11 +339,18 @@ async Task connectHTTPProxy(TcpClient client) Log.Info("APNS-Client[{0}]: Connecting Proxy (Batch ID={1})", id, batchId); await client.ConnectAsync(Configuration.ProxyHost, Configuration.ProxyPort).ConfigureAwait(false); var stream = client.GetStream(); - var buffer = Encoding.UTF8.GetBytes(string.Format("CONNECT {0}:{1} HTTP/1.1\r\nHost: {0}:{1}\r\nProxy-Connection: keep-alive\r\n\r\n", Configuration.Host, Configuration.Port)); + var authorization = string.Empty; + if (Configuration.ProxyCredentials != null) + { + var credential = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{Configuration.ProxyCredentials.UserName}:{Configuration.ProxyCredentials.Password}")); + authorization = $"\r\nAuthorization: Basic {credential}"; + } + var buffer = Encoding.UTF8.GetBytes(string.Format("CONNECT {0}:{1} HTTP/1.1\r\nHost: {0}:{1}{2}\r\nProxy-Connection: keep-alive\r\n\r\n", Configuration.Host, Configuration.Port, authorization)); + await stream.WriteAsync(buffer, 0, buffer.Length); await stream.FlushAsync(); buffer = new byte[client.Client.ReceiveBufferSize]; - using(var resp = new MemoryStream()) + using (var resp = new MemoryStream()) { do { @@ -377,53 +384,6 @@ async Task connectHTTPProxy(TcpClient client) } } - /// - /// Source: - /// https://web.archive.org/web/20160317134733/https://nitormobiledevelopment.wordpress.com/2013/08/13/push-sharp-using-proxy/ - /// - /// - /// - /// - /// - /// - private TcpClient getClientViaHTTPProxy(string targetHost, int targetPort, string httpProxyHost, int httpProxyPort) - { - try - { - var uriBuilder = new UriBuilder - { - Scheme = Uri.UriSchemeHttp, - Host = httpProxyHost, - Port = httpProxyPort - }; - var proxyUri = uriBuilder.Uri; - var request = WebRequest.Create("http://" + targetHost + ":" + targetPort); - var webProxy = new WebProxy(proxyUri); - request.Proxy = webProxy; - request.Method = "CONNECT"; - webProxy.Credentials = Configuration.ProxyCredentials; - var response = request.GetResponse(); - var responseStream = response.GetResponseStream(); - Debug.Assert(responseStream != null); - const System.Reflection.BindingFlags Flags = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance; - var rsType = responseStream.GetType(); - var connectionProperty = rsType.GetProperty("Connection", Flags); - var connection = connectionProperty.GetValue(responseStream, null); - var connectionType = connection.GetType(); - var networkStreamProperty = connectionType.GetProperty("NetworkStream", Flags); - Stream networkStream1 = (Stream)networkStreamProperty.GetValue(connection, null); - var nsType = networkStream1.GetType(); - var socketProperty = nsType.GetProperty("Socket", Flags); - var socket = (Socket)socketProperty.GetValue(networkStream1, null); - return new TcpClient { Client = socket }; - } - catch (Exception ex) - { - Log.Error(ex.Message); - throw; - } - } - async Task connect () { From 6ae2a91ea857242963015a3fd695e7a960318905 Mon Sep 17 00:00:00 2001 From: Rafael Araujo Date: Wed, 14 Dec 2016 18:43:41 -0200 Subject: [PATCH 10/16] System.Net was missing in GcmConfiguration --- PushSharp.Google/GcmConfiguration.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/PushSharp.Google/GcmConfiguration.cs b/PushSharp.Google/GcmConfiguration.cs index 4f8d5d4d..f0939105 100644 --- a/PushSharp.Google/GcmConfiguration.cs +++ b/PushSharp.Google/GcmConfiguration.cs @@ -1,4 +1,5 @@ using System; +using System.Net; namespace PushSharp.Google { From c63e5caa9bbf109266269a337b8444b1c74b544e Mon Sep 17 00:00:00 2001 From: Rafael Araujo Date: Wed, 14 Dec 2016 16:27:28 -0200 Subject: [PATCH 11/16] Using http proxy with basic auth (not using socket hijacking) in APNS --- PushSharp.Apple/ApnsConnection.cs | 169 ++++++++++++++++++------------ 1 file changed, 103 insertions(+), 66 deletions(-) diff --git a/PushSharp.Apple/ApnsConnection.cs b/PushSharp.Apple/ApnsConnection.cs index 2e60a321..cd9ef25d 100644 --- a/PushSharp.Apple/ApnsConnection.cs +++ b/PushSharp.Apple/ApnsConnection.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Net.Sockets; using System.Net.Security; @@ -7,6 +7,9 @@ using System.Collections.Generic; using System.Threading; using System.Net; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; using PushSharp.Core; using System.Diagnostics; @@ -128,28 +131,37 @@ async Task SendBatch () Log.Info ("APNS-Client[{0}]: Sending Batch ID={1}, Count={2}", id, batchId, toSend.Count); - try { + try + { var data = createBatch (toSend); - if (data != null && data.Length > 0) { + if (data != null && data.Length > 0) + { - for (var i = 0; i <= Configuration.InternalBatchFailureRetryCount; i++) { + for (var i = 0; i <= Configuration.InternalBatchFailureRetryCount; i++) + { await connectingSemaphore.WaitAsync (); - try { + try + { // See if we need to connect if (!socketCanWrite () || i > 0) await connect (); - } finally { + } + finally + { connectingSemaphore.Release (); } - try { + try + { await networkStream.WriteAsync(data, 0, data.Length).ConfigureAwait(false); break; - } catch (Exception ex) when (i != Configuration.InternalBatchFailureRetryCount) { + } + catch (Exception ex) when (i != Configuration.InternalBatchFailureRetryCount) + { Log.Info("APNS-CLIENT[{0}]: Retrying Batch: Batch ID={1}, Error={2}", id, batchId, ex); } } @@ -157,11 +169,18 @@ async Task SendBatch () foreach (var n in toSend) sent.Add (new SentNotification (n)); } - - } catch (Exception ex) { + } + catch (ApnsConnectionException ex) + { Log.Error ("APNS-CLIENT[{0}]: Send Batch Error: Batch ID={1}, Error={2}", id, batchId, ex); foreach (var n in toSend) n.CompleteFailed (new ApnsNotificationException (ApnsNotificationErrorStatusCode.ConnectionError, n.Notification, ex)); + return; + } + catch (Exception ex) { + Log.Error ("APNS-CLIENT[{0}]: Send Batch Error: Batch ID={1}, Error={2}", id, batchId, ex); + foreach (var n in toSend) + n.CompleteFailed(new ApnsNotificationException(ApnsNotificationErrorStatusCode.ConnectionError, n.Notification, ex)); } Log.Info ("APNS-Client[{0}]: Sent Batch, waiting for possible response...", id); @@ -315,51 +334,53 @@ bool socketCanWrite () return p; } - - /// - /// Source: - /// https://web.archive.org/web/20160317134733/https://nitormobiledevelopment.wordpress.com/2013/08/13/push-sharp-using-proxy/ - /// - /// - /// - /// - /// - /// - private TcpClient getClientViaHTTPProxy(string targetHost, int targetPort, string httpProxyHost, int httpProxyPort) + async Task connectHTTPProxy(TcpClient client) { - try + Log.Info("APNS-Client[{0}]: Connecting Proxy (Batch ID={1})", id, batchId); + await client.ConnectAsync(Configuration.ProxyHost, Configuration.ProxyPort).ConfigureAwait(false); + var stream = client.GetStream(); + var authorization = string.Empty; + if (Configuration.ProxyCredentials != null) + { + var credential = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{Configuration.ProxyCredentials.UserName}:{Configuration.ProxyCredentials.Password}")); + authorization = $"\r\nAuthorization: Basic {credential}"; + } + var buffer = Encoding.UTF8.GetBytes(string.Format("CONNECT {0}:{1} HTTP/1.1\r\nHost: {0}:{1}{2}\r\nProxy-Connection: keep-alive\r\n\r\n", Configuration.Host, Configuration.Port, authorization)); + + await stream.WriteAsync(buffer, 0, buffer.Length); + await stream.FlushAsync(); + buffer = new byte[client.Client.ReceiveBufferSize]; + using (var resp = new MemoryStream()) { - var uriBuilder = new UriBuilder + do { - Scheme = Uri.UriSchemeHttp, - Host = httpProxyHost, - Port = httpProxyPort - }; - var proxyUri = uriBuilder.Uri; - var request = WebRequest.Create("http://" + targetHost + ":" + targetPort); - var webProxy = new WebProxy(proxyUri); - request.Proxy = webProxy; - request.Method = "CONNECT"; - webProxy.Credentials = Configuration.ProxyCredentials; - var response = request.GetResponse(); - var responseStream = response.GetResponseStream(); - Debug.Assert(responseStream != null); - const System.Reflection.BindingFlags Flags = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance; - var rsType = responseStream.GetType(); - var connectionProperty = rsType.GetProperty("Connection", Flags); - var connection = connectionProperty.GetValue(responseStream, null); - var connectionType = connection.GetType(); - var networkStreamProperty = connectionType.GetProperty("NetworkStream", Flags); - Stream networkStream1 = (Stream)networkStreamProperty.GetValue(connection, null); - var nsType = networkStream1.GetType(); - var socketProperty = nsType.GetProperty("Socket", Flags); - var socket = (Socket)socketProperty.GetValue(networkStream1, null); - return new TcpClient { Client = socket }; + var len = await stream.ReadAsync(buffer, 0, buffer.Length); + resp.Write(buffer, 0, len); + } + while (client.Client.Available > 0); + buffer = resp.ToArray(); } - catch (Exception ex) + var content = Encoding.UTF8.GetString(buffer); + var i = content.IndexOf('\r'); + string statusCode; + if (i > 9) { - Log.Error(ex.Message); - throw; + statusCode = content.Substring(9, i - 9); + } + else + { + var max = content.Length; + if (max > 50) + { + max = 50; + } + var append = max == 50 ? "..." : string.Empty; + statusCode = content.Substring(0, max) + append; + } + Log.Info("APNS-Client[{0}]: Proxy Connected (Batch ID={1}) : {2}", id, batchId, statusCode); + if (!statusCode.StartsWith("200")) + { + throw new ApnsConnectionException($"Proxy returned {statusCode}. Check proxy settings and if it allows ssl through ports different of 443."); } } @@ -371,14 +392,14 @@ async Task connect () Log.Info ("APNS-Client[{0}]: Connecting (Batch ID={1})", id, batchId); - if (Configuration.UseProxy) - { - client = getClientViaHTTPProxy(Configuration.Host, Configuration.Port, Configuration.ProxyHost, Configuration.ProxyPort); - } - else - { + //if (Configuration.UseProxy) + //{ + // client = getClientViaHTTPProxy(Configuration.Host, Configuration.Port, Configuration.ProxyHost, Configuration.ProxyPort); + //} + //else + //{ client = new TcpClient(); - } + //} try { @@ -386,21 +407,37 @@ async Task connect () { await client.ConnectAsync (Configuration.Host, Configuration.Port).ConfigureAwait (false); } + else + { + await connectHTTPProxy(client).ConfigureAwait(false); + } //Set keep alive on the socket may help maintain our APNS connection - try { - client.Client.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); - } catch { + try + { + client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); + } + catch + { } //Really not sure if this will work on MONO.... // This may help windows azure users - try { - SetSocketKeepAliveValues (client.Client, (int)Configuration.KeepAlivePeriod.TotalMilliseconds, (int)Configuration.KeepAliveRetryPeriod.TotalMilliseconds); - } catch { + try + { + SetSocketKeepAliveValues(client.Client, (int)Configuration.KeepAlivePeriod.TotalMilliseconds, (int)Configuration.KeepAliveRetryPeriod.TotalMilliseconds); } - } catch (Exception ex) { - throw new ApnsConnectionException ("Failed to Connect, check your firewall settings!", ex); + catch + { + } + } + catch (ApnsConnectionException) + { + throw; + } + catch (Exception ex) + { + throw new ApnsConnectionException("Failed to Connect, check your firewall settings!", ex); } // We can configure skipping ssl all together, ie: if we want to hit a test server @@ -410,7 +447,7 @@ async Task connect () // Create our ssl stream stream = new SslStream (client.GetStream (), - true, + false, ValidateRemoteCertificate, (sender, targetHost, localCerts, remoteCert, acceptableIssuers) => certificate); From 644a84b56d6e21560164426976e976f396c5db10 Mon Sep 17 00:00:00 2001 From: Rafael Araujo Date: Mon, 19 Dec 2016 11:26:03 -0200 Subject: [PATCH 12/16] New class ProxyHelper to permit proxy connect in any place using a TcpClient --- PushSharp.Apple/PushSharp.Apple.csproj | 2 +- PushSharp.Core/ProxyHelper.cs | 86 ++++++++++++++++++++++++++ PushSharp.Core/PushSharp.Core.csproj | 3 +- 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 PushSharp.Core/ProxyHelper.cs diff --git a/PushSharp.Apple/PushSharp.Apple.csproj b/PushSharp.Apple/PushSharp.Apple.csproj index 3c9344a0..c43093ef 100644 --- a/PushSharp.Apple/PushSharp.Apple.csproj +++ b/PushSharp.Apple/PushSharp.Apple.csproj @@ -1,4 +1,4 @@ - + Debug diff --git a/PushSharp.Core/ProxyHelper.cs b/PushSharp.Core/ProxyHelper.cs new file mode 100644 index 00000000..9a6506c6 --- /dev/null +++ b/PushSharp.Core/ProxyHelper.cs @@ -0,0 +1,86 @@ +using System; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace PushSharp.Core +{ + public delegate void BeforeConnectDelegate(); + + public delegate void AfterConnectDelegate(string status); + + public class ProxyHelper + { + public Func ProxyConnectionExceptionCreator { get; set; } + public event BeforeConnectDelegate BeforeConnect; + public event AfterConnectDelegate AfterConnect; + + public async Task Connect(TcpClient client, string targetHost, int targetPort, string proxyHost, int proxyPort, NetworkCredential proxyCredential = null) + { + this.OnBeforeConnect(); + await client.ConnectAsync(proxyHost, proxyPort).ConfigureAwait(false); + var stream = client.GetStream(); + var authorization = string.Empty; + if (proxyCredential != null) + { + var credential = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{proxyCredential.UserName}:{proxyCredential.Password}")); + authorization = $"\r\nAuthorization: Basic {credential}"; + } + var buffer = + Encoding.UTF8.GetBytes( + string.Format("CONNECT {0}:{1} HTTP/1.1\r\nHost: {0}:{1}{2}\r\nProxy-Connection: keep-alive\r\n\r\n", targetHost, targetPort, authorization)); + await stream.WriteAsync(buffer, 0, buffer.Length); + await stream.FlushAsync(); + buffer = new byte[client.Client.ReceiveBufferSize]; + using (var resp = new MemoryStream()) + { + do + { + var len = await stream.ReadAsync(buffer, 0, buffer.Length); + resp.Write(buffer, 0, len); + } + while (client.Client.Available > 0); + buffer = resp.ToArray(); + } + var content = Encoding.UTF8.GetString(buffer); + var i = content.IndexOf('\r'); + string statusCode; + if (i > 9) + { + statusCode = content.Substring(9, i - 9); + } + else + { + var max = content.Length; + if (max > 50) + { + max = 50; + } + var append = max == 50 ? "..." : string.Empty; + statusCode = content.Substring(0, max) + append; + } + this.OnAfterConnect(statusCode); + if (!statusCode.StartsWith("200")) + { + var fac = this.ProxyConnectionExceptionCreator; + if (fac != null) + { + throw fac($"Proxy returned {statusCode}. Check proxy settings and if it allows ssl through ports different of 443."); + } + throw new Exception($"Proxy returned {statusCode}. Check proxy settings and if it allows ssl through ports different of 443."); + } + } + + protected virtual void OnBeforeConnect() + { + this.BeforeConnect?.Invoke(); + } + + protected virtual void OnAfterConnect(string status) + { + this.AfterConnect?.Invoke(status); + } + } +} \ No newline at end of file diff --git a/PushSharp.Core/PushSharp.Core.csproj b/PushSharp.Core/PushSharp.Core.csproj index e46f9756..f78d4861 100644 --- a/PushSharp.Core/PushSharp.Core.csproj +++ b/PushSharp.Core/PushSharp.Core.csproj @@ -1,4 +1,4 @@ - + Debug @@ -38,6 +38,7 @@ + From fc3903516f57666ee16c717396e17977c5d44852 Mon Sep 17 00:00:00 2001 From: Rafael Araujo Date: Mon, 19 Dec 2016 11:26:48 -0200 Subject: [PATCH 13/16] Using ProxyHelper in ApnsConnection --- PushSharp.Apple/ApnsConfiguration.cs | 3 +- PushSharp.Apple/ApnsConnection.cs | 71 +++------------------------- 2 files changed, 8 insertions(+), 66 deletions(-) diff --git a/PushSharp.Apple/ApnsConfiguration.cs b/PushSharp.Apple/ApnsConfiguration.cs index c4693559..6a16a870 100644 --- a/PushSharp.Apple/ApnsConfiguration.cs +++ b/PushSharp.Apple/ApnsConfiguration.cs @@ -83,7 +83,7 @@ void Initialize (ApnsServerEnvironment serverEnvironment, X509Certificate2 certi FeedbackIntervalMinutes = 10; FeedbackTimeIsUTC = false; - + AdditionalCertificates = new List (); AddLocalAndMachineCertificateStores = false; @@ -101,7 +101,6 @@ void Initialize (ApnsServerEnvironment serverEnvironment, X509Certificate2 certi InternalBatchFailureRetryCount = 1; } - void CheckIsApnsCertificate () { if (Certificate != null) { diff --git a/PushSharp.Apple/ApnsConnection.cs b/PushSharp.Apple/ApnsConnection.cs index cd9ef25d..a209f5a8 100644 --- a/PushSharp.Apple/ApnsConnection.cs +++ b/PushSharp.Apple/ApnsConnection.cs @@ -61,7 +61,7 @@ public ApnsConnection (ApnsConfiguration configuration) }), null, Timeout.Infinite, Timeout.Infinite); } - + public ApnsConfiguration Configuration { get; private set; } X509CertificateCollection certificates; @@ -72,7 +72,6 @@ public ApnsConnection (ApnsConfiguration configuration) byte[] buffer = new byte[6]; int id; - SemaphoreSlim connectingSemaphore = new SemaphoreSlim (1); SemaphoreSlim batchSendSemaphore = new SemaphoreSlim (1); object notificationBatchQueueLock = new object (); @@ -107,7 +106,7 @@ public void Send (CompletableApnsNotification notification) } long batchId = 0; - + async Task SendBatch () { batchId++; @@ -334,57 +333,6 @@ bool socketCanWrite () return p; } - async Task connectHTTPProxy(TcpClient client) - { - Log.Info("APNS-Client[{0}]: Connecting Proxy (Batch ID={1})", id, batchId); - await client.ConnectAsync(Configuration.ProxyHost, Configuration.ProxyPort).ConfigureAwait(false); - var stream = client.GetStream(); - var authorization = string.Empty; - if (Configuration.ProxyCredentials != null) - { - var credential = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{Configuration.ProxyCredentials.UserName}:{Configuration.ProxyCredentials.Password}")); - authorization = $"\r\nAuthorization: Basic {credential}"; - } - var buffer = Encoding.UTF8.GetBytes(string.Format("CONNECT {0}:{1} HTTP/1.1\r\nHost: {0}:{1}{2}\r\nProxy-Connection: keep-alive\r\n\r\n", Configuration.Host, Configuration.Port, authorization)); - - await stream.WriteAsync(buffer, 0, buffer.Length); - await stream.FlushAsync(); - buffer = new byte[client.Client.ReceiveBufferSize]; - using (var resp = new MemoryStream()) - { - do - { - var len = await stream.ReadAsync(buffer, 0, buffer.Length); - resp.Write(buffer, 0, len); - } - while (client.Client.Available > 0); - buffer = resp.ToArray(); - } - var content = Encoding.UTF8.GetString(buffer); - var i = content.IndexOf('\r'); - string statusCode; - if (i > 9) - { - statusCode = content.Substring(9, i - 9); - } - else - { - var max = content.Length; - if (max > 50) - { - max = 50; - } - var append = max == 50 ? "..." : string.Empty; - statusCode = content.Substring(0, max) + append; - } - Log.Info("APNS-Client[{0}]: Proxy Connected (Batch ID={1}) : {2}", id, batchId, statusCode); - if (!statusCode.StartsWith("200")) - { - throw new ApnsConnectionException($"Proxy returned {statusCode}. Check proxy settings and if it allows ssl through ports different of 443."); - } - } - - async Task connect () { if (client != null) @@ -392,15 +340,7 @@ async Task connect () Log.Info ("APNS-Client[{0}]: Connecting (Batch ID={1})", id, batchId); - //if (Configuration.UseProxy) - //{ - // client = getClientViaHTTPProxy(Configuration.Host, Configuration.Port, Configuration.ProxyHost, Configuration.ProxyPort); - //} - //else - //{ - client = new TcpClient(); - //} - + client = new TcpClient(); try { if (!Configuration.UseProxy) @@ -409,7 +349,10 @@ async Task connect () } else { - await connectHTTPProxy(client).ConfigureAwait(false); + var proxyHelper = new ProxyHelper { ProxyConnectionExceptionCreator = (message) => new ApnsConnectionException(message) }; + proxyHelper.BeforeConnect += () => Log.Info("APNS-Client[{0}]: Connecting Proxy (Batch ID={1})", id, batchId); + proxyHelper.AfterConnect += (status) => Log.Info("APNS-Client[{0}]: Proxy Connected (Batch ID={1}) : {2}", id, batchId, status); + await proxyHelper.Connect(client, Configuration.Host, Configuration.Port, Configuration.ProxyHost, Configuration.ProxyPort, Configuration.ProxyCredentials).ConfigureAwait(false); } //Set keep alive on the socket may help maintain our APNS connection From a2ef7970c3180b6787b8f61c54563479072315cf Mon Sep 17 00:00:00 2001 From: Rafael Araujo Date: Mon, 19 Dec 2016 11:27:49 -0200 Subject: [PATCH 14/16] Supporting proxy in ApnsFeedbackService. --- PushSharp.Apple/ApnsFeedbackService.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/PushSharp.Apple/ApnsFeedbackService.cs b/PushSharp.Apple/ApnsFeedbackService.cs index 57fb887a..778e4311 100644 --- a/PushSharp.Apple/ApnsFeedbackService.cs +++ b/PushSharp.Apple/ApnsFeedbackService.cs @@ -8,6 +8,7 @@ using System.Net.Security; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; +using PushSharp.Core; namespace PushSharp.Apple { @@ -32,9 +33,22 @@ public void Check () var certificates = new X509CertificateCollection(); certificates.Add(certificate); - var client = new TcpClient (Configuration.FeedbackHost, Configuration.FeedbackPort); + var client = new TcpClient(); + Log.Info("APNS-FeedbackService: Connecting"); + if (Configuration.UseProxy) + { + var proxyHelper = new ProxyHelper { ProxyConnectionExceptionCreator = (message) => new ApnsConnectionException(message) }; + proxyHelper.BeforeConnect += () => Log.Info("APNS-FeedbackService: Connecting Proxy"); + proxyHelper.AfterConnect += (status) => Log.Info("APNS-FeedbackService: Proxy Connected : {0}", status); + proxyHelper.Connect(client, Configuration.FeedbackHost, Configuration.FeedbackPort, Configuration.ProxyHost, Configuration.ProxyPort, Configuration.ProxyCredentials).Wait(); + } + else + { + client.Connect(Configuration.FeedbackHost, Configuration.FeedbackPort); + } + Log.Info("APNS-FeedbackService: Connected"); - var stream = new SslStream (client.GetStream(), true, + var stream = new SslStream(client.GetStream(), true, (sender, cert, chain, sslErrs) => { return true; }, (sender, targetHost, localCerts, remoteCert, acceptableIssuers) => { return certificate; }); From 1914ea10aa69ea9bdaa40967b5fca343307cc61e Mon Sep 17 00:00:00 2001 From: Rafael Araujo Date: Mon, 19 Dec 2016 11:30:26 -0200 Subject: [PATCH 15/16] Changes in ApnsFeedbackService to return the tokens list intead of raising events --- PushSharp.Apple/ApnsFeedbackService.cs | 72 ++++++++++++++++++-------- 1 file changed, 49 insertions(+), 23 deletions(-) diff --git a/PushSharp.Apple/ApnsFeedbackService.cs b/PushSharp.Apple/ApnsFeedbackService.cs index 778e4311..fdf17087 100644 --- a/PushSharp.Apple/ApnsFeedbackService.cs +++ b/PushSharp.Apple/ApnsFeedbackService.cs @@ -25,6 +25,11 @@ public FeedbackService (ApnsConfiguration configuration) public event FeedbackReceivedDelegate FeedbackReceived; public void Check () + { + this.GetTokenExpirations(); + + } + public IEnumerable GetTokenExpirations() { var encoding = Encoding.ASCII; @@ -58,32 +63,35 @@ public void Check () //Set up byte[] buffer = new byte[4096]; int recd = 0; - var data = new List (); + var data = new List(); + + Log.Info("APNS-FeedbackService: Getting expirations"); //Get the first feedback recd = stream.Read(buffer, 0, buffer.Length); + var tokenBatch = new List(); //Continue while we have results and are not disposing while (recd > 0) { // Add the received data to a list buffer to work with (easier to manipulate) for (int i = 0; i < recd; i++) - data.Add (buffer [i]); - + data.Add(buffer[i]); + //Process each complete notification "packet" available in the buffer while (data.Count >= (4 + 2 + 32)) // Minimum size for a valid packet { - var secondsBuffer = data.GetRange (0, 4).ToArray (); - var tokenLengthBuffer = data.GetRange (4, 2).ToArray (); + var secondsBuffer = data.GetRange(0, 4).ToArray(); + var tokenLengthBuffer = data.GetRange(4, 2).ToArray(); // Get our seconds since epoch // Check endianness and reverse if needed if (BitConverter.IsLittleEndian) - Array.Reverse (secondsBuffer); - var seconds = BitConverter.ToInt32 (secondsBuffer, 0); + Array.Reverse(secondsBuffer); + var seconds = BitConverter.ToInt32(secondsBuffer, 0); //Add seconds since 1970 to that date, in UTC - var timestamp = new DateTime (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds (seconds); + var timestamp = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(seconds); //flag to allow feedback times in UTC or local, but default is local if (!Configuration.FeedbackTimeIsUTC) @@ -91,50 +99,68 @@ public void Check () if (BitConverter.IsLittleEndian) - Array.Reverse (tokenLengthBuffer); - var tokenLength = BitConverter.ToInt16 (tokenLengthBuffer, 0); + Array.Reverse(tokenLengthBuffer); + var tokenLength = BitConverter.ToInt16(tokenLengthBuffer, 0); - if (data.Count >= 4 + 2 + tokenLength) { + if (data.Count >= 4 + 2 + tokenLength) + { - var tokenBuffer = data.GetRange (6, tokenLength).ToArray (); + var tokenBuffer = data.GetRange(6, tokenLength).ToArray(); // Strings shouldn't care about endian-ness... this shouldn't be reversed //if (BitConverter.IsLittleEndian) // Array.Reverse (tokenBuffer); - var token = BitConverter.ToString (tokenBuffer).Replace ("-", "").ToLower ().Trim (); + var token = BitConverter.ToString(tokenBuffer).Replace("-", "").ToLower().Trim(); // Remove what we parsed from the buffer - data.RemoveRange (0, 4 + 2 + tokenLength); + data.RemoveRange(0, 4 + 2 + tokenLength); + tokenBatch.Add(new ApnsTokeExpirationInfo(token, timestamp)); // Raise the event to the consumer var evt = FeedbackReceived; if (evt != null) - evt (token, timestamp); - } else { + evt(token, timestamp); + + } + else + { continue; } - } //Read the next feedback - recd = stream.Read (buffer, 0, buffer.Length); + recd = stream.Read(buffer, 0, buffer.Length); } try { - stream.Close (); + stream.Close(); stream.Dispose(); } catch { } - try + try { - client.Client.Shutdown (SocketShutdown.Both); - client.Client.Dispose (); + client.Client.Shutdown(SocketShutdown.Both); + client.Client.Dispose(); } catch { } - try { client.Close (); } catch { } + try { client.Close(); } catch { } + Log.Info("APNS-FeedbackService: {0} expiration(s) received.", tokenBatch.Count); + return tokenBatch; } } + + public class ApnsTokeExpirationInfo + { + public ApnsTokeExpirationInfo(string token, DateTime timestamp) + { + this.Token = token; + this.Timestamp = timestamp; + } + + public string Token { get; private set; } + public DateTime Timestamp { get; private set; } + } } From 2886c3faba8c2176f2ec01bfdd16e1c908c77251 Mon Sep 17 00:00:00 2001 From: Caezary Date: Thu, 20 Sep 2018 13:46:31 +0200 Subject: [PATCH 16/16] add proxy usage section to readme --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index b3ae1f9b..0a046c62 100644 --- a/README.md +++ b/README.md @@ -252,6 +252,25 @@ foreach (var uri in MY_DEVICE_CHANNEL_URIS) { wnsBroker.Stop (); ``` +### Use a Proxy with APNS or GCM + +To configure a proxy call `SetProxy` method on the appropriate configuration object. + +```csharp +// create ApnsConfiguration or GcmConfiguration as needed +var config = new ApnsConfiguration (ApnsConfiguration.ApnsServerEnvironment.Sandbox, + "push-cert.p12", "push-cert-pwd"); + +// set up a proxy +if (useProxy) +{ + config.SetProxy(proxyAddress, proxyPort); +} + +// continue as in the examples above +// ... +``` + ## How to Migrate from PushSharp 2.x to 3.x and higher