123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- namespace OSHttpServer
- {
- /// <summary>
- /// Contains some kind of input from the browser/client.
- /// can be QueryString, form data or any other request body content.
- /// </summary>
- public class HttpInput : IHttpInput
- {
- /// <summary> Representation of a non-initialized class instance </summary>
- public static readonly HttpInput Empty = new("Empty", true);
- private readonly Dictionary<string, HttpInputItem> _items = new();
- private string _name;
- /// <summary> Variable telling the class that it is non-initialized <see cref="Empty"/> </summary>
- protected readonly bool _ignoreChanges;
- /// <summary>
- /// Initializes a new instance of the <see cref="HttpInput"/> class.
- /// </summary>
- /// <param name="name">form name.</param>
- public HttpInput(string name)
- {
- Name = name;
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="HttpInput"/> class.
- /// </summary>
- /// <param name="name">form name.</param>
- /// <param name="ignoreChanges">if set to <c>true</c> all changes will be ignored. </param>
- /// <remarks>this constructor should only be used by Empty</remarks>
- protected HttpInput(string name, bool ignoreChanges)
- {
- _name = name;
- _ignoreChanges = ignoreChanges;
- }
- /// <summary>Creates a deep copy of the HttpInput class</summary>
- /// <param name="input">The object to copy</param>
- /// <remarks>The function makes a deep copy of quite a lot which can be slow</remarks>
- protected HttpInput(HttpInput input)
- {
- foreach (HttpInputItem item in input)
- _items.Add(item.Name, new HttpInputItem(item));
- _name = input._name;
- _ignoreChanges = input._ignoreChanges;
- }
- /// <summary>
- /// Form name as lower case
- /// </summary>
- public string Name
- {
- get { return _name; }
- set { _name = value; }
- }
- /// <summary>
- /// Add a new element. Form array elements are parsed
- /// and added in a correct hierarchy.
- /// </summary>
- /// <param name="name">Name is converted to lower case.</param>
- /// <param name="value"></param>
- /// <exception cref="ArgumentNullException"><c>name</c> is null.</exception>
- /// <exception cref="InvalidOperationException">Cannot add stuff to <see cref="HttpInput.Empty"/>.</exception>
- public void Add(string name, string value)
- {
- if (name is null)
- throw new ArgumentNullException("name");
- if (_ignoreChanges)
- throw new InvalidOperationException("Cannot add stuff to HttpInput.Empty.");
- // Check if it's a sub item.
- // we can have multiple levels of sub items as in user[extension[id]] => user -> extension -> id
- int pos = name.IndexOf('[');
- if (pos != -1)
- {
- string name1 = name[..pos];
- string name2 = ExtractOne(name);
- if (!_items.TryGetValue(name1, out HttpInputItem it))
- {
- it = new HttpInputItem(name1, null);
- _items.Add(name1, it);
- }
- it.Add(name2, value);
- }
- else
- {
- if (_items.TryGetValue(name, out HttpInputItem it))
- it.Add(value);
- else
- _items.Add(name, new HttpInputItem(name, value));
- }
- }
- /// <summary>
- /// Get a form item.
- /// </summary>
- /// <param name="name"></param>
- /// <returns>Returns <see cref="HttpInputItem.Empty"/> if item was not found.</returns>
- public HttpInputItem this[string name]
- {
- get
- {
- return _items.ContainsKey(name) ? _items[name] : HttpInputItem.Empty;
- }
- }
- /// <summary>
- /// Returns true if the class contains a <see cref="HttpInput"/> with the corresponding name.
- /// </summary>
- /// <param name="name">The field/query string name</param>
- /// <returns>True if the value exists</returns>
- public bool Contains(string name)
- {
- return _items.TryGetValue(name, out HttpInputItem it) && it.Value is not null;
- }
- /// <summary>
- /// Parses an item and returns it.
- /// This function is primarily used to parse array items as in user[name].
- /// </summary>
- /// <param name="name"></param>
- /// <param name="value"></param>
- /// <returns></returns>
- public static HttpInputItem ParseItem(string name, string value)
- {
- HttpInputItem item;
- // Check if it's a sub item.
- // we can have multiple levels of sub items as in user[extension[id]]] => user -> extension -> id
- int pos = name.IndexOf('[');
- if (pos != -1)
- {
- string name1 = name[..pos];
- string name2 = ExtractOne(name);
- item = new HttpInputItem(name1, null)
- {
- { name2, value }
- };
- }
- else
- item = new HttpInputItem(name, value);
- return item;
- }
- /// <summary> Outputs the instance representing all its values joined together </summary>
- /// <returns></returns>
- public override string ToString()
- {
- string temp = string.Empty;
- foreach (KeyValuePair<string, HttpInputItem> item in _items)
- temp += item.Value.ToString(Name);
- return temp;
- }
- /// <summary>Returns all items as an unescaped query string.</summary>
- /// <returns></returns>
- public string ToString(bool asQueryString)
- {
- if (!asQueryString)
- return ToString();
- string temp = string.Empty;
- foreach (KeyValuePair<string, HttpInputItem> item in _items)
- temp += item.Value.ToString(null, true) + '&';
- return temp.Length > 0 ? temp[..^1] : string.Empty;
- }
- /// <summary>
- /// Extracts one parameter from an array
- /// </summary>
- /// <param name="value">Containing the string array</param>
- /// <returns>All but the first value</returns>
- /// <example>
- /// string test1 = ExtractOne("system[user][extension][id]");
- /// string test2 = ExtractOne(test1);
- /// string test3 = ExtractOne(test2);
- /// // test1 = user[extension][id]
- /// // test2 = extension[id]
- /// // test3 = id
- /// </example>
- public static string ExtractOne(string value)
- {
- int pos = value.IndexOf('[');
- if (pos != -1)
- {
- ++pos;
- int gotMore = value.IndexOf('[', pos + 1);
- if (gotMore != -1)
- value = string.Concat(value.AsSpan(pos, gotMore - pos - 1), value.AsSpan(gotMore));
- else
- value = value.Substring(pos, value.Length - pos - 1);
- }
- return value;
- }
- /// <summary>Resets all data contained by class</summary>
- virtual public void Clear()
- {
- _name = string.Empty;
- _items.Clear();
- }
- ///<summary>
- ///Returns an enumerator that iterates through the collection.
- ///</summary>
- ///
- ///<returns>
- ///A <see cref="T:System.Collections.Generic.IEnumerator`1"></see> that can be used to iterate through the collection.
- ///</returns>
- ///<filterpriority>1</filterpriority>
- IEnumerator<HttpInputItem> IEnumerable<HttpInputItem>.GetEnumerator()
- {
- return _items.Values.GetEnumerator();
- }
- ///<summary>
- ///Returns an enumerator that iterates through a collection.
- ///</summary>
- ///
- ///<returns>
- ///An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
- ///</returns>
- ///<filterpriority>2</filterpriority>
- public IEnumerator GetEnumerator()
- {
- return _items.Values.GetEnumerator();
- }
- }
- /// <summary>
- /// Base class for request data containers
- /// </summary>
- public interface IHttpInput : IEnumerable<HttpInputItem>
- {
- /// <summary>
- /// Adds a parameter mapped to the presented name
- /// </summary>
- /// <param name="name">The name to map the parameter to</param>
- /// <param name="value">The parameter value</param>
- void Add(string name, string value);
- /// <summary>
- /// Returns a request parameter
- /// </summary>
- /// <param name="name">The name associated with the parameter</param>
- /// <returns></returns>
- HttpInputItem this[string name]
- { get; }
- /// <summary>
- /// Returns true if the container contains the requested parameter
- /// </summary>
- /// <param name="name">Parameter id</param>
- /// <returns>True if parameter exists</returns>
- bool Contains(string name);
- }
- }
|