//------------------------------------------------------------------------------ // 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 // 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 // CSDN博客:https://blog.csdn.net/qq_40374647 // 哔哩哔哩视频:https://space.bilibili.com/94253567 // Gitee源代码仓库:https://gitee.com/RRQM_Home // Github源代码仓库:https://github.com/RRQM // API首页:https://www.yuque.com/rrqm/touchsocket/index // 交流QQ群:234762506 // 感谢您的下载和使用 //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ using System; using System.Linq; using System.Text; using System.Text.RegularExpressions; using TouchSocket.Core; using TouchSocket.Sockets; namespace TouchSocket.Http { /// /// Http响应 /// public class HttpResponse : HttpBase { private bool m_canRead; private bool m_canWrite; private ITcpClientBase m_client; private byte[] m_content; private bool m_responsed; private bool m_sentHeader; private long m_sentLength; /// /// 构造函数 /// /// /// public HttpResponse(ITcpClientBase client, bool isServer = true) { m_client = client; if (isServer) { m_canRead = false; m_canWrite = true; } else { m_canRead = true; m_canWrite = false; } } /// /// 构造函数 /// public HttpResponse() { m_canRead = false; m_canWrite = false; } /// /// /// public override bool CanRead => m_canRead; /// /// /// public override bool CanWrite => m_canWrite; /// /// /// public override ITcpClientBase Client => m_client; /// /// 关闭会话请求 /// public bool CloseConnection { get { return GetHeader(HttpHeaders.Connection).Equals("close", StringComparison.CurrentCultureIgnoreCase); } } /// /// 是否分块 /// public bool IsChunk { get; set; } /// /// 是否代理权限验证。 /// public bool IsProxyAuthenticationRequired { get { return StatusCode == "407"; } } /// /// 是否重定向 /// public bool IsRedirect { get { return StatusCode == "301" || StatusCode == "302"; } } /// /// 是否已经响应数据。 /// public bool Responsed => m_responsed; /// /// 状态码,默认200 /// public string StatusCode { get; set; } = "200"; /// /// 状态消息,默认Success /// public string StatusMessage { get; set; } = "Success"; /// /// 构建数据并回应。 /// 该方法仅在具有Client实例时有效。 /// public void Answer() { if (m_responsed) { return; } using (ByteBlock byteBlock = new ByteBlock()) { Build(byteBlock); if (m_client.CanSend) { m_client.DefaultSend(byteBlock); } m_responsed = true; } } /// /// 构建响应数据。 /// 当数据较大时,不建议这样操作,可直接 /// /// /// public void Build(ByteBlock byteBlock, bool responsed = true) { if (m_responsed) { throw new Exception("该对象已被响应。"); } BuildHeader(byteBlock); BuildContent(byteBlock); m_responsed = responsed; } /// /// 构建数据为字节数组。 /// /// public byte[] BuildAsBytes() { using (ByteBlock byteBlock = new ByteBlock()) { Build(byteBlock); return byteBlock.ToArray(); } } /// /// 当传输模式是Chunk时,用于结束传输。 /// public void Complete() { m_canWrite = false; if (IsChunk) { using (ByteBlock byteBlock = new ByteBlock()) { byteBlock.Write(Encoding.UTF8.GetBytes($"{0:X}\r\n")); byteBlock.Write(Encoding.UTF8.GetBytes("\r\n")); m_client.DefaultSend(byteBlock); m_responsed = true; } } } /// /// /// /// public override void SetContent(byte[] content) { m_content = content; ContentLength = content.Length; ContentComplated = true; } /// /// /// /// public override bool TryGetContent(out byte[] content) { if (!ContentComplated.HasValue) { if (!IsChunk && m_contentLength == 0) { m_content = new byte[0]; content = m_content; return true; } try { using (ByteBlock block1 = new ByteBlock(1024 * 1024)) { using (ByteBlock block2 = new ByteBlock()) { byte[] buffer = block2.Buffer; while (true) { int r = Read(buffer, 0, buffer.Length); if (r == 0) { break; } block1.Write(buffer, 0, r); } ContentComplated = true; m_content = block1.ToArray(); content = m_content; return true; } } } catch { ContentComplated = false; content = null; return false; } finally { m_canRead = false; } } else if (ContentComplated == true) { content = m_content; return true; } else { content = null; return false; } } /// /// /// /// /// /// public override void WriteContent(byte[] buffer, int offset, int count) { if (m_responsed) { throw new Exception("该对象已被响应。"); } if (!CanWrite) { throw new NotSupportedException("该对象不支持持续写入内容。"); } if (!m_sentHeader) { using (ByteBlock byteBlock = new ByteBlock()) { BuildHeader(byteBlock); m_client.DefaultSend(byteBlock); } m_sentHeader = true; } if (IsChunk) { using (ByteBlock byteBlock = new ByteBlock(count + 1024)) { byteBlock.Write(Encoding.UTF8.GetBytes($"{count.ToString("X")}\r\n")); byteBlock.Write(buffer, offset, count); byteBlock.Write(Encoding.UTF8.GetBytes("\r\n")); m_client.DefaultSend(byteBlock); m_sentLength += count; } } else { if (m_sentLength + count <= m_contentLength) { m_client.DefaultSend(buffer, offset, count); m_sentLength += count; if (m_sentLength == ContentLength) { m_canWrite = false; m_responsed = true; } } } } /// /// /// /// protected override void Dispose(bool disposing) { m_client = null; base.Dispose(disposing); } /// /// 读取数据 /// protected override void LoadHeaderProterties() { var first = Regex.Split(RequestLine, @"(\s+)").Where(e => e.Trim() != string.Empty).ToArray(); if (first.Length > 0) { string[] ps = first[0].Split('/'); if (ps.Length == 2) { Protocols = ps[0]; ProtocolVersion = ps[1]; } } if (first.Length > 1) { StatusCode = first[1]; } string msg = string.Empty; for (int i = 2; i < first.Length; i++) { msg += first[i] + " "; } StatusMessage = msg; string transferEncoding = GetHeader(HttpHeaders.TransferEncoding); if ("chunked".Equals(transferEncoding, StringComparison.OrdinalIgnoreCase)) { IsChunk = true; } } private void BuildContent(ByteBlock byteBlock) { if (ContentLength > 0) { byteBlock.Write(m_content); } } /// /// 构建响应头部 /// /// private void BuildHeader(ByteBlock byteBlock) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append($"HTTP/{ProtocolVersion} {StatusCode} {StatusMessage}\r\n"); if (ContentLength > 0) { this.SetHeader(HttpHeaders.ContentLength, ContentLength.ToString()); } if (IsChunk) { this.SetHeader(HttpHeaders.TransferEncoding, "chunked"); } foreach (var headerkey in Headers.AllKeys) { stringBuilder.Append($"{headerkey}: "); stringBuilder.Append(Headers[headerkey] + "\r\n"); } stringBuilder.Append("\r\n"); byteBlock.Write(Encoding.UTF8.GetBytes(stringBuilder.ToString())); } } }