Files
DnsServer/Apps/FailoverApp/WebHook.cs
2021-11-06 13:35:02 +05:30

384 lines
12 KiB
C#

/*
Technitium DNS Server
Copyright (C) 2021 Shreyas Zare (shreyas@technitium.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using TechnitiumLibrary.Net.Dns.ResourceRecords;
using TechnitiumLibrary.Net.Proxy;
namespace Failover
{
class WebHook : IDisposable
{
#region variables
readonly HealthService _service;
string _name;
bool _enabled;
Uri[] _urls;
SocketsHttpHandler _httpHandler;
HttpClient _httpClient;
#endregion
#region constructor
public WebHook(HealthService service, dynamic jsonWebHook)
{
_service = service;
Reload(jsonWebHook);
}
#endregion
#region IDisposable
bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
if (_httpClient != null)
{
_httpClient.Dispose();
_httpClient = null;
}
if (_httpHandler != null)
{
_httpHandler.Dispose();
_httpHandler = null;
}
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
#region private
private void ConditionalHttpReload()
{
bool handlerChanged = false;
NetProxy proxy = _service.DnsServer.Proxy;
if (_httpHandler is null)
{
SocketsHttpHandler httpHandler = new SocketsHttpHandler();
httpHandler.Proxy = proxy;
httpHandler.AllowAutoRedirect = true;
httpHandler.MaxAutomaticRedirections = 10;
_httpHandler = httpHandler;
handlerChanged = true;
}
else
{
if (_httpHandler.Proxy != proxy)
{
SocketsHttpHandler httpHandler = new SocketsHttpHandler();
httpHandler.Proxy = proxy;
httpHandler.AllowAutoRedirect = true;
httpHandler.MaxAutomaticRedirections = 10;
SocketsHttpHandler oldHttpHandler = _httpHandler;
_httpHandler = httpHandler;
handlerChanged = true;
oldHttpHandler.Dispose();
}
}
if (_httpClient is null)
{
HttpClient httpClient = new HttpClient(_httpHandler);
_httpClient = httpClient;
}
else
{
if (handlerChanged)
{
HttpClient httpClient = new HttpClient(_httpHandler);
HttpClient oldHttpClient = _httpClient;
_httpClient = httpClient;
oldHttpClient.Dispose();
}
}
}
private async Task CallAsync(HttpContent content)
{
ConditionalHttpReload();
async Task CallWebHook(Uri url)
{
try
{
HttpResponseMessage response = await _httpClient.PostAsync(url, content);
response.EnsureSuccessStatusCode();
}
catch (Exception ex)
{
_service.DnsServer.WriteLog(ex);
}
}
List<Task> tasks = new List<Task>();
foreach (Uri url in _urls)
tasks.Add(CallWebHook(url));
await Task.WhenAll(tasks);
}
#endregion
#region public
public void Reload(dynamic jsonWebHook)
{
if (jsonWebHook.name is null)
_name = "default";
else
_name = jsonWebHook.name.Value;
if (jsonWebHook.enabled is null)
_enabled = false;
else
_enabled = jsonWebHook.enabled.Value;
if (jsonWebHook.urls is null)
{
_urls = null;
}
else
{
_urls = new Uri[jsonWebHook.urls.Count];
for (int i = 0; i < _urls.Length; i++)
_urls[i] = new Uri(jsonWebHook.urls[i].Value);
}
ConditionalHttpReload();
}
public Task CallAsync(IPAddress address, string healthCheck, HealthCheckResponse healthCheckResponse)
{
if (!_enabled)
return Task.CompletedTask;
HttpContent content;
{
using (MemoryStream mS = new MemoryStream())
{
JsonTextWriter jsonWriter = new JsonTextWriter(new StreamWriter(mS));
jsonWriter.WriteStartObject();
jsonWriter.WritePropertyName("address");
jsonWriter.WriteValue(address.ToString());
jsonWriter.WritePropertyName("healthCheck");
jsonWriter.WriteValue(healthCheck);
jsonWriter.WritePropertyName("status");
jsonWriter.WriteValue(healthCheckResponse.Status.ToString());
if (healthCheckResponse.Status == HealthStatus.Failed)
{
jsonWriter.WritePropertyName("failureReason");
jsonWriter.WriteValue(healthCheckResponse.FailureReason);
}
jsonWriter.WritePropertyName("dateTime");
jsonWriter.WriteValue(healthCheckResponse.DateTime);
jsonWriter.WriteEndObject();
jsonWriter.Flush();
content = new ByteArrayContent(mS.ToArray());
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
}
}
return CallAsync(content);
}
public Task CallAsync(IPAddress address, string healthCheck, Exception ex)
{
if (!_enabled)
return Task.CompletedTask;
HttpContent content;
{
using (MemoryStream mS = new MemoryStream())
{
JsonTextWriter jsonWriter = new JsonTextWriter(new StreamWriter(mS));
jsonWriter.WriteStartObject();
jsonWriter.WritePropertyName("address");
jsonWriter.WriteValue(address.ToString());
jsonWriter.WritePropertyName("healthCheck");
jsonWriter.WriteValue(healthCheck);
jsonWriter.WritePropertyName("status");
jsonWriter.WriteValue("Error");
jsonWriter.WritePropertyName("failureReason");
jsonWriter.WriteValue(ex.ToString());
jsonWriter.WritePropertyName("dateTime");
jsonWriter.WriteValue(DateTime.UtcNow);
jsonWriter.WriteEndObject();
jsonWriter.Flush();
content = new ByteArrayContent(mS.ToArray());
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
}
}
return CallAsync(content);
}
public Task CallAsync(string domain, DnsResourceRecordType type, string healthCheck, HealthCheckResponse healthCheckResponse)
{
if (!_enabled)
return Task.CompletedTask;
HttpContent content;
{
using (MemoryStream mS = new MemoryStream())
{
JsonTextWriter jsonWriter = new JsonTextWriter(new StreamWriter(mS));
jsonWriter.WriteStartObject();
jsonWriter.WritePropertyName("domain");
jsonWriter.WriteValue(domain);
jsonWriter.WritePropertyName("recordType");
jsonWriter.WriteValue(type.ToString());
jsonWriter.WritePropertyName("healthCheck");
jsonWriter.WriteValue(healthCheck);
jsonWriter.WritePropertyName("status");
jsonWriter.WriteValue(healthCheckResponse.Status.ToString());
if (healthCheckResponse.Status == HealthStatus.Failed)
{
jsonWriter.WritePropertyName("failureReason");
jsonWriter.WriteValue(healthCheckResponse.FailureReason);
}
jsonWriter.WritePropertyName("dateTime");
jsonWriter.WriteValue(healthCheckResponse.DateTime);
jsonWriter.WriteEndObject();
jsonWriter.Flush();
content = new ByteArrayContent(mS.ToArray());
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
}
}
return CallAsync(content);
}
public Task CallAsync(string domain, DnsResourceRecordType type, string healthCheck, Exception ex)
{
if (!_enabled)
return Task.CompletedTask;
HttpContent content;
{
using (MemoryStream mS = new MemoryStream())
{
JsonTextWriter jsonWriter = new JsonTextWriter(new StreamWriter(mS));
jsonWriter.WriteStartObject();
jsonWriter.WritePropertyName("domain");
jsonWriter.WriteValue(domain);
jsonWriter.WritePropertyName("recordType");
jsonWriter.WriteValue(type.ToString());
jsonWriter.WritePropertyName("healthCheck");
jsonWriter.WriteValue(healthCheck);
jsonWriter.WritePropertyName("status");
jsonWriter.WriteValue("Error");
jsonWriter.WritePropertyName("failureReason");
jsonWriter.WriteValue(ex.ToString());
jsonWriter.WritePropertyName("dateTime");
jsonWriter.WriteValue(DateTime.UtcNow);
jsonWriter.WriteEndObject();
jsonWriter.Flush();
content = new ByteArrayContent(mS.ToArray());
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
}
}
return CallAsync(content);
}
#endregion
#region properties
public string Name
{ get { return _name; } }
public bool Enabled
{ get { return _enabled; } }
public IReadOnlyList<Uri> Urls
{ get { return _urls; } }
#endregion
}
}