-
-
Save benaadams/2ac588cc3dd65088db2bb51f9e4a89e1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Buffers.Binary; | |
using System.Runtime.InteropServices; | |
using Microsoft.Extensions.Primitives; | |
public class Program | |
{ | |
static void Main() | |
{ | |
var connection = HttpParser.ParseConnection(default); | |
} | |
} | |
public class HttpParser | |
{ | |
[Flags] | |
public enum ConnectionOptions | |
{ | |
None = 0, | |
Close = 1, | |
KeepAlive = 2, | |
Upgrade = 4 | |
} | |
public static ConnectionOptions ParseConnection(StringValues connection) | |
{ | |
char lowerCaseChar = (char)0x0020; | |
ulong lowerCaseULong = 0x0020_0020_0020_0020; | |
uint lowerCaseUInt = 0x0020_0020; | |
ushort lowerCaseUShort = 0x0020; | |
// Keep-alive | |
ulong lowerCaseKeep = 0x0000_0020_0020_0020; // Don't lowercase hyphen | |
ulong keepChars = 0x002d_0070_0065_0065; // 4 chars "eep-" | |
ulong alivChars = 0x0076_0069_006c_0061; // 4 chars "aliv" | |
ushort eChars = 0x0065; // 4 chars "aliv" | |
// Upgrade | |
ulong pgraChars = 0x0061_0072_0067_0070; // 4 chars "pgra" | |
uint deChars = 0x0065_0064; // 2 chars "de" | |
// Close | |
ulong loseChars = 0x0065_0073_006f_006c; // 4 chars "lose" | |
var connectionOptions = ConnectionOptions.None; | |
var connectionCount = connection.Count; | |
for (var i = 0; i < connectionCount; i++) | |
{ | |
var value = connection[i].AsSpan(); | |
while (value.Length > 0) | |
{ | |
int offset; | |
char c = '\0'; | |
// Skip any spaces and empty values | |
for (offset = 0; offset < value.Length; offset++) | |
{ | |
c = value[offset]; | |
if (c == ' ' || c == ',') | |
{ | |
continue; | |
} | |
break; | |
} | |
if (offset == value.Length) | |
{ | |
// Consumed enitre string, move to next | |
break; | |
} | |
var potentialConnectionOptions = ConnectionOptions.None; | |
value = value.Slice(offset + 1); | |
offset = 0; | |
var byteValue = MemoryMarshal.AsBytes(value); | |
if ((c | lowerCaseChar) == 'k' && byteValue.Length >= (2 * sizeof(ulong) + sizeof(ushort))) | |
{ | |
if ((BinaryPrimitives.ReadUInt64LittleEndian(byteValue) | lowerCaseKeep) == keepChars) | |
{ | |
offset += sizeof(ulong) / 2; | |
byteValue = byteValue.Slice(sizeof(ulong)); | |
if ((BinaryPrimitives.ReadUInt64LittleEndian(byteValue) | lowerCaseULong) == alivChars) | |
{ | |
offset += sizeof(ulong) / 2; | |
byteValue = byteValue.Slice(sizeof(ulong)); | |
if ((BinaryPrimitives.ReadUInt16LittleEndian(byteValue) | lowerCaseUShort) == eChars) | |
{ | |
offset += sizeof(ushort) / 2; | |
potentialConnectionOptions = ConnectionOptions.KeepAlive; | |
} | |
} | |
} | |
} | |
else if ((c | lowerCaseChar) == 'u' && byteValue.Length >= (sizeof(ulong) + sizeof(uint))) | |
{ | |
if ((BinaryPrimitives.ReadUInt64LittleEndian(byteValue) | lowerCaseULong) == pgraChars) | |
{ | |
offset += sizeof(ulong) / 2; | |
byteValue = byteValue.Slice(sizeof(ulong)); | |
if ((BinaryPrimitives.ReadUInt32LittleEndian(byteValue) | lowerCaseUInt) == deChars) | |
{ | |
offset += sizeof(uint) / 2; | |
potentialConnectionOptions = ConnectionOptions.Upgrade; | |
} | |
} | |
} | |
else if ((c | lowerCaseChar) == 'c' && byteValue.Length >= sizeof(ulong)) | |
{ | |
if ((BinaryPrimitives.ReadUInt64LittleEndian(byteValue) | lowerCaseULong) == loseChars) | |
{ | |
offset += sizeof(ulong) / 2; | |
potentialConnectionOptions = ConnectionOptions.Close; | |
} | |
} | |
if (offset == value.Length) | |
{ | |
// Consumed enitre string, move to next string | |
connectionOptions |= potentialConnectionOptions; | |
break; | |
} | |
value = value.Slice(offset); | |
for (offset = 0; offset < value.Length; offset++) | |
{ | |
c = value[offset]; | |
if (c == ' ') | |
{ | |
continue; | |
} | |
if (c == ',') | |
{ | |
break; | |
} | |
else | |
{ | |
// Value contains extra chars; this is not the matched one. | |
potentialConnectionOptions = ConnectionOptions.None; | |
continue; | |
} | |
} | |
if (offset == value.Length) | |
{ | |
// Consumed enitre string, move to next string | |
connectionOptions |= potentialConnectionOptions; | |
break; | |
} | |
else if (c == ',') | |
{ | |
// Consumed value, move to next value | |
connectionOptions |= potentialConnectionOptions; | |
value = value.Slice(offset + 1); | |
continue; | |
} | |
} | |
} | |
return connectionOptions; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment