private void OnSubscribeOrRenewSubscriptionResponseReceived(IAsyncResult ar)
        {
            ChangeEventSubscriptionState state = (ChangeEventSubscriptionState)ar.AsyncState;

            lock (_cpData.SyncObj)
                _pendingCalls.Remove(state);
            CpService service = state.Service;

            try
            {
                HttpWebResponse response = (HttpWebResponse)state.Request.EndGetResponse(ar);
                try
                {
                    if (response.StatusCode != HttpStatusCode.OK)
                    {
                        service.InvokeEventSubscriptionFailed(new UPnPError((uint)response.StatusCode, response.StatusDescription));
                        return;
                    }
                    string   dateStr    = response.Headers.Get("DATE");
                    string   sid        = response.Headers.Get("SID");
                    string   timeoutStr = response.Headers.Get("TIMEOUT");
                    DateTime date       = DateTime.ParseExact(dateStr, "R", CultureInfo.InvariantCulture).ToLocalTime();
                    int      timeout;
                    if (string.IsNullOrEmpty(timeoutStr) || (!timeoutStr.StartsWith("Second-") ||
                                                             !int.TryParse(timeoutStr.Substring("Second-".Length).Trim(), out timeout)))
                    {
                        service.InvokeEventSubscriptionFailed(new UPnPError((int)HttpStatusCode.BadRequest, "Invalid answer from UPnP device"));
                        return;
                    }
                    DateTime          expiration = date.AddSeconds(timeout);
                    EventSubscription subscription;
                    lock (_cpData.SyncObj)
                    {
                        if (_subscriptions.TryGetValue(sid, out subscription))
                        {
                            subscription.Expiration = expiration;
                        }
                        else
                        {
                            _subscriptions.Add(sid, new EventSubscription(sid, state.ServiceDescriptor, service, expiration));
                        }
                        CheckSubscriptionRenewalTimer(_subscriptions.Values);
                    }
                }
                finally
                {
                    response.Close();
                }
            }
            catch (WebException e)
            {
                HttpWebResponse response = (HttpWebResponse)e.Response;
                service.InvokeEventSubscriptionFailed(new UPnPError(response == null ? 503 : (uint)response.StatusCode, "Cannot complete event subscription"));
                if (response != null)
                {
                    response.Close();
                }
                return;
            }
        }
        private void OnSubscribeOrRenewSubscriptionResponseReceived(IAsyncResult ar)
        {
            ChangeEventSubscriptionState state = (ChangeEventSubscriptionState)ar.AsyncState;

            lock (_cpData.SyncObj)
                _pendingCalls.Remove(state);
            CpService service = state.Service;

            try
            {
                HttpWebResponse response = (HttpWebResponse)state.Request.EndGetResponse(ar);
                try
                {
                    if (response.StatusCode != HttpStatusCode.OK)
                    {
                        service.InvokeEventSubscriptionFailed(new UPnPError((uint)response.StatusCode, response.StatusDescription));
                        return;
                    }
                    string dateStr    = response.Headers.Get("DATE");
                    string sid        = response.Headers.Get("SID");
                    string timeoutStr = response.Headers.Get("TIMEOUT");
                    int    timeout;
                    if (string.IsNullOrEmpty(timeoutStr) || (!timeoutStr.StartsWith("Second-") ||
                                                             !int.TryParse(timeoutStr.Substring("Second-".Length).Trim(), out timeout)))
                    {
                        service.InvokeEventSubscriptionFailed(new UPnPError((int)HttpStatusCode.BadRequest, "Invalid answer from UPnP device"));
                        return;
                    }

                    // The date header is not always available, and it is not always accurate either.
                    DateTime date = DateTime.Now;
                    try
                    {
                        date = DateTime.ParseExact(dateStr, "R", CultureInfo.InvariantCulture).ToLocalTime();
                    }
                    catch { }

                    DateTime expiration = date.AddSeconds(timeout);
                    if (expiration < DateTime.Now)
                    {
                        // If the timeout is in the past already, assume it is invalid and estimate
                        // the timeout timestamp. This workaround is necessary for devices that do
                        // not have their date set correctly (so the DATE header is unusable).
                        expiration = DateTime.Now.AddSeconds(timeout);
                    }

                    EventSubscription subscription;
                    lock (_cpData.SyncObj)
                    {
                        if (_subscriptions.TryGetValue(sid, out subscription))
                        {
                            subscription.Expiration = expiration;
                        }
                        else
                        {
                            _subscriptions.Add(sid, new EventSubscription(sid, state.ServiceDescriptor, service, expiration));
                        }
                        CheckSubscriptionRenewalTimer(_subscriptions.Values);
                    }
                }
                finally
                {
                    response.Close();
                }
            }
            catch (WebException e)
            {
                HttpWebResponse response = (HttpWebResponse)e.Response;
                service.InvokeEventSubscriptionFailed(new UPnPError(response == null ? 503 : (uint)response.StatusCode, "Cannot complete event subscription"));
                if (response != null)
                {
                    response.Close();
                }
            }
        }