private async Task LoadVersionLoop() { while (ShutdownToken.IsCancellationRequested == false) { ConsulResponse response = null; var config = GetConfig(); if (config.UseLongPolling) { response = await LoadServiceVersion().ConfigureAwait(false); } else { await _waitForConfigChange.Task.ConfigureAwait(false); continue; } var delay = TimeSpan.FromMilliseconds(0); if (response.Success) { _initializedVersion.TrySetResult(true); } else if (response.IsDeploymentDefined == false) { delay = config.ServiceMissingRetryInterval; } else { delay = config.ErrorRetryInterval; } await _dateTime.Delay(delay).ConfigureAwait(false); } }
private async Task UpdateAndMonitorAvailabilityZoneAsync() { void SetInfoStatus(AvailabilityZoneInfo.StatusCodes statusCode, string errorMessage, string consulZoneSettingsKey, Exception innerException) { var tags = GetUnencryptedTags(new Dictionary <string, string> { { "ConsulZoneSettingsKey", consulZoneSettingsKey } }); Info.StatusCode = statusCode; Info.Exception = new EnvironmentException(errorMessage, innerException, unencrypted: tags); } const string folder = "ZoneSettings"; ulong modifyIndex = 0; SetInfoStatus(AvailabilityZoneInfo.StatusCodes.InitializingConnectionToConsul, "Initialize connection to consul", $"{folder}/{Info.ServiceName}", null); while (_disposeCancellationToken.IsCancellationRequested == false) { try { ConsulResponse <DbKeyValue> response = null; bool exceptionThrown = false; try { response = await _consulClient.GetKey <DbKeyValue>(modifyIndex, folder, Info.ServiceName, _disposeCancellationToken); } catch (Exception e) { SetInfoStatus(AvailabilityZoneInfo.StatusCodes.FailedOrInvalidKeyFromConsul, "Failed to get key or Invalid key", $"{folder}/{Info.ServiceName}", e); exceptionThrown = true; } if (exceptionThrown) { await Task.Delay(TimeSpan.FromSeconds(1), _disposeCancellationToken); continue; } if (response == null) { SetInfoStatus(AvailabilityZoneInfo.StatusCodes.FailedConnectToConsul, $"Failed to connect to consul. Service Unavailable{Environment.NewLine}Keeping activity on previous valid deployment identifier cluster", $"{folder}/{Info.ServiceName}", null); } else if (response.StatusCode == null || response.StatusCode >= HttpStatusCode.InternalServerError || response.StatusCode < HttpStatusCode.OK) { if (response.StatusCode == HttpStatusCode.Continue) { // TODO: handle continue } SetInfoStatus(AvailabilityZoneInfo.StatusCodes.ConsulInternalError, $"Consul internal error. Service returned error status: '{(response.StatusCode?.ToString() ?? "NULL")}', error: '{response.ResponseContent}'{Environment.NewLine}Keeping activity on previous valid cluster", $"{folder}/{Info.ServiceName}", null); } else if (response.Error != null) { SetInfoStatus(AvailabilityZoneInfo.StatusCodes.MissingOrInvalidKeyValue, $"Missing or invalid key in consul. Error: {response.Error.Message}", $"{folder}/{Info.ServiceName}", response.Error); } else { modifyIndex = response.ModifyIndex ?? 0; await SetDeploymentIdentifierAsync(response.ResponseObject, _disposeCancellationToken); if (Info.StatusCode == AvailabilityZoneInfo.StatusCodes.Ok) { _initialReadZonesTask.TrySetResult(default);
private async Task <ConsulResponse <TResponseData> > MakeRequestAsync <TResponseData>(ConsulRequest request, string resourcePath, HttpMethod httpMethod, object requestData = null, IDictionary <string, string> headers = null, bool rawRequest = false, bool rawResponse = false, Func <HttpResponseMessage, bool> postResponseFunc = null) where TResponseData : class { try { if (request != null) { if (request.Index != null) { var kv = "index=" + request.Index.Value; var joiner = resourcePath.Contains("?") ? "&" : "?"; resourcePath = resourcePath + joiner + kv; } if (!string.IsNullOrWhiteSpace(request.Wait)) { var kv = "wait=" + request.Wait; var joiner = resourcePath.Contains("?") ? "&" : "?"; resourcePath = resourcePath + joiner + kv; } if (request.ConsistencyMode != ConsistencyMode.@default) { var joiner = resourcePath.Contains("?") ? "&" : "?"; resourcePath = resourcePath + joiner + request.ConsistencyMode.ToString(); } if (!string.IsNullOrWhiteSpace(request.ContentHash)) { var kv = "hash=" + request.ContentHash; var joiner = resourcePath.Contains("?") ? "&" : "?"; resourcePath = resourcePath + joiner + kv; } if (!string.IsNullOrWhiteSpace(request.FilterExpression)) { var kv = "filter=" + request.FilterExpression; var joiner = resourcePath.Contains("?") ? "&" : "?"; resourcePath = resourcePath + joiner + kv; } if (request.Cached == true) { var joiner = resourcePath.Contains("?") ? "&" : "?"; resourcePath = resourcePath + joiner + "cached"; } if (request.PrettyJsonResponse == true) { var joiner = resourcePath.Contains("?") ? "&" : "?"; resourcePath = resourcePath + joiner + "pretty"; } } byte[] dummyByteArray = new byte[1]; var requestUri = new Uri(_httpClient.BaseAddress, resourcePath); HttpContent requestContent = null; if (requestData != null) { if (dummyByteArray.GetType() == requestData.GetType()) { requestContent = new ByteArrayContent(requestData as byte[]); } else { requestContent = new StringContent((rawRequest ? requestData.ToString() : JsonConvert.SerializeObject(requestData)), Encoding.UTF8); } } HttpRequestMessage httpRequestMessage = null; switch (httpMethod.ToString().ToUpperInvariant()) { case "GET": httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, requestUri); break; case "DELETE": httpRequestMessage = new HttpRequestMessage(HttpMethod.Delete, requestUri); break; case "POST": httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, requestUri) { Content = requestContent }; break; case "PUT": httpRequestMessage = new HttpRequestMessage(HttpMethod.Put, requestUri) { Content = requestContent }; break; case "HEAD": httpRequestMessage = new HttpRequestMessage(HttpMethod.Head, requestUri); break; default: throw new NotSupportedException("The Http Method is not supported: " + httpMethod); } if (headers != null) { foreach (var kv in headers) { httpRequestMessage.Headers.Remove(kv.Key); httpRequestMessage.Headers.Add(kv.Key, kv.Value); } } ConsulClientSettings.BeforeApiRequestAction?.Invoke(_httpClient, httpRequestMessage); var httpResponseMessage = await _httpClient.SendAsync(httpRequestMessage).ConfigureAwait(ConsulClientSettings.ContinueAsyncTasksOnCapturedContext); var response = new ConsulResponse <TResponseData>(); IEnumerable <string> values; if (httpResponseMessage.Headers.TryGetValues(ConsulIndexHeaderKey, out values) && values != null) { // for cross platform, use the iterator instead of linq stuff. foreach (var value in values) { response.Index = ulong.Parse(value); break; } } values = null; if (httpResponseMessage.Headers.TryGetValues(ConsulLastContactHeaderKey, out values) && values != null) { foreach (var value in values) { response.LastContactMilliseconds = ulong.Parse(value); break; } } values = null; if (httpResponseMessage.Headers.TryGetValues(ConsulKnownLeaderHeaderKey, out values) && values != null) { foreach (var value in values) { response.KnownLeader = bool.Parse(value); break; } } values = null; if (httpResponseMessage.Headers.TryGetValues(ConsulContentHashHeaderKey, out values) && values != null) { foreach (var value in values) { response.ContentHash = value; break; } } values = null; if (httpResponseMessage.Headers.TryGetValues(CacheHeaderKey, out values) && values != null) { foreach (var value in values) { response.CacheAction = value; break; } } values = null; if (httpResponseMessage.Headers.TryGetValues(AgeHeaderKey, out values) && values != null) { foreach (var value in values) { if (int.TryParse(value, out var intValue)) { response.CacheHitAgeSeconds = intValue; break; } } } ConsulClientSettings.AfterApiResponseAction?.Invoke(httpResponseMessage); if (httpResponseMessage.IsSuccessStatusCode || (postResponseFunc?.Invoke(httpResponseMessage) == true)) { if (rawResponse) { if (typeof(TResponseData) == dummyByteArray.GetType()) { dummyByteArray = await httpResponseMessage.Content.ReadAsByteArrayAsync().ConfigureAwait(ConsulClientSettings.ContinueAsyncTasksOnCapturedContext); response.Data = dummyByteArray as TResponseData; } else { var responseText = await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(ConsulClientSettings.ContinueAsyncTasksOnCapturedContext); if (!string.IsNullOrWhiteSpace(responseText)) { response.Data = responseText as TResponseData; } } } else { var responseText = await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(ConsulClientSettings.ContinueAsyncTasksOnCapturedContext); if (!string.IsNullOrWhiteSpace(responseText)) { response.Data = JsonConvert.DeserializeObject <TResponseData>(responseText); } } return(response); } throw new ConsulApiException(httpResponseMessage.StatusCode, await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(ConsulClientSettings.ContinueAsyncTasksOnCapturedContext)); } catch (WebException ex) { if (ex.Status == WebExceptionStatus.ProtocolError) { if (ex.Response is HttpWebResponse response) { string responseText; using (var stream = new StreamReader(response.GetResponseStream())) { responseText = await stream.ReadToEndAsync().ConfigureAwait(ConsulClientSettings.ContinueAsyncTasksOnCapturedContext); } throw new ConsulApiException(response.StatusCode, responseText); } throw; } throw; } }