PageRenderTime 42ms CodeModel.GetById 10ms app.highlight 13ms RepoModel.GetById 16ms app.codeStats 0ms

/SignalR.Client/Transports/HttpBasedTransport.cs

https://github.com/kpmrafeeq/SignalR
C# | 270 lines | 210 code | 51 blank | 9 comment | 24 complexity | 843ce272b15452f8e60f9a3ccd8ae6a9 MD5 | raw file
  1using System;
  2using System.Collections.Generic;
  3using System.Diagnostics;
  4using System.Linq;
  5using System.Text;
  6using System.Threading;
  7using System.Threading.Tasks;
  8using Newtonsoft.Json;
  9using Newtonsoft.Json.Linq;
 10using SignalR.Client.Http;
 11
 12namespace SignalR.Client.Transports
 13{
 14    public abstract class HttpBasedTransport : IClientTransport
 15    {
 16        // The send query string
 17        private const string _sendQueryString = "?transport={0}&connectionId={1}{2}";
 18
 19        // The transport name
 20        protected readonly string _transport;
 21
 22        protected const string HttpRequestKey = "http.Request";
 23
 24        protected readonly IHttpClient _httpClient;
 25
 26        public HttpBasedTransport(IHttpClient httpClient, string transport)
 27        {
 28            _httpClient = httpClient;
 29            _transport = transport;
 30        }
 31
 32        public Task<NegotiationResponse> Negotiate(IConnection connection)
 33        {
 34            return GetNegotiationResponse(_httpClient, connection);
 35        }
 36
 37        internal static Task<NegotiationResponse> GetNegotiationResponse(IHttpClient httpClient, IConnection connection)
 38        {
 39#if SILVERLIGHT || WINDOWS_PHONE
 40            string negotiateUrl = connection.Url + "negotiate?" + GetNoCacheUrlParam();
 41#else
 42            string negotiateUrl = connection.Url + "negotiate";
 43#endif
 44
 45
 46            return httpClient.GetAsync(negotiateUrl, connection.PrepareRequest).Then(response =>
 47            {
 48                string raw = response.ReadAsString();
 49
 50                if (raw == null)
 51                {
 52                    throw new InvalidOperationException("Server negotiation failed.");
 53                }
 54
 55                return JsonConvert.DeserializeObject<NegotiationResponse>(raw);
 56            });
 57        }
 58
 59        public Task Start(IConnection connection, string data)
 60        {
 61            var tcs = new TaskCompletionSource<object>();
 62
 63            OnStart(connection, data, () => tcs.TrySetResult(null), exception => tcs.TrySetException(exception));
 64
 65            return tcs.Task;
 66        }
 67
 68        protected abstract void OnStart(IConnection connection, string data, Action initializeCallback, Action<Exception> errorCallback);
 69
 70        public Task<T> Send<T>(IConnection connection, string data)
 71        {
 72            string url = connection.Url + "send";
 73            string customQueryString = GetCustomQueryString(connection);
 74
 75            url += String.Format(_sendQueryString, _transport, connection.ConnectionId, customQueryString);
 76
 77            var postData = new Dictionary<string, string> {
 78                { "data", data }
 79            };
 80
 81            return _httpClient.PostAsync(url, connection.PrepareRequest, postData).Then(response =>
 82            {
 83                string raw = response.ReadAsString();
 84
 85                if (String.IsNullOrEmpty(raw))
 86                {
 87                    return default(T);
 88                }
 89
 90                return JsonConvert.DeserializeObject<T>(raw);
 91            });
 92        }
 93
 94        protected string GetReceiveQueryString(IConnection connection, string data)
 95        {
 96            // ?transport={0}&connectionId={1}&messageId={2}&groups={3}&connectionData={4}{5}
 97            var qsBuilder = new StringBuilder();
 98            qsBuilder.Append("?transport=" + _transport)
 99                     .Append("&connectionId=" + Uri.EscapeDataString(connection.ConnectionId));
100
101            if (connection.MessageId != null)
102            {
103                qsBuilder.Append("&messageId=" + connection.MessageId);
104            }
105
106            if (connection.Groups != null && connection.Groups.Any())
107            {
108                qsBuilder.Append("&groups=" + Uri.EscapeDataString(JsonConvert.SerializeObject(connection.Groups)));
109            }
110
111            if (data != null)
112            {
113                qsBuilder.Append("&connectionData=" + data);
114            }
115
116            string customQuery = GetCustomQueryString(connection);
117
118            if (!String.IsNullOrEmpty(customQuery))
119            {
120                qsBuilder.Append("&")
121                         .Append(customQuery);
122            }
123
124#if SILVERLIGHT || WINDOWS_PHONE
125            qsBuilder.Append("&").Append(GetNoCacheUrlParam());
126#endif
127            return qsBuilder.ToString();
128        }
129
130        private static string GetNoCacheUrlParam()
131        {
132            return "noCache=" + Guid.NewGuid().ToString();
133        }
134
135        protected virtual Action<IRequest> PrepareRequest(IConnection connection)
136        {
137            return request =>
138            {
139                // Setup the user agent along with any other defaults
140                connection.PrepareRequest(request);
141
142                connection.Items[HttpRequestKey] = request;
143            };
144        }
145
146        public void Stop(IConnection connection)
147        {
148            var httpRequest = connection.GetValue<IRequest>(HttpRequestKey);
149            if (httpRequest != null)
150            {
151                try
152                {
153                    OnBeforeAbort(connection);
154
155                    // Abort the server side connection
156                    AbortConnection(connection);
157
158                    // Now abort the client connection
159                    httpRequest.Abort();
160                }
161                catch (NotImplementedException)
162                {
163                    // If this isn't implemented then do nothing
164                }
165            }
166        }
167
168        private void AbortConnection(IConnection connection)
169        {
170            string url = connection.Url + "abort" + String.Format(_sendQueryString, _transport, connection.ConnectionId, null);
171
172            try
173            {
174                // Attempt to perform a clean disconnect, but only wait 2 seconds
175                _httpClient.PostAsync(url, connection.PrepareRequest).Wait(TimeSpan.FromSeconds(2));
176            }
177            catch (Exception ex)
178            {
179                // Swallow any exceptions, but log them
180                Debug.WriteLine("Clean disconnect failed. " + ex.Unwrap().Message);
181            }
182        }
183
184
185        protected virtual void OnBeforeAbort(IConnection connection)
186        {
187
188        }
189
190        protected static void ProcessResponse(IConnection connection, string response, out bool timedOut, out bool disconnected)
191        {
192            timedOut = false;
193            disconnected = false;
194
195            if (String.IsNullOrEmpty(response))
196            {
197                return;
198            }
199
200            try
201            {
202                var result = JValue.Parse(response);
203
204                if (!result.HasValues)
205                {
206                    return;
207                }
208
209                timedOut = result.Value<bool>("TimedOut");
210                disconnected = result.Value<bool>("Disconnect");
211
212                if (disconnected)
213                {
214                    return;
215                }
216
217                var messages = result["Messages"] as JArray;
218                if (messages != null)
219                {
220                    foreach (JToken message in messages)
221                    {
222                        try
223                        {
224                            connection.OnReceived(message);
225                        }
226                        catch (Exception ex)
227                        {
228#if NET35
229                            Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Failed to process message: {0}", ex));
230#else
231                            Debug.WriteLine("Failed to process message: {0}", ex);
232#endif
233
234                            connection.OnError(ex);
235                        }
236                    }
237
238                    connection.MessageId = result["MessageId"].Value<string>();
239
240                    var transportData = result["TransportData"] as JObject;
241
242                    if (transportData != null)
243                    {
244                        var groups = (JArray)transportData["Groups"];
245                        if (groups != null)
246                        {
247                            connection.Groups = groups.Select(token => token.Value<string>());
248                        }
249                    }
250                }
251            }
252            catch (Exception ex)
253            {
254#if NET35
255                Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Failed to response: {0}", ex));
256#else
257                Debug.WriteLine("Failed to response: {0}", ex);
258#endif
259                connection.OnError(ex);
260            }
261        }
262
263        private static string GetCustomQueryString(IConnection connection)
264        {
265            return String.IsNullOrEmpty(connection.QueryString)
266                            ? ""
267                            : "&" + connection.QueryString;
268        }
269    }
270}