FailoverApp: added feature to auto generate health check url based of the app record domain name. added support for https type for auto generating health check url with expected scheme.

This commit is contained in:
Shreyas Zare
2021-08-14 12:13:26 +05:30
parent 5b5d6264aa
commit 21444ef9ce
4 changed files with 94 additions and 59 deletions

View File

@@ -159,6 +159,9 @@ namespace Failover
if (jsonAppRecordData.healthCheckUrl != null)
healthCheckUrl = new Uri(jsonAppRecordData.healthCheckUrl.Value);
if (healthCheckUrl is null)
healthCheckUrl = new Uri("http://" + question.Name);
List<DnsResourceRecord> answers = new List<DnsResourceRecord>();
GetAnswers(jsonAppRecordData.primary, question, appRecordTtl, healthCheck, healthCheckUrl, answers);
@@ -199,6 +202,9 @@ namespace Failover
if (jsonAppRecordData.healthCheckUrl != null)
healthCheckUrl = new Uri(jsonAppRecordData.healthCheckUrl.Value);
if (healthCheckUrl is null)
healthCheckUrl = new Uri("http://" + question.Name);
List<DnsResourceRecord> answers = new List<DnsResourceRecord>();
GetStatusAnswers(jsonAppRecordData.primary, FailoverType.Primary, question, 30, healthCheck, healthCheckUrl, answers);
@@ -218,7 +224,7 @@ namespace Failover
#region properties
public string Description
{ get { return "Returns A or AAAA records from primary set of addresses with a continous health check as configured in the app config. When none of the primary addresses are healthy, the app returns healthy addresses from the secondary set of addresses. When none of the primary and secondary addresses are healthy, the app returns healthy addresses from the server down set of addresses. The server down feature is expected to be used for showing a service status page and not to serve the actual content.\n\nSet 'allowTxtStatus' to 'true' in your APP record data to allow checking health status by querying for TXT record."; } }
{ get { return "Returns A or AAAA records from primary set of addresses with a continous health check as configured in the app config. When none of the primary addresses are healthy, the app returns healthy addresses from the secondary set of addresses. When none of the primary and secondary addresses are healthy, the app returns healthy addresses from the server down set of addresses. The server down feature is expected to be used for showing a service status page and not to serve the actual content.\n\nWhen an URL is not provided in 'healthCheckUrl' parameter for 'http' or 'https' type health check, the domain name of the APP record will be used to auto generate an URL.\n\nSet 'allowTxtStatus' parameter to 'true' in your APP record data to allow checking health status by querying for TXT record."; } }
public string ApplicationRecordDataTemplate
{
@@ -236,8 +242,8 @@ namespace Failover
""serverDown"": [
""3.3.3.3""
],
""healthCheck"": ""http"",
""healthCheckUrl"": ""https://www.example.com"",
""healthCheck"": ""https"",
""healthCheckUrl"": null,
""allowTxtStatus"": false
}";
}

View File

@@ -135,6 +135,9 @@ namespace Failover
if (jsonAppRecordData.healthCheckUrl != null)
healthCheckUrl = new Uri(jsonAppRecordData.healthCheckUrl.Value);
if (healthCheckUrl is null)
healthCheckUrl = new Uri("http://" + question.Name);
IReadOnlyList<DnsResourceRecord> answers;
if (question.Type == DnsResourceRecordType.TXT)
@@ -192,7 +195,7 @@ namespace Failover
#region properties
public string Description
{ get { return "Returns CNAME record for primary domain name with a continous health check as configured in the app config. When the primary domain name is unhealthy, the app returns one of the secondary domain names in the given order of preference that is healthy. When none of the primary and secondary domain names are healthy, the app returns the server down domain name. The server down feature is expected to be used for showing a service status page and not to serve the actual content. Note that the app will return ANAME record for an APP record at zone apex.\n\nSet 'allowTxtStatus' to 'true' in your APP record data to allow checking health status by querying for TXT record."; } }
{ get { return "Returns CNAME record for primary domain name with a continous health check as configured in the app config. When the primary domain name is unhealthy, the app returns one of the secondary domain names in the given order of preference that is healthy. When none of the primary and secondary domain names are healthy, the app returns the server down domain name. The server down feature is expected to be used for showing a service status page and not to serve the actual content. Note that the app will return ANAME record for an APP record at zone apex.\n\nWhen an URL is not provided in 'healthCheckUrl' parameter for 'http' or 'https' type health check, the domain name of the APP record will be used to auto generate an URL.\n\nSet 'allowTxtStatus' parameter to 'true' in your APP record data to allow checking health status by querying for TXT record."; } }
public string ApplicationRecordDataTemplate
{

View File

@@ -35,7 +35,8 @@ namespace Failover
Unknown = 0,
Ping = 1,
Tcp = 2,
Http = 3
Http = 3,
Https = 4
}
class HealthCheck : IDisposable
@@ -111,25 +112,14 @@ namespace Failover
private void ConditionalHttpReload()
{
if (_type == HealthCheckType.Http)
switch (_type)
{
bool handlerChanged = false;
NetProxy proxy = _service.DnsServer.Proxy;
case HealthCheckType.Http:
case HealthCheckType.Https:
bool handlerChanged = false;
NetProxy proxy = _service.DnsServer.Proxy;
if (_httpHandler is null)
{
SocketsHttpHandler httpHandler = new SocketsHttpHandler();
httpHandler.ConnectTimeout = TimeSpan.FromMilliseconds(_timeout);
httpHandler.Proxy = proxy;
httpHandler.AllowAutoRedirect = true;
httpHandler.MaxAutomaticRedirections = 10;
_httpHandler = httpHandler;
handlerChanged = true;
}
else
{
if ((_httpHandler.ConnectTimeout.TotalMilliseconds != _timeout) || (_httpHandler.Proxy != proxy))
if (_httpHandler is null)
{
SocketsHttpHandler httpHandler = new SocketsHttpHandler();
httpHandler.ConnectTimeout = TimeSpan.FromMilliseconds(_timeout);
@@ -137,50 +127,64 @@ namespace Failover
httpHandler.AllowAutoRedirect = true;
httpHandler.MaxAutomaticRedirections = 10;
SocketsHttpHandler oldHttpHandler = _httpHandler;
_httpHandler = httpHandler;
handlerChanged = true;
oldHttpHandler.Dispose();
}
}
else
{
if ((_httpHandler.ConnectTimeout.TotalMilliseconds != _timeout) || (_httpHandler.Proxy != proxy))
{
SocketsHttpHandler httpHandler = new SocketsHttpHandler();
httpHandler.ConnectTimeout = TimeSpan.FromMilliseconds(_timeout);
httpHandler.Proxy = proxy;
httpHandler.AllowAutoRedirect = true;
httpHandler.MaxAutomaticRedirections = 10;
if (_httpClient is null)
{
HttpClient httpClient = new HttpClient(_httpHandler);
httpClient.Timeout = TimeSpan.FromMilliseconds(_timeout);
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(HTTP_HEALTH_CHECK_USER_AGENT);
SocketsHttpHandler oldHttpHandler = _httpHandler;
_httpHandler = httpHandler;
handlerChanged = true;
_httpClient = httpClient;
}
else
{
if (handlerChanged || (_httpClient.Timeout.TotalMilliseconds != _timeout))
oldHttpHandler.Dispose();
}
}
if (_httpClient is null)
{
HttpClient httpClient = new HttpClient(_httpHandler);
httpClient.Timeout = TimeSpan.FromMilliseconds(_timeout);
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(HTTP_HEALTH_CHECK_USER_AGENT);
HttpClient oldHttpClient = _httpClient;
_httpClient = httpClient;
oldHttpClient.Dispose();
}
}
}
else
{
if (_httpClient != null)
{
_httpClient.Dispose();
_httpClient = null;
}
else
{
if (handlerChanged || (_httpClient.Timeout.TotalMilliseconds != _timeout))
{
HttpClient httpClient = new HttpClient(_httpHandler);
httpClient.Timeout = TimeSpan.FromMilliseconds(_timeout);
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(HTTP_HEALTH_CHECK_USER_AGENT);
if (_httpHandler != null)
{
_httpHandler.Dispose();
_httpHandler = null;
}
HttpClient oldHttpClient = _httpClient;
_httpClient = httpClient;
oldHttpClient.Dispose();
}
}
break;
default:
if (_httpClient != null)
{
_httpClient.Dispose();
_httpClient = null;
}
if (_httpHandler != null)
{
_httpHandler.Dispose();
_httpHandler = null;
}
break;
}
}
@@ -386,6 +390,7 @@ namespace Failover
}
case HealthCheckType.Http:
case HealthCheckType.Https:
{
ConditionalHttpReload();
@@ -397,12 +402,23 @@ namespace Failover
try
{
Uri url = healthCheckUrl;
if (url is null)
{
url = _url;
if (url is null)
return new HealthCheckStatus(false, "Missing health check URL in APP record as well as in app config.");
}
if (url is null)
return new HealthCheckStatus(false, "Missing health check URL in APP record as well as in app config.");
if (_type == HealthCheckType.Http)
{
if (url.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase))
url = new Uri("http://" + url.Host + (url.IsDefaultPort ? "" : ":" + url.Port) + url.PathAndQuery);
}
else
{
if (url.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase))
url = new Uri("https://" + url.Host + (url.IsDefaultPort ? "" : ":" + url.Port) + url.PathAndQuery);
}
IPEndPoint ep = new IPEndPoint(address, url.Port);
Uri queryUri = new Uri(url.Scheme + "://" + ep.ToString() + url.PathAndQuery);

View File

@@ -30,13 +30,23 @@
"interval": 60,
"retries": 3,
"timeout": 10,
"url": null,
"emailAlert": "default",
"webHook": "default",
"comments": "The APP record data must contain 'healthCheckUrl' property with an url when no url is configured for a 'http' type health check."
"webHook": "default"
},
{
"name": "https",
"type": "https",
"interval": 60,
"retries": 3,
"timeout": 10,
"url": null,
"emailAlert": "default",
"webHook": "default"
},
{
"name": "www.example.com",
"type": "http",
"type": "https",
"interval": 60,
"retries": 3,
"timeout": 10,