diff --git a/src/Entity/CircularBuffer.cs b/src/Entity/CircularBuffer.cs
index 66e398d..0ffdbb2 100644
--- a/src/Entity/CircularBuffer.cs
+++ b/src/Entity/CircularBuffer.cs
@@ -16,7 +16,7 @@ namespace Sisk.Core.Entity;
///
/// The type of elements stored in the buffer.
///
-public class CircularBuffer : IEnumerable, IReadOnlyList
+public sealed class CircularBuffer : IEnumerable, IReadOnlyList
{
private T[] items;
diff --git a/src/Entity/HttpHeaderCollection.cs b/src/Entity/HttpHeaderCollection.cs
index 3b5d2a0..8d56eb1 100644
--- a/src/Entity/HttpHeaderCollection.cs
+++ b/src/Entity/HttpHeaderCollection.cs
@@ -8,7 +8,10 @@
// Repository: https://github.com/sisk-http/core
using Sisk.Core.Http;
+using System.Collections;
using System.Collections.Specialized;
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection.PortableExecutable;
using System.Text;
using Header = Sisk.Core.Internal.HttpKnownHeaderNames;
@@ -17,8 +20,11 @@ namespace Sisk.Core.Entity;
///
/// Represents an collection of HTTP headers with their name and values.
///
-public sealed class HttpHeaderCollection : NameValueCollection
+public sealed class HttpHeaderCollection : IDictionary
{
+ private List<(string headerName, string headerValue)> _headers = new();
+ internal bool isReadOnly = false;
+
///
/// Create an new instance of the class.
///
@@ -31,50 +37,264 @@ public HttpHeaderCollection()
/// headers.
///
/// The header collection.
- public HttpHeaderCollection(NameValueCollection headers) : base(headers)
+ public HttpHeaderCollection(NameValueCollection headers)
{
+ foreach (string headerName in headers)
+ {
+ string[]? values = headers.GetValues(headerName);
+ if (values is not null)
+ {
+ for (int i = 0; i < values.Length; i++)
+ {
+ string value = values[i];
+ Add(headerName, value);
+ }
+ }
+ }
}
+
///
public override string ToString()
{
StringBuilder sb = new StringBuilder();
- foreach (string key in this.Keys)
+ foreach (var item in _headers)
{
- sb.AppendLine($"{key}: {this[key]}");
+ sb.AppendLine($"{item.headerName}: {item.headerValue}");
}
return sb.ToString();
}
+ ///
+ public (string, string) this[int index]
+ {
+ get
+ {
+ var h = _headers[index];
+ return (h.headerName, h.headerValue);
+ }
+ }
+
+ ///
+ public string? this[string headerName]
+ {
+ get
+ {
+ return string.Join(", ", GetValues(headerName));
+ }
+ set
+ {
+ Set(headerName, value);
+ }
+ }
+
+ #region Setters
+ ///
+ public void Add(string name, string? value)
+ {
+ ThrowIfReadOnly();
+ if (value is null)
+ return;
+
+ _headers.Add((name, value));
+ }
+
+ ///
+ /// Sets the specified header to the specified value.
+ ///
+ ///
+ /// If the header specified in the header does not exist, the Set method inserts a new
+ /// header into the list of header name/value pairs. If the header specified in header is
+ /// already present, value replaces the existing value.
+ ///
+ /// The header name.
+ /// The header value.
+ public void Set(string headerName, string? headerValue)
+ {
+ Remove(headerName);
+ Add(headerName, headerValue);
+ }
+ #endregion
+
+ #region Getters
+ ///
+ /// Gets the first headers value associated with the specified header name, or nothing it if no value
+ /// is associated with the specified header.
+ ///
+ /// The header name.
+ public string? GetValue(string name)
+ {
+ for (int i = 0; i < _headers.Count; i++)
+ {
+ var item = _headers[i];
+ if (string.Compare(item.headerName, name, true) == 0)
+ {
+ return item.headerValue;
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// Gets all headers values associated with the specified header name.
+ ///
+ /// The header name.
+ public IEnumerable GetValues(string name)
+ {
+ for (int i = 0; i < _headers.Count; i++)
+ {
+ var item = _headers[i];
+ if (string.Compare(item.headerName, name, true) == 0)
+ {
+ yield return item.headerValue;
+ }
+ }
+ }
+ #endregion
+
+ #region Interface methods
+
+ ///
+ public bool ContainsKey(string key)
+ {
+ for (int i = 0; i < _headers.Count; i++)
+ {
+ if (string.Compare(_headers[i].headerName, key, true) == 0)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ ///
+ public bool Remove(string key)
+ {
+ ThrowIfReadOnly();
+ List toRemove = new List();
+ for (int i = 0; i < _headers.Count; i++)
+ {
+ if (string.Compare(_headers[i].headerName, key, true) == 0)
+ {
+ toRemove.Add(i);
+ }
+ }
+ if (toRemove.Count > 0)
+ {
+ toRemove.Reverse();
+ for (int i = 0; i < toRemove.Count; i++)
+ {
+ _headers.RemoveAt(i);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ public bool TryGetValue(string key, [MaybeNullWhen(false)] out string? value)
+ {
+ for (int i = 0; i < _headers.Count; i++)
+ {
+ var item = _headers[i];
+ if (string.Compare(item.headerName, key, true) == 0)
+ {
+ value = item.headerValue;
+ return true;
+ }
+ }
+ value = default;
+ return false;
+ }
+
+ ///
+ public void Add(KeyValuePair item)
+ {
+ Add(item.Key, item.Value);
+ }
+
+ ///
+ public void Clear()
+ {
+ ThrowIfReadOnly();
+ _headers.Clear();
+ }
+
+ ///
+ public bool Contains(KeyValuePair item)
+ {
+ for (int i = 0; i < _headers.Count; i++)
+ {
+ var hitem = _headers[i];
+ if (string.Compare(hitem.headerName, item.Key, true) == 0 && hitem.headerValue.Equals(item.Value))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// This method is not implemented and should not be used.
+ ///
+ [Obsolete("This method is not implemented and should not be used.")]
+ public void CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ public bool Remove(KeyValuePair item)
+ {
+ return Remove(item.Key);
+ }
+
+ ///
+ public IEnumerator> GetEnumerator()
+ {
+ for (int i = 0; i < _headers.Count; i++)
+ {
+ var item = _headers[i];
+ yield return new KeyValuePair(item.headerName, item.headerValue);
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ #endregion
+
+ #region Helper properties
///
/// Gets or sets the value of the HTTP Accept header.
///
- public string? Accept { get => base[Header.Accept]; set => base[Header.Accept] = value; }
+ public string? Accept { get => this[Header.Accept]; set => this[Header.Accept] = value; }
///
/// Gets or sets the value of the HTTP Accept-Charset header.
///
- public string? AcceptCharset { get => base[Header.AcceptCharset]; set => base[Header.AcceptCharset] = value; }
+ public string? AcceptCharset { get => this[Header.AcceptCharset]; set => this[Header.AcceptCharset] = value; }
///
/// Gets or sets the value of the HTTP Accept-Encoding header.
///
- public string? AcceptEncoding { get => base[Header.AcceptEncoding]; set => base[Header.AcceptEncoding] = value; }
+ public string? AcceptEncoding { get => this[Header.AcceptEncoding]; set => this[Header.AcceptEncoding] = value; }
///
/// Gets or sets the value of the HTTP Accept-Language header.
///
- public string? AcceptLanguage { get => base[Header.AcceptLanguage]; set => base[Header.AcceptLanguage] = value; }
+ public string? AcceptLanguage { get => this[Header.AcceptLanguage]; set => this[Header.AcceptLanguage] = value; }
///
/// Gets or sets the value of the HTTP Accept-Patch header.
///
- public string? AcceptPatch { get => base[Header.AcceptPatch]; set => base[Header.AcceptPatch] = value; }
+ public string? AcceptPatch { get => this[Header.AcceptPatch]; set => this[Header.AcceptPatch] = value; }
///
/// Gets or sets the value of the HTTP Accept-Ranges header.
///
- public string? AcceptRanges { get => base[Header.AcceptRanges]; set => base[Header.AcceptRanges] = value; }
+ public string? AcceptRanges { get => this[Header.AcceptRanges]; set => this[Header.AcceptRanges] = value; }
///
/// Gets or sets the value of the HTTP Access-Control-Allow-Credentials header.
@@ -82,7 +302,7 @@ public override string ToString()
///
/// Note: this header can be overwritten by the current configuration.
///
- public string? AccessControlAllowCredentials { get => base[Header.AccessControlAllowCredentials]; set => base[Header.AccessControlAllowCredentials] = value; }
+ public string? AccessControlAllowCredentials { get => this[Header.AccessControlAllowCredentials]; set => this[Header.AccessControlAllowCredentials] = value; }
///
/// Gets or sets the value of the HTTP Access-Control-Allow-Headers header.
@@ -90,12 +310,12 @@ public override string ToString()
///
/// Note: this header can be overwritten by the current configuration.
///
- public string? AccessControlAllowHeaders { get => base[Header.AccessControlAllowHeaders]; set => base[Header.AccessControlAllowHeaders] = value; }
+ public string? AccessControlAllowHeaders { get => this[Header.AccessControlAllowHeaders]; set => this[Header.AccessControlAllowHeaders] = value; }
///
/// Gets or sets the value of the HTTP Access-Control-Allow-Methods header.
///
- public string? AccessControlAllowMethods { get => base[Header.AccessControlAllowMethods]; set => base[Header.AccessControlAllowMethods] = value; }
+ public string? AccessControlAllowMethods { get => this[Header.AccessControlAllowMethods]; set => this[Header.AccessControlAllowMethods] = value; }
///
/// Gets or sets the value of the HTTP Access-Control-Allow-Origin header.
@@ -103,7 +323,7 @@ public override string ToString()
///
/// Note: this header can be overwritten by the current configuration.
///
- public string? AccessControlAllowOrigin { get => base[Header.AccessControlAllowOrigin]; set => base[Header.AccessControlAllowOrigin] = value; }
+ public string? AccessControlAllowOrigin { get => this[Header.AccessControlAllowOrigin]; set => this[Header.AccessControlAllowOrigin] = value; }
///
/// Gets or sets the value of the HTTP Access-Control-Expose-Headers header.
@@ -111,7 +331,7 @@ public override string ToString()
///
/// Note: this header can be overwritten by the current configuration.
///
- public string? AccessControlExposeHeaders { get => base[Header.AccessControlExposeHeaders]; set => base[Header.AccessControlExposeHeaders] = value; }
+ public string? AccessControlExposeHeaders { get => this[Header.AccessControlExposeHeaders]; set => this[Header.AccessControlExposeHeaders] = value; }
///
/// Gets or sets the value of the HTTP Access-Control-Max-Age header.
@@ -119,52 +339,52 @@ public override string ToString()
///
/// Note: this header can be overwritten by the current configuration.
///
- public string? AccessControlMaxAge { get => base[Header.AccessControlMaxAge]; set => base[Header.AccessControlMaxAge] = value; }
+ public string? AccessControlMaxAge { get => this[Header.AccessControlMaxAge]; set => this[Header.AccessControlMaxAge] = value; }
///
/// Gets or sets the value of the HTTP Age header.
///
- public string? Age { get => base[Header.Age]; set => base[Header.Age] = value; }
+ public string? Age { get => this[Header.Age]; set => this[Header.Age] = value; }
///
/// Gets or sets the value of the HTTP Allow header.
///
- public string? Allow { get => base[Header.Allow]; set => base[Header.Allow] = value; }
+ public string? Allow { get => this[Header.Allow]; set => this[Header.Allow] = value; }
///
/// Gets or sets the value of the HTTP Alt-Svc header.
///
- public string? AltSvc { get => base[Header.AltSvc]; set => base[Header.AltSvc] = value; }
+ public string? AltSvc { get => this[Header.AltSvc]; set => this[Header.AltSvc] = value; }
///
/// Gets or sets the value of the HTTP Authorization header.
///
- public string? Authorization { get => base[Header.Authorization]; set => base[Header.Authorization] = value; }
+ public string? Authorization { get => this[Header.Authorization]; set => this[Header.Authorization] = value; }
///
/// Gets or sets the value of the HTTP Cache-Control header.
///
- public string? CacheControl { get => base[Header.CacheControl]; set => base[Header.CacheControl] = value; }
+ public string? CacheControl { get => this[Header.CacheControl]; set => this[Header.CacheControl] = value; }
///
/// Gets or sets the value of the HTTP Content-Disposition header.
///
- public string? ContentDisposition { get => base[Header.ContentDisposition]; set => base[Header.ContentDisposition] = value; }
+ public string? ContentDisposition { get => this[Header.ContentDisposition]; set => this[Header.ContentDisposition] = value; }
///
/// Gets or sets the value of the HTTP Content-Encoding header.
///
- public string? ContentEncoding { get => base[Header.ContentEncoding]; set => base[Header.ContentEncoding] = value; }
+ public string? ContentEncoding { get => this[Header.ContentEncoding]; set => this[Header.ContentEncoding] = value; }
///
/// Gets or sets the value of the HTTP Content-Language header.
///
- public string? ContentLanguage { get => base[Header.ContentLanguage]; set => base[Header.ContentLanguage] = value; }
+ public string? ContentLanguage { get => this[Header.ContentLanguage]; set => this[Header.ContentLanguage] = value; }
///
/// Gets or sets the value of the HTTP Content-Range header.
///
- public string? ContentRange { get => base[Header.ContentRange]; set => base[Header.ContentRange] = value; }
+ public string? ContentRange { get => this[Header.ContentRange]; set => this[Header.ContentRange] = value; }
///
/// Gets or sets the value of the HTTP Content-Type header.
@@ -172,7 +392,7 @@ public override string ToString()
///
/// Note: setting the value of this header, the value present in the response's will be overwritten.
///
- public string? ContentType { get => base[Header.ContentType]; set => base[Header.ContentType] = value; }
+ public string? ContentType { get => this[Header.ContentType]; set => this[Header.ContentType] = value; }
///
/// Gets or sets the value of the HTTP Cookie header.
@@ -181,121 +401,117 @@ public override string ToString()
/// Tip: use property to getting cookies values from requests and
/// on to set cookies.
///
- public string? Cookie { get => base[Header.Cookie]; set => base[Header.Cookie] = value; }
+ public string? Cookie { get => this[Header.Cookie]; set => this[Header.Cookie] = value; }
///
/// Gets or sets the value of the HTTP Expect header.
///
- public string? Expect { get => base[Header.Expect]; set => base[Header.Expect] = value; }
+ public string? Expect { get => this[Header.Expect]; set => this[Header.Expect] = value; }
///
/// Gets or sets the value of the HTTP Expires header.
///
- public string? Expires { get => base[Header.Expires]; set => base[Header.Expires] = value; }
+ public string? Expires { get => this[Header.Expires]; set => this[Header.Expires] = value; }
///
/// Gets or sets the value of the HTTP Host header.
///
- public string? Host { get => base[Header.Host]; set => base[Header.Host] = value; }
+ public string? Host { get => this[Header.Host]; set => this[Header.Host] = value; }
///
/// Gets or sets the value of the HTTP Origin header.
///
- public string? Origin { get => base[Header.Origin]; set => base[Header.Origin] = value; }
+ public string? Origin { get => this[Header.Origin]; set => this[Header.Origin] = value; }
///
/// Gets or sets the value of the HTTP Range header.
///
- public string? Range { get => base[Header.Range]; set => base[Header.Range] = value; }
+ public string? Range { get => this[Header.Range]; set => this[Header.Range] = value; }
///
/// Gets or sets the value of the HTTP Referer header.
///
- public string? Referer { get => base[Header.Referer]; set => base[Header.Referer] = value; }
+ public string? Referer { get => this[Header.Referer]; set => this[Header.Referer] = value; }
///
/// Gets or sets the value of the HTTP Retry-After header.
///
- public string? RetryAfter { get => base[Header.RetryAfter]; set => base[Header.RetryAfter] = value; }
+ public string? RetryAfter { get => this[Header.RetryAfter]; set => this[Header.RetryAfter] = value; }
///
/// Gets or sets the value of the HTTP If-Match header.
///
- public string? IfMatch { get => base[Header.IfMatch]; set => base[Header.IfMatch] = value; }
+ public string? IfMatch { get => this[Header.IfMatch]; set => this[Header.IfMatch] = value; }
///
/// Gets or sets the value of the HTTP If-None-Match header.
///
- public string? IfNoneMatch { get => base[Header.IfNoneMatch]; set => base[Header.IfNoneMatch] = value; }
+ public string? IfNoneMatch { get => this[Header.IfNoneMatch]; set => this[Header.IfNoneMatch] = value; }
///
/// Gets or sets the value of the HTTP If-Range header.
///
- public string? IfRange { get => base[Header.IfRange]; set => base[Header.IfRange] = value; }
+ public string? IfRange { get => this[Header.IfRange]; set => this[Header.IfRange] = value; }
///
/// Gets or sets the value of the HTTP If-Modified-Since header.
///
- public string? IfModifiedSince { get => base[Header.IfModifiedSince]; set => base[Header.IfModifiedSince] = value; }
+ public string? IfModifiedSince { get => this[Header.IfModifiedSince]; set => this[Header.IfModifiedSince] = value; }
///
/// Gets or sets the value of the HTTP If-Unmodified-Since header.
///
- public string? IfUnmodifiedSince { get => base[Header.IfUnmodifiedSince]; set => base[Header.IfUnmodifiedSince] = value; }
+ public string? IfUnmodifiedSince { get => this[Header.IfUnmodifiedSince]; set => this[Header.IfUnmodifiedSince] = value; }
///
/// Gets or sets the value of the HTTP Max-Forwards header.
///
- public string? MaxForwards { get => base[Header.MaxForwards]; set => base[Header.MaxForwards] = value; }
+ public string? MaxForwards { get => this[Header.MaxForwards]; set => this[Header.MaxForwards] = value; }
///
/// Gets or sets the value of the HTTP Pragma header.
///
- public string? Pragma { get => base[Header.Pragma]; set => base[Header.Pragma] = value; }
+ public string? Pragma { get => this[Header.Pragma]; set => this[Header.Pragma] = value; }
///
/// Gets or sets the value of the HTTP Proxy-Authorization header.
///
- public string? ProxyAuthorization { get => base[Header.ProxyAuthorization]; set => base[Header.ProxyAuthorization] = value; }
+ public string? ProxyAuthorization { get => this[Header.ProxyAuthorization]; set => this[Header.ProxyAuthorization] = value; }
///
/// Gets or sets the value of the HTTP TE header.
///
- public string? TE { get => base[Header.TE]; set => base[Header.TE] = value; }
+ public string? TE { get => this[Header.TE]; set => this[Header.TE] = value; }
///
/// Gets or sets the value of the HTTP Trailer header.
///
- public string? Trailer { get => base[Header.Trailer]; set => base[Header.Trailer] = value; }
+ public string? Trailer { get => this[Header.Trailer]; set => this[Header.Trailer] = value; }
///
/// Gets or sets the value of the HTTP Via header.
///
- public string? Via { get => base[Header.Via]; set => base[Header.Via] = value; }
+ public string? Via { get => this[Header.Via]; set => this[Header.Via] = value; }
///
/// Gets or sets the value of the HTTP Set-Cookie header.
///
- ///
- /// Tip: use property to getting cookies values from requests and
- /// on to set cookies.
- ///
- public string? SetCookie { get => base[Header.SetCookie]; set => base[Header.SetCookie] = value; }
+ public string? SetCookie { get => this[Header.SetCookie]; set => this[Header.SetCookie] = value; }
///
/// Gets or sets the value of the HTTP User-Agent header.
///
- public string? UserAgent { get => base[Header.UserAgent]; set => base[Header.UserAgent] = value; }
+ public string? UserAgent { get => this[Header.UserAgent]; set => this[Header.UserAgent] = value; }
///
/// Gets or sets the value of the HTTP Vary header.
///
- public string? Vary { get => base[Header.Vary]; set => base[Header.Vary] = value; }
+ public string? Vary { get => this[Header.Vary]; set => this[Header.Vary] = value; }
///
/// Gets or sets the value of the HTTP WWW-Authenticate header.
///
- public string? WWWAuthenticate { get => base[Header.WWWAuthenticate]; set => base[Header.WWWAuthenticate] = value; }
+ public string? WWWAuthenticate { get => this[Header.WWWAuthenticate]; set => this[Header.WWWAuthenticate] = value; }
///
/// Gets or sets the value of the HTTP X-Forwarded-For header.
@@ -303,7 +519,7 @@ public override string ToString()
///
/// Tip: enable the property to obtain the user client proxied IP throught .
///
- public string? XForwardedFor { get => base[Header.XForwardedFor]; set => base[Header.XForwardedFor] = value; }
+ public string? XForwardedFor { get => this[Header.XForwardedFor]; set => this[Header.XForwardedFor] = value; }
///
/// Gets or sets the value of the HTTP X-Forwarded-Host header.
@@ -311,6 +527,46 @@ public override string ToString()
///
/// Tip: enable the property to obtain the client requested host throught .
///
- public string? XForwardedHost { get => base[Header.XForwardedHost]; set => base[Header.XForwardedHost] = value; }
+ public string? XForwardedHost { get => this[Header.XForwardedHost]; set => this[Header.XForwardedHost] = value; }
+ ///
+ public ICollection Keys
+ {
+ get
+ {
+ List headerNames = new List(_headers.Count);
+ for (int i = 0; i < _headers.Count; i++)
+ {
+ headerNames.Add(_headers[i].headerName);
+ }
+ return headerNames;
+ }
+ }
+
+ ///
+ public ICollection Values
+ {
+ get
+ {
+ List headerValues = new List(_headers.Count);
+ for (int i = 0; i < _headers.Count; i++)
+ {
+ headerValues.Add(_headers[i].headerValue);
+ }
+ return headerValues;
+ }
+ }
+
+ ///
+ public int Count => _headers.Count;
+
+ ///
+ public bool IsReadOnly => isReadOnly;
+ #endregion
+
+ void ThrowIfReadOnly()
+ {
+ if (isReadOnly)
+ throw new InvalidOperationException(SR.Collection_ReadOnly);
+ }
}
diff --git a/src/Entity/MultipartObject.cs b/src/Entity/MultipartObject.cs
index 947957a..1c93b55 100644
--- a/src/Entity/MultipartObject.cs
+++ b/src/Entity/MultipartObject.cs
@@ -81,7 +81,7 @@ public MultipartObjectCommonFormat GetCommonFileFormat()
{
Span len8 = ContentBytes.AsSpan(0, 8);
- if (len8.SequenceEqual(new byte[] { 137, 80, 78, 71, 13, 10, 26, 10 }))
+ if (len8.SequenceEqual(MultipartObjectCommonFormatByteMark.PNG))
{
return MultipartObjectCommonFormat.PNG;
}
@@ -90,39 +90,40 @@ public MultipartObjectCommonFormat GetCommonFileFormat()
{
Span len4 = ContentBytes.AsSpan(0, 4);
- if (len4.SequenceEqual(new byte[] { (byte)'R', (byte)'I', (byte)'F', (byte)'F' }))
+ if (len4.SequenceEqual(MultipartObjectCommonFormatByteMark.WEBP))
{
return MultipartObjectCommonFormat.WEBP;
}
- else if (len4.SequenceEqual(new byte[] { 0x25, 0x50, 0x44, 0x46 }))
+ else if (len4.SequenceEqual(MultipartObjectCommonFormatByteMark.PDF))
{
return MultipartObjectCommonFormat.PDF;
}
+ else if (len4.SequenceEqual(MultipartObjectCommonFormatByteMark.TIFF))
+ {
+ return MultipartObjectCommonFormat.TIFF;
+ }
}
if (byteLen >= 3)
{
Span len3 = ContentBytes.AsSpan(0, 3);
- if (len3.SequenceEqual(new byte[] { 0xFF, 0xD8, 0xFF }))
+ if (len3.SequenceEqual(MultipartObjectCommonFormatByteMark.JPEG))
{
return MultipartObjectCommonFormat.JPEG;
}
- else if (len3.SequenceEqual(new byte[] { 73, 73, 42 }))
- {
- return MultipartObjectCommonFormat.TIFF;
- }
- else if (len3.SequenceEqual(new byte[] { 77, 77, 42 }))
+ else if (len3.SequenceEqual(MultipartObjectCommonFormatByteMark.GIF))
{
- return MultipartObjectCommonFormat.TIFF;
+ return MultipartObjectCommonFormat.GIF;
}
- else if (len3.SequenceEqual(new byte[] { 0x42, 0x4D }))
+ }
+ if (byteLen >= 2)
+ {
+ Span len2 = ContentBytes.AsSpan(0, 2);
+
+ if (len2.SequenceEqual(MultipartObjectCommonFormatByteMark.BMP))
{
return MultipartObjectCommonFormat.BMP;
}
- else if (len3.SequenceEqual(new byte[] { 0x47, 0x46, 0x49 }))
- {
- return MultipartObjectCommonFormat.GIF;
- }
}
return MultipartObjectCommonFormat.Unknown;
@@ -130,7 +131,7 @@ public MultipartObjectCommonFormat GetCommonFileFormat()
internal MultipartObject(NameValueCollection headers, string? filename, string name, byte[]? body, Encoding encoding)
{
- Headers = new HttpHeaderCollection(headers);
+ Headers = new HttpHeaderCollection(headers) { isReadOnly = true };
Filename = filename;
Name = name;
ContentBytes = body ?? Array.Empty();
@@ -314,50 +315,4 @@ bool Equals(byte[] source, byte[] separator, int index)
return new MultipartFormCollection(outputObjects);
}
}
-
- ///
- /// Represents an image format for Multipart objects.
- ///
- public enum MultipartObjectCommonFormat
- {
- ///
- /// Represents that the object is not a recognized image.
- ///
- Unknown = 0,
-
- ///
- /// Represents an JPEG/JPG image.
- ///
- JPEG = 100,
-
- ///
- /// Represents an GIF image.
- ///
- GIF = 101,
-
- ///
- /// Represents an PNG image.
- ///
- PNG = 102,
-
- ///
- /// Represents an TIFF image.
- ///
- TIFF = 103,
-
- ///
- /// Represents an bitmap image.
- ///
- BMP = 104,
-
- ///
- /// Represents an WebP image.
- ///
- WEBP = 105,
-
- ///
- /// Represents an PDF file.
- ///
- PDF = 200
- }
}
diff --git a/src/Entity/MultipartObjectCommonFormat.cs b/src/Entity/MultipartObjectCommonFormat.cs
new file mode 100644
index 0000000..b191652
--- /dev/null
+++ b/src/Entity/MultipartObjectCommonFormat.cs
@@ -0,0 +1,56 @@
+// The Sisk Framework source code
+// Copyright (c) 2023 PROJECT PRINCIPIUM
+//
+// The code below is licensed under the MIT license as
+// of the date of its publication, available at
+//
+// File name: MultipartObjectCommonFormat.cs
+// Repository: https://github.com/sisk-http/core
+
+namespace Sisk.Core.Entity;
+
+///
+/// Represents an image format for Multipart objects.
+///
+public enum MultipartObjectCommonFormat
+{
+ ///
+ /// Represents that the object is not a recognized image.
+ ///
+ Unknown = 0,
+
+ ///
+ /// Represents an JPEG/JPG image.
+ ///
+ JPEG = 100,
+
+ ///
+ /// Represents an GIF image.
+ ///
+ GIF = 101,
+
+ ///
+ /// Represents an PNG image.
+ ///
+ PNG = 102,
+
+ ///
+ /// Represents an TIFF image.
+ ///
+ TIFF = 103,
+
+ ///
+ /// Represents an bitmap image.
+ ///
+ BMP = 104,
+
+ ///
+ /// Represents an WebP image.
+ ///
+ WEBP = 105,
+
+ ///
+ /// Represents an PDF file.
+ ///
+ PDF = 200
+}
diff --git a/src/Entity/MultipartObjectCommonFormatByteMark.cs b/src/Entity/MultipartObjectCommonFormatByteMark.cs
new file mode 100644
index 0000000..59f2d9e
--- /dev/null
+++ b/src/Entity/MultipartObjectCommonFormatByteMark.cs
@@ -0,0 +1,25 @@
+// The Sisk Framework source code
+// Copyright (c) 2023 PROJECT PRINCIPIUM
+//
+// The code below is licensed under the MIT license as
+// of the date of its publication, available at
+//
+// File name: MultipartObjectCommonFormatByteMark.cs
+// Repository: https://github.com/sisk-http/core
+
+namespace Sisk.Core.Entity;
+
+internal static class MultipartObjectCommonFormatByteMark
+{
+ public static readonly byte[] PNG = new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
+
+ public static readonly byte[] PDF = new byte[] { 0x25, 0x50, 0x44, 0x46 };
+ public static readonly byte[] WEBP = new byte[] { 0x52, 0x49, 0x46, 0x46 };
+ public static readonly byte[] TIFF = new byte[] { 0x4D, 0x4D, 0x00, 0x2A };
+ public static readonly byte[] WEBM = new byte[] { 0x1A, 0x45, 0xDF, 0xA3 };
+
+ public static readonly byte[] JPEG = new byte[] { 0xFF, 0xD8, 0xFF };
+ public static readonly byte[] GIF = new byte[] { 0x47, 0x46, 0x49 };
+
+ public static readonly byte[] BMP = new byte[] { 0x42, 0x4D };
+}
diff --git a/src/Entity/StringValueCollection.cs b/src/Entity/StringValueCollection.cs
index 573ac23..7247623 100644
--- a/src/Entity/StringValueCollection.cs
+++ b/src/Entity/StringValueCollection.cs
@@ -74,9 +74,9 @@ public NameValueCollection AsNameValueCollection()
{
NameValueCollection n = new NameValueCollection();
- foreach (string key in items.Keys)
+ foreach (var item in items)
{
- n.Add(key, items[key]);
+ n.Add(item.Key, item.Value);
}
return n;
diff --git a/src/Http/Handlers/HttpServerHandlerRepository.cs b/src/Http/Handlers/HttpServerHandlerRepository.cs
index 9687c4a..8878479 100644
--- a/src/Http/Handlers/HttpServerHandlerRepository.cs
+++ b/src/Http/Handlers/HttpServerHandlerRepository.cs
@@ -16,7 +16,7 @@ namespace Sisk.Core.Http.Handlers;
internal class HttpServerHandlerRepository
{
private readonly HttpServer parent;
- private readonly List handlers = new List();
+ private readonly List handlers = new List(20);
internal readonly DefaultHttpServerHandler _default = new DefaultHttpServerHandler();
public HttpServerHandlerRepository(HttpServer parent)
diff --git a/src/Http/HttpContext.cs b/src/Http/HttpContext.cs
index 9046f73..c4774ad 100644
--- a/src/Http/HttpContext.cs
+++ b/src/Http/HttpContext.cs
@@ -7,6 +7,7 @@
// File name: HttpContext.cs
// Repository: https://github.com/sisk-http/core
+using Sisk.Core.Entity;
using Sisk.Core.Routing;
using System.Collections.Specialized;
@@ -21,7 +22,7 @@ public sealed class HttpContext
/// Gets or sets an indicating HTTP headers which
/// will overwrite headers set by CORS, router response or request handlers.
///
- public NameValueCollection OverrideHeaders { get; set; } = new NameValueCollection();
+ public HttpHeaderCollection OverrideHeaders { get; set; } = new HttpHeaderCollection();
///
/// Gets the instance of this HTTP context.
diff --git a/src/Http/HttpRequest.cs b/src/Http/HttpRequest.cs
index 1982129..8014034 100644
--- a/src/Http/HttpRequest.cs
+++ b/src/Http/HttpRequest.cs
@@ -151,8 +151,10 @@ public HttpHeaderCollection Headers
}
else
{
- headers = new HttpHeaderCollection(listenerRequest.Headers);
+ headers = new HttpHeaderCollection((WebHeaderCollection)listenerRequest.Headers);
}
+
+ headers.isReadOnly = true;
}
return headers;
@@ -367,10 +369,9 @@ public string GetRawHttpRequest(bool includeBody = true, bool appendExtraInfo =
sb.AppendLine($":request-id: {RequestId}");
sb.AppendLine($":request-proto: {(IsSecure ? "https" : "http")}");
}
- foreach (string hName in Headers)
+ foreach (var header in Headers)
{
- string hValue = Headers[hName]!;
- sb.AppendLine($"{hName}: {hValue}");
+ sb.AppendLine($"{header.Key}: {header.Value}");
}
sb.AppendLine();
diff --git a/src/Http/HttpResponse.cs b/src/Http/HttpResponse.cs
index 41928c9..48cedee 100644
--- a/src/Http/HttpResponse.cs
+++ b/src/Http/HttpResponse.cs
@@ -102,10 +102,9 @@ public string GetRawHttpResponse(bool includeBody = true)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"HTTP/1.1 {StatusInformation}");
- foreach (string header in Headers)
+ foreach (var header in Headers)
{
- sb.Append(header + ": ");
- sb.Append(Headers[header]);
+ sb.Append($"{header.Key}: {header.Value}");
sb.Append('\n');
}
if (Content?.Headers is not null)
@@ -167,7 +166,7 @@ public HttpResponse WithContent(HttpContent content)
/// The self object.
public HttpResponse WithHeader(string headerKey, string headerValue)
{
- Headers.Set(headerKey, headerValue);
+ Headers.Add(headerKey, headerValue);
return this;
}
@@ -179,7 +178,7 @@ public HttpResponse WithHeader(string headerKey, string headerValue)
public HttpResponse WithHeader(NameValueCollection headers)
{
foreach (string key in headers.Keys)
- Headers.Set(key, headers[key]);
+ Headers.Add(key, headers[key]);
return this;
}
@@ -286,7 +285,7 @@ public HttpResponse(HttpStatusCode status, HttpContent? content)
///
protected override void SetCookieHeader(String name, String value)
{
- Headers.Set(name, value);
+ Headers.Add(name, value);
}
}
}
diff --git a/src/Http/HttpServer__Core.cs b/src/Http/HttpServer__Core.cs
index 19298d5..705eb6f 100644
--- a/src/Http/HttpServer__Core.cs
+++ b/src/Http/HttpServer__Core.cs
@@ -10,7 +10,6 @@
using Sisk.Core.Entity;
using Sisk.Core.Internal;
using Sisk.Core.Routing;
-using System.Collections.Specialized;
using System.Diagnostics;
using System.Net;
using System.Runtime.CompilerServices;
@@ -308,23 +307,25 @@ private void ProcessRequest(HttpListenerContext context)
#endregion
#region Step 4 - Response computing
- NameValueCollection resHeaders = new NameValueCollection
- {
- response.Headers
- };
+ HttpHeaderCollection resHeaders = response.Headers;
+ HttpHeaderCollection overrideHeaders = srContext.OverrideHeaders;
long? responseContentLength = response.Content?.Headers.ContentLength;
- if (srContext?.OverrideHeaders.Count > 0) resHeaders.Add(srContext.OverrideHeaders);
+ if (overrideHeaders.Count > 0)
+ {
+ for (int i = 0; i < overrideHeaders.Count; i++)
+ {
+ var overridingHeader = overrideHeaders[i];
+ resHeaders.Set(overridingHeader.Item1, overridingHeader.Item2);
+ }
+ }
for (int i = 0; i < resHeaders.Count; i++)
{
- string? incameHeader = resHeaders.Keys[i];
- if (string.IsNullOrEmpty(incameHeader)) continue;
-
- string? value = resHeaders[incameHeader];
- if (string.IsNullOrEmpty(value)) continue;
+ (string, string) incameHeader = resHeaders[i];
+ if (string.IsNullOrEmpty(incameHeader.Item1)) continue;
- baseResponse.Headers[incameHeader] = value;
+ baseResponse.Headers.Add(incameHeader.Item1, incameHeader.Item2);
}
_debugState = "sent_headers";
diff --git a/src/Internal/LoggingConstants.cs b/src/Internal/LoggingConstants.cs
index 566b191..3d8309a 100644
--- a/src/Internal/LoggingConstants.cs
+++ b/src/Internal/LoggingConstants.cs
@@ -22,7 +22,7 @@ internal class LoggingFormatter
readonly DateTime d;
readonly Uri? bReqUri;
readonly IPAddress? bReqIpAddr;
- readonly NameValueCollection? reqHeaders;
+ readonly IDictionary? reqHeaders;
readonly int bResStatusCode;
readonly string? bResStatusDescr;
readonly string bReqMethod;
@@ -34,7 +34,7 @@ public LoggingFormatter(
HttpServerExecutionResult res,
DateTime d, Uri? bReqUri,
IPAddress? bReqIpAddr,
- NameValueCollection? reqHeaders,
+ IDictionary? reqHeaders,
int bResStatusCode,
string? bResStatusDescr,
long execTime,
diff --git a/src/Internal/SR.cs b/src/Internal/SR.cs
index 93bda01..e908585 100644
--- a/src/Internal/SR.cs
+++ b/src/Internal/SR.cs
@@ -89,6 +89,8 @@ static partial class SR
public const string ValueItem_ValueNull = "The value \"{0}\" contained at this {1} is null or it's undefined.";
public const string ValueItem_CastException = "Cannot cast the value \"{0}\" at parameter {1} into an {2}.";
+ public const string Collection_ReadOnly = "Cannot insert items to this collection as it is read-only.";
+
public static string Format(string format, params object?[] items)
{
return String.Format(format, items);
diff --git a/src/Sisk.Core.csproj b/src/Sisk.Core.csproj
index 571d861..ee5b789 100644
--- a/src/Sisk.Core.csproj
+++ b/src/Sisk.Core.csproj
@@ -31,7 +31,7 @@
1.0.0.0
1.0.0.0
- 1.0.0.0-rc4
+ 1.0.0.0-rc5
LICENSE.txt
False