/// <summary> /// Asynchonously force the session to be started, and invalidate session if idle; e.g. when <see cref="LastActionTime"/> is longer than <see cref="IdleTimeout"/> ago. /// </summary> protected async Task ForceSessionAsync(CancellationToken ct) { // Quit this method if already cancelled ct.ThrowIfCancellationRequested(); if (!IsStarted) { throw new InvalidSessionException("Session not started"); } if (LastActionTime.TimeoutOccurred(IdleTimeout)) { // Check timeout if (await InvalidateAsync(ct)) { return; } if (!AutoReconnect) { throw new InvalidSessionException("Session timed out"); } await LoginAsync(ct); } }
/// <summary> /// Asynchronously finalize the api session /// </summary> /// <returns>Success?</returns> public async Task <Boolean> LogoutAsync(CancellationToken ct) { await ForceSessionAsync(ct); var uri = new Uri(Host, URL_HOME + String.Format(SID_ARG_FORMAT, Id)); var request = (HttpWebRequest)WebRequest.Create(uri); var responseArgs = new Dictionary <String, String> { { @"logout", @"1" } }; // Register the callback to a method that can unblock. // Dispose of the CancellationTokenRegistration object // after the callback has completed. using (ct.Register(request.Abort)) { try { // Store response data in a seperate stream and parse content using (var content = await request.PostUrlencodedAsync(responseArgs)) { // Read stream data String text = await content.ReadStringAsync(ct); LastActionTime.Reset(); Id = SessionId.Invalid; return(text.Contains(@"/login.lua")); } } catch (WebException e) { if (e.Status == WebExceptionStatus.RequestCanceled) { ct.ThrowIfCancellationRequested(); } throw; } } }
/// <summary> /// Asynchronously loads a query-item partition /// </summary> /// <param name="partition">The partition to load</param> /// <param name="urlStringBase">The base url string</param> /// <param name="resultDict">The result dictionary</param> /// <param name="ct">The CancellationToken used to cancel this async operation</param> /// <param name="progress">The progress update handler</param> /// <param name="total">The total number of items, needed for progress calculation</param> /// <returns>A task performing this operation</returns> protected async Task LoadPartition(IDictionary <String, String> partition, String urlStringBase, ConcurrentDictionary <UInt64, String> resultDict, CancellationToken ct, IProgress <Tuple <int, int> > progress, int total) { var uri = new Uri(Host, urlStringBase + @"&" + HttpWebRequestExtension.FormUrlEncodeParameters(partition)); var request = (HttpWebRequest)WebRequest.Create(uri); request.Method = HTTP_METHOD_GET; // Quit this method if already cancelled ct.ThrowIfCancellationRequested(); // Register the callback to a method that can unblock. // Dispose of the CancellationTokenRegistration object // after the callback has completed. using (ct.Register(request.Abort)) { try { // Store response data in a seperate stream using (var content = await request.ReadResponseAsync()) { LastActionTime.Update(); // Read stream data var text = await content.ReadStringAsync(ct); // Parse JSON Items like // "a0": "74.05.50" foreach (Match match in QUERY_JSON_RESPONSE_ITEM_REGEX.Matches(text)) { UInt64 p; if (!UInt64.TryParse(match.Groups[@"id"].Value, out p)) { throw new Exception("Invalid argument number"); } if (resultDict.ContainsKey(p)) { throw new Exception("Duplicate element"); } if (resultDict.TryAdd(p, match.Groups[@"value"].Value) && progress != null) { progress.Report(Tuple.Create(resultDict.Count, total)); } } } } catch (WebException e) { if (e.Status == WebExceptionStatus.RequestCanceled) { ct.ThrowIfCancellationRequested(); } throw; } } }
/// <summary> /// Asynchronously invalidates the current session state /// </summary> /// <returns>Validity of the current session</returns> public async Task <Boolean> InvalidateAsync(CancellationToken ct) { if (!IsStarted) { return(false); } var xml = await ReadSessionDataAsync(Id, ct); var sid = new SessionId(xml.DocumentElement[FIELD_NAME_SID].InnerText); LastActionTime.Update(); return(sid.IsValid); }
/// <summary> /// Asynchonously execute commands /// </summary> /// <param name="commands">The commands to execute</param> /// <param name="ct">The cancellation token used to cancel this operation</param> /// <returns>The api response-text</returns> public async Task <String> SendCommandsAsync(IEnumerable <KeyValuePair <String, String> > commands, CancellationToken ct) { await ForceSessionAsync(ct); var uri = new Uri(Host, URL_WEBCM); var request = (HttpWebRequest)WebRequest.Create(uri); var responseArgs = new Dictionary <String, String> { { @"sid", Id } }; // Copy over commands foreach (var command in commands) { responseArgs.Add(command.Key, command.Value); } // Store response data in a seperate stream and read to end String responseText; // Quit this method if already cancelled ct.ThrowIfCancellationRequested(); // Register the callback to a method that can unblock. // Dispose of the CancellationTokenRegistration object // after the callback has completed. using (ct.Register(request.Abort)) { try { using (var content = await request.PostUrlencodedAsync(responseArgs)) { LastActionTime.Update(); // Read stream data responseText = await content.ReadStringAsync(ct); } } catch (WebException e) { if (e.Status == WebExceptionStatus.RequestCanceled) { ct.ThrowIfCancellationRequested(); } throw; } } return(responseText); }
/// <summary> /// Asynchronously start a new api session /// </summary> public async Task LoginAsync(CancellationToken ct) { ct.ThrowIfCancellationRequested(); // Request login challange XmlDocument xml = await ReadSessionDataAsync(Id, ct); // We may already be logged in var currentSid = new SessionId(xml.DocumentElement[FIELD_NAME_SID].InnerText); if (currentSid.IsValid) { Id = currentSid; LastActionTime.Update(); return; } // Login var challenge = xml.DocumentElement[FIELD_NAME_CHALLENGE].InnerText; var responseString = CalculateLoginResponse(challenge, Password); var uri = new Uri(Host, URL_LOGIN); var request = (HttpWebRequest)WebRequest.Create(uri); var responseArgs = new Dictionary <String, String> { { ARG_NAME_RESPONSE, responseString } }; if (!String.IsNullOrEmpty(Username)) { responseArgs.Add(ARG_NAME_USERNAME, Username); } // Quit this method if already cancelled ct.ThrowIfCancellationRequested(); // Register the callback to a method that can unblock. // Dispose of the CancellationTokenRegistration object // after the callback has completed. using (ct.Register(request.Abort)) { try { // Store response data in a seperate stream and parse content using (var content = await request.PostUrlencodedAsync(responseArgs)) { xml = await content.ReadXmlAsync(ct); } } catch (WebException e) { if (e.Status == WebExceptionStatus.RequestCanceled) { ct.ThrowIfCancellationRequested(); } throw; } } var sid = new SessionId(xml.DocumentElement[FIELD_NAME_SID].InnerText); if (!sid.IsValid) { throw new LoginFailedException(); } Id = sid; LastActionTime.Update(); }