Files
TouchSocket/src/TouchSocket.Http/Common/TouchSocketHttpUtility.cs
若汝棋茗 d2ef2e941d 发布:4.0.2
2025-12-06 15:25:46 +08:00

212 lines
6.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//------------------------------------------------------------------------------
// 此代码版权除特别声明或在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://touchsocket.net/
// 交流QQ群234762506
// 感谢您的下载和使用
//------------------------------------------------------------------------------
using System.Runtime.CompilerServices;
namespace TouchSocket.Http;
static class TouchSocketHttpUtility
{
public const int MaxReadSize = 1024 * 1024;
public const byte COLON = (byte)':';
public const byte SPACE = (byte)' ';
public const byte TAB = (byte)'\t';
public static ReadOnlySpan<byte> CRLF => "\r\n"u8;
public static ReadOnlySpan<byte> CRLFCRLF => "\r\n\r\n"u8;
private static readonly byte[] s_http11Response = "HTTP/1.1 "u8.ToArray();
private static readonly byte[] s_http10Response = "HTTP/1.0 "u8.ToArray();
private static readonly string[] s_statusCodeCache = new string[600];
private static readonly byte[][] s_statusCodeBytesCache = new byte[600][];
static TouchSocketHttpUtility()
{
for (var i = 0; i < 600; i++)
{
s_statusCodeCache[i] = i.ToString();
s_statusCodeBytesCache[i] = Encoding.UTF8.GetBytes(s_statusCodeCache[i]);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static ReadOnlySpan<byte> GetStatusCodeBytes(int statusCode)
{
if (statusCode >= 0 && statusCode < 600)
{
return s_statusCodeBytesCache[statusCode];
}
return Encoding.UTF8.GetBytes(statusCode.ToString());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static ReadOnlySpan<byte> GetHttpVersionBytes(string version)
{
if (version == "1.1")
{
return s_http11Response.AsSpan(0, 8);
}
if (version == "1.0")
{
return s_http10Response.AsSpan(0, 8);
}
return default;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AppendAnd<TWriter>(ref TWriter writer) where TWriter : IBytesWriter
{
writer.Write("&"u8);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AppendColon<TWriter>(ref TWriter writer) where TWriter : IBytesWriter
{
writer.Write(":"u8);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AppendEqual<TWriter>(ref TWriter writer) where TWriter : IBytesWriter
{
writer.Write("="u8);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AppendHTTP<TWriter>(ref TWriter writer) where TWriter : IBytesWriter
{
writer.Write("HTTP"u8);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AppendQuestionMark<TWriter>(ref TWriter writer) where TWriter : IBytesWriter
{
writer.Write("?"u8);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AppendRn<TWriter>(ref TWriter writer) where TWriter : IBytesWriter
{
writer.Write(CRLF);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AppendSlash<TWriter>(ref TWriter writer) where TWriter : IBytesWriter
{
writer.Write("/"u8);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AppendSpace<TWriter>(ref TWriter writer) where TWriter : IBytesWriter
{
writer.Write(StringExtension.DefaultSpaceUtf8Span);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AppendUtf8String<TWriter>(ref TWriter writer, string value) where TWriter : IBytesWriter
{
WriterExtension.WriteNormalString(ref writer, value, Encoding.UTF8);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AppendHex<TWriter>(ref TWriter writer, int value) where TWriter : IBytesWriter
{
AppendUtf8String(ref writer, $"{value:X}");
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool IsWhitespace(byte b) => b == SPACE || b == TAB;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static ReadOnlySpan<byte> TrimWhitespace(ReadOnlySpan<byte> span)
{
var start = 0;
var end = span.Length - 1;
while (start <= end && IsWhitespace(span[start]))
{
start++;
}
while (end >= start && IsWhitespace(span[end]))
{
end--;
}
return start > end ? [] : span[start..(end + 1)];
}
internal static string UnescapeDataString(ReadOnlySpan<byte> urlSpan)
{
#if NET9_0_OR_GREATER
Span<char> charBuffer = stackalloc char[urlSpan.Length];
var charCount = Encoding.UTF8.GetChars(urlSpan, charBuffer);
return Uri.UnescapeDataString(charBuffer.Slice(0, charCount));
#else
return Uri.UnescapeDataString(urlSpan.ToString(Encoding.UTF8));
#endif
}
internal static string UnescapeDataString(ReadOnlySpan<char> urlSpan)
{
#if NET9_0_OR_GREATER
return Uri.UnescapeDataString(urlSpan);
#else
return Uri.UnescapeDataString(urlSpan.ToString());
#endif
}
internal static int FindNextWhitespace(ReadOnlySpan<byte> span, int start)
{
for (var i = start; i < span.Length; i++)
{
if (TouchSocketHttpUtility.IsWhitespace(span[i]))
{
return i;
}
}
return -1;
}
internal static int SkipSpaces(ReadOnlySpan<byte> span, int start)
{
while (start < span.Length && TouchSocketHttpUtility.IsWhitespace(span[start]))
{
start++;
}
return start;
}
internal static void ProcessKeyValuePair(ReadOnlySpan<char> kvSpan, InternalHttpParams parameters)
{
var eqIndex = kvSpan.IndexOf('=');
ReadOnlySpan<char> keySpan, valueSpan;
if (eqIndex >= 0)
{
keySpan = kvSpan.Slice(0, eqIndex);
valueSpan = kvSpan.Slice(eqIndex + 1);
}
else
{
keySpan = kvSpan;
valueSpan = [];
}
if (!keySpan.IsEmpty)
{
var key = TouchSocketHttpUtility.UnescapeDataString(keySpan);
var value = TouchSocketHttpUtility.UnescapeDataString(valueSpan);
parameters.Add(key, value);
}
}
}