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

3 years ago
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.Specialized;
  4. using System.IO;
  5. using System.Net;
  6. using System.Security.Cryptography.X509Certificates;
  7. using System.Text;
  8. namespace Apewer.Web
  9. {
  10. /// <summary>响应。</summary>
  11. public sealed class MiniResponse
  12. {
  13. bool _disposed = false;
  14. /// <summary>上下文。</summary>
  15. public MiniContext Context { get; private set; }
  16. /// <summary>保持连接。</summary>
  17. internal bool KeepAlive { get; set; }
  18. /// <summary>HTTP 状态码。</summary>
  19. /// <remarks>默认值:200</remarks>
  20. public int Status { get; set; }
  21. /// <summary>头。</summary>
  22. public StringPairs Headers { get; set; }
  23. /// <summary>内容类型。</summary>
  24. public string ContentType { get; set; }
  25. /// <summary>内容长度。</summary>
  26. public long ContentLength { get; set; }
  27. /// <summary>重定向地址。</summary>
  28. internal string Location { get; set; }
  29. /// <summary></summary>
  30. public Stream Body { get => Context.Connection.GetResponseStream(); }
  31. internal MiniResponse(MiniContext context)
  32. {
  33. Context = context;
  34. Headers = new StringPairs();
  35. ContentLength = -1;
  36. Status = 200;
  37. }
  38. /// <summary></summary>
  39. public Json ToJson()
  40. {
  41. var json = new Json();
  42. json.SetProperty("Status", Status);
  43. json.SetProperty("Location", Location);
  44. json.SetProperty("ContentType", ContentType);
  45. json.SetProperty("ContentLength", ContentLength);
  46. json.SetProperty("Headers", Json.From(Headers));
  47. return json;
  48. }
  49. /// <summary>重定向到指定的 URL。</summary>
  50. /// <exception cref="ArgumentNullException"></exception>
  51. /// <exception cref="ArgumentException"></exception>
  52. public void Redirect(string url, int status)
  53. {
  54. if (url == null) throw new ArgumentNullException(nameof(url));
  55. url = TextUtility.Trim(url);
  56. if (url.IsEmpty()) throw new ArgumentException("参数 url 为空。");
  57. Status = 302;
  58. Location = url;
  59. }
  60. void Close(bool force)
  61. {
  62. if (_disposed) return;
  63. _disposed = true;
  64. Context.Connection.Close(force);
  65. }
  66. /// <summary>关闭连接。</summary>
  67. public void Close() => Close(false);
  68. #region cookies
  69. /// <summary>生成 Cookie 的头字段。</summary>
  70. public static string Format(Cookie cookie)
  71. {
  72. if (cookie == null) return null;
  73. if (string.IsNullOrEmpty(cookie.Name)) return null;
  74. var segs = new List<string>(4);
  75. if (cookie.Version > 0) segs.Add("Version=" + cookie.Version.ToString());
  76. if (!string.IsNullOrEmpty(cookie.Name)) segs.Add(cookie.Name + "=" + (cookie.Value ?? ""));
  77. if (!string.IsNullOrEmpty(cookie.Path)) segs.Add("Path=" + cookie.Path);
  78. if (!string.IsNullOrEmpty(cookie.Domain)) segs.Add("Domain=" + cookie.Domain);
  79. if (!string.IsNullOrEmpty(cookie.Port)) segs.Add("Port=" + cookie.Port);
  80. if (segs.Count < 1) return null;
  81. var text = string.Join(";", segs.ToArray());
  82. return text;
  83. }
  84. static string QuotedString(Cookie cookie, string value)
  85. {
  86. if (cookie.Version == 0 || IsToken(value)) return value;
  87. else return "\"" + value.Replace("\"", "\\\"") + "\"";
  88. }
  89. static bool IsToken(string value)
  90. {
  91. // from RFC 2965, 2068
  92. const string tspecials = "()<>@,;:\\\"/[]?={} \t";
  93. int len = value.Length;
  94. for (int i = 0; i < len; i++)
  95. {
  96. char c = value[i];
  97. if (c < 0x20 || c >= 0x7f || tspecials.IndexOf(c) != -1) return false;
  98. }
  99. return true;
  100. }
  101. #endregion
  102. }
  103. }