This commit is contained in:
2025-07-25 21:30:24 +02:00
parent 182129d9fa
commit ec32464c5b
7 changed files with 549 additions and 622 deletions

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Net.Sockets;
namespace EonaCat.Logger.Servers.GrayLog;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.

View File

@@ -1,155 +1,159 @@
using EonaCat.Json;
namespace EonaCat.Logger.Servers.Splunk;
using EonaCat.Logger.Servers.Splunk.Models;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
/// <summary>
/// Splunk Server.
/// </summary>
using System;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using EonaCat.Json;
using EonaCat.Logger.Servers.Splunk.Models;
public class Splunk : IDisposable
namespace EonaCat.Logger.Servers.Splunk
{
internal readonly object SendLock = new();
private string _splunkHecUrl = "https://127.0.0.1:8088/services/collector/event";
public string SplunkHecToken { get; set; } = "splunk-hec-token";
public HttpClientHandler SplunkClientHandler { get; private set; }
public HttpClient HttpClient { get; private set; }
public string Nickname { get; set; }
public bool HasHecToken => !string.IsNullOrWhiteSpace(SplunkHecToken) && SplunkHecToken != "splunk-hec-token";
public bool HasHecUrl => !string.IsNullOrWhiteSpace(SplunkHecUrl);
public bool IsHttpsHecUrl => HasHecUrl && _splunkHecUrl.ToLower().StartsWith("https");
public bool IsLocalHost => HasHecUrl && (_splunkHecUrl.ToLower().Contains("127.0.0.1") || _splunkHecUrl.ToLower().Contains("localhost"));
/// <summary>
/// Splunk Server
/// Splunk Server.
/// </summary>
/// <param name="splunkHecUrl"></param>
/// <param name="splunkHecToken"></param>
/// <param name="httpClientHandler"></param>
/// <param name="nickName"></param>
/// <param name="typesToLog"></param>
/// <exception cref="ArgumentNullException"></exception>
public Splunk(string splunkHecUrl, string splunkHecToken, HttpClientHandler httpClientHandler = null, string nickName = null, List<ELogType> typesToLog = null)
public class Splunk : IDisposable
{
SplunkHecUrl = splunkHecUrl ?? throw new ArgumentNullException(nameof(splunkHecUrl));
SplunkHecToken = splunkHecToken ?? throw new ArgumentNullException(nameof(splunkHecToken));
private string _splunkHecUrl = "https://127.0.0.1:8088/services/collector/event";
private HttpClient _httpClient;
private HttpClientHandler _httpClientHandler;
public event EventHandler<Exception> OnException;
Nickname = nickName ?? $"{splunkHecUrl}_{splunkHecToken}";
SplunkClientHandler = httpClientHandler ?? new HttpClientHandler();
TypesToLog = typesToLog;
public string SplunkHecToken { get; set; } = "splunk-hec-token";
public string Nickname { get; set; }
public List<ELogType> TypesToLog { get; set; }
CreateHttpClient();
}
public string SplunkHecUrl
{
get => _splunkHecUrl;
set
public string SplunkHecUrl
{
if (!string.IsNullOrWhiteSpace(value) && !value.ToLower().Contains("http"))
get => _splunkHecUrl;
set
{
value = $"http://{value}";
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentNullException(nameof(SplunkHecUrl));
_splunkHecUrl = value.StartsWith("http", StringComparison.OrdinalIgnoreCase)
? value
: $"http://{value}";
RecreateHttpClient();
}
_splunkHecUrl = value;
CreateHttpClient();
}
}
public List<ELogType> TypesToLog { get; set; }
private void CreateHttpClient()
{
DisposeHttpClient();
SplunkClientHandler ??= new HttpClientHandler();
HttpClient = new HttpClient(SplunkClientHandler)
{
BaseAddress = new Uri(SplunkHecUrl)
};
HttpClient.DefaultRequestHeaders.Add("Authorization", $"Splunk {SplunkHecToken}");
}
public void DisableSSLValidation()
{
DisposeHttpClient();
SplunkClientHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
};
CreateHttpClient();
}
public async Task<HttpResponseMessage> SendAsync(SplunkPayload splunkPayload, bool disableSplunkSSL = false)
{
if (splunkPayload == null)
{
return null;
}
var eventObject = new
{
@event = splunkPayload.EventData,
sourcetype = splunkPayload.SourceType,
host = splunkPayload.Host
};
public bool HasHecToken => !string.IsNullOrWhiteSpace(SplunkHecToken) && SplunkHecToken != "splunk-hec-token";
public bool HasHecUrl => !string.IsNullOrWhiteSpace(_splunkHecUrl);
public bool IsHttpsHecUrl => _splunkHecUrl.StartsWith("https", StringComparison.OrdinalIgnoreCase);
public bool IsLocalHost => _splunkHecUrl.ToLower().Contains("127.0.0.1") || _splunkHecUrl.ToLower().Contains("localhost");
if (disableSplunkSSL)
{
DisableSSLValidation();
}
if (HttpClient == null)
public Splunk(string splunkHecUrl, string splunkHecToken, HttpClientHandler handler = null, string nickName = null, List<ELogType> typesToLog = null)
{
SplunkHecToken = splunkHecToken ?? throw new ArgumentNullException(nameof(splunkHecToken));
SplunkHecUrl = splunkHecUrl ?? throw new ArgumentNullException(nameof(splunkHecUrl));
Nickname = nickName ?? $"{splunkHecUrl}_{splunkHecToken}";
TypesToLog = typesToLog;
_httpClientHandler = handler ?? new HttpClientHandler();
CreateHttpClient();
}
var eventJson = JsonHelper.ToJson(eventObject);
if (!HasHecToken)
private void CreateHttpClient()
{
DisposeHttpClient();
_httpClientHandler ??= new HttpClientHandler();
_httpClient = new HttpClient(_httpClientHandler)
{
BaseAddress = new Uri(SplunkHecUrl)
};
if (!_httpClient.DefaultRequestHeaders.Contains("Authorization"))
{
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Splunk {SplunkHecToken}");
}
}
private void RecreateHttpClient()
{
DisposeHttpClient();
_httpClientHandler = new HttpClientHandler();
CreateHttpClient();
}
using var content = new StringContent(eventJson, Encoding.UTF8, "application/json");
return await HttpClient.PostAsync("/services/collector/event", content);
}
internal void DisposeHttpClient()
{
if (HttpClient != null)
public void DisableSSLValidation()
{
HttpClient.Dispose();
HttpClient = null;
DisposeHttpClient();
_httpClientHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (_, _, _, _) => true
};
CreateHttpClient();
}
if (SplunkClientHandler != null)
public async Task<HttpResponseMessage> SendAsync(SplunkPayload payload, bool disableSplunkSSL = false)
{
SplunkClientHandler.Dispose();
SplunkClientHandler = null;
try
{
if (payload == null)
return null;
if (!HasHecToken || !HasHecUrl)
return null;
if (disableSplunkSSL)
DisableSSLValidation();
_httpClient ??= new HttpClient(_httpClientHandler ?? new HttpClientHandler())
{
BaseAddress = new Uri(SplunkHecUrl)
};
var eventObject = new
{
@event = payload.EventData,
sourcetype = payload.SourceType,
host = payload.Host
};
var json = JsonHelper.ToJson(eventObject);
var request = new HttpRequestMessage(HttpMethod.Post, "services/collector/event")
{
Content = new StringContent(json, Encoding.UTF8, "application/json")
};
return await _httpClient.SendAsync(request);
}
catch (Exception ex)
{
OnException?.Invoke(this, ex);
return null;
}
}
internal void DisposeHttpClient()
{
try
{
_httpClient?.Dispose();
_httpClientHandler?.Dispose();
}
catch
{
// Silent fail
}
finally
{
_httpClient = null;
_httpClientHandler = null;
}
}
public void Dispose()
{
DisposeHttpClient();
GC.SuppressFinalize(this);
}
~Splunk()
{
Dispose();
}
}
public void Dispose()
{
DisposeHttpClient();
GC.SuppressFinalize(this);
}
~Splunk()
{
Dispose();
}
}
}

View File

@@ -1,194 +1,191 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace EonaCat.Logger.Servers.Syslog;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
/// <summary>
/// Syslog server.
/// </summary>
public class Syslog : IDisposable
{
const int MaxUdpPacketSize = 4096;
private string _Hostname = "127.0.0.1";
private int _Port = 514;
public bool IsConnected { get; private set; }
internal UdpClient Udp;
public Syslog() { }
/// <summary>
/// Syslog server
/// </summary>
/// <param name="hostname"></param>
/// <param name="port"></param>
/// <param name="nickName"></param>
/// <param name="typesToLog"></param>
/// <param name="convertToRfc5424"></param>
/// <param name="convertToRfc3164"></param>
public Syslog(string hostname = "127.0.0.1", int port = 514, string nickName = null, List<ELogType> typesToLog = null, bool convertToRfc5424 = false, bool convertToRfc3164 = false)
{
Hostname = hostname;
Port = port;
Nickname = nickName ?? IpPort;
TypesToLog = typesToLog;
ConvertToRfc5424 = convertToRfc5424;
ConvertToRfc3164 = convertToRfc3164;
}
public string Hostname
{
get => _Hostname;
set
{
if (string.IsNullOrEmpty(value))
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace EonaCat.Logger.Servers.Syslog
{
// This file is part of the EonaCat project(s) released under the Apache License.
public class Syslog : IDisposable
{
private const int MaxUdpPacketSize = 4096;
private string _hostname = "127.0.0.1";
private int _port = 514;
private UdpClient _udp;
public event EventHandler<Exception> OnException;
public bool IsConnected { get; private set; }
public string Nickname { get; set; }
public string IpPort => $"{_hostname}:{_port}";
public bool SupportsTcp { get; set; }
public List<ELogType> TypesToLog { get; set; }
public bool ConvertToRfc5424 { get; set; }
public bool ConvertToRfc3164 { get; set; }
public Syslog() { }
public Syslog(string hostname = "127.0.0.1", int port = 514, string nickName = null, List<ELogType> typesToLog = null, bool convertToRfc5424 = false, bool convertToRfc3164 = false)
{
Hostname = hostname;
Port = port;
Nickname = nickName ?? IpPort;
TypesToLog = typesToLog;
ConvertToRfc5424 = convertToRfc5424;
ConvertToRfc3164 = convertToRfc3164;
}
public string Hostname
{
get => _hostname;
set
{
throw new ArgumentNullException(nameof(Hostname));
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentNullException(nameof(Hostname));
_hostname = value;
SetUdp();
}
}
public int Port
{
get => _port;
set
{
if (value < 0)
throw new ArgumentException("Port must be zero or greater.");
_port = value;
SetUdp();
}
}
internal void SetUdp()
{
try
{
DisposeUdp();
_udp = new UdpClient(_hostname, _port);
IsConnected = true;
}
catch
{
IsConnected = false;
}
}
internal void DisposeUdp()
{
IsConnected = false;
try
{
_udp?.Close();
_udp?.Dispose();
}
catch
{
// Swallow cleanup exceptions
}
_Hostname = value;
SetUdp();
}
}
public int Port
{
get => _Port;
set
{
if (value < 0)
_udp = null;
}
public async Task WriteAsync(string message)
{
if (string.IsNullOrWhiteSpace(message))
return;
var data = Encoding.UTF8.GetBytes(message);
await SendAsync(data);
}
public async Task WriteAsync(byte[] data)
{
if (data is { Length: > 0 })
await SendAsync(data);
}
private async Task SendAsync(byte[] data)
{
if (_udp == null)
return;
if (data.Length <= MaxUdpPacketSize)
{
throw new ArgumentException("Port must be zero or greater.");
try
{
await _udp.SendAsync(data, data.Length);
}
catch (Exception ex)
{
OnException?.Invoke(this, ex);
IsConnected = false;
}
}
else if (SupportsTcp)
{
await SendViaTcpAsync(data);
}
else
{
await SendUdpInChunksAsync(data, MaxUdpPacketSize);
}
_Port = value;
SetUdp();
}
}
public string IpPort => _Hostname + ":" + _Port;
public bool SupportsTcp { get; set; }
public string Nickname { get; set; }
public List<ELogType> TypesToLog { get; set; }
public bool ConvertToRfc5424 { get; set; }
public bool ConvertToRfc3164 { get; set; }
internal void SetUdp()
{
try
{
DisposeUdp();
Udp = new UdpClient(_Hostname, _Port);
IsConnected = true;
}
catch
{
IsConnected = false;
}
}
internal void DisposeUdp()
{
if (Udp != null)
{
IsConnected = false;
try
{
Udp?.Close();
Udp?.Dispose();
}
catch
{
// Do nothing
}
Udp = null;
}
}
public void Dispose()
{
DisposeUdp();
GC.SuppressFinalize(this);
}
public async Task WriteAsync(string message)
{
var data = Encoding.UTF8.GetBytes(message);
await SendAsync(data);
}
public async Task WriteAsync(byte[] data)
{
await SendAsync(data);
}
private async Task SendAsync(byte[] data)
{
if (data == null)
{
return;
}
if (Udp == null)
private async Task SendViaTcpAsync(byte[] data)
{
return;
try
{
using var tcpClient = new TcpClient();
await tcpClient.ConnectAsync(_hostname, _port);
var stream = tcpClient.GetStream();
await stream.WriteAsync(data, 0, data.Length);
await stream.FlushAsync();
}
catch
{
// TCP failure fallback is silent here
}
}
if (data.Length <= MaxUdpPacketSize)
private async Task SendUdpInChunksAsync(byte[] data, int chunkSize)
{
// Send via UDP (single packet)
await Udp?.SendAsync(data, data.Length);
int offset = 0;
byte[] buffer = ArrayPool<byte>.Shared.Rent(chunkSize);
try
{
while (offset < data.Length)
{
int size = Math.Min(chunkSize, data.Length - offset);
Buffer.BlockCopy(data, offset, buffer, 0, size);
await _udp.SendAsync(buffer, size);
offset += size;
}
}
catch
{
IsConnected = false;
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
else if (SupportsTcp)
public void Dispose()
{
// Send via TCP if supported
await SendViaTcpAsync(this, data);
DisposeUdp();
GC.SuppressFinalize(this);
}
else
~Syslog()
{
// Chunk large messages for UDP
await SendUdpInChunksAsync(this, data, MaxUdpPacketSize);
Dispose();
}
}
/// <summary>
/// Sends a message via TCP to a syslog server.
/// </summary>
private static async Task SendViaTcpAsync(Servers.Syslog.Syslog server, byte[] data)
{
if (server == null)
{
return;
}
using var tcpClient = new System.Net.Sockets.TcpClient();
await tcpClient.ConnectAsync(server.Hostname, server.Port);
using var stream = tcpClient.GetStream();
await stream.WriteAsync(data, 0, data.Length);
await stream.FlushAsync();
}
/// <summary>
/// Sends large messages in chunks over UDP.
/// </summary>
private static async Task SendUdpInChunksAsync(Servers.Syslog.Syslog server, byte[] data, int chunkSize)
{
for (int i = 0; i < data.Length; i += chunkSize)
{
var chunk = data.Skip(i).Take(chunkSize).ToArray();
await server.Udp.SendAsync(chunk, chunk.Length);
}
}
~Syslog()
{
Dispose();
}
}
}

View File

@@ -2,35 +2,30 @@
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace EonaCat.Logger.Servers.Tcp
{
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class Tcp : IDisposable
{
private string _Hostname = "127.0.0.1";
private int _Port = 514;
internal TcpClient _tcp;
private string _hostname = "127.0.0.1";
private int _port = 514;
private TcpClient _tcp;
private readonly SemaphoreSlim _sendLock = new(1, 1);
public event EventHandler<Exception> OnException;
public bool IsConnected { get; private set; }
public string Nickname { get; set; }
public string IpPort => _Hostname + ":" + _Port;
public string IpPort => $"{_hostname}:{_port}";
public List<ELogType> TypesToLog { get; set; }
/// <summary>
/// Tcp Server
/// </summary>
/// <param name="hostname"></param>
/// <param name="port"></param>
/// <param name="nickname"></param>
/// <param name="typesToLog"></param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public Tcp(string hostname, int port, string nickname = null, List<ELogType> typesToLog = null)
{
_Hostname = hostname ?? throw new ArgumentNullException(nameof(hostname));
_Port = port >= 0 ? port : throw new ArgumentException("Port must be zero or greater.");
_hostname = hostname ?? throw new ArgumentNullException(nameof(hostname));
_port = port >= 0 ? port : throw new ArgumentException("Port must be zero or greater.");
Nickname = nickname ?? IpPort;
TypesToLog = typesToLog;
@@ -39,42 +34,36 @@ namespace EonaCat.Logger.Servers.Tcp
public string Hostname
{
get => _Hostname;
get => _hostname;
set
{
if (string.IsNullOrEmpty(value))
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentNullException(nameof(Hostname));
}
_Hostname = value;
_hostname = value;
SetTcp();
}
}
public int Port
{
get => _Port;
get => _port;
set
{
if (value < 0)
{
throw new ArgumentException("Port must be zero or greater.");
}
_Port = value;
_port = value;
SetTcp();
}
}
public List<ELogType> TypesToLog { get; set; }
internal void SetTcp()
{
try
{
DisposeTcp();
_tcp = new TcpClient(_Hostname, _Port);
_tcp = new TcpClient(_hostname, _port);
IsConnected = true;
}
catch
@@ -85,80 +74,67 @@ namespace EonaCat.Logger.Servers.Tcp
internal void DisposeTcp()
{
if (_tcp != null)
{
IsConnected = false;
IsConnected = false;
try
{
_tcp.Close();
_tcp.Dispose();
_tcp = null;
}
catch
{
// Do nothing
}
try
{
_tcp?.Close();
_tcp?.Dispose();
}
catch
{
// Silent fail
}
_tcp = null;
}
public async Task WriteAsync(byte[] data)
{
if (data == null || data.Length == 0) return;
await InternalWriteAsync(data);
}
public async Task WriteAsync(string data)
{
if (string.IsNullOrWhiteSpace(data)) return;
var bytes = Encoding.UTF8.GetBytes(data);
await InternalWriteAsync(bytes);
}
private async Task InternalWriteAsync(byte[] data)
{
await _sendLock.WaitAsync();
try
{
if (!IsConnected || _tcp == null)
SetTcp();
if (!IsConnected || _tcp == null)
return;
var stream = _tcp.GetStream();
await stream.WriteAsync(data, 0, data.Length);
await stream.FlushAsync();
}
catch (Exception ex)
{
OnException?.Invoke(this, ex);
IsConnected = false;
}
finally
{
_sendLock.Release();
}
}
public void Dispose()
{
DisposeTcp();
_sendLock.Dispose();
GC.SuppressFinalize(this);
}
public async Task WriteAsync(byte[] data)
{
if (_tcp == null)
{
return;
}
if (data == null)
{
return;
}
if (!IsConnected)
{
SetTcp();
}
if (IsConnected)
{
using var stream = _tcp.GetStream();
await stream.WriteAsync(data, 0, data.Length);
await stream.FlushAsync();
}
}
public async Task WriteAsync(string data)
{
if (_tcp == null)
{
return;
}
if (string.IsNullOrEmpty(data))
{
return;
}
if (!IsConnected)
{
SetTcp();
}
if (IsConnected)
{
var sendData = Encoding.UTF8.GetBytes(data);
using var stream = _tcp.GetStream();
await stream.WriteAsync(sendData, 0, sendData.Length);
await stream.FlushAsync();
}
}
~Tcp()
{
Dispose();

View File

@@ -1,36 +1,33 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace EonaCat.Logger.Servers.Udp
{
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class Udp : IDisposable
{
const int MaxUdpPacketSize = 4096;
private string _Hostname = "127.0.0.1";
private int _Port = 514;
internal UdpClient _udp;
private const int MaxUdpPacketSize = 4096;
private string _hostname = "127.0.0.1";
private int _port = 514;
private UdpClient _udp;
private readonly SemaphoreSlim _sendLock = new(1, 1);
public event EventHandler<Exception> OnException;
public bool IsConnected { get; private set; }
public string Nickname { get; set; }
public string IpPort => _Hostname + ":" + _Port;
public string IpPort => $"{_hostname}:{_port}";
public List<ELogType> TypesToLog { get; set; }
/// <summary>
/// Udp Server
/// </summary>
/// <param name="hostname"></param>
/// <param name="port"></param>
/// <param name="nickname"></param>
/// <param name="typesToLog"></param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public Udp(string hostname, int port, string nickname = null, List<ELogType> typesToLog = null)
{
_Hostname = hostname ?? throw new ArgumentNullException(nameof(hostname));
_Port = port >= 0 ? port : throw new ArgumentException("Port must be zero or greater.");
_hostname = hostname ?? throw new ArgumentNullException(nameof(hostname));
_port = port >= 0 ? port : throw new ArgumentException("Port must be zero or greater.");
TypesToLog = typesToLog;
Nickname = nickname ?? IpPort;
SetUdp();
@@ -38,42 +35,39 @@ namespace EonaCat.Logger.Servers.Udp
public string Hostname
{
get => _Hostname;
get => _hostname;
set
{
if (string.IsNullOrEmpty(value))
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentNullException(nameof(Hostname));
}
_Hostname = value;
_hostname = value;
SetUdp();
}
}
public int Port
{
get => _Port;
get => _port;
set
{
if (value < 0)
{
throw new ArgumentException("Port must be zero or greater.");
}
_Port = value;
_port = value;
SetUdp();
}
}
public List<ELogType> TypesToLog { get; set; }
internal void SetUdp()
{
try
{
DisposeUdp();
_udp = new UdpClient(_Hostname, _Port);
_udp = new UdpClient(_hostname, _port)
{
DontFragment = false
};
IsConnected = true;
}
catch
@@ -84,92 +78,88 @@ namespace EonaCat.Logger.Servers.Udp
internal void DisposeUdp()
{
if (_udp != null)
IsConnected = false;
try
{
_udp?.Close();
_udp?.Dispose();
}
catch
{
// Silently ignore
}
_udp = null;
}
public async Task WriteAsync(byte[] data, bool dontFragment = false)
{
if (data == null || data.Length == 0)
return;
await _sendLock.WaitAsync();
try
{
if (!IsConnected)
SetUdp();
if (!IsConnected || _udp == null)
return;
_udp.DontFragment = dontFragment;
await SendChunksAsync(data);
}
finally
{
_sendLock.Release();
}
}
public async Task WriteAsync(string data, bool dontFragment = false)
{
if (string.IsNullOrWhiteSpace(data))
return;
var bytes = Encoding.UTF8.GetBytes(data);
await WriteAsync(bytes, dontFragment);
}
private async Task SendChunksAsync(byte[] data)
{
int offset = 0;
int length = data.Length;
byte[] buffer = ArrayPool<byte>.Shared.Rent(MaxUdpPacketSize);
try
{
while (offset < length)
{
int chunkSize = Math.Min(MaxUdpPacketSize, length - offset);
Buffer.BlockCopy(data, offset, buffer, 0, chunkSize);
await _udp.SendAsync(buffer, chunkSize);
offset += chunkSize;
}
}
catch (Exception ex)
{
IsConnected = false;
try
{
_udp?.Close();
_udp.Dispose();
}
catch
{
// Do nothing
}
_udp = null;
OnException?.Invoke(this, ex);
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
public void Dispose()
{
DisposeUdp();
_sendLock.Dispose();
GC.SuppressFinalize(this);
}
public async Task WriteAsync(byte[] data, bool dontFragment = false)
{
if (_udp == null || data == null)
{
return;
}
if (!IsConnected)
{
SetUdp();
}
if (IsConnected)
{
_udp.DontFragment = dontFragment;
int maxChunkSize = MaxUdpPacketSize;
int offset = 0;
while (offset < data.Length)
{
int chunkSize = Math.Min(maxChunkSize, data.Length - offset);
byte[] chunk = new byte[chunkSize];
Array.Copy(data, offset, chunk, 0, chunkSize);
await _udp?.SendAsync(chunk, chunk.Length);
offset += chunkSize;
}
}
}
public async Task WriteAsync(string data, bool dontFragment = false)
{
if (_udp == null || string.IsNullOrEmpty(data))
{
return;
}
if (!IsConnected)
{
SetUdp();
}
if (IsConnected)
{
var sendData = Encoding.UTF8.GetBytes(data);
_udp.DontFragment = dontFragment;
int maxChunkSize = MaxUdpPacketSize;
int offset = 0;
while (offset < sendData.Length)
{
int chunkSize = Math.Min(maxChunkSize, sendData.Length - offset);
byte[] chunk = new byte[chunkSize];
Array.Copy(sendData, offset, chunk, 0, chunkSize);
await _udp?.SendAsync(chunk, chunk.Length);
offset += chunkSize;
}
}
}
~Udp()
{
Dispose();

View File

@@ -5,144 +5,107 @@ using System.IO;
using System.Net;
using System.Text;
namespace EonaCat.Logger.Servers.Zabbix.API;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class ZabbixApi
namespace EonaCat.Logger.Servers.Zabbix.API
{
/// <summary>
/// Zabbix Api
/// Zabbix API client for authentication and communication with Zabbix server.
/// </summary>
/// <param name="user"></param>
/// <param name="password"></param>
/// <param name="zabbixURL"></param>
/// <param name="basicAuth"></param>
public ZabbixApi(string user, string password, string zabbixURL, bool basicAuth)
public class ZabbixApi
{
_user = user;
_password = password;
_zabbixURL = zabbixURL;
private readonly string _user;
private readonly string _password;
private readonly string _zabbixUrl;
private readonly string _basicAuth;
private string _auth;
public event EventHandler<Exception> OnException;
if (basicAuth)
public bool IsLoggedIn { get; private set; }
public ZabbixApi(string user, string password, string zabbixUrl, bool useBasicAuth = false)
{
_basicAuth = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(_user + ":" + _password));
_user = user ?? throw new ArgumentNullException(nameof(user));
_password = password ?? throw new ArgumentNullException(nameof(password));
_zabbixUrl = zabbixUrl ?? throw new ArgumentNullException(nameof(zabbixUrl));
_basicAuth = useBasicAuth
? Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes($"{_user}:{_password}"))
: null;
}
_auth = null;
}
public ZabbixApi(string user, string password, string zabbixURL) : this(user, password, zabbixURL, false)
{
}
private readonly string _user;
private readonly string _password;
private readonly string _zabbixURL;
private string _auth;
private readonly string _basicAuth = null;
/// <summary>
/// Determines if the user is logged in
/// </summary>
public bool IsLoggedIn { get; set; }
/// <summary>
/// Logs in the user
/// </summary>
public void Login()
{
dynamic authentication = new ExpandoObject();
authentication.user = _user;
authentication.password = _password;
ZabbixApiResponse zabbixApiResponse = ResponseAsObject("user.login", authentication);
_auth = zabbixApiResponse.Result;
IsLoggedIn = !string.IsNullOrEmpty(_auth);
}
/// <summary>
/// Logs out the user
/// </summary>
/// <returns></returns>
public bool Logout()
{
ZabbixApiResponse zbxResponse = ResponseAsObject("user.logout", new string[] { });
var result = zbxResponse.Result;
return result;
}
/// <summary>
/// Generic method to send a request to the Zabbix API
/// </summary>
/// <param name="method"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public string ResponseAsJson(string method, object parameters)
{
ZabbixApiRequest zbxRequest = new ZabbixApiRequest("2.0", method, 1, _auth, parameters);
string jsonParameters = JsonHelper.ToJson(zbxRequest);
return SendRequest(jsonParameters);
}
/// <summary>
/// Generic method to send a request to the Zabbix API
/// </summary>
/// <param name="method"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public ZabbixApiResponse ResponseAsObject(string method, object parameters)
{
ZabbixApiRequest zbxRequest = new ZabbixApiRequest("2.0", method, 1, _auth, parameters);
string jsonParameters = JsonHelper.ToJson(zbxRequest);
return CreateResponse(SendRequest(jsonParameters));
}
/// <summary>
/// Creates a ZabbixApiResponse object from a JSON string
/// </summary>
/// <param name="json"></param>
/// <returns></returns>
private ZabbixApiResponse CreateResponse(string json)
{
ZabbixApiResponse zbxResponse = JsonHelper.ToObject<ZabbixApiResponse>(json);
return zbxResponse;
}
/// <summary>
/// Sends a request to the Zabbix API
/// </summary>
/// <param name="jsonParams"></param>
/// <returns></returns>
private string SendRequest(string jsonParams)
{
WebRequest request = WebRequest.Create(_zabbixURL);
if (_basicAuth != null)
/// <summary>
/// Login and retrieve authentication token from Zabbix API.
/// </summary>
public void Login()
{
request.Headers.Add("Authorization", "Basic " + _basicAuth);
dynamic authParams = new ExpandoObject();
authParams.user = _user;
authParams.password = _password;
var response = ResponseAsObject("user.login", authParams);
_auth = response?.Result;
IsLoggedIn = !string.IsNullOrEmpty(_auth);
}
request.ContentType = "application/json-rpc";
request.Method = "POST";
string jsonResult;
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
/// <summary>
/// Logout from Zabbix API.
/// </summary>
public bool Logout()
{
streamWriter.Write(jsonParams);
streamWriter.Flush();
streamWriter.Close();
var response = ResponseAsObject("user.logout", Array.Empty<string>());
return response?.Result ?? false;
}
WebResponse response = request.GetResponse();
using (var streamReader = new StreamReader(response.GetResponseStream()))
/// <summary>
/// Send a Zabbix API request and return JSON response as string.
/// </summary>
public string ResponseAsJson(string method, object parameters)
{
jsonResult = streamReader.ReadToEnd();
var request = CreateRequest(method, parameters);
var jsonParams = JsonHelper.ToJson(request);
return SendRequest(jsonParams);
}
return jsonResult;
/// <summary>
/// Send a Zabbix API request and return deserialized response.
/// </summary>
public ZabbixApiResponse ResponseAsObject(string method, object parameters)
{
var request = CreateRequest(method, parameters);
var jsonParams = JsonHelper.ToJson(request);
var responseJson = SendRequest(jsonParams);
return JsonHelper.ToObject<ZabbixApiResponse>(responseJson);
}
private ZabbixApiRequest CreateRequest(string method, object parameters) =>
new("2.0", method, 1, _auth, parameters);
private string SendRequest(string jsonParams)
{
try
{
var request = WebRequest.Create(_zabbixUrl);
request.Method = "POST";
request.ContentType = "application/json-rpc";
if (!string.IsNullOrEmpty(_basicAuth))
{
request.Headers.Add("Authorization", $"Basic {_basicAuth}");
}
using (var writer = new StreamWriter(request.GetRequestStream()))
{
writer.Write(jsonParams);
}
using var response = request.GetResponse();
using var reader = new StreamReader(response.GetResponseStream());
return reader.ReadToEnd();
}
catch (Exception ex)
{
OnException?.Invoke(this, ex);
return null;
}
}
}
}

View File

@@ -7,10 +7,6 @@ using EonaCat.Logger.Test.Web;
using EonaCat.Versioning.Helpers;
using EonaCat.Web.RateLimiter;
using EonaCat.Web.RateLimiter.Endpoints.Extensions;
using EonaCat.Web.Tracer.Extensions;
using EonaCat.Web.Tracer.Models;
using Microsoft.Extensions.Logging;
using System.Runtime.Versioning;
var builder = WebApplication.CreateBuilder(args);
int onLogCounter = 0;