Added new classes, fixes.

Timestamp - unixtime timestamps.
JsonFileReader - read config from json files.
YamlFileReader - read config from yaml files.
New tests.
Fix index scores in Sleopok.Engine.
master
ogoun 3 days ago
parent 61532ae1a0
commit e8ea0e4ef8

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using Xunit; using Xunit;
using ZeroLevel.Services.Config; using ZeroLevel.Services.Config;
@ -125,6 +126,103 @@ namespace ZeroLevel.UnitTests
Assert.Equal(i, set.Default.First<int>("i")); Assert.Equal(i, set.Default.First<int>("i"));
} }
/*
{
"url": "http://tes.io",
"port": 9091,
"limits": {
"min": {
"cpu": 20,
"gpu": 0,
"mem": 200
},
"max": {
"cpu": 40,
"gpu": 50,
"mem": 600
},
"optional": null,
"persistent": true
}
}
*/
[Fact]
public void JsonConfigTest()
{
// Arrange
var json = ZeroLevel.UnitTests.Properties.Resources.test_json_config;
var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
File.WriteAllBytes(tempFilePath, json);
// Act
IConfigurationSet set;
try
{
set = Configuration.ReadSetFromJsonFile(tempFilePath);
}
finally
{
File.Delete(tempFilePath);
}
// Assert
Assert.Equal("http://tes.io", set.Default.First("url"));
Assert.Equal(9091, set.Default.First<int>("port"));
Assert.Equal(20, set["limits.min"].First<int>("cpu"));
Assert.Equal(0, set["limits.min"].First<int>("gpu"));
Assert.Equal(200, set["limits.min"].First<int>("mem"));
Assert.Equal(40, set["limits.max"].First<int>("cpu"));
Assert.Equal(50, set["limits.max"].First<int>("gpu"));
Assert.Equal(600, set["limits.max"].First<int>("mem"));
Assert.Equal(string.Empty, set["limits"].First("optional"));
Assert.Equal(true, set["limits"].First<bool>("persistent"));
}
/*
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
spec:
rules:
- host: my-app.s<номер своего логина>.edu.slurm.io
http:
paths:
- backend:
serviceName: my-service
servicePort: 80
...
*/
[Fact]
public void YamlConfigTest()
{
// Arrange
var json = ZeroLevel.UnitTests.Properties.Resources.test_yaml_config;
var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
File.WriteAllBytes(tempFilePath, json);
// Act
IConfigurationSet set;
try
{
set = Configuration.ReadSetFromYamlFile(tempFilePath);
}
finally
{
File.Delete(tempFilePath);
}
// Assert
Assert.Equal("extensions/v1beta1", set.Default.First("apiVersion"));
Assert.Equal("Ingress", set.Default.First("kind"));
Assert.Equal("my-ingress", set["metadata"].First("name"));
Assert.Equal("my-app.s<номер своего логина>.edu.slurm.io", set["spec.rules.0"].First("host"));
Assert.Equal("my-service", set["spec.rules.0.http.paths.0.backend"].First("serviceName"));
Assert.Equal(80, set["spec.rules.0.http.paths.0.backend"].First<int>("servicePort"));
}
[Fact] [Fact]
public void MergeTest() public void MergeTest()

@ -0,0 +1,83 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace ZeroLevel.UnitTests.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ZeroLevel.UnitTests.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] test_json_config {
get {
object obj = ResourceManager.GetObject("test_json_config", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] test_yaml_config {
get {
object obj = ResourceManager.GetObject("test_yaml_config", resourceCulture);
return ((byte[])(obj));
}
}
}
}

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="test_json_config" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\test_json_config.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="test_yaml_config" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\test_yaml_config.yaml;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root>

@ -0,0 +1,18 @@
{
"url": "http://tes.io",
"port": 9091,
"limits": {
"min": {
"cpu": 20,
"gpu": 0,
"mem": 200
},
"max": {
"cpu": 40,
"gpu": 50,
"mem": 600
},
"optional": null,
"persistent": true
}
}

@ -0,0 +1,18 @@
{
"url": "http://tes.io",
"port": 9091,
"limits": {
"min": {
"cpu": 20,
"gpu": 0,
"mem": 200
},
"max": {
"cpu": 40,
"gpu": 50,
"mem": 600
},
"optional": null,
"persistent": true
}
}

@ -0,0 +1,15 @@
---
# file: practice/1.kube-basics-lecture/1.9.ingress/ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
spec:
rules:
- host: my-app.s<номер своего логина>.edu.slurm.io
http:
paths:
- backend:
serviceName: my-service
servicePort: 80
...

@ -21,4 +21,19 @@
<ProjectReference Include="..\..\ZeroLevel\ZeroLevel.csproj" /> <ProjectReference Include="..\..\ZeroLevel\ZeroLevel.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project> </Project>

@ -0,0 +1,18 @@
{
"url": "http://tes.io",
"port": 9091,
"limits": {
"min": {
"cpu": 20,
"gpu": 0,
"mem": 200
},
"max": {
"cpu": 40,
"gpu": 50,
"mem": 600
},
"optional": null,
"persistent": true
}
}

@ -0,0 +1,14 @@
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
spec:
rules:
- host: my-app.s<номер своего логина>.edu.slurm.io
http:
paths:
- backend:
serviceName: my-service
servicePort: 80
...

@ -44,22 +44,15 @@ namespace ZeroLevel.Sleopok.Engine.Models
switch (member.MemberType) switch (member.MemberType)
{ {
case MemberTypes.Field: case MemberTypes.Field:
getter = TypeGetterSetterBuilder.BuildGetter(member as FieldInfo); getter = TypeGetterSetterBuilder.BuildGetter((member as FieldInfo)!);
break; break;
case MemberTypes.Property: case MemberTypes.Property:
getter = TypeGetterSetterBuilder.BuildGetter(member as PropertyInfo); getter = TypeGetterSetterBuilder.BuildGetter((member as PropertyInfo)!);
break; break;
default: return; default: return;
} }
var name = FSUtils.FileNameCorrection(string.IsNullOrWhiteSpace(sleoAttribute.Name) ? member.Name : sleoAttribute.Name); var name = FSUtils.FileNameCorrection(string.IsNullOrWhiteSpace(sleoAttribute.Name) ? member.Name : sleoAttribute.Name);
_fields.Add(new SleoField _fields.Add(new SleoField(type, name, sleoAttribute.Boost, sleoAttribute.AvaliableForExactMatch, getter));
{
FieldType = type,
Boost = sleoAttribute.Boost,
Name = name,
Getter = getter,
ExactMatch = sleoAttribute.AvaliableForExactMatch
});
} }
}); });
} }

@ -15,6 +15,9 @@ namespace ZeroLevel.Sleopok.Engine.Models
} }
internal sealed class SleoField internal sealed class SleoField
{ {
internal SleoField(SleoFieldType fieldType, string name, float boost, bool exactMatch, Func<object, object> getter) =>
(FieldType, Name, Boost, ExactMatch, Getter) = (fieldType, name, boost, exactMatch, getter);
public SleoFieldType FieldType; public SleoFieldType FieldType;
public string Name; public string Name;
public float Boost; public float Boost;

@ -5,9 +5,9 @@ namespace ZeroLevel.Sleopok.Engine.Models
public sealed class SleoIndexAttribute public sealed class SleoIndexAttribute
: Attribute : Attribute
{ {
public string Name { get; private set; } public readonly string Name;
public float Boost { get; private set; } = 1.0f; public readonly float Boost;
public bool AvaliableForExactMatch { get; private set; } = false; public readonly bool AvaliableForExactMatch;
public SleoIndexAttribute(string name, float boost = 1.0f, bool avaliableForExactMatch = false) public SleoIndexAttribute(string name, float boost = 1.0f, bool avaliableForExactMatch = false)
{ {

@ -8,8 +8,9 @@ namespace ZeroLevel.Sleopok.Engine.Services.Indexes
{ {
public class FieldRecords public class FieldRecords
{ {
public FieldRecords(string field, IDictionary<string, List<string>> records) => (Field, Records) = (field, records);
public string Field { get; set; } public string Field { get; set; }
public Dictionary<string, List<string>> Records { get; set; } public IDictionary<string, List<string>> Records { get; set; }
} }
internal sealed class IndexReader<T> internal sealed class IndexReader<T>
@ -54,11 +55,7 @@ namespace ZeroLevel.Sleopok.Engine.Services.Indexes
foreach (var field in _indexInfo.Fields) foreach (var field in _indexInfo.Fields)
{ {
var docs = await _storage.GetAllDocuments(field.Name); var docs = await _storage.GetAllDocuments(field.Name);
yield return new FieldRecords yield return new FieldRecords(field.Name, docs);
{
Field = field.Name,
Records = docs
};
} }
} }
} }

@ -112,7 +112,7 @@ namespace ZeroLevel.Sleopok.Engine.Services.Storage
public IPartitionDataWriter GetWriter(string field) public IPartitionDataWriter GetWriter(string field)
{ {
return new DateSourceWriter(_store.CreateBuilder(new StoreMetadata { Field = field })); return new DateSourceWriter(_store.CreateBuilder(new StoreMetadata(field)));
} }
/// <summary> /// <summary>
@ -126,7 +126,7 @@ namespace ZeroLevel.Sleopok.Engine.Services.Storage
public async Task<Dictionary<string, float>> GetDocuments(string field, string[] tokens, float boost, bool exactMatch) public async Task<Dictionary<string, float>> GetDocuments(string field, string[] tokens, float boost, bool exactMatch)
{ {
var documents = new Dictionary<string, PositionDocScore>(); var documents = new Dictionary<string, PositionDocScore>();
var accessor = _store.CreateAccessor(new StoreMetadata { Field = field }); var accessor = _store.CreateAccessor(new StoreMetadata(field));
if (accessor != null) if (accessor != null)
{ {
using (accessor) using (accessor)
@ -156,7 +156,7 @@ namespace ZeroLevel.Sleopok.Engine.Services.Storage
public async Task<Dictionary<string, List<string>>> GetAllDocuments(string field) public async Task<Dictionary<string, List<string>>> GetAllDocuments(string field)
{ {
var documents = new Dictionary<string, List<string>>(); var documents = new Dictionary<string, List<string>>();
var accessor = _store.CreateAccessor(new StoreMetadata { Field = field }); var accessor = _store.CreateAccessor(new StoreMetadata(field));
if (accessor != null) if (accessor != null)
{ {
using (accessor) using (accessor)
@ -183,7 +183,7 @@ namespace ZeroLevel.Sleopok.Engine.Services.Storage
{ {
using (TextWriter writer = new StreamWriter(stream)) using (TextWriter writer = new StreamWriter(stream))
{ {
await foreach (var i in _store.Bypass(new StoreMetadata { Field = key })) await foreach (var i in _store.Bypass(new StoreMetadata(key)))
{ {
writer.WriteLine(i.Key); writer.WriteLine(i.Key);
writer.WriteLine(string.Join(' ', Compressor.DecompressToDocuments(i.Value))); writer.WriteLine(string.Join(' ', Compressor.DecompressToDocuments(i.Value)));
@ -193,7 +193,7 @@ namespace ZeroLevel.Sleopok.Engine.Services.Storage
public int HasData(string field) public int HasData(string field)
{ {
var partition = _store.CreateAccessor(new StoreMetadata { Field = field }); var partition = _store.CreateAccessor(new StoreMetadata(field));
if (partition != null) if (partition != null)
{ {
using (partition) using (partition)

@ -5,6 +5,8 @@
/// </summary> /// </summary>
public sealed class StoreMetadata public sealed class StoreMetadata
{ {
public StoreMetadata(string field) => Field = field;
/// <summary> /// <summary>
/// Поле документа /// Поле документа
/// </summary> /// </summary>

@ -1,14 +0,0 @@
namespace ZeroLevel.Sleopok.Engine.Services.Storage
{
public sealed class StoreRecord
{
/// <summary>
/// Токен / ключ
/// </summary>
public string Token { get; set; }
/// <summary>
/// Идентификаторы документов / значение
/// </summary>
public string[] Documents { get; set; }
}
}

@ -0,0 +1,53 @@
using System;
namespace ZeroLevel.Services.Collections
{
/// <summary>
/// Циклический буфер
/// </summary>
public class CyclicBuffer<T>
{
private T[] _buffer;
private int _head;
private int _tail;
public CyclicBuffer(int size)
{
if (size <= 0)
throw new ArgumentException($"{nameof(size)} must be positive.");
_buffer = new T[size];
_head = 0;
_tail = 0;
}
public bool IsFull { get { return (_tail + 1) % _buffer.Length == _head; } }
public bool IsEmpty { get { return _head == _tail; } }
public void Enqueue(T item)
{
_buffer[_tail] = item;
_tail = (_tail + 1) % _buffer.Length;
// Если буфер полон, сдвигаем head
if (IsFull)
_head = (_head + 1) % _buffer.Length;
}
public T this[int index]
{
get
{
if (index < 0 || index >= Count)
throw new ArgumentOutOfRangeException(nameof(index));
int effectiveIndex = (_head + index) % _buffer.Length;
return _buffer[effectiveIndex];
}
}
public int Count
{
get { return (_tail >= _head) ? _tail - _head : _buffer.Length + _tail - _head; }
}
}
}

@ -139,7 +139,7 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadFromEnvironmentVariables] Can't read environment variables"); Log.Error(ex, $"[{nameof(Configuration)}.ReadFromEnvironmentVariables] Can't read environment variables");
throw; throw;
} }
} }
@ -156,7 +156,7 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadFromApplicationConfig] Can't read app.config file"); Log.Error(ex, $"[{nameof(Configuration)}.ReadFromApplicationConfig] Can't read app.config file");
throw; throw;
} }
} }
@ -168,7 +168,7 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadOrEmptyFromApplicationConfig] Can't read app.config file"); Log.Error(ex, $"[{nameof(Configuration)}.ReadOrEmptyFromApplicationConfig] Can't read app.config file");
} }
return _empty; return _empty;
} }
@ -185,7 +185,7 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadSetFromApplicationConfig] Can't read app.config file"); Log.Error(ex, $"[{nameof(Configuration)}.ReadSetFromApplicationConfig] Can't read app.config file");
throw; throw;
} }
} }
@ -197,7 +197,7 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadOrEmptySetFromApplicationConfig] Can't read app.config file"); Log.Error(ex, $"[{nameof(Configuration)}.ReadOrEmptySetFromApplicationConfig] Can't read app.config file");
} }
return _emptySet; return _emptySet;
} }
@ -214,7 +214,7 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadFromApplicationConfig] Can't read config file '{configFilePath}'"); Log.Error(ex, $"[{nameof(Configuration)}.ReadFromApplicationConfig] Can't read config file '{configFilePath}'");
throw; throw;
} }
} }
@ -226,7 +226,7 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadOrEmptyFromApplicationConfig] Can't read config file '{configFilePath}'"); Log.Error(ex, $"[{nameof(Configuration)}.ReadOrEmptyFromApplicationConfig] Can't read config file '{configFilePath}'");
} }
return _empty; return _empty;
} }
@ -243,7 +243,7 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadSetFromApplicationConfig] Can't read config file '{configFilePath}'"); Log.Error(ex, $"[{nameof(Configuration)}.ReadSetFromApplicationConfig] Can't read config file '{configFilePath}'");
throw; throw;
} }
} }
@ -255,7 +255,7 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadOrEmptySetFromApplicationConfig] Can't read config file '{configFilePath}'"); Log.Error(ex, $"[{nameof(Configuration)}.ReadOrEmptySetFromApplicationConfig] Can't read config file '{configFilePath}'");
} }
return _emptySet; return _emptySet;
} }
@ -273,7 +273,7 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadFromIniFile] Can't read config file '{path}'"); Log.Error(ex, $"[{nameof(Configuration)}.ReadFromIniFile] Can't read config file '{path}'");
throw; throw;
} }
} }
@ -285,7 +285,7 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadOrEmptyFromIniFile] Can't read config file '{path}'"); Log.Error(ex, $"[{nameof(Configuration)}.ReadOrEmptyFromIniFile] Can't read config file '{path}'");
} }
return _empty; return _empty;
} }
@ -303,7 +303,7 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadSetFromIniFile] Can't read config file '{path}'"); Log.Error(ex, $"[{nameof(Configuration)}.ReadSetFromIniFile] Can't read config file '{path}'");
throw; throw;
} }
} }
@ -315,7 +315,7 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadOrEmptySetFromIniFile] Can't read config file '{path}'"); Log.Error(ex, $"[{nameof(Configuration)}.ReadOrEmptySetFromIniFile] Can't read config file '{path}'");
} }
return _emptySet; return _emptySet;
} }
@ -333,7 +333,7 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadFromCommandLine] Can't read command line args"); Log.Error(ex, $"[{nameof(Configuration)}.ReadFromCommandLine] Can't read command line args");
throw; throw;
} }
} }
@ -345,7 +345,7 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadOrEmptyFromCommandLine] Can't read command line args"); Log.Error(ex, $"[{nameof(Configuration)}.ReadOrEmptyFromCommandLine] Can't read command line args");
} }
return _empty; return _empty;
} }
@ -358,7 +358,7 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadFromBinaryReader] Can't read config from binaryReader"); Log.Error(ex, $"[{nameof(Configuration)}.ReadFromBinaryReader] Can't read config from binaryReader");
throw; throw;
} }
} }
@ -370,7 +370,7 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadOrEmptyFromBinaryReader] Can't read config from binaryReader"); Log.Error(ex, $"[{nameof(Configuration)}.ReadOrEmptyFromBinaryReader] Can't read config from binaryReader");
} }
return _empty; return _empty;
} }
@ -383,7 +383,7 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadSetFromBinaryReader] Can't read config from binaryReader"); Log.Error(ex, $"[{nameof(Configuration)}.ReadSetFromBinaryReader] Can't read config from binaryReader");
throw; throw;
} }
} }
@ -395,10 +395,131 @@ namespace ZeroLevel
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, $"[Configuration.ReadSetOrEmptyFromBinaryReader] Can't read config from binaryReader"); Log.Error(ex, $"[{nameof(Configuration)}.ReadSetOrEmptyFromBinaryReader] Can't read config from binaryReader");
} }
return _emptySet; return _emptySet;
} }
/// <summary>
/// Create configuration from Json file
/// </summary>
/// <param name="path">Path to the Json file</param>
/// <returns>Configuration</returns>
public static IConfiguration ReadFromJsonFile(string path)
{
try
{
return new JsonFileReader(path).ReadConfiguration();
}
catch (Exception ex)
{
Log.Error(ex, $"[{nameof(Configuration)}.ReadFromJsonFile] Can't read config file '{path}'");
throw;
}
}
public static IConfiguration ReadOrEmptyFromJsonFile(string path)
{
try
{
return new JsonFileReader(path).ReadConfiguration();
}
catch (Exception ex)
{
Log.Error(ex, $"[{nameof(Configuration)}.ReadOrEmptyFromJsonFile] Can't read config file '{path}'");
}
return _empty;
}
/// <summary>
/// Creating a configuration from an Json file, including sections
/// </summary>
/// <param name="path">Path to the Json file</param>
/// <returns>Configuration</returns>
public static IConfigurationSet ReadSetFromJsonFile(string path)
{
try
{
return new JsonFileReader(path).ReadConfigurationSet();
}
catch (Exception ex)
{
Log.Error(ex, $"[{nameof(Configuration)}.ReadSetFromJsonFile] Can't read config file '{path}'");
throw;
}
}
public static IConfigurationSet ReadSetOrEmptyFromJsonFile(string path)
{
try
{
return new JsonFileReader(path).ReadConfigurationSet();
}
catch (Exception ex)
{
Log.Error(ex, $"[{nameof(Configuration)}.ReadSetOrEmptyFromJsonFile] Can't read config file '{path}'");
}
return _emptySet;
}
/// <summary>
/// Create configuration from Yaml file
/// </summary>
/// <param name="path">Path to the Yaml file</param>
/// <returns>Configuration</returns>
public static IConfiguration ReadFromYamlFile(string path)
{
try
{
return new YamlFileReader(path).ReadConfiguration();
}
catch (Exception ex)
{
Log.Error(ex, $"[{nameof(Configuration)}.ReadFromYamlFile] Can't read config file '{path}'");
throw;
}
}
public static IConfiguration ReadOrEmptyFromYamlFile(string path)
{
try
{
return new YamlFileReader(path).ReadConfiguration();
}
catch (Exception ex)
{
Log.Error(ex, $"[{nameof(Configuration)}.ReadOrEmptyFromYamlFile] Can't read config file '{path}'");
}
return _empty;
}
/// <summary>
/// Creating a configuration from an Yaml file, including sections
/// </summary>
/// <param name="path">Path to the Yaml file</param>
/// <returns>Configuration</returns>
public static IConfigurationSet ReadSetFromYamlFile(string path)
{
try
{
return new YamlFileReader(path).ReadConfigurationSet();
}
catch (Exception ex)
{
Log.Error(ex, $"[{nameof(Configuration)}.ReadSetFromYamlFile] Can't read config file '{path}'");
throw;
}
}
public static IConfigurationSet ReadSetOrEmptyFromYamlFile(string path)
{
try
{
return new YamlFileReader(path).ReadConfigurationSet();
}
catch (Exception ex)
{
Log.Error(ex, $"[{nameof(Configuration)}.ReadSetOrEmptyFromYamlFile] Can't read config file '{path}'");
}
return _emptySet;
}
#endregion Read configuration #endregion Read configuration
public static IConfiguration Merge(ConfigurationRecordExistBehavior existRecordBehavior, params IConfiguration[] configurations) public static IConfiguration Merge(ConfigurationRecordExistBehavior existRecordBehavior, params IConfiguration[] configurations)

@ -15,13 +15,17 @@ namespace ZeroLevel.Services.Config.Implementation
internal IniFileReader(string configPath) internal IniFileReader(string configPath)
{ {
if (String.IsNullOrWhiteSpace(configPath)) if (String.IsNullOrWhiteSpace(configPath))
throw new ArgumentNullException("configPath", "File path not found"); {
Log.Fatal($"[{nameof(IniFileReader)}] File path is null or empty");
throw new ArgumentNullException("configPath", "File path is null or empty");
}
if (!File.Exists(configPath)) if (!File.Exists(configPath))
{ {
configPath = Path.Combine(Configuration.BaseDirectory, configPath); configPath = Path.Combine(Configuration.BaseDirectory, configPath);
if (!File.Exists(configPath)) if (!File.Exists(configPath))
{ {
throw new FileNotFoundException("File path not exists: " + configPath); Log.Fatal($"[{nameof(IniFileReader)}] File path '{configPath}' not exists");
throw new FileNotFoundException($"File path '{configPath}' not exists");
} }
} }
_iniPath = configPath; _iniPath = configPath;

@ -0,0 +1,89 @@
using System;
using System.IO;
namespace ZeroLevel.Services.Config.Implementation
{
/// <summary>
/// Read from JSON file, aka 'ConfigurationBuilder().AddJsonFile(configPath)' from Microsoft.Extensions.Configuration
/// </summary>
internal sealed class JsonFileReader
: IConfigurationReader
{
private readonly string _jsonPath;
internal JsonFileReader(string configPath)
{
if (String.IsNullOrWhiteSpace(configPath))
{
Log.Fatal($"[{nameof(JsonFileReader)}] File path is null or empty");
throw new ArgumentNullException("configPath", "File path is null or empty");
}
if (!File.Exists(configPath))
{
configPath = Path.Combine(Configuration.BaseDirectory, configPath);
if (!File.Exists(configPath))
{
Log.Fatal($"[{nameof(JsonFileReader)}] File path '{configPath}' not exists");
throw new FileNotFoundException($"File path '{configPath}' not exists");
}
}
_jsonPath = configPath;
}
public IConfiguration ReadConfiguration()
{
var set = ReadConfigurationSet();
return set.Default;
}
public IConfigurationSet ReadConfigurationSet()
{
try
{
using (Stream stream = new FileStream(_jsonPath,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite,
bufferSize: 1,
FileOptions.SequentialScan))
{
IConfigurationSet set = Configuration.CreateSet();
var dict = JsonConfigurationFileParser.Parse(stream);
foreach (var kv in dict)
{
if (string.CompareOrdinal(Configuration.DEFAULT_SECTION_NAME, kv.Key) == 0)
{
foreach (var set_kv in kv.Value)
{
set.Default.Append(set_kv.Key, set_kv.Value);
}
}
else
{
var sectionName = kv.Key;
IConfiguration section;
if (false == set.ContainsSection(sectionName))
{
section = set.CreateSection(sectionName);
}
else
{
section = set.GetSection(sectionName);
}
foreach (var set_kv in kv.Value)
{
section.Append(set_kv.Key, set_kv.Value);
}
}
}
return set;
}
}
catch (Exception ex)
{
Log.Error(ex, $"[JsonFileReader] Failed to load configuration from file '{_jsonPath}'.");
throw new InvalidDataException($"Failed to load configuration from file '{_jsonPath}'.");
}
}
}
}

@ -0,0 +1,113 @@
using System;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization;
using System.IO;
using ZeroLevel.Services.Formats.YAML;
namespace ZeroLevel.Services.Config.Implementation
{
internal sealed class YamlFileReader
: IConfigurationReader
{
private readonly string _yamlPath;
internal YamlFileReader(string configPath)
{
if (String.IsNullOrWhiteSpace(configPath))
{
Log.Fatal($"[{nameof(JsonFileReader)}] File path is null or empty");
throw new ArgumentNullException("configPath", "File path is null or empty");
}
if (!File.Exists(configPath))
{
configPath = Path.Combine(Configuration.BaseDirectory, configPath);
if (!File.Exists(configPath))
{
Log.Fatal($"[{nameof(JsonFileReader)}] File path '{configPath}' not exists");
throw new FileNotFoundException($"File path '{configPath}' not exists");
}
}
_yamlPath = configPath;
}
public IConfiguration ReadConfiguration()
{
var set = ReadConfigurationSet();
return set.Default;
}
private static Stream GenerateStreamFromString(string s)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
public IConfigurationSet ReadConfigurationSet()
{
try
{
var yaml = File.ReadAllText(_yamlPath);
var json = FullYamlToJsonConverter.Convert(yaml);
using (Stream stream = GenerateStreamFromString(json))
{
IConfigurationSet set = Configuration.CreateSet();
var dict = JsonConfigurationFileParser.Parse(stream);
foreach (var kv in dict)
{
if (string.CompareOrdinal(Configuration.DEFAULT_SECTION_NAME, kv.Key) == 0)
{
foreach (var set_kv in kv.Value)
{
set.Default.Append(set_kv.Key, set_kv.Value);
}
}
else
{
var sectionName = kv.Key;
IConfiguration section;
if (false == set.ContainsSection(sectionName))
{
section = set.CreateSection(sectionName);
}
else
{
section = set.GetSection(sectionName);
}
foreach (var set_kv in kv.Value)
{
section.Append(set_kv.Key, set_kv.Value);
}
}
}
return set;
}
}
catch (Exception ex)
{
Log.Error(ex, $"[YamlFileReader] Failed to load configuration from file '{_yamlPath}'.");
throw new InvalidDataException($"Failed to load configuration from file '{_yamlPath}'.");
}
}
private static class FullYamlToJsonConverter
{
public static string Convert(string yaml)
{
var deserializer = new DeserializerBuilder().Build();
var yamlObject = deserializer.Deserialize(yaml);
var serializer = new SerializerBuilder()
.JsonCompatible()
.Build();
var json = serializer.Serialize(yamlObject);
return json;
}
}
}
}

@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.Json;
namespace ZeroLevel.Services.Config
{
/// <summary>
/// Edited version from Microsoft.Extensions.Configuration
/// </summary>
internal sealed class JsonConfigurationFileParser
{
private JsonConfigurationFileParser() { }
private readonly Dictionary<string, Dictionary<string, List<string>>> _data = new Dictionary<string, Dictionary<string, List<string>>>(StringComparer.OrdinalIgnoreCase);
private readonly Stack<string> _paths = new Stack<string>();
public static IDictionary<string, Dictionary<string, List<string>>> Parse(Stream input)
=> new JsonConfigurationFileParser().ParseStream(input);
private Dictionary<string, Dictionary<string, List<string>>> ParseStream(Stream input)
{
var jsonDocumentOptions = new JsonDocumentOptions
{
CommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true,
};
using (var reader = new StreamReader(input))
using (JsonDocument doc = JsonDocument.Parse(reader.ReadToEnd(), jsonDocumentOptions))
{
if (doc.RootElement.ValueKind != JsonValueKind.Object)
{
throw new FormatException($"Top-level JSON element must be an object. Instead, '{doc.RootElement.ValueKind}' was found.");
}
VisitObjectElement(doc.RootElement);
}
return _data;
}
private void VisitObjectElement(JsonElement element)
{
foreach (JsonProperty property in element.EnumerateObject())
{
EnterContext(property.Name);
VisitValue(property.Value);
ExitContext();
}
}
private void VisitArrayElement(JsonElement element)
{
int index = 0;
foreach (JsonElement arrayElement in element.EnumerateArray())
{
EnterContext(index.ToString());
VisitValue(arrayElement);
ExitContext();
index++;
}
}
private void VisitValue(JsonElement value)
{
Debug.Assert(_paths.Count > 0);
switch (value.ValueKind)
{
case JsonValueKind.Object:
VisitObjectElement(value);
break;
case JsonValueKind.Array:
VisitArrayElement(value);
break;
case JsonValueKind.Number:
case JsonValueKind.String:
case JsonValueKind.True:
case JsonValueKind.False:
case JsonValueKind.Null:
if (_data.ContainsKey(CurrentSection) == false)
{
_data[CurrentSection] = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
}
var data = _data[CurrentSection];
string key = _paths.Peek();
if (data.ContainsKey(key))
{
data[key].Add(value.ToString());
}
else
{
data[key] = new List<string> { value.ToString() };
}
break;
default:
throw new FormatException($"Unsupported JSON token '{value.ValueKind}' was found.");
}
}
public static readonly string KeyDelimiter = ".";
private string CurrentSection = Configuration.DEFAULT_SECTION_NAME;
private void EnterContext(string context)
{
if (_paths.Count > 0)
{
CurrentSection = string.Join(KeyDelimiter, _paths.Reverse());
}
else
{
CurrentSection = Configuration.DEFAULT_SECTION_NAME;
}
_paths.Push(context);
}
private void ExitContext() => _paths.Pop();
}
}

@ -0,0 +1,284 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace ZeroLevel.Services.Formats.YAML
{
public static class YamlToJsonConverter
{
public static string Convert(string yaml)
{
if (string.IsNullOrEmpty(yaml))
{
return "{}";
}
yaml = RemoveComments(yaml);
Dictionary<string, object> yamlData = ParseYaml(yaml, 0);
return ConvertToJson(yamlData);
}
private static string RemoveComments(string yaml)
{
return Regex.Replace(yaml, @"#.*", string.Empty);
}
private static Dictionary<string, object> ParseYaml(string yaml, int indentLevel)
{
Dictionary<string, object> data = new Dictionary<string, object>();
string[] lines = yaml.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
string currentKey = null;
for (int i = 0; i < lines.Length; i++)
{
string line = lines[i].Trim();
if (string.IsNullOrEmpty(line))
{
continue;
}
int currentLineIndent = CountLeadingSpaces(lines[i]);
if (currentLineIndent < indentLevel)
{
break;
}
if (currentLineIndent > indentLevel)
{
continue;
}
string trimmedLine = line.TrimStart();
if (trimmedLine.StartsWith("-"))
{
if (!data.ContainsKey(currentKey))
{
data[currentKey] = new List<object>();
}
List<object> list = (List<object>)data[currentKey];
string listValue = trimmedLine.Substring(1).Trim();
if (listValue.StartsWith("{"))
{
listValue = listValue.Substring(1, listValue.Length - 2).Trim();
string[] entries = listValue.Split(',');
Dictionary<string, object> inlineObj = new Dictionary<string, object>();
foreach (string entry in entries)
{
string[] kvp = entry.Trim().Split(':');
inlineObj.Add(kvp[0].Trim(), kvp[1].Trim());
}
list.Add(inlineObj);
}
else if (listValue.StartsWith("["))
{
listValue = listValue.Substring(1, listValue.Length - 2).Trim();
string[] entries = listValue.Split(',');
List<object> inlineList = new List<object>();
foreach (string entry in entries)
{
inlineList.Add(entry.Trim());
}
list.Add(inlineList);
}
else
{
list.Add(GetValue(listValue));
}
}
else
{
string[] parts = trimmedLine.Split(new[] { ':' }, 2);
if (parts.Length == 2)
{
currentKey = parts[0].Trim();
string value = parts[1].Trim();
if (string.IsNullOrEmpty(value))
{
int nextIndentLevel = int.MaxValue;
for (int j = i + 1; j < lines.Length; j++)
{
int tempIndent = CountLeadingSpaces(lines[j]);
if (tempIndent > currentLineIndent)
{
nextIndentLevel = tempIndent;
break;
}
}
StringBuilder subYaml = new StringBuilder();
for (int j = i + 1; j < lines.Length; j++)
{
if (CountLeadingSpaces(lines[j]) >= nextIndentLevel)
{
subYaml.AppendLine(lines[j]);
}
else
{
break;
}
}
data[currentKey] = ParseYaml(subYaml.ToString(), nextIndentLevel);
i += subYaml.ToString().Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None).Length;
}
else if (value.StartsWith("{"))
{
value = value.Substring(1, value.Length - 2).Trim();
string[] entries = value.Split(',');
Dictionary<string, object> inlineObj = new Dictionary<string, object>();
foreach (string entry in entries)
{
string[] kvp = entry.Trim().Split(':');
inlineObj.Add(kvp[0].Trim(), kvp[1].Trim());
}
data.Add(currentKey, inlineObj);
}
else if (value.StartsWith("["))
{
value = value.Substring(1, value.Length - 2).Trim();
string[] entries = value.Split(',');
List<object> inlineList = new List<object>();
foreach (string entry in entries)
{
inlineList.Add(entry.Trim());
}
data.Add(currentKey, inlineList);
}
else
{
data[currentKey] = GetValue(value);
}
}
}
}
return data;
}
private static object GetValue(string value)
{
if (value.ToLower() == "true")
{
return true;
}
if (value.ToLower() == "false")
{
return false;
}
if (value.ToLower() == "null")
{
return null;
}
if (int.TryParse(value, out int intValue))
{
return intValue;
}
if (double.TryParse(value, out double doubleValue))
{
return doubleValue;
}
return value;
}
private static string ConvertToJson(Dictionary<string, object> data)
{
StringBuilder json = new StringBuilder("{");
bool first = true;
foreach (KeyValuePair<string, object> pair in data)
{
if (!first)
{
json.Append(",");
}
json.Append($"\"{pair.Key}\":");
if (pair.Value is Dictionary<string, object> subData)
{
json.Append(ConvertToJson(subData));
}
else if (pair.Value is List<object> listData)
{
json.Append(ConvertListToJson(listData));
}
else if (pair.Value is string)
{
json.Append($"\"{pair.Value}\"");
}
else if (pair.Value is bool || pair.Value is int || pair.Value is double || pair.Value == null)
{
json.Append($"{pair.Value.ToString().ToLower()}");
}
first = false;
}
json.Append("}");
return json.ToString();
}
private static string ConvertListToJson(List<object> list)
{
StringBuilder json = new StringBuilder("[");
bool first = true;
foreach (object item in list)
{
if (!first)
{
json.Append(",");
}
if (item is Dictionary<string, object>)
{
json.Append(ConvertToJson((Dictionary<string, object>)item));
}
else if (item is List<object>)
{
json.Append(ConvertListToJson((List<object>)item));
}
else if (item is string)
{
json.Append($"\"{item}\"");
}
else if (item is bool || item is int || item is double || item == null)
{
json.Append($"{item.ToString().ToLower()}");
}
first = false;
}
json.Append("]");
return json.ToString();
}
private static int CountLeadingSpaces(string line)
{
int count = 0;
foreach (char c in line)
{
if (c == ' ')
{
count++;
}
else
{
break;
}
}
return count;
}
}
}

@ -8,17 +8,17 @@ namespace ZeroLevel.Services.Network.Proxies
public class Proxy public class Proxy
: IDisposable : IDisposable
{ {
private readonly ProxyBalancer _balancer = new ProxyBalancer(); private readonly ProxyBalancer _balancer = new();
public void AppendServer(IPEndPoint ep) => _balancer.AddEndpoint(ep); public void AppendServer(IPEndPoint ep) => _balancer.AddEndpoint(ep);
private Socket _incomingSocket; private readonly Socket _incomingSocket;
public Proxy(IPEndPoint listenEndpoint) public Proxy(IPEndPoint listenEndpoint)
{ {
_incomingSocket = new Socket(listenEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); _incomingSocket = new Socket(listenEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_incomingSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true); _incomingSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
_incomingSocket.Bind(listenEndpoint); _incomingSocket.Bind(listenEndpoint);
} }
public void Run() public void Run()
@ -32,10 +32,10 @@ namespace ZeroLevel.Services.Network.Proxies
{ {
var socket = await _incomingSocket.AcceptAsync(); var socket = await _incomingSocket.AcceptAsync();
// no await! // no await!
Task.Run(async () => await Task.Run(async () =>
{ {
await CreateProxyConnection(socket); await CreateProxyConnection(socket);
}); }).ConfigureAwait(false);
} }
} }
catch (Exception ex) catch (Exception ex)
@ -53,10 +53,8 @@ namespace ZeroLevel.Services.Network.Proxies
var server = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); var server = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true); server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
server.Connect(endpoint); server.Connect(endpoint);
using (var bind = new ProxyBinding(connection, server)) using var bind = new ProxyBinding(connection, server);
{ await bind.Bind();
await bind.Bind();
}
} }
catch (Exception ex) catch (Exception ex)
{ {

@ -526,8 +526,8 @@ namespace ZeroLevel.Services.PartitionStorage
source.Seek(range.Start, SeekOrigin.Begin); source.Seek(range.Start, SeekOrigin.Begin);
var size = range.End - range.Start; var size = range.End - range.Start;
byte[] buffer = new byte[size]; byte[] buffer = new byte[size];
source.Read(buffer, 0, buffer.Length); var count = source.Read(buffer, 0, buffer.Length);
target.Write(buffer, 0, buffer.Length); target.Write(buffer, 0, count);
} }
#endregion #endregion

@ -0,0 +1,23 @@
using System;
namespace ZeroLevel.Services.Utils
{
public static class Timestamp
{
/// <summary>
/// Current unix timestamp in ms
/// </summary>
public static long UtcNow => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
/// <summary>
/// Unix timestamp in ms
/// </summary>
/// <param name="offset"></param>
/// <returns></returns>
public static long FromDateTimeOffset(DateTimeOffset offset) => offset.ToUnixTimeMilliseconds();
public static long UtcNowAddDays(int days) => DateTimeOffset.UtcNow.AddDays(days).ToUnixTimeMilliseconds();
public static long UtcNowAddSeconds(int seconds) => DateTimeOffset.UtcNow.AddSeconds(seconds).ToUnixTimeMilliseconds();
public static long Max => DateTimeOffset.MaxValue.ToUnixTimeMilliseconds();
public static long Min => DateTimeOffset.MinValue.ToUnixTimeMilliseconds();
public static DateTimeOffset ToDateTimeOffsest(long timeStamp) => DateTimeOffset.FromUnixTimeMilliseconds(timeStamp);
}
}

@ -8,7 +8,7 @@
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<Title>ZeroLevel</Title> <Title>ZeroLevel</Title>
<FileVersion>$(AssemblyVersion)</FileVersion> <FileVersion>$(AssemblyVersion)</FileVersion>
<AssemblyVersion>4.0.0.1</AssemblyVersion> <AssemblyVersion>4.0.0.2</AssemblyVersion>
<Version>$(AssemblyVersion)</Version> <Version>$(AssemblyVersion)</Version>
<AnalysisLevel>latest</AnalysisLevel> <AnalysisLevel>latest</AnalysisLevel>
<Authors>Ogoun</Authors> <Authors>Ogoun</Authors>
@ -38,7 +38,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" /> <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.0" />
<PackageReference Include="System.Text.Json" Version="9.0.4" />
<PackageReference Include="YamlDotNet" Version="16.3.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

Loading…
Cancel
Save

Powered by TurnKey Linux.