namespace Uwaa.HTTP; /// /// General purpose implementation of . /// 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; } /// /// Extra fields to include. /// public readonly Dictionary Misc = new Dictionary(StringComparer.OrdinalIgnoreCase); /// /// Sets a field. The string will be parsed for non-string fields like Accept. /// 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; } } } /// /// Generates strings for the fields and sends them to the provided callback. /// 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, }