You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
129 lines
4.0 KiB
129 lines
4.0 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using System.Text;
|
|
|
|
namespace Apewer.Web
|
|
{
|
|
|
|
/// <summary>响应。</summary>
|
|
public sealed class MiniResponse
|
|
{
|
|
|
|
bool _disposed = false;
|
|
|
|
/// <summary>上下文。</summary>
|
|
public MiniContext Context { get; private set; }
|
|
|
|
/// <summary>保持连接。</summary>
|
|
internal bool KeepAlive { get; set; }
|
|
|
|
/// <summary>HTTP 状态码。</summary>
|
|
/// <remarks>默认值:200</remarks>
|
|
public int Status { get; set; }
|
|
|
|
/// <summary>头。</summary>
|
|
public StringPairs Headers { get; set; }
|
|
|
|
/// <summary>内容类型。</summary>
|
|
public string ContentType { get; set; }
|
|
|
|
/// <summary>内容长度。</summary>
|
|
public long ContentLength { get; set; }
|
|
|
|
/// <summary>重定向地址。</summary>
|
|
internal string Location { get; set; }
|
|
|
|
/// <summary></summary>
|
|
public Stream Body { get => Context.Connection.GetResponseStream(); }
|
|
|
|
internal MiniResponse(MiniContext context)
|
|
{
|
|
Context = context;
|
|
Headers = new StringPairs();
|
|
ContentLength = -1;
|
|
Status = 200;
|
|
}
|
|
|
|
/// <summary></summary>
|
|
public Json ToJson()
|
|
{
|
|
var json = new Json();
|
|
json.SetProperty("Status", Status);
|
|
json.SetProperty("Location", Location);
|
|
json.SetProperty("ContentType", ContentType);
|
|
json.SetProperty("ContentLength", ContentLength);
|
|
json.SetProperty("Headers", Json.From(Headers));
|
|
return json;
|
|
}
|
|
|
|
/// <summary>重定向到指定的 URL。</summary>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
/// <exception cref="ArgumentException"></exception>
|
|
public void Redirect(string url, int status)
|
|
{
|
|
if (url == null) throw new ArgumentNullException(nameof(url));
|
|
url = TextUtility.Trim(url);
|
|
if (url.IsEmpty()) throw new ArgumentException("参数 url 为空。");
|
|
Status = 302;
|
|
Location = url;
|
|
}
|
|
|
|
void Close(bool force)
|
|
{
|
|
if (_disposed) return;
|
|
_disposed = true;
|
|
Context.Connection.Close(force);
|
|
}
|
|
|
|
/// <summary>关闭连接。</summary>
|
|
public void Close() => Close(false);
|
|
|
|
#region cookies
|
|
|
|
/// <summary>生成 Cookie 的头字段。</summary>
|
|
public static string Format(Cookie cookie)
|
|
{
|
|
if (cookie == null) return null;
|
|
if (string.IsNullOrEmpty(cookie.Name)) return null;
|
|
|
|
var segs = new List<string>(4);
|
|
if (cookie.Version > 0) segs.Add("Version=" + cookie.Version.ToString());
|
|
if (!string.IsNullOrEmpty(cookie.Name)) segs.Add(cookie.Name + "=" + (cookie.Value ?? ""));
|
|
if (!string.IsNullOrEmpty(cookie.Path)) segs.Add("Path=" + cookie.Path);
|
|
if (!string.IsNullOrEmpty(cookie.Domain)) segs.Add("Domain=" + cookie.Domain);
|
|
if (!string.IsNullOrEmpty(cookie.Port)) segs.Add("Port=" + cookie.Port);
|
|
|
|
if (segs.Count < 1) return null;
|
|
var text = string.Join(";", segs.ToArray());
|
|
return text;
|
|
}
|
|
|
|
static string QuotedString(Cookie cookie, string value)
|
|
{
|
|
if (cookie.Version == 0 || IsToken(value)) return value;
|
|
else return "\"" + value.Replace("\"", "\\\"") + "\"";
|
|
}
|
|
|
|
static bool IsToken(string value)
|
|
{
|
|
// from RFC 2965, 2068
|
|
const string tspecials = "()<>@,;:\\\"/[]?={} \t";
|
|
|
|
int len = value.Length;
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
char c = value[i];
|
|
if (c < 0x20 || c >= 0x7f || tspecials.IndexOf(c) != -1) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
}
|
|
|