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