Uwaa/HTTP/HttpFields.cs
2024-12-15 08:02:08 +00:00

188 lines
No EOL
5.2 KiB
C#

namespace Uwaa.HTTP;
/// <summary>
/// General purpose implementation of <see cref="IHttpFields"/>.
/// </summary>
public record HttpFields
{
public MIMEType[]? Accept { get; set; }
public ConnectionType? Connection { get; set; }
public string? Host { get; set; }
public string? Upgrade { get; set; }
public string? UserAgent { get; set; }
public string? Authorization { get; set; }
public string? WebSocketKey { get; set; }
public string? WebSocketAccept { get; set; }
public string? WebSocketProtocol { get; set; }
public string? Location { get; set; }
public string? Referrer { get; set; }
public int? ContentLength { get; set; }
public MIMEType? ContentType { get; set; }
/// <summary>
/// Extra fields to include.
/// </summary>
public readonly Dictionary<string, string> Misc = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Sets a field. The string will be parsed for non-string fields like Accept.
/// </summary>
public virtual string? this[string key]
{
set
{
switch (key.ToLowerInvariant())
{
case "accept":
Accept = value == null ? null : HttpHelpers.ParseAccept(value);
return;
case "connection":
{
if (Enum.TryParse(value, true, out ConnectionType conType))
Connection = conType;
return;
}
case "host":
Host = value;
return;
case "upgrade":
Upgrade = value;
return;
case "user-agent":
UserAgent = value;
return;
case "authorization":
Authorization = value;
return;
case "sec-websocket-key":
WebSocketKey = value;
return;
case "sec-websocket-accept":
WebSocketAccept = value;
return;
case "sec-websocket-protocol":
WebSocketProtocol = value;
return;
case "location":
Location = value;
return;
case "referer":
case "referrer":
Referrer = value;
return;
case "content-length":
{
if (value == null)
{
ContentLength = 0;
}
else
{
if (!int.TryParse(value, out int contentLength))
throw new HttpException("Invalid Content-Length");
ContentLength = contentLength;
}
return;
}
case "content-type":
ContentType = value == null ? (MIMEType?)null: new MIMEType(value);
return;
default:
if (value == null)
Misc?.Remove(key);
else
Misc[key] = value;
return;
}
}
}
/// <summary>
/// Generates strings for the fields and sends them to the provided callback.
/// </summary>
public virtual void EmitAll(FieldCallback callback)
{
if (ContentLength.HasValue)
callback("Content-Length", ContentLength.Value.ToString());
if (ContentType.HasValue)
callback("Content-Type", ContentType.Value.ToString());
if (Accept != null)
callback("Accept", string.Join(", ", Accept));
if (Connection.HasValue)
callback("Connection", Connection.Value switch
{
ConnectionType.Close => "close",
ConnectionType.KeepAlive => "keepalive",
ConnectionType.Upgrade => "upgrade",
_ => "close",
});
if (Host != null)
callback("Host", Host);
if (UserAgent != null)
callback("User-Agent", UserAgent);
if (Authorization != null)
callback("Authorization", Authorization);
if (Upgrade != null)
callback("Upgrade", Upgrade);
if (Location != null)
callback("Location", Location);
if (Referrer != null)
callback("Referer", Referrer);
if (WebSocketKey != null)
callback("Sec-WebSocket-Key", WebSocketKey);
if (WebSocketAccept != null)
callback("Sec-WebSocket-Accept", WebSocketAccept);
if (WebSocketProtocol != null)
callback("Sec-WebSocket-Protocol", WebSocketProtocol);
if (Misc != null)
foreach (var pair in Misc)
callback(pair.Key, pair.Value);
}
}
public delegate void FieldCallback(string name, string value);
public enum ConnectionType
{
Close,
KeepAlive,
Upgrade,
}