-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5e269d3
commit 1a69e86
Showing
5 changed files
with
613 additions
and
0 deletions.
There are no files selected for viewing
111 changes: 111 additions & 0 deletions
111
extensions/Sisk.IniConfiguration/IniConfigurationPipeline.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
using Sisk.Core.Http; | ||
using Sisk.Core.Http.Hosting; | ||
using Sisk.IniConfiguration.Parser; | ||
using System.Text; | ||
|
||
namespace Sisk.IniConfiguration; | ||
|
||
/// <summary> | ||
/// Provides an INI-Document based configuration-reader pipeline. | ||
/// </summary> | ||
public sealed class IniConfigurationPipeline : IConfigurationReader | ||
{ | ||
/// <inheritdoc/> | ||
public void ReadConfiguration(ConfigurationContext context) | ||
{ | ||
IniDocument document = IniDocument.FromFile(context.ConfigurationFile); | ||
|
||
string parsingNode = ""; | ||
try | ||
{ | ||
var serverSection = document.GetSection("Server"); | ||
if (serverSection is not null) | ||
{ | ||
parsingNode = "Server.Listen"; | ||
string[] listeningPorts = serverSection.GetMany("Listen"); | ||
context.TargetListeningHost.Ports = listeningPorts.Select(n => ListeningPort.Parse(n, null)).ToArray(); | ||
|
||
parsingNode = "Server.Encoding"; | ||
if (serverSection.GetOne("Encoding") is { } encoding) | ||
context.Host.ServerConfiguration.DefaultEncoding = Encoding.GetEncoding(encoding); | ||
|
||
parsingNode = "Server.MaximumContentLength"; | ||
if (serverSection.GetOne("MaximumContentLength") is { } MaximumContentLength) | ||
context.Host.ServerConfiguration.MaximumContentLength = Int32.Parse(MaximumContentLength); | ||
|
||
parsingNode = "Server.IncludeRequestIdHeader"; | ||
if (serverSection.GetOne("IncludeRequestIdHeader") is { } IncludeRequestIdHeader) | ||
context.Host.ServerConfiguration.IncludeRequestIdHeader = IniParser.IniNamingComparer.Compare(IncludeRequestIdHeader, "true") == 0; | ||
|
||
parsingNode = "Server.ThrowExceptions"; | ||
if (serverSection.GetOne("ThrowExceptions") is { } ThrowExceptions) | ||
context.Host.ServerConfiguration.ThrowExceptions = IniParser.IniNamingComparer.Compare(ThrowExceptions, "true") == 0; | ||
|
||
parsingNode = "Server.AccessLogsStream"; | ||
if (serverSection.GetOne("AccessLogsStream") is { } AccessLogsStream) | ||
context.Host.ServerConfiguration.AccessLogsStream = string.Compare(AccessLogsStream, "console", true) == 0 ? | ||
LogStream.ConsoleOutput : new LogStream(AccessLogsStream); | ||
|
||
parsingNode = "Server.ErrorsLogsStream"; | ||
if (serverSection.GetOne("ErrorsLogsStream") is { } ErrorsLogsStream) | ||
context.Host.ServerConfiguration.ErrorsLogsStream = string.Compare(ErrorsLogsStream, "console", true) == 0 ? | ||
LogStream.ConsoleOutput : new LogStream(ErrorsLogsStream); | ||
} | ||
|
||
var paramsSection = document.GetSection("Parameters"); | ||
if (paramsSection is not null) | ||
{ | ||
foreach (string key in paramsSection.Keys) | ||
{ | ||
string? value = paramsSection.GetOne(key); | ||
if (value is not null) | ||
context.Parameters.Add(key, value); | ||
} | ||
} | ||
|
||
parsingNode = "Cors"; | ||
var corsSection = document.GetSection("CORS"); | ||
if (corsSection is not null) | ||
{ | ||
parsingNode = "Cors.AllowMethods"; | ||
if (corsSection.GetOne("AllowMethods") is { } AllowMethods) | ||
context.TargetListeningHost.CrossOriginResourceSharingPolicy.AllowMethods | ||
= AllowMethods.Split(",", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); | ||
|
||
parsingNode = "Cors.AllowHeaders"; | ||
if (corsSection.GetOne("AllowHeaders") is { } AllowHeaders) | ||
context.TargetListeningHost.CrossOriginResourceSharingPolicy.AllowHeaders | ||
= AllowHeaders.Split(",", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); | ||
|
||
parsingNode = "Cors.AllowOrigins"; | ||
if (corsSection.GetOne("AllowOrigins") is { } AllowOrigins) | ||
context.TargetListeningHost.CrossOriginResourceSharingPolicy.AllowOrigins | ||
= AllowOrigins.Split(",", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); | ||
|
||
parsingNode = "Cors.AllowOrigin"; | ||
if (corsSection.GetOne("AllowOrigin") is { } AllowOrigin) | ||
context.TargetListeningHost.CrossOriginResourceSharingPolicy.AllowOrigin | ||
= AllowOrigin; | ||
|
||
parsingNode = "Cors.ExposeHeaders"; | ||
if (corsSection.GetOne("ExposeHeaders") is { } ExposeHeaders) | ||
context.TargetListeningHost.CrossOriginResourceSharingPolicy.ExposeHeaders | ||
= ExposeHeaders.Split(",", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); | ||
|
||
parsingNode = "Cors.AllowCredentials"; | ||
if (corsSection.GetOne("AllowCredentials") is { } AllowCredentials) | ||
context.TargetListeningHost.CrossOriginResourceSharingPolicy.AllowCredentials | ||
= string.Compare(AllowCredentials, "True", true) == 0; | ||
|
||
parsingNode = "Cors.MaxAge"; | ||
if (corsSection.GetOne("MaxAge") is { } MaxAge) | ||
context.TargetListeningHost.CrossOriginResourceSharingPolicy.MaxAge | ||
= TimeSpan.FromSeconds(int.Parse(MaxAge)); | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
throw new Exception($"Caught exception while trying to read the property {parsingNode}: {ex.Message}", ex); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
using Sisk.IniConfiguration.Parser; | ||
using System.Text; | ||
|
||
namespace Sisk.IniConfiguration; | ||
|
||
/// <summary> | ||
/// Represents an INI configuration document. | ||
/// </summary> | ||
public sealed class IniDocument | ||
{ | ||
/// <summary> | ||
/// Gets all INI sections defined in this INI document. | ||
/// </summary> | ||
public IniSection[] Sections { get; } | ||
|
||
/// <summary> | ||
/// Gets the global INI section, which is the primary section in the document. | ||
/// </summary> | ||
public IniSection Global { get => Sections[0]; } | ||
|
||
/// <summary> | ||
/// Creates an new <see cref="IniDocument"/> document from the specified | ||
/// string, reading it as an UTF-8 string. | ||
/// </summary> | ||
/// <param name="iniConfiguration">The UTF-8 string.</param> | ||
public static IniDocument FromString(string iniConfiguration) | ||
{ | ||
using TextReader reader = new StringReader(iniConfiguration); | ||
using IniParser parser = new IniParser(reader); | ||
return parser.Parse(); | ||
} | ||
|
||
/// <summary> | ||
/// Creates an new <see cref="IniDocument"/> document from the specified | ||
/// file using the specified encoding. | ||
/// </summary> | ||
/// <param name="filePath">The absolute or relative file path to the INI document.</param> | ||
/// <param name="encoding">Optional. The encoding used to read the file. Defaults to UTF-8.</param> | ||
public static IniDocument FromFile(string filePath, Encoding? encoding = null) | ||
{ | ||
using TextReader reader = new StreamReader(filePath, encoding ?? Encoding.UTF8); | ||
using IniParser parser = new IniParser(reader); | ||
return parser.Parse(); | ||
} | ||
|
||
/// <summary> | ||
/// Creates an new <see cref="IniDocument"/> document from the specified | ||
/// stream using the specified encoding. | ||
/// </summary> | ||
/// <param name="stream">The input stream where the INI document is.</param> | ||
/// <param name="encoding">Optional. The encoding used to read the stream. Defaults to UTF-8.</param> | ||
public static IniDocument FromStream(Stream stream, Encoding? encoding = null) | ||
{ | ||
using TextReader reader = new StreamReader(stream, encoding ?? Encoding.UTF8); | ||
using IniParser parser = new IniParser(reader); | ||
return parser.Parse(); | ||
} | ||
|
||
/// <summary> | ||
/// Creates an new <see cref="IniDocument"/> document from the specified | ||
/// <see cref="TextReader"/>. | ||
/// </summary> | ||
/// <param name="reader">The <see cref="TextReader"/> instance.</param> | ||
public static IniDocument FromStream(TextReader reader) | ||
{ | ||
using IniParser parser = new IniParser(reader); | ||
return parser.Parse(); | ||
} | ||
|
||
internal IniDocument(IniSection[] sections) | ||
{ | ||
Sections = sections; | ||
} | ||
|
||
/// <summary> | ||
/// Gets an defined INI section from this document. The search is case-insensitive. | ||
/// </summary> | ||
/// <param name="sectionName">The section name.</param> | ||
/// <returns>The <see cref="IniSection"/> object if found, or null if not defined.</returns> | ||
public IniSection? GetSection(string sectionName) | ||
{ | ||
for (int i = 0; i < Sections.Length; i++) | ||
{ | ||
IniSection section = Sections[i]; | ||
if (IniParser.IniNamingComparer.Compare(section.Name, sectionName) == 0) | ||
return section; | ||
} | ||
return null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
using Sisk.IniConfiguration.Parser; | ||
using System.Collections; | ||
using System.Diagnostics.CodeAnalysis; | ||
|
||
namespace Sisk.IniConfiguration; | ||
|
||
/// <summary> | ||
/// Represents an INI section, which contains it's own properties. | ||
/// </summary> | ||
public sealed class IniSection : IReadOnlyDictionary<string, string[]> | ||
{ | ||
internal (string, string)[] items; | ||
|
||
/// <summary> | ||
/// Gets the INI section name. | ||
/// </summary> | ||
public string Name { get; } | ||
|
||
internal IniSection(string name, (string, string)[] items) | ||
{ | ||
this.items = items; | ||
Name = name; | ||
} | ||
|
||
/// <summary> | ||
/// Gets all values associated with the specified property name, performing an case-insensitive search. | ||
/// </summary> | ||
/// <param name="key">The property name.</param> | ||
public string[] this[string key] | ||
{ | ||
get | ||
{ | ||
return items | ||
.Where(k => IniParser.IniNamingComparer.Compare(key, k.Item1) == 0) | ||
.Select(k => k.Item2) | ||
.ToArray(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Gets all keys defined in this INI section, without duplicates. | ||
/// </summary> | ||
public IEnumerable<string> Keys | ||
{ | ||
get | ||
{ | ||
return items.Select(i => i.Item1).Distinct().ToArray(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Gets all values defined in this INI section. | ||
/// </summary> | ||
public IEnumerable<string[]> Values | ||
{ | ||
get | ||
{ | ||
using (var e = GetEnumerator()) | ||
{ | ||
while (e.MoveNext()) | ||
{ | ||
yield return e.Current.Value; | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Gets the number of properties in this INI section. | ||
/// </summary> | ||
public int Count => items.Length; | ||
|
||
/// <summary> | ||
/// Gets the last value defined in this INI section by their property name. | ||
/// </summary> | ||
/// <param name="key">The property name.</param> | ||
/// <returns>The last value associated with the specified property name, or null if nothing is found.</returns> | ||
public string? GetOne(string key) | ||
{ | ||
return items | ||
.Where(k => IniParser.IniNamingComparer.Compare(key, k.Item1) == 0) | ||
.Select(k => k.Item2) | ||
.LastOrDefault(); | ||
} | ||
|
||
/// <summary> | ||
/// Gets all values defined in this INI section by their property name. | ||
/// </summary> | ||
/// <param name="key">The property name.</param> | ||
/// <returns>All values associated with the specified property name.</returns> | ||
public string[] GetMany(string key) | ||
{ | ||
return this[key]; | ||
} | ||
|
||
/// <summary> | ||
/// Gets an boolean indicating if the specified key/property name is | ||
/// defined in this <see cref="IniSection"/>. | ||
/// </summary> | ||
/// <param name="key">The property name.</param> | ||
/// <returns>An <see cref="bool"/> indicating if the specified property name is defined or not.</returns> | ||
public bool ContainsKey(string key) | ||
{ | ||
for (int i = 0; i < items.Length; i++) | ||
{ | ||
(string, string?) item = items[i]; | ||
|
||
if (IniParser.IniNamingComparer.Compare(item.Item1, key) == 0) | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
/// <inheritdoc/> | ||
public IEnumerator<KeyValuePair<string, string[]>> GetEnumerator() | ||
{ | ||
string[] keysDistinct = items.Select(i => i.Item1).Distinct().ToArray(); | ||
|
||
foreach (string key in keysDistinct) | ||
{ | ||
string[] valuesByKey = items | ||
.Where(i => i.Item1 == key) | ||
.Select(i => i.Item2) | ||
.ToArray(); | ||
|
||
yield return new KeyValuePair<string, string[]>(key, valuesByKey); | ||
} | ||
} | ||
|
||
/// <inheritdoc/> | ||
public bool TryGetValue(string key, [MaybeNullWhen(false)] out string[] value) | ||
{ | ||
value = this[key]; | ||
return value.Length > 0; | ||
} | ||
|
||
IEnumerator IEnumerable.GetEnumerator() | ||
{ | ||
return GetEnumerator(); | ||
} | ||
} |
Oops, something went wrong.