Index: /binary-improvements2/SpaceWizards.HttpListener/SpaceWizards.HttpListener-LICENSE.TXT
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/SpaceWizards.HttpListener-LICENSE.TXT	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/SpaceWizards.HttpListener-LICENSE.TXT	(revision 376)
@@ -0,0 +1,23 @@
+The MIT License (MIT)
+
+Copyright (c) .NET Foundation and Contributors
+
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
Index: /binary-improvements2/SpaceWizards.HttpListener/SpaceWizards.HttpListener.DotSettings
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/SpaceWizards.HttpListener.DotSettings	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/SpaceWizards.HttpListener.DotSettings	(revision 376)
@@ -0,0 +1,2 @@
+﻿<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+	<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp70</s:String></wpf:ResourceDictionary>
Index: /binary-improvements2/SpaceWizards.HttpListener/SpaceWizards.HttpListener.csproj
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/SpaceWizards.HttpListener.csproj	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/SpaceWizards.HttpListener.csproj	(revision 376)
@@ -0,0 +1,110 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{1C5368E1-A4CF-4580-86BB-DFFB20AB682C}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <RootNamespace>SpaceWizards.HttpListener</RootNamespace>
+    <AssemblyName>SpaceWizards.HttpListener</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <LangVersion>8</LangVersion>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>none</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>..\bin\Mods\Allocs_WebAndMapRendering\</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+    <NoStdLib>true</NoStdLib>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <DefineConstants>UNITY_NETFRAMEWORK</DefineConstants>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_Profiler|AnyCPU' ">
+    <OutputPath>..\bin\Mods\Allocs_WebAndMapRendering\</OutputPath>
+    <DefineConstants>ENABLE_PROFILER;UNITY_NETFRAMEWORK</DefineConstants>
+    <Optimize>true</Optimize>
+    <WarningLevel>4</WarningLevel>
+    <NoStdLib>true</NoStdLib>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <OutputPath>..\bin\Mods\Allocs_WebAndMapRendering\</OutputPath>
+    <DebugType>full</DebugType>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <DefineConstants>UNITY_NETFRAMEWORK</DefineConstants>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="src\AssemblyInfo.cs" />
+    <Compile Include="src\SR.cs" />
+    <Compile Include="src\System\HexConverter.cs" />
+    <Compile Include="src\System\Net\AuthenticationSchemeSelector.cs" />
+    <Compile Include="src\System\Net\AuthenticationTypes.cs" />
+    <Compile Include="src\System\Net\BoundaryType.cs" />
+    <Compile Include="src\System\Net\CaseInsensitiveAscii.cs" />
+    <Compile Include="src\System\Net\CookieComparer.cs" />
+    <Compile Include="src\System\Net\CookieFields.cs" />
+    <Compile Include="src\System\Net\CookieParser.cs" />
+    <Compile Include="src\System\Net\EntitySendFormat.cs" />
+    <Compile Include="src\System\Net\ExceptionCheck.cs" />
+    <Compile Include="src\System\Net\HttpKnownHeaderNames.cs" />
+    <Compile Include="src\System\Net\HttpListener.cs" />
+    <Compile Include="src\System\Net\HttpListenerBasicIdentity.cs" />
+    <Compile Include="src\System\Net\HttpListenerContext.cs" />
+    <Compile Include="src\System\Net\HttpListenerException.cs" />
+    <Compile Include="src\System\Net\HttpListenerPrefixCollection.cs" />
+    <Compile Include="src\System\Net\HttpListenerRequest.cs" />
+    <Compile Include="src\System\Net\HttpListenerRequestUriBuilder.cs" />
+    <Compile Include="src\System\Net\HttpListenerResponse.cs" />
+    <Compile Include="src\System\Net\HttpRequestStream.cs" />
+    <Compile Include="src\System\Net\HttpResponseStream.cs" />
+    <Compile Include="src\System\Net\HttpStatusDescription.cs" />
+    <Compile Include="src\System\Net\LazyAsyncResult.cs" />
+    <Compile Include="src\System\Net\ListenerClientCertState.cs" />
+    <Compile Include="src\System\Net\Logging\NetEventSource.Common.cs" />
+    <Compile Include="src\System\Net\Managed\ChunkedInputStream.cs" />
+    <Compile Include="src\System\Net\Managed\ChunkStream.cs" />
+    <Compile Include="src\System\Net\Managed\HttpConnection.cs" />
+    <Compile Include="src\System\Net\Managed\HttpEndPointListener.cs" />
+    <Compile Include="src\System\Net\Managed\HttpEndPointManager.cs" />
+    <Compile Include="src\System\Net\Managed\HttpHeaderStrings.cs" />
+    <Compile Include="src\System\Net\Managed\HttpListener.Certificates.cs" />
+    <Compile Include="src\System\Net\Managed\HttpListener.Managed.cs" />
+    <Compile Include="src\System\Net\Managed\HttpListenerContext.Managed.cs" />
+    <Compile Include="src\System\Net\Managed\HttpListenerRequest.Managed.cs" />
+    <Compile Include="src\System\Net\Managed\HttpListenerResponse.Managed.cs" />
+    <Compile Include="src\System\Net\Managed\HttpListenerTimeoutManager.Managed.cs" />
+    <Compile Include="src\System\Net\Managed\HttpRequestStream.Managed.cs" />
+    <Compile Include="src\System\Net\Managed\HttpResponseStream.Managed.cs" />
+    <Compile Include="src\System\Net\Managed\HttpStreamAsyncResult.cs" />
+    <Compile Include="src\System\Net\Managed\ListenerAsyncResult.Managed.cs" />
+    <Compile Include="src\System\Net\Managed\ListenerPrefix.cs" />
+    <Compile Include="src\System\Net\Managed\WebSockets\HttpWebSocket.Managed.cs" />
+    <Compile Include="src\System\Net\NetEventSource.HttpListener.cs" />
+    <Compile Include="src\System\Net\ServiceNameStore.cs" />
+    <Compile Include="src\System\Net\StreamHelper.cs" />
+    <Compile Include="src\System\Net\UriScheme.cs" />
+    <Compile Include="src\System\Net\WebHeaderEncoding.cs" />
+    <Compile Include="src\System\Net\WebSockets\HttpListenerWebSocketContext.cs" />
+    <Compile Include="src\System\Net\WebSockets\HttpWebSocket.cs" />
+    <Compile Include="src\System\Net\WebSockets\WebSocketValidate.cs" />
+    <Compile Include="src\System\Net\Windows\CookieExtensions.cs" />
+    <Compile Include="src\System\StringExtensions.cs" />
+    <Compile Include="src\System\Threading\Tasks\TaskToApm.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Reference Include="mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+      <HintPath>..\7dtd-binaries\mscorlib.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+      <HintPath>..\7dtd-binaries\System.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+</Project>
Index: /binary-improvements2/SpaceWizards.HttpListener/src/SR.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/SR.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/SR.cs	(revision 376)
@@ -0,0 +1,257 @@
+﻿namespace System
+{
+    internal static class SR
+    {
+        internal static string Format(string resourceFormat, object? p1)
+        {
+            return string.Format(resourceFormat, p1);
+        }
+
+        internal static string Format(string resourceFormat, object? p1, object? p2)
+        {
+            return string.Format(resourceFormat, p1, p2);
+        }
+
+        internal static string Format(string resourceFormat, object? p1, object? p2, object? p3)
+        {
+            return string.Format(resourceFormat, p1, p2, p3);
+        }
+
+        internal static string Format(string resourceFormat, params object?[]? args)
+        {
+            if (args != null)
+            {
+                return string.Format(resourceFormat, args);
+            }
+
+            return resourceFormat;
+        }
+
+        internal static string Format(IFormatProvider? provider, string resourceFormat, object? p1)
+        {
+            return string.Format(provider, resourceFormat, p1);
+        }
+
+        internal static string Format(IFormatProvider? provider, string resourceFormat, object? p1, object? p2)
+        {
+            return string.Format(provider, resourceFormat, p1, p2);
+        }
+
+        internal static string Format(IFormatProvider? provider, string resourceFormat, object? p1, object? p2,
+            object? p3)
+        {
+            return string.Format(provider, resourceFormat, p1, p2, p3);
+        }
+
+        internal static string Format(IFormatProvider? provider, string resourceFormat, params object?[]? args)
+        {
+            if (args != null)
+            {
+                return string.Format(provider, resourceFormat, args);
+            }
+
+            return resourceFormat;
+        }
+
+
+        public const string net_log_listener_delegate_exception =
+            "Sending 500 response, AuthenticationSchemeSelectorDelegate threw an exception: {0}.";
+
+        public const string net_log_listener_unsupported_authentication_scheme =
+            "Received a request with an unsupported authentication scheme, Authorization:{0} SupportedSchemes:{1}.";
+
+        public const string net_log_listener_unmatched_authentication_scheme =
+            "Received a request with an unmatched or no authentication scheme. AuthenticationSchemes:{0}, Authorization:{1}.";
+
+        public const string net_io_invalidasyncresult =
+            "The IAsyncResult object was not returned from the corresponding asynchronous method on this class.";
+
+        public const string net_io_invalidendcall = "{0} can only be called once for each asynchronous operation.";
+        public const string net_listener_cannot_set_custom_cbt = "Custom channel bindings are not supported.";
+        public const string net_listener_detach_error = "Can't detach Url group from request queue. Status code: {0}.";
+
+        public const string net_listener_scheme =
+            "Only Uri prefixes starting with 'http://' or 'https://' are supported.";
+
+        public const string net_listener_host = "Only Uri prefixes with a valid hostname are supported.";
+        public const string net_listener_not_supported = "The request is not supported.";
+        public const string net_listener_mustcall = "Please call the {0} method before calling this method.";
+        public const string net_listener_slash = "Only Uri prefixes ending in '/' are allowed.";
+
+        public const string net_listener_already =
+            "Failed to listen on prefix '{0}' because it conflicts with an existing registration on the machine.";
+
+        public const string net_log_listener_no_cbt_disabled =
+            "No channel binding check because extended protection is disabled.";
+
+        public const string net_log_listener_no_cbt_http =
+            "No channel binding check for requests without a secure channel.";
+
+        public const string net_log_listener_no_cbt_trustedproxy =
+            "No channel binding check for the trusted proxy scenario.";
+
+        public const string net_log_listener_cbt = "Channel binding check enabled.";
+
+        public const string net_log_listener_no_spn_kerberos =
+            "No explicit service name check because Kerberos authentication already validates the service name.";
+
+        public const string net_log_listener_no_spn_disabled =
+            "No service name check because extended protection is disabled.";
+
+        public const string net_log_listener_no_spn_cbt =
+            "No service name check because the channel binding was already checked.";
+
+        public const string net_log_listener_no_spn_whensupported =
+            "No service name check because the client did not provide a service name and the server was configured for PolicyEnforcement.WhenSupported.";
+
+        public const string net_log_listener_no_spn_loopback =
+            "No service name check because the authentication was from a client on the local machine.";
+
+        public const string net_log_listener_spn = "Client provided service name '{0}'.";
+        public const string net_log_listener_spn_passed = "Service name check succeeded.";
+        public const string net_log_listener_spn_failed = "Service name check failed.";
+
+        public const string net_log_listener_spn_failed_always =
+            "Service name check failed because the client did not provide a service name and the server was configured for PolicyEnforcement.Always.";
+
+        public const string net_log_listener_spn_failed_empty = "No acceptable service names were configured!";
+        public const string net_log_listener_spn_failed_dump = "Dumping acceptable service names:";
+        public const string net_log_listener_spn_add = "Adding default service name '{0}' from prefix '{1}'.";
+        public const string net_log_listener_spn_not_add = "No default service name added for prefix '{0}'.";
+        public const string net_log_listener_spn_remove = "Removing default service name '{0}' from prefix '{1}'.";
+        public const string net_log_listener_spn_not_remove = "No default service name removed for prefix '{0}'.";
+
+        public const string net_listener_no_spns =
+            "No service names could be determined from the registered prefixes. Either add prefixes from which default service names can be derived or specify an ExtendedProtectionPolicy object which contains an explicit list of service names.";
+
+        public const string net_ssp_dont_support_cbt =
+            "The Security Service Providers don't support extended protection. Please install the latest Security Service Providers update.";
+
+        public const string net_PropertyNotImplementedException = "This property is not implemented by this class.";
+        public const string net_array_too_small = "The target array is too small.";
+        public const string net_listener_mustcompletecall = "The in-progress method {0} must be completed first.";
+        public const string net_listener_invalid_cbt_type = "Querying the {0} Channel Binding is not supported.";
+
+        public const string net_listener_callinprogress =
+            "Cannot re-call {0} while a previous call is still in progress.";
+
+        public const string net_log_listener_cant_create_uri = "Can't create Uri from string '{0}://{1}{2}{3}'.";
+
+        public const string net_log_listener_cant_convert_raw_path =
+            "Can't convert Uri path '{0}' using encoding '{1}'.";
+
+        public const string net_log_listener_cant_convert_percent_value = "Can't convert percent encoded value '{0}'.";
+        public const string net_log_listener_cant_convert_to_utf8 = "Can't convert string '{0}' into UTF-8 bytes: {1}";
+
+        public const string net_log_listener_cant_convert_bytes =
+            "Can't convert bytes '{0}' into UTF-16 characters: {1}";
+
+        public const string net_invalidstatus = "The status code must be exactly three digits.";
+        public const string net_WebHeaderInvalidControlChars = "Specified value has invalid Control characters.";
+
+        public const string net_rspsubmitted =
+            "This operation cannot be performed after the response has been submitted.";
+
+        public const string net_nochunkuploadonhttp10 =
+            "Chunked encoding upload is not supported on the HTTP/1.0 protocol.";
+
+        public const string net_cookie_exists = "Cookie already exists.";
+        public const string net_clsmall = "The Content-Length value must be greater than or equal to zero.";
+        public const string net_wrongversion = "Only HTTP/1.0 and HTTP/1.1 version requests are currently supported.";
+        public const string net_noseek = "This stream does not support seek operations.";
+        public const string net_writeonlystream = "The stream does not support reading.";
+
+        public const string net_entitytoobig =
+            "Bytes to be written to the stream exceed the Content-Length bytes size specified.";
+
+        public const string net_io_notenoughbyteswritten = "Cannot close stream until all bytes are written.";
+        public const string net_listener_close_urlgroup_error = "Can't close Url group. Status code: {0}.";
+
+        public const string net_WebSockets_NativeSendResponseHeaders =
+            "An error occurred when sending the WebSocket HTTP upgrade response during the {0} operation. The HRESULT returned is '{1}'";
+
+        public const string net_WebSockets_ClientAcceptingNoProtocols =
+            "The WebSocket client did not request any protocols, but server attempted to accept '{0}' protocol(s). ";
+
+        public const string net_WebSockets_AcceptUnsupportedProtocol =
+            "The WebSocket client request requested '{0}' protocol(s), but server is only accepting '{1}' protocol(s).";
+
+        public const string net_WebSockets_AcceptNotAWebSocket =
+            "The {0} operation was called on an incoming request that did not specify a '{1}: {2}' header or the {2} header not contain '{3}'. {2} specified by the client was '{4}'.";
+
+        public const string net_WebSockets_AcceptHeaderNotFound =
+            "The {0} operation was called on an incoming WebSocket request without required '{1}' header. ";
+
+        public const string net_WebSockets_AcceptUnsupportedWebSocketVersion =
+            "The {0} operation was called on an incoming request with WebSocket version '{1}', expected '{2}'. ";
+
+        public const string net_WebSockets_InvalidEmptySubProtocol =
+            "Empty string is not a valid subprotocol value. Please use \"null\" to specify no value.";
+
+        public const string net_WebSockets_InvalidCharInProtocolString =
+            "The WebSocket protocol '{0}' is invalid because it contains the invalid character '{1}'.";
+
+        public const string net_WebSockets_ReasonNotNull =
+            "The close status description '{0}' is invalid. When using close status code '{1}' the description must be null.";
+
+        public const string net_WebSockets_InvalidCloseStatusCode =
+            "The close status code '{0}' is reserved for system use only and cannot be specified when calling this method.";
+
+        public const string net_WebSockets_InvalidCloseStatusDescription =
+            "The close status description '{0}' is too long. The UTF8-representation of the status description must not be longer than {1} bytes.";
+
+        public const string net_WebSockets_ArgumentOutOfRange_TooSmall =
+            "The argument must be a value greater than {0}.";
+
+        public const string net_WebSockets_ArgumentOutOfRange_TooBig =
+            "The value of the '{0}' parameter ({1}) must be less than or equal to {2}.";
+
+        public const string net_WebSockets_UnsupportedPlatform =
+            "The WebSocket protocol is not supported on this platform.";
+
+        public const string net_readonlystream = "The stream does not support writing.";
+
+        public const string net_WebSockets_InvalidState_ClosedOrAborted =
+            "The '{0}' instance cannot be used for communication because it has been transitioned into the '{1}' state.";
+
+        public const string net_WebSockets_ReceiveAsyncDisallowedAfterCloseAsync =
+            "The WebSocket is in an invalid state for this operation. The '{0}' method has already been called before on this instance. Use '{1}' instead to keep being able to receive data but close the output channel.";
+
+        public const string net_Websockets_AlreadyOneOutstandingOperation =
+            "There is already one outstanding '{0}' call for this WebSocket instance. ReceiveAsync and SendAsync can be called simultaneously, but at most one outstanding operation for each of them is allowed at the same time.";
+
+        public const string net_WebSockets_InvalidMessageType =
+            "The received message type '{2}' is invalid after calling {0}. {0} should only be used if no more data is expected from the remote endpoint. Use '{1}' instead to keep being able to receive data but close the output channel.";
+
+        public const string net_WebSockets_InvalidBufferType =
+            "The buffer type '{0}' is invalid. Valid buffer types are: '{1}', '{2}', '{3}', '{4}', '{5}'.";
+
+        public const string net_WebSockets_ArgumentOutOfRange_InternalBuffer =
+            "The byte array must have a length of at least '{0}' bytes.  ";
+
+        public const string net_WebSockets_Argument_InvalidMessageType =
+            "The message type '{0}' is not allowed for the '{1}' operation. Valid message types are: '{2}, {3}'. To close the WebSocket, use the '{4}' operation instead. ";
+
+        public const string net_securitypackagesupport = "The requested security package is not supported.";
+        public const string net_log_operation_failed_with_error = "{0} failed with error {1}.";
+        public const string net_MethodNotImplementedException = "This method is not implemented by this class.";
+        public const string event_OperationReturnedSomething = "{0} returned {1}.";
+        public const string net_invalid_enum = "The specified value is not valid in the '{0}' enumeration.";
+
+        public const string net_auth_message_not_encrypted =
+            "Protocol error: A received message contains a valid signature but it was not encrypted as required by the effective Protection Level.";
+
+        public const string SSPIInvalidHandleType = "'{0}' is not a supported handle type.";
+        public const string net_io_operation_aborted = "I/O operation aborted: '{0}'.";
+        public const string net_invalid_path = "Invalid path.";
+        public const string net_listener_auth_errors = "Authentication errors.";
+        public const string net_listener_close = "Listener closed.";
+        public const string net_invalid_port = "Invalid port in prefix.";
+
+        public const string net_WebSockets_InvalidState =
+            "The WebSocket is in an invalid state ('{0}') for this operation. Valid states are: '{1}'";
+
+        public const string SystemNetHttpListener_PlatformNotSupported =
+            "System.Net.HttpListener is not supported on this platform.";
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/HexConverter.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/HexConverter.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/HexConverter.cs	(revision 376)
@@ -0,0 +1,259 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#nullable disable
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace System
+{
+    internal static class HexConverter
+    {
+        public enum Casing : uint
+        {
+            // Output [ '0' .. '9' ] and [ 'A' .. 'F' ].
+            Upper = 0,
+
+            // Output [ '0' .. '9' ] and [ 'a' .. 'f' ].
+            // This works because values in the range [ 0x30 .. 0x39 ] ([ '0' .. '9' ])
+            // already have the 0x20 bit set, so ORing them with 0x20 is a no-op,
+            // while outputs in the range [ 0x41 .. 0x46 ] ([ 'A' .. 'F' ])
+            // don't have the 0x20 bit set, so ORing them maps to
+            // [ 0x61 .. 0x66 ] ([ 'a' .. 'f' ]), which is what we want.
+            Lower = 0x2020U,
+        }
+
+        // We want to pack the incoming byte into a single integer [ 0000 HHHH 0000 LLLL ],
+        // where HHHH and LLLL are the high and low nibbles of the incoming byte. Then
+        // subtract this integer from a constant minuend as shown below.
+        //
+        //   [ 1000 1001 1000 1001 ]
+        // - [ 0000 HHHH 0000 LLLL ]
+        // =========================
+        //   [ *YYY **** *ZZZ **** ]
+        //
+        // The end result of this is that YYY is 0b000 if HHHH <= 9, and YYY is 0b111 if HHHH >= 10.
+        // Similarly, ZZZ is 0b000 if LLLL <= 9, and ZZZ is 0b111 if LLLL >= 10.
+        // (We don't care about the value of asterisked bits.)
+        //
+        // To turn a nibble in the range [ 0 .. 9 ] into hex, we calculate hex := nibble + 48 (ascii '0').
+        // To turn a nibble in the range [ 10 .. 15 ] into hex, we calculate hex := nibble - 10 + 65 (ascii 'A').
+        //                                                                => hex := nibble + 55.
+        // The difference in the starting ASCII offset is (55 - 48) = 7, depending on whether the nibble is <= 9 or >= 10.
+        // Since 7 is 0b111, this conveniently matches the YYY or ZZZ value computed during the earlier subtraction.
+
+        // The commented out code below is code that directly implements the logic described above.
+
+        // uint packedOriginalValues = (((uint)value & 0xF0U) << 4) + ((uint)value & 0x0FU);
+        // uint difference = 0x8989U - packedOriginalValues;
+        // uint add7Mask = (difference & 0x7070U) >> 4; // line YYY and ZZZ back up with the packed values
+        // uint packedResult = packedOriginalValues + add7Mask + 0x3030U /* ascii '0' */;
+
+        // The code below is equivalent to the commented out code above but has been tweaked
+        // to allow codegen to make some extra optimizations.
+
+        // The low byte of the packed result contains the hex representation of the incoming byte's low nibble.
+        // The adjacent byte of the packed result contains the hex representation of the incoming byte's high nibble.
+
+        // Finally, write to the output buffer starting with the *highest* index so that codegen can
+        // elide all but the first bounds check. (This only works if 'startingIndex' is a compile-time constant.)
+
+        // The JIT can elide bounds checks if 'startingIndex' is constant and if the caller is
+        // writing to a span of known length (or the caller has already checked the bounds of the
+        // furthest access).
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static void ToBytesBuffer(byte value, Span<byte> buffer, int startingIndex = 0, Casing casing = Casing.Upper)
+        {
+            uint difference = (((uint)value & 0xF0U) << 4) + ((uint)value & 0x0FU) - 0x8989U;
+            uint packedResult = ((((uint)(-(int)difference) & 0x7070U) >> 4) + difference + 0xB9B9U) | (uint)casing;
+
+            buffer[startingIndex + 1] = (byte)packedResult;
+            buffer[startingIndex] = (byte)(packedResult >> 8);
+        }
+
+#if ALLOW_PARTIALLY_TRUSTED_CALLERS
+        [System.Security.SecuritySafeCriticalAttribute]
+#endif
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static void ToCharsBuffer(byte value, Span<char> buffer, int startingIndex = 0, Casing casing = Casing.Upper)
+        {
+            uint difference = (((uint)value & 0xF0U) << 4) + ((uint)value & 0x0FU) - 0x8989U;
+            uint packedResult = ((((uint)(-(int)difference) & 0x7070U) >> 4) + difference + 0xB9B9U) | (uint)casing;
+
+            buffer[startingIndex + 1] = (char)(packedResult & 0xFF);
+            buffer[startingIndex] = (char)(packedResult >> 8);
+        }
+
+        public static void EncodeToUtf16(ReadOnlySpan<byte> bytes, Span<char> chars, Casing casing = Casing.Upper)
+        {
+            Debug.Assert(chars.Length >= bytes.Length * 2);
+
+            for (int pos = 0; pos < bytes.Length; ++pos)
+            {
+                ToCharsBuffer(bytes[pos], chars, pos * 2, casing);
+            }
+        }
+
+#if ALLOW_PARTIALLY_TRUSTED_CALLERS
+        [System.Security.SecuritySafeCriticalAttribute]
+#endif
+        public static unsafe string ToString(ReadOnlySpan<byte> bytes, Casing casing = Casing.Upper)
+        {
+#if NETFRAMEWORK || NETSTANDARD1_0 || NETSTANDARD1_3 || NETSTANDARD2_0
+            Span<char> result = stackalloc char[0];
+            if (bytes.Length > 16)
+            {
+                var array = new char[bytes.Length * 2];
+                result = array.AsSpan();
+            }
+            else
+            {
+                result = stackalloc char[bytes.Length * 2];
+            }
+
+            int pos = 0;
+            foreach (byte b in bytes)
+            {
+                ToCharsBuffer(b, result, pos, casing);
+                pos += 2;
+            }
+            return result.ToString();
+#else
+            fixed (byte* bytesPtr = bytes)
+            {
+                return string.Create(bytes.Length * 2, (Ptr: (IntPtr)bytesPtr, bytes.Length, casing), static (chars, args) =>
+                {
+                    var ros = new ReadOnlySpan<byte>((byte*)args.Ptr, args.Length);
+                    EncodeToUtf16(ros, chars, args.casing);
+                });
+            }
+#endif
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static char ToCharUpper(int value)
+        {
+            value &= 0xF;
+            value += '0';
+
+            if (value > '9')
+            {
+                value += ('A' - ('9' + 1));
+            }
+
+            return (char)value;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static char ToCharLower(int value)
+        {
+            value &= 0xF;
+            value += '0';
+
+            if (value > '9')
+            {
+                value += ('a' - ('9' + 1));
+            }
+
+            return (char)value;
+        }
+
+        public static bool TryDecodeFromUtf16(ReadOnlySpan<char> chars, Span<byte> bytes)
+        {
+            return TryDecodeFromUtf16(chars, bytes, out _);
+        }
+
+        public static bool TryDecodeFromUtf16(ReadOnlySpan<char> chars, Span<byte> bytes, out int charsProcessed)
+        {
+            Debug.Assert(chars.Length % 2 == 0, "Un-even number of characters provided");
+            Debug.Assert(chars.Length / 2 == bytes.Length, "Target buffer not right-sized for provided characters");
+
+            int i = 0;
+            int j = 0;
+            int byteLo = 0;
+            int byteHi = 0;
+            while (j < bytes.Length)
+            {
+                byteLo = FromChar(chars[i + 1]);
+                byteHi = FromChar(chars[i]);
+
+                // byteHi hasn't been shifted to the high half yet, so the only way the bitwise or produces this pattern
+                // is if either byteHi or byteLo was not a hex character.
+                if ((byteLo | byteHi) == 0xFF)
+                    break;
+
+                bytes[j++] = (byte)((byteHi << 4) | byteLo);
+                i += 2;
+            }
+
+            if (byteLo == 0xFF)
+                i++;
+
+            charsProcessed = i;
+            return (byteLo | byteHi) != 0xFF;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static int FromChar(int c)
+        {
+            return c >= CharToHexLookup.Length ? 0xFF : CharToHexLookup[c];
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static int FromUpperChar(int c)
+        {
+            return c > 71 ? 0xFF : CharToHexLookup[c];
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static int FromLowerChar(int c)
+        {
+            if ((uint)(c - '0') <= '9' - '0')
+                return c - '0';
+
+            if ((uint)(c - 'a') <= 'f' - 'a')
+                return c - 'a' + 10;
+
+            return 0xFF;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static bool IsHexChar(int c)
+        {
+            return FromChar(c) != 0xFF;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static bool IsHexUpperChar(int c)
+        {
+            return (uint)(c - '0') <= 9 || (uint)(c - 'A') <= ('F' - 'A');
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static bool IsHexLowerChar(int c)
+        {
+            return (uint)(c - '0') <= 9 || (uint)(c - 'a') <= ('f' - 'a');
+        }
+
+        /// <summary>Map from an ASCII char to its hex value, e.g. arr['b'] == 11. 0xFF means it's not a hex digit.</summary>
+        public static ReadOnlySpan<byte> CharToHexLookup => new byte[]
+        {
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 15
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 31
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 47
+            0x0,  0x1,  0x2,  0x3,  0x4,  0x5,  0x6,  0x7,  0x8,  0x9,  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 63
+            0xFF, 0xA,  0xB,  0xC,  0xD,  0xE,  0xF,  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 79
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 95
+            0xFF, 0xa,  0xb,  0xc,  0xd,  0xe,  0xf,  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 111
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 127
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 143
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 159
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 175
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 191
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 207
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 223
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 239
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF  // 255
+        };
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/AuthenticationSchemeSelector.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/AuthenticationSchemeSelector.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/AuthenticationSchemeSelector.cs	(revision 376)
@@ -0,0 +1,9 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Net;
+
+namespace SpaceWizards.HttpListener
+{
+    public delegate AuthenticationSchemes AuthenticationSchemeSelector(HttpListenerRequest httpRequest);
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/AuthenticationTypes.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/AuthenticationTypes.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/AuthenticationTypes.cs	(revision 376)
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace SpaceWizards.HttpListener
+{
+    internal sealed class AuthenticationTypes
+    {
+        internal const string NTLM = "NTLM";
+        internal const string Negotiate = "Negotiate";
+        internal const string Basic = "Basic";
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/BoundaryType.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/BoundaryType.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/BoundaryType.cs	(revision 376)
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace SpaceWizards.HttpListener
+{
+    internal enum BoundaryType
+    {
+        ContentLength = 0, // Content-Length: XXX
+        Chunked = 1, // Transfer-Encoding: chunked
+        Multipart = 3,
+        None = 4,
+        Invalid = 5,
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/CaseInsensitiveAscii.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/CaseInsensitiveAscii.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/CaseInsensitiveAscii.cs	(revision 376)
@@ -0,0 +1,130 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#nullable enable
+using System.Collections;
+
+namespace System.Net
+{
+    internal class CaseInsensitiveAscii : IEqualityComparer, IComparer
+    {
+        // ASCII char ToLower table
+        internal static readonly CaseInsensitiveAscii StaticInstance = new CaseInsensitiveAscii();
+        internal static ReadOnlySpan<byte> AsciiToLower => new byte[] {
+              0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
+             10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
+             20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
+             30,  31,  32,  33,  34,  35,  36,  37,  38,  39,
+             40,  41,  42,  43,  44,  45,  46,  47,  48,  49,
+             50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
+             60,  61,  62,  63,  64,  97,  98,  99, 100, 101,
+            102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+            112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
+            122,  91,  92,  93,  94,  95,  96,  97,  98,  99,
+            100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+            110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+            120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
+            130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
+            140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+            150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+            160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
+            170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+            180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
+            190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
+            200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
+            210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
+            220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
+            230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+            240, 241, 242, 243, 244, 245, 246, 247, 248, 249,
+            250, 251, 252, 253, 254, 255
+        };
+
+        // ASCII string case insensitive hash function
+        public int GetHashCode(object myObject)
+        {
+            string? myString = myObject as string;
+            if (myString == null)
+            {
+                return 0;
+            }
+            int myHashCode = myString.Length;
+            if (myHashCode == 0)
+            {
+                return 0;
+            }
+            myHashCode ^= AsciiToLower[(byte)myString[0]] << 24 ^ AsciiToLower[(byte)myString[myHashCode - 1]] << 16;
+            return myHashCode;
+        }
+
+        // ASCII string case insensitive comparer
+        public int Compare(object? firstObject, object? secondObject)
+        {
+            string? firstString = firstObject as string;
+            string? secondString = secondObject as string;
+            if (firstString == null)
+            {
+                return secondString == null ? 0 : -1;
+            }
+            if (secondString == null)
+            {
+                return 1;
+            }
+            int result = firstString.Length - secondString.Length;
+            int comparisons = result > 0 ? secondString.Length : firstString.Length;
+            int difference, index = 0;
+            while (index < comparisons)
+            {
+                difference = (int)(AsciiToLower[firstString[index]] - AsciiToLower[secondString[index]]);
+                if (difference != 0)
+                {
+                    result = difference;
+                    break;
+                }
+                index++;
+            }
+            return result;
+        }
+
+        // ASCII string case insensitive hash function
+        private int FastGetHashCode(string myString)
+        {
+            int myHashCode = myString.Length;
+            if (myHashCode != 0)
+            {
+                myHashCode ^= AsciiToLower[(byte)myString[0]] << 24 ^ AsciiToLower[(byte)myString[myHashCode - 1]] << 16;
+            }
+            return myHashCode;
+        }
+
+        // ASCII string case insensitive comparer
+        public new bool Equals(object? firstObject, object? secondObject)
+        {
+            string? firstString = firstObject as string;
+            string? secondString = secondObject as string;
+            if (firstString == null)
+            {
+                return secondString == null;
+            }
+            if (secondString != null)
+            {
+                int index = firstString.Length;
+                if (index == secondString.Length)
+                {
+                    if (FastGetHashCode(firstString) == FastGetHashCode(secondString))
+                    {
+                        while (index > 0)
+                        {
+                            index--;
+                            if (AsciiToLower[firstString[index]] != AsciiToLower[secondString[index]])
+                            {
+                                return false;
+                            }
+                        }
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/CookieComparer.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/CookieComparer.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/CookieComparer.cs	(revision 376)
@@ -0,0 +1,27 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Net
+{
+    internal static class CookieComparer
+    {
+        internal static int Compare(Cookie left, Cookie right)
+        {
+            int result;
+
+            if ((result = string.Compare(left.Name, right.Name, StringComparison.OrdinalIgnoreCase)) != 0)
+            {
+                return result;
+            }
+
+            if ((result = string.Compare(left.Domain, right.Domain, StringComparison.OrdinalIgnoreCase)) != 0)
+            {
+                return result;
+            }
+
+            // NB: Only the path is case sensitive as per spec. However, many Windows applications assume
+            //     case-insensitivity.
+            return string.Compare(left.Path, right.Path, StringComparison.Ordinal);
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/CookieFields.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/CookieFields.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/CookieFields.cs	(revision 376)
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Net
+{
+    internal static class CookieFields
+    {
+        internal const string CommentAttributeName = "Comment";
+        internal const string CommentUrlAttributeName = "CommentURL";
+        internal const string DiscardAttributeName = "Discard";
+        internal const string DomainAttributeName = "Domain";
+        internal const string ExpiresAttributeName = "Expires";
+        internal const string MaxAgeAttributeName = "Max-Age";
+        internal const string PathAttributeName = "Path";
+        internal const string PortAttributeName = "Port";
+        internal const string SecureAttributeName = "Secure";
+        internal const string VersionAttributeName = "Version";
+        internal const string HttpOnlyAttributeName = "HttpOnly";
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/CookieParser.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/CookieParser.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/CookieParser.cs	(revision 376)
@@ -0,0 +1,870 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#nullable enable
+using System.Diagnostics;
+using System.Globalization;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+namespace System.Net
+{
+    internal enum CookieToken
+    {
+        // State types
+        Nothing,
+        NameValuePair,  // X=Y
+        Attribute,      // X
+        EndToken,       // ';'
+        EndCookie,      // ','
+        End,            // EOLN
+        Equals,
+
+        // Value types
+        Comment,
+        CommentUrl,
+        CookieName,
+        Discard,
+        Domain,
+        Expires,
+        MaxAge,
+        Path,
+        Port,
+        Secure,
+        HttpOnly,
+        Unknown,
+        Version
+    }
+
+    // CookieTokenizer
+    //
+    // Used to split a single or multi-cookie (header) string into individual
+    // tokens.
+    internal struct CookieTokenizer
+    {
+        private bool _eofCookie;
+        private int _index;
+        private readonly int _length;
+        private string? _name;
+        private bool _quoted;
+        private int _start;
+        private CookieToken _token;
+        private int _tokenLength;
+        private readonly string _tokenStream;
+        private string _value;
+        private int _cookieStartIndex;
+        private int _cookieLength;
+
+        internal CookieTokenizer(string tokenStream) : this()
+        {
+            _length = tokenStream.Length;
+            _tokenStream = tokenStream;
+            _value = string.Empty;
+        }
+
+        internal bool EndOfCookie
+        {
+            get
+            {
+                return _eofCookie;
+            }
+            set
+            {
+                _eofCookie = value;
+            }
+        }
+
+        internal bool Eof
+        {
+            get
+            {
+                return _index >= _length;
+            }
+        }
+
+        internal string? Name
+        {
+            get
+            {
+                return _name;
+            }
+            set
+            {
+                _name = value;
+            }
+        }
+
+        internal bool Quoted
+        {
+            get
+            {
+                return _quoted;
+            }
+            set
+            {
+                _quoted = value;
+            }
+        }
+
+        internal CookieToken Token
+        {
+            get
+            {
+                return _token;
+            }
+            set
+            {
+                _token = value;
+            }
+        }
+
+        internal string Value
+        {
+            get
+            {
+                return _value;
+            }
+            set
+            {
+                _value = value;
+            }
+        }
+
+        // Extract
+        //
+        // Extracts the current token
+        internal string Extract()
+        {
+            string tokenString = string.Empty;
+
+            if (_tokenLength != 0)
+            {
+                tokenString = Quoted ?
+                    _tokenStream.Substring(_start, _tokenLength) :
+                    _tokenStream.SubstringTrim(_start, _tokenLength);
+            }
+            return tokenString;
+        }
+
+        // FindNext
+        //
+        // Find the start and length of the next token. The token is terminated
+        // by one of:
+        //     - end-of-line
+        //     - end-of-cookie: unquoted comma separates multiple cookies
+        //     - end-of-token: unquoted semi-colon
+        //     - end-of-name: unquoted equals
+        //
+        // Inputs:
+        // <argument>  ignoreComma
+        //     true if parsing doesn't stop at a comma. This is only true when
+        //     we know we're parsing an original cookie that has an expires=
+        //     attribute, because the format of the time/date used in expires
+        //     is:
+        //         Wdy, dd-mmm-yyyy HH:MM:SS GMT
+        //
+        // <argument>  ignoreEquals
+        //     true if parsing doesn't stop at an equals sign. The LHS of the
+        //     first equals sign is an attribute name. The next token may
+        //     include one or more equals signs. For example:
+        //          SESSIONID=ID=MSNx45&q=33
+        //
+        // Outputs:
+        // <member>    _index
+        //     incremented to the last position in _tokenStream contained by
+        //     the current token
+        //
+        // <member>    _start
+        //     incremented to the start of the current token
+        //
+        // <member>    _tokenLength
+        //     set to the length of the current token
+        //
+        // Assumes: Nothing
+        //
+        // Returns:
+        // type of CookieToken found:
+        //
+        //     End         - end of the cookie string
+        //     EndCookie   - end of current cookie in (potentially) a
+        //                   multi-cookie string
+        //     EndToken    - end of name=value pair, or end of an attribute
+        //     Equals      - end of name=
+        //
+        // Throws: Nothing
+        internal CookieToken FindNext(bool ignoreComma, bool ignoreEquals)
+        {
+            _tokenLength = 0;
+            _start = _index;
+            while ((_index < _length) && char.IsWhiteSpace(_tokenStream[_index]))
+            {
+                ++_index;
+                ++_start;
+            }
+
+            CookieToken token = CookieToken.End;
+            int increment = 1;
+
+            if (!Eof)
+            {
+                if (_tokenStream[_index] == '"')
+                {
+                    Quoted = true;
+                    ++_index;
+                    bool quoteOn = false;
+                    while (_index < _length)
+                    {
+                        char currChar = _tokenStream[_index];
+                        if (!quoteOn && currChar == '"')
+                        {
+                            break;
+                        }
+
+                        if (quoteOn)
+                        {
+                            quoteOn = false;
+                        }
+                        else if (currChar == '\\')
+                        {
+                            quoteOn = true;
+                        }
+                        ++_index;
+                    }
+                    if (_index < _length)
+                    {
+                        ++_index;
+                    }
+                    _tokenLength = _index - _start;
+                    increment = 0;
+                    // If we are here, reset ignoreComma.
+                    // In effect, we ignore everything after quoted string until the next delimiter.
+                    ignoreComma = false;
+                }
+                while ((_index < _length)
+                       && (_tokenStream[_index] != ';')
+                       && (ignoreEquals || (_tokenStream[_index] != '='))
+                       && (ignoreComma || (_tokenStream[_index] != ',')))
+                {
+                    // Fixing 2 things:
+                    // 1) ignore day of week in cookie string
+                    // 2) revert ignoreComma once meet it, so won't miss the next cookie)
+                    if (_tokenStream[_index] == ',')
+                    {
+                        _start = _index + 1;
+                        _tokenLength = -1;
+                        ignoreComma = false;
+                    }
+                    ++_index;
+                    _tokenLength += increment;
+                }
+                if (!Eof)
+                {
+                    switch (_tokenStream[_index])
+                    {
+                        case ';':
+                            token = CookieToken.EndToken;
+                            break;
+
+                        case '=':
+                            token = CookieToken.Equals;
+                            break;
+
+                        default:
+                            _cookieLength = _index - _cookieStartIndex;
+                            token = CookieToken.EndCookie;
+                            break;
+                    }
+                    ++_index;
+                }
+
+                if (Eof)
+                {
+                    _cookieLength = _index - _cookieStartIndex;
+                }
+            }
+            return token;
+        }
+
+        // Next
+        //
+        // Get the next cookie name/value or attribute
+        //
+        // Cookies come in the following formats:
+        //
+        //     1. Version0
+        //         Set-Cookie: [<name>][=][<value>]
+        //                     [; expires=<date>]
+        //                     [; path=<path>]
+        //                     [; domain=<domain>]
+        //                     [; secure]
+        //         Cookie: <name>=<value>
+        //
+        //         Notes: <name> and/or <value> may be blank
+        //                <date> is the RFC 822/1123 date format that
+        //                incorporates commas, e.g.
+        //                "Wednesday, 09-Nov-99 23:12:40 GMT"
+        //
+        //     2. RFC 2109
+        //         Set-Cookie: 1#{
+        //                         <name>=<value>
+        //                         [; comment=<comment>]
+        //                         [; domain=<domain>]
+        //                         [; max-age=<seconds>]
+        //                         [; path=<path>]
+        //                         [; secure]
+        //                         ; Version=<version>
+        //                     }
+        //         Cookie: $Version=<version>
+        //                 1#{
+        //                     ; <name>=<value>
+        //                     [; path=<path>]
+        //                     [; domain=<domain>]
+        //                 }
+        //
+        //     3. RFC 2965
+        //         Set-Cookie2: 1#{
+        //                         <name>=<value>
+        //                         [; comment=<comment>]
+        //                         [; commentURL=<comment>]
+        //                         [; discard]
+        //                         [; domain=<domain>]
+        //                         [; max-age=<seconds>]
+        //                         [; path=<path>]
+        //                         [; ports=<portlist>]
+        //                         [; secure]
+        //                         ; Version=<version>
+        //                      }
+        //         Cookie: $Version=<version>
+        //                 1#{
+        //                     ; <name>=<value>
+        //                     [; path=<path>]
+        //                     [; domain=<domain>]
+        //                     [; port="<port>"]
+        //                 }
+        //         [Cookie2: $Version=<version>]
+        //
+        // Inputs:
+        // <argument>  first
+        //     true if this is the first name/attribute that we have looked for
+        //     in the cookie stream
+        //
+        // Outputs:
+        //
+        // Assumes:
+        // Nothing
+        //
+        // Returns:
+        // type of CookieToken found:
+        //
+        //     - Attribute
+        //         - token was single-value. May be empty. Caller should check
+        //           Eof or EndCookie to determine if any more action needs to
+        //           be taken
+        //
+        //     - NameValuePair
+        //         - Name and Value are meaningful. Either may be empty
+        //
+        // Throws:
+        // Nothing
+        internal CookieToken Next(bool first, bool parseResponseCookies)
+        {
+            Reset();
+
+            if (first)
+            {
+                _cookieStartIndex = _index;
+                _cookieLength = 0;
+            }
+
+            CookieToken terminator = FindNext(false, false);
+            if (terminator == CookieToken.EndCookie)
+            {
+                EndOfCookie = true;
+            }
+
+            if ((terminator == CookieToken.End) || (terminator == CookieToken.EndCookie))
+            {
+                if ((Name = Extract()).Length != 0)
+                {
+                    Token = TokenFromName(parseResponseCookies);
+                    return CookieToken.Attribute;
+                }
+                return terminator;
+            }
+            Name = Extract();
+            if (first)
+            {
+                Token = CookieToken.CookieName;
+            }
+            else
+            {
+                Token = TokenFromName(parseResponseCookies);
+            }
+            if (terminator == CookieToken.Equals)
+            {
+                terminator = FindNext(!first && (Token == CookieToken.Expires), true);
+                if (terminator == CookieToken.EndCookie)
+                {
+                    EndOfCookie = true;
+                }
+                Value = Extract();
+                return CookieToken.NameValuePair;
+            }
+            else
+            {
+                return CookieToken.Attribute;
+            }
+        }
+
+        // Reset
+        //
+        // Sets this tokenizer up for finding the next name/value pair,
+        // attribute, or end-of-{token,cookie,line}.
+        internal void Reset()
+        {
+            _eofCookie = false;
+            _name = string.Empty;
+            _quoted = false;
+            _start = _index;
+            _token = CookieToken.Nothing;
+            _tokenLength = 0;
+            _value = string.Empty;
+        }
+
+        private struct RecognizedAttribute
+        {
+            private readonly string _name;
+            private readonly CookieToken _token;
+
+            internal RecognizedAttribute(string name, CookieToken token)
+            {
+                _name = name;
+                _token = token;
+            }
+
+            internal CookieToken Token
+            {
+                get
+                {
+                    return _token;
+                }
+            }
+
+            internal bool IsEqualTo(string? value)
+            {
+                return string.Equals(_name, value, StringComparison.OrdinalIgnoreCase);
+            }
+        }
+
+        // Recognized attributes in order of expected frequency.
+        private static readonly RecognizedAttribute[] s_recognizedAttributes = {
+            new RecognizedAttribute(CookieFields.PathAttributeName, CookieToken.Path),
+            new RecognizedAttribute(CookieFields.MaxAgeAttributeName, CookieToken.MaxAge),
+            new RecognizedAttribute(CookieFields.ExpiresAttributeName, CookieToken.Expires),
+            new RecognizedAttribute(CookieFields.VersionAttributeName, CookieToken.Version),
+            new RecognizedAttribute(CookieFields.DomainAttributeName, CookieToken.Domain),
+            new RecognizedAttribute(CookieFields.SecureAttributeName, CookieToken.Secure),
+            new RecognizedAttribute(CookieFields.DiscardAttributeName, CookieToken.Discard),
+            new RecognizedAttribute(CookieFields.PortAttributeName, CookieToken.Port),
+            new RecognizedAttribute(CookieFields.CommentAttributeName, CookieToken.Comment),
+            new RecognizedAttribute(CookieFields.CommentUrlAttributeName, CookieToken.CommentUrl),
+            new RecognizedAttribute(CookieFields.HttpOnlyAttributeName, CookieToken.HttpOnly),
+        };
+
+        private static readonly RecognizedAttribute[] s_recognizedServerAttributes = {
+            new RecognizedAttribute('$' + CookieFields.PathAttributeName, CookieToken.Path),
+            new RecognizedAttribute('$' + CookieFields.VersionAttributeName, CookieToken.Version),
+            new RecognizedAttribute('$' + CookieFields.DomainAttributeName, CookieToken.Domain),
+            new RecognizedAttribute('$' + CookieFields.PortAttributeName, CookieToken.Port),
+            new RecognizedAttribute('$' + CookieFields.HttpOnlyAttributeName, CookieToken.HttpOnly),
+        };
+
+        internal CookieToken TokenFromName(bool parseResponseCookies)
+        {
+            if (!parseResponseCookies)
+            {
+                for (int i = 0; i < s_recognizedServerAttributes.Length; ++i)
+                {
+                    if (s_recognizedServerAttributes[i].IsEqualTo(Name))
+                    {
+                        return s_recognizedServerAttributes[i].Token;
+                    }
+                }
+            }
+            else
+            {
+                for (int i = 0; i < s_recognizedAttributes.Length; ++i)
+                {
+                    if (s_recognizedAttributes[i].IsEqualTo(Name))
+                    {
+                        return s_recognizedAttributes[i].Token;
+                    }
+                }
+            }
+            return CookieToken.Unknown;
+        }
+    }
+
+    // CookieParser
+    //
+    // Takes a cookie header, makes cookies.
+    internal struct CookieParser
+    {
+        private CookieTokenizer _tokenizer;
+        private Cookie? _savedCookie;
+
+        internal CookieParser(string cookieString)
+        {
+            _tokenizer = new CookieTokenizer(cookieString);
+            _savedCookie = null;
+        }
+
+#if SYSTEM_NET_PRIMITIVES_DLL
+        private static bool InternalSetNameMethod(Cookie cookie, string? value)
+        {
+            return cookie.InternalSetName(value);
+        }
+#else
+        private static Func<Cookie, string?, bool>? s_internalSetNameMethod;
+        private static Func<Cookie, string?, bool> InternalSetNameMethod
+        {
+            get
+            {
+                if (s_internalSetNameMethod == null)
+                {
+                    // TODO https://github.com/dotnet/runtime/issues/19348:
+                    // We need to use Cookie.InternalSetName instead of the Cookie.set_Name wrapped in a try catch block, as
+                    // Cookie.set_Name keeps the original name if the string is empty or null.
+                    // Unfortunately this API is internal so we use reflection to access it. The method is cached for performance reasons.
+                    MethodInfo? method = typeof(Cookie).GetMethod("InternalSetName", BindingFlags.Instance | BindingFlags.NonPublic);
+                    Debug.Assert(method != null, "We need to use an internal method named InternalSetName that is declared on Cookie.");
+                    s_internalSetNameMethod = (Func<Cookie, string?, bool>)Delegate.CreateDelegate(typeof(Func<Cookie, string?, bool>), method);
+                }
+
+                return s_internalSetNameMethod;
+            }
+        }
+#endif
+
+        private static FieldInfo? s_isQuotedDomainField;
+        private static FieldInfo IsQuotedDomainField
+        {
+            get
+            {
+                if (s_isQuotedDomainField == null)
+                {
+                    // TODO https://github.com/dotnet/runtime/issues/19348:
+                    FieldInfo? field = typeof(Cookie).GetField("IsQuotedDomain", BindingFlags.Instance | BindingFlags.NonPublic);
+                    Debug.Assert(field != null, "We need to use an internal field named IsQuotedDomain that is declared on Cookie.");
+                    s_isQuotedDomainField = field;
+                }
+
+                return s_isQuotedDomainField;
+            }
+        }
+
+        private static FieldInfo? s_isQuotedVersionField;
+        private static FieldInfo IsQuotedVersionField
+        {
+            get
+            {
+                if (s_isQuotedVersionField == null)
+                {
+                    // TODO https://github.com/dotnet/runtime/issues/19348:
+                    FieldInfo? field = typeof(Cookie).GetField("IsQuotedVersion", BindingFlags.Instance | BindingFlags.NonPublic);
+                    Debug.Assert(field != null, "We need to use an internal field named IsQuotedVersion that is declared on Cookie.");
+                    s_isQuotedVersionField = field;
+                }
+
+                return s_isQuotedVersionField;
+            }
+        }
+
+        // Get
+        //
+        // Gets the next cookie or null if there are no more cookies.
+        internal Cookie? Get()
+        {
+            Cookie? cookie = null;
+
+            // Only the first occurrence of an attribute value must be counted.
+            bool commentSet = false;
+            bool commentUriSet = false;
+            bool domainSet = false;
+            bool expiresSet = false;
+            bool pathSet = false;
+            bool portSet = false; // Special case: may have no value in header.
+            bool versionSet = false;
+            bool secureSet = false;
+            bool discardSet = false;
+
+            do
+            {
+                CookieToken token = _tokenizer.Next(cookie == null, true);
+                if (cookie == null && (token == CookieToken.NameValuePair || token == CookieToken.Attribute))
+                {
+                    cookie = new Cookie();
+                    InternalSetNameMethod(cookie, _tokenizer.Name);
+                    cookie.Value = _tokenizer.Value;
+                }
+                else
+                {
+                    switch (token)
+                    {
+                        case CookieToken.NameValuePair:
+                            switch (_tokenizer.Token)
+                            {
+                                case CookieToken.Comment:
+                                    if (!commentSet)
+                                    {
+                                        commentSet = true;
+                                        cookie!.Comment = _tokenizer.Value;
+                                    }
+                                    break;
+
+                                case CookieToken.CommentUrl:
+                                    if (!commentUriSet)
+                                    {
+                                        commentUriSet = true;
+                                        if (Uri.TryCreate(CheckQuoted(_tokenizer.Value), UriKind.Absolute, out Uri? parsed))
+                                        {
+                                            cookie!.CommentUri = parsed;
+                                        }
+                                    }
+                                    break;
+
+                                case CookieToken.Domain:
+                                    if (!domainSet)
+                                    {
+                                        domainSet = true;
+                                        cookie!.Domain = CheckQuoted(_tokenizer.Value);
+                                        IsQuotedDomainField.SetValue(cookie, _tokenizer.Quoted);
+                                    }
+                                    break;
+
+                                case CookieToken.Expires:
+                                    if (!expiresSet)
+                                    {
+                                        expiresSet = true;
+
+                                        if (DateTime.TryParse(CheckQuoted(_tokenizer.Value),
+                                            CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AdjustToUniversal, out DateTime expires))
+                                        {
+                                            cookie!.Expires = expires;
+                                        }
+                                        else
+                                        {
+                                            // This cookie will be rejected
+                                            InternalSetNameMethod(cookie!, string.Empty);
+                                        }
+                                    }
+                                    break;
+
+                                case CookieToken.MaxAge:
+                                    if (!expiresSet)
+                                    {
+                                        expiresSet = true;
+                                        if (int.TryParse(CheckQuoted(_tokenizer.Value), out int parsed))
+                                        {
+                                            cookie!.Expires = DateTime.Now.AddSeconds(parsed);
+                                        }
+                                        else
+                                        {
+                                            // This cookie will be rejected
+                                            InternalSetNameMethod(cookie!, string.Empty);
+                                        }
+                                    }
+                                    break;
+
+                                case CookieToken.Path:
+                                    if (!pathSet)
+                                    {
+                                        pathSet = true;
+                                        cookie!.Path = _tokenizer.Value;
+                                    }
+                                    break;
+
+                                case CookieToken.Port:
+                                    if (!portSet)
+                                    {
+                                        portSet = true;
+                                        try
+                                        {
+                                            cookie!.Port = _tokenizer.Value;
+                                        }
+                                        catch
+                                        {
+                                            // This cookie will be rejected
+                                            InternalSetNameMethod(cookie!, string.Empty);
+                                        }
+                                    }
+                                    break;
+
+                                case CookieToken.Version:
+                                    if (!versionSet)
+                                    {
+                                        versionSet = true;
+                                        int parsed;
+                                        if (int.TryParse(CheckQuoted(_tokenizer.Value), out parsed))
+                                        {
+                                            cookie!.Version = parsed;
+                                            IsQuotedVersionField.SetValue(cookie, _tokenizer.Quoted);
+                                        }
+                                        else
+                                        {
+                                            // This cookie will be rejected
+                                            InternalSetNameMethod(cookie!, string.Empty);
+                                        }
+                                    }
+                                    break;
+                            }
+                            break;
+
+                        case CookieToken.Attribute:
+                            switch (_tokenizer.Token)
+                            {
+                                case CookieToken.Discard:
+                                    if (!discardSet)
+                                    {
+                                        discardSet = true;
+                                        cookie!.Discard = true;
+                                    }
+                                    break;
+
+                                case CookieToken.Secure:
+                                    if (!secureSet)
+                                    {
+                                        secureSet = true;
+                                        cookie!.Secure = true;
+                                    }
+                                    break;
+
+                                case CookieToken.HttpOnly:
+                                    cookie!.HttpOnly = true;
+                                    break;
+
+                                case CookieToken.Port:
+                                    if (!portSet)
+                                    {
+                                        portSet = true;
+                                        cookie!.Port = string.Empty;
+                                    }
+                                    break;
+                            }
+                            break;
+                    }
+                }
+            } while (!_tokenizer.Eof && !_tokenizer.EndOfCookie);
+
+            return cookie;
+        }
+
+        internal Cookie? GetServer()
+        {
+            Cookie? cookie = _savedCookie;
+            _savedCookie = null;
+
+            // Only the first occurrence of an attribute value must be counted.
+            bool domainSet = false;
+            bool pathSet = false;
+            bool portSet = false; // Special case: may have no value in header.
+
+            do
+            {
+                bool first = cookie == null || string.IsNullOrEmpty(cookie.Name);
+                CookieToken token = _tokenizer.Next(first, false);
+
+                if (first && (token == CookieToken.NameValuePair || token == CookieToken.Attribute))
+                {
+                    if (cookie == null)
+                    {
+                        cookie = new Cookie();
+                    }
+                    InternalSetNameMethod(cookie, _tokenizer.Name);
+                    cookie.Value = _tokenizer.Value;
+                }
+                else
+                {
+                    switch (token)
+                    {
+                        case CookieToken.NameValuePair:
+                            switch (_tokenizer.Token)
+                            {
+                                case CookieToken.Domain:
+                                    if (!domainSet)
+                                    {
+                                        domainSet = true;
+                                        cookie!.Domain = CheckQuoted(_tokenizer.Value);
+                                        IsQuotedDomainField.SetValue(cookie, _tokenizer.Quoted);
+                                    }
+                                    break;
+
+                                case CookieToken.Path:
+                                    if (!pathSet)
+                                    {
+                                        pathSet = true;
+                                        cookie!.Path = _tokenizer.Value;
+                                    }
+                                    break;
+
+                                case CookieToken.Port:
+                                    if (!portSet)
+                                    {
+                                        portSet = true;
+                                        try
+                                        {
+                                            cookie!.Port = _tokenizer.Value;
+                                        }
+                                        catch (CookieException)
+                                        {
+                                            // This cookie will be rejected
+                                            InternalSetNameMethod(cookie!, string.Empty);
+                                        }
+                                    }
+                                    break;
+
+                                case CookieToken.Version:
+                                    // this is a new cookie, this token is for the next cookie.
+                                    _savedCookie = new Cookie();
+                                    if (int.TryParse(_tokenizer.Value, out int parsed))
+                                    {
+                                        _savedCookie.Version = parsed;
+                                    }
+                                    return cookie;
+
+                                case CookieToken.Unknown:
+                                    // this is a new cookie, the token is for the next cookie.
+                                    _savedCookie = new Cookie();
+                                    InternalSetNameMethod(_savedCookie, _tokenizer.Name);
+                                    _savedCookie.Value = _tokenizer.Value;
+                                    return cookie;
+                            }
+                            break;
+
+                        case CookieToken.Attribute:
+                            if (_tokenizer.Token == CookieToken.Port && !portSet)
+                            {
+                                portSet = true;
+                                cookie!.Port = string.Empty;
+                            }
+                            break;
+                    }
+                }
+            } while (!_tokenizer.Eof && !_tokenizer.EndOfCookie);
+            return cookie;
+        }
+
+        internal static string CheckQuoted(string value)
+        {
+            if (value.Length < 2 || value[0] != '\"' || value[value.Length - 1] != '\"')
+                return value;
+
+            return value.Length == 2 ? string.Empty : value.Substring(1, value.Length - 2);
+        }
+
+        internal bool EndofHeader()
+        {
+            return _tokenizer.Eof;
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/EntitySendFormat.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/EntitySendFormat.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/EntitySendFormat.cs	(revision 376)
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace SpaceWizards.HttpListener
+{
+    internal enum EntitySendFormat
+    {
+        ContentLength = 0, // Content-Length: XXX
+        Chunked = 1, // Transfer-Encoding: chunked
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/ExceptionCheck.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/ExceptionCheck.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/ExceptionCheck.cs	(revision 376)
@@ -0,0 +1,10 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Net
+{
+    internal static class ExceptionCheck
+    {
+        internal static bool IsFatal(Exception exception) => exception is OutOfMemoryException;
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpKnownHeaderNames.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpKnownHeaderNames.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpKnownHeaderNames.cs	(revision 376)
@@ -0,0 +1,94 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Net
+{
+    internal static partial class HttpKnownHeaderNames
+    {
+        // When adding a new constant, add it to HttpKnownHeaderNames.TryGetHeaderName.cs as well.
+
+        public const string Accept = "Accept";
+        public const string AcceptCharset = "Accept-Charset";
+        public const string AcceptEncoding = "Accept-Encoding";
+        public const string AcceptLanguage = "Accept-Language";
+        public const string AcceptPatch = "Accept-Patch";
+        public const string AcceptRanges = "Accept-Ranges";
+        public const string AccessControlAllowCredentials = "Access-Control-Allow-Credentials";
+        public const string AccessControlAllowHeaders = "Access-Control-Allow-Headers";
+        public const string AccessControlAllowMethods = "Access-Control-Allow-Methods";
+        public const string AccessControlAllowOrigin = "Access-Control-Allow-Origin";
+        public const string AccessControlExposeHeaders = "Access-Control-Expose-Headers";
+        public const string AccessControlMaxAge = "Access-Control-Max-Age";
+        public const string Age = "Age";
+        public const string Allow = "Allow";
+        public const string AltSvc = "Alt-Svc";
+        public const string Authorization = "Authorization";
+        public const string CacheControl = "Cache-Control";
+        public const string Connection = "Connection";
+        public const string ContentDisposition = "Content-Disposition";
+        public const string ContentEncoding = "Content-Encoding";
+        public const string ContentLanguage = "Content-Language";
+        public const string ContentLength = "Content-Length";
+        public const string ContentLocation = "Content-Location";
+        public const string ContentMD5 = "Content-MD5";
+        public const string ContentRange = "Content-Range";
+        public const string ContentSecurityPolicy = "Content-Security-Policy";
+        public const string ContentType = "Content-Type";
+        public const string Cookie = "Cookie";
+        public const string Cookie2 = "Cookie2";
+        public const string Date = "Date";
+        public const string ETag = "ETag";
+        public const string Expect = "Expect";
+        public const string Expires = "Expires";
+        public const string From = "From";
+        public const string Host = "Host";
+        public const string IfMatch = "If-Match";
+        public const string IfModifiedSince = "If-Modified-Since";
+        public const string IfNoneMatch = "If-None-Match";
+        public const string IfRange = "If-Range";
+        public const string IfUnmodifiedSince = "If-Unmodified-Since";
+        public const string KeepAlive = "Keep-Alive";
+        public const string LastModified = "Last-Modified";
+        public const string Link = "Link";
+        public const string Location = "Location";
+        public const string MaxForwards = "Max-Forwards";
+        public const string Origin = "Origin";
+        public const string P3P = "P3P";
+        public const string Pragma = "Pragma";
+        public const string ProxyAuthenticate = "Proxy-Authenticate";
+        public const string ProxyAuthorization = "Proxy-Authorization";
+        public const string ProxyConnection = "Proxy-Connection";
+        public const string PublicKeyPins = "Public-Key-Pins";
+        public const string Range = "Range";
+        public const string Referer = "Referer"; // NB: The spelling-mistake "Referer" for "Referrer" must be matched.
+        public const string RetryAfter = "Retry-After";
+        public const string SecWebSocketAccept = "Sec-WebSocket-Accept";
+        public const string SecWebSocketExtensions = "Sec-WebSocket-Extensions";
+        public const string SecWebSocketKey = "Sec-WebSocket-Key";
+        public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol";
+        public const string SecWebSocketVersion = "Sec-WebSocket-Version";
+        public const string Server = "Server";
+        public const string SetCookie = "Set-Cookie";
+        public const string SetCookie2 = "Set-Cookie2";
+        public const string StrictTransportSecurity = "Strict-Transport-Security";
+        public const string TE = "TE";
+        public const string TSV = "TSV";
+        public const string Trailer = "Trailer";
+        public const string TransferEncoding = "Transfer-Encoding";
+        public const string Upgrade = "Upgrade";
+        public const string UpgradeInsecureRequests = "Upgrade-Insecure-Requests";
+        public const string UserAgent = "User-Agent";
+        public const string Vary = "Vary";
+        public const string Via = "Via";
+        public const string WWWAuthenticate = "WWW-Authenticate";
+        public const string Warning = "Warning";
+        public const string XAspNetVersion = "X-AspNet-Version";
+        public const string XContentDuration = "X-Content-Duration";
+        public const string XContentTypeOptions = "X-Content-Type-Options";
+        public const string XFrameOptions = "X-Frame-Options";
+        public const string XMSEdgeRef = "X-MSEdge-Ref";
+        public const string XPoweredBy = "X-Powered-By";
+        public const string XRequestID = "X-Request-ID";
+        public const string XUACompatible = "X-UA-Compatible";
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListener.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListener.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListener.cs	(revision 376)
@@ -0,0 +1,294 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections;
+using System.Diagnostics.CodeAnalysis;
+using System.Net;
+using System.Security.Authentication.ExtendedProtection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SpaceWizards.HttpListener
+{
+    public sealed unsafe partial class HttpListener : IDisposable
+    {
+        public delegate ExtendedProtectionPolicy ExtendedProtectionSelector(HttpListenerRequest request);
+
+        private readonly object _internalLock;
+        private volatile State _state; // _state is set only within lock blocks, but often read outside locks.
+        private readonly HttpListenerPrefixCollection _prefixes;
+        internal Hashtable _uriPrefixes = new Hashtable();
+        private bool _ignoreWriteExceptions;
+        private readonly ServiceNameStore _defaultServiceNames;
+        private readonly HttpListenerTimeoutManager _timeoutManager;
+        private ExtendedProtectionPolicy _extendedProtectionPolicy;
+        private AuthenticationSchemeSelector? _authenticationDelegate;
+        private AuthenticationSchemes _authenticationScheme = AuthenticationSchemes.Anonymous;
+        private ExtendedProtectionSelector? _extendedProtectionSelectorDelegate;
+        private string? _realm;
+
+        internal ICollection PrefixCollection => _uriPrefixes.Keys;
+
+        public HttpListener()
+        {
+            _state = State.Stopped;
+            _internalLock = new object();
+            _defaultServiceNames = new ServiceNameStore();
+
+            _timeoutManager = new HttpListenerTimeoutManager(this);
+            _prefixes = new HttpListenerPrefixCollection(this);
+
+            // default: no CBT checks on any platform (appcompat reasons); applies also to PolicyEnforcement
+            // config element
+            _extendedProtectionPolicy = new ExtendedProtectionPolicy(PolicyEnforcement.Never);
+        }
+
+        public AuthenticationSchemeSelector? AuthenticationSchemeSelectorDelegate
+        {
+            get => _authenticationDelegate;
+            set
+            {
+                CheckDisposed();
+                _authenticationDelegate = value;
+            }
+        }
+
+        [DisallowNull]
+        public ExtendedProtectionSelector? ExtendedProtectionSelectorDelegate
+        {
+            get => _extendedProtectionSelectorDelegate;
+            set
+            {
+                CheckDisposed();
+                if (value == null)
+                {
+                    throw new ArgumentNullException(nameof(value));
+                }
+
+                _extendedProtectionSelectorDelegate = value;
+            }
+        }
+
+        public AuthenticationSchemes AuthenticationSchemes
+        {
+            get => _authenticationScheme;
+            set
+            {
+                CheckDisposed();
+                _authenticationScheme = value;
+            }
+        }
+
+        public ExtendedProtectionPolicy ExtendedProtectionPolicy
+        {
+            get => _extendedProtectionPolicy;
+            set
+            {
+                CheckDisposed();
+                if (value == null)
+                {
+                    throw new ArgumentNullException(nameof(value));
+                }
+                if (value.CustomChannelBinding != null)
+                {
+                    throw new ArgumentException(SR.net_listener_cannot_set_custom_cbt, nameof(value));
+                }
+
+                _extendedProtectionPolicy = value;
+            }
+        }
+
+        public ServiceNameCollection DefaultServiceNames => _defaultServiceNames.ServiceNames;
+
+        public HttpListenerPrefixCollection Prefixes
+        {
+            get
+            {
+                CheckDisposed();
+                return _prefixes;
+            }
+        }
+
+        internal void AddPrefix(string uriPrefix)
+        {
+            string? registeredPrefix = null;
+            try
+            {
+                if (uriPrefix == null)
+                {
+                    throw new ArgumentNullException(nameof(uriPrefix));
+                }
+                CheckDisposed();
+                int i;
+                if (string.Compare(uriPrefix, 0, "http://", 0, 7, StringComparison.OrdinalIgnoreCase) == 0)
+                {
+                    i = 7;
+                }
+                else if (string.Compare(uriPrefix, 0, "https://", 0, 8, StringComparison.OrdinalIgnoreCase) == 0)
+                {
+                    i = 8;
+                }
+                else
+                {
+                    throw new ArgumentException(SR.net_listener_scheme, nameof(uriPrefix));
+                }
+
+                int j = ServiceNameStore.FindEndOfHostname(uriPrefix, i);
+                if (i == j)
+                {
+                    throw new ArgumentException(SR.net_listener_host, nameof(uriPrefix));
+                }
+                if (uriPrefix[uriPrefix.Length - 1] != '/')
+                {
+                    throw new ArgumentException(SR.net_listener_slash, nameof(uriPrefix));
+                }
+                StringBuilder registeredPrefixBuilder = new StringBuilder();
+                if (uriPrefix[j] == ':')
+                {
+                    registeredPrefixBuilder.Append(uriPrefix);
+                }
+                else
+                {
+                    registeredPrefixBuilder.Append(uriPrefix, 0, j);
+                    registeredPrefixBuilder.Append(i == 7 ? ":80" : ":443");
+                    registeredPrefixBuilder.Append(uriPrefix, j, uriPrefix.Length - j);
+                }
+                for (i = 0; registeredPrefixBuilder[i] != ':'; i++)
+                {
+                    registeredPrefixBuilder[i] = (char)CaseInsensitiveAscii.AsciiToLower[(byte)registeredPrefixBuilder[i]];
+                }
+                registeredPrefix = registeredPrefixBuilder.ToString();
+                if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"mapped uriPrefix: {uriPrefix} to registeredPrefix: {registeredPrefix}");
+                if (_state == State.Started)
+                {
+                    AddPrefixCore(registeredPrefix);
+                }
+                _uriPrefixes[uriPrefix] = registeredPrefix;
+                _defaultServiceNames.Add(uriPrefix);
+            }
+            catch (Exception exception)
+            {
+                if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, exception);
+                throw;
+            }
+        }
+
+        internal bool ContainsPrefix(string uriPrefix) => _uriPrefixes.Contains(uriPrefix);
+
+        internal bool RemovePrefix(string uriPrefix)
+        {
+            try
+            {
+                CheckDisposed();
+                if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"uriPrefix: {uriPrefix}");
+                if (uriPrefix == null)
+                {
+                    throw new ArgumentNullException(nameof(uriPrefix));
+                }
+
+                if (!_uriPrefixes.Contains(uriPrefix))
+                {
+                    return false;
+                }
+
+                if (_state == State.Started)
+                {
+                    RemovePrefixCore((string)_uriPrefixes[uriPrefix]!);
+                }
+
+                _uriPrefixes.Remove(uriPrefix);
+                _defaultServiceNames.Remove(uriPrefix);
+            }
+            catch (Exception exception)
+            {
+                if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, exception);
+                throw;
+            }
+            return true;
+        }
+
+        internal void RemoveAll(bool clear)
+        {
+            CheckDisposed();
+            // go through the uri list and unregister for each one of them
+            if (_uriPrefixes.Count > 0)
+            {
+                if (_state == State.Started)
+                {
+                    foreach (string registeredPrefix in _uriPrefixes.Values)
+                    {
+                        RemovePrefixCore(registeredPrefix);
+                    }
+                }
+
+                if (clear)
+                {
+                    _uriPrefixes.Clear();
+                    _defaultServiceNames.Clear();
+                }
+            }
+        }
+
+        public string? Realm
+        {
+            get => _realm;
+            set
+            {
+                CheckDisposed();
+                _realm = value;
+            }
+        }
+
+        public bool IsListening => _state == State.Started;
+
+        public bool IgnoreWriteExceptions
+        {
+            get => _ignoreWriteExceptions;
+            set
+            {
+                CheckDisposed();
+                _ignoreWriteExceptions = value;
+            }
+        }
+
+        public Task<HttpListenerContext> GetContextAsync()
+        {
+            return Task.Factory.FromAsync(
+                (callback, state) => ((HttpListener)state!).BeginGetContext(callback, state),
+                iar => ((HttpListener)iar!.AsyncState!).EndGetContext(iar),
+                this);
+        }
+
+        public void Close()
+        {
+            try
+            {
+                if (NetEventSource.Log.IsEnabled()) NetEventSource.Info("HttpListenerRequest::Close()");
+                ((IDisposable)this).Dispose();
+            }
+            catch (Exception exception)
+            {
+                if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, $"Close {exception}");
+                throw;
+            }
+        }
+
+        internal void CheckDisposed()
+        {
+            if (_state == State.Closed)
+            {
+                throw new ObjectDisposedException(GetType().FullName);
+            }
+        }
+
+        private enum State
+        {
+            Stopped,
+            Started,
+            Closed,
+        }
+
+        void IDisposable.Dispose() => Dispose();
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerBasicIdentity.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerBasicIdentity.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerBasicIdentity.cs	(revision 376)
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Security.Principal;
+
+namespace SpaceWizards.HttpListener
+{
+    public class HttpListenerBasicIdentity : GenericIdentity
+    {
+        public HttpListenerBasicIdentity(string username, string password) :
+            base(username, "Basic")
+        {
+            Password = password;
+        }
+
+        public virtual string Password { get; }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerContext.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerContext.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerContext.cs	(revision 376)
@@ -0,0 +1,50 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Net;
+using System.Net.WebSockets;
+using System.Security.Principal;
+using System.Threading.Tasks;
+using SpaceWizards.HttpListener.WebSockets;
+using HttpListenerWebSocketContext = SpaceWizards.HttpListener.WebSockets.HttpListenerWebSocketContext;
+
+namespace SpaceWizards.HttpListener
+{
+    public sealed unsafe partial class HttpListenerContext
+    {
+        internal HttpListener? _listener;
+        private HttpListenerResponse? _response;
+        private IPrincipal? _user;
+
+        public HttpListenerRequest Request { get; }
+
+        public IPrincipal? User => _user;
+
+        // This can be used to cache the results of HttpListener.AuthenticationSchemeSelectorDelegate.
+        internal AuthenticationSchemes AuthenticationSchemes { get; set; }
+
+        public HttpListenerResponse Response
+        {
+            get
+            {
+                if (_response == null)
+                {
+                    _response = new HttpListenerResponse(this);
+                }
+
+                return _response;
+            }
+        }
+
+        public Task<WebSockets.HttpListenerWebSocketContext> AcceptWebSocketAsync(string? subProtocol)
+        {
+            return AcceptWebSocketAsync(subProtocol, HttpWebSocket.DefaultReceiveBufferSize, WebSocket.DefaultKeepAliveInterval);
+        }
+
+        public Task<WebSockets.HttpListenerWebSocketContext> AcceptWebSocketAsync(string? subProtocol, TimeSpan keepAliveInterval)
+        {
+            return AcceptWebSocketAsync(subProtocol, HttpWebSocket.DefaultReceiveBufferSize, keepAliveInterval);
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerException.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerException.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerException.cs	(revision 376)
@@ -0,0 +1,40 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.ComponentModel;
+using System.Net;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+
+namespace SpaceWizards.HttpListener
+{
+    [Serializable]
+    public class HttpListenerException : Win32Exception
+    {
+        public HttpListenerException() : base(Marshal.GetLastPInvokeError())
+        {
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"{NativeErrorCode}:{Message}");
+        }
+
+        public HttpListenerException(int errorCode) : base(errorCode)
+        {
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"{NativeErrorCode}:{Message}");
+        }
+
+        public HttpListenerException(int errorCode, string message) : base(errorCode, message)
+        {
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"{NativeErrorCode}:{Message}");
+        }
+
+        protected HttpListenerException(SerializationInfo serializationInfo, StreamingContext streamingContext)
+            : base(serializationInfo, streamingContext)
+        {
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"{NativeErrorCode}:{Message}");
+        }
+
+        // the base class returns the HResult with this property
+        // we need the Win32 Error Code, hence the override.
+        public override int ErrorCode => NativeErrorCode;
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerPrefixCollection.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerPrefixCollection.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerPrefixCollection.cs	(revision 376)
@@ -0,0 +1,104 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace SpaceWizards.HttpListener
+{
+    internal sealed class ListenerPrefixEnumerator : IEnumerator<string>
+    {
+        private readonly IEnumerator _enumerator;
+
+        internal ListenerPrefixEnumerator(IEnumerator enumerator)
+        {
+            _enumerator = enumerator;
+        }
+
+        public string Current => (string)_enumerator.Current;
+
+        public bool MoveNext() => _enumerator.MoveNext();
+
+        public void Dispose() { }
+
+        void IEnumerator.Reset() => _enumerator.Reset();
+
+        object IEnumerator.Current => _enumerator.Current;
+    }
+
+    public class HttpListenerPrefixCollection : ICollection<string>
+    {
+        private readonly HttpListener _httpListener;
+
+        internal HttpListenerPrefixCollection(HttpListener listener)
+        {
+            _httpListener = listener;
+        }
+
+        public void CopyTo(Array array, int offset)
+        {
+            _httpListener.CheckDisposed();
+            if (array == null)
+            {
+                throw new ArgumentNullException(nameof(array));
+            }
+            if (Count > array.Length)
+            {
+                throw new ArgumentOutOfRangeException(nameof(array), SR.net_array_too_small);
+            }
+            if (offset + Count > array.Length)
+            {
+                throw new ArgumentOutOfRangeException(nameof(offset));
+            }
+            int index = 0;
+            foreach (string uriPrefix in _httpListener.PrefixCollection)
+            {
+                array.SetValue(uriPrefix, offset + index++);
+            }
+        }
+
+        public void CopyTo(string[] array, int offset)
+        {
+            _httpListener.CheckDisposed();
+            if (array == null)
+            {
+                throw new ArgumentNullException(nameof(array));
+            }
+            if (Count > array.Length)
+            {
+                throw new ArgumentOutOfRangeException(nameof(array), SR.net_array_too_small);
+            }
+            if (offset + Count > array.Length)
+            {
+                throw new ArgumentOutOfRangeException(nameof(offset));
+            }
+            int index = 0;
+            foreach (string uriPrefix in _httpListener.PrefixCollection)
+            {
+                array[offset + index++] = uriPrefix;
+            }
+        }
+
+        public int Count => _httpListener.PrefixCollection.Count;
+
+        public bool IsSynchronized => false;
+
+        public bool IsReadOnly => false;
+
+        public void Add(string uriPrefix) => _httpListener.AddPrefix(uriPrefix);
+
+        public bool Contains(string uriPrefix) => _httpListener.ContainsPrefix(uriPrefix);
+
+        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+        public IEnumerator<string> GetEnumerator()
+        {
+            return new ListenerPrefixEnumerator(_httpListener.PrefixCollection.GetEnumerator());
+        }
+
+        public bool Remove(string uriPrefix) => _httpListener.RemovePrefix(uriPrefix);
+
+        public void Clear() => _httpListener.RemoveAll(true);
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerRequest.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerRequest.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerRequest.cs	(revision 376)
@@ -0,0 +1,604 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Globalization;
+using System.Net;
+using System.Net.WebSockets;
+using System.Reflection;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Threading.Tasks;
+using SpaceWizards.HttpListener.WebSockets;
+
+namespace SpaceWizards.HttpListener
+{
+    public sealed unsafe partial class HttpListenerRequest
+    {
+        private CookieCollection? _cookies;
+        private bool? _keepAlive;
+        private string? _rawUrl;
+        private Uri? _requestUri;
+        private Version _version;
+
+        public string[]? AcceptTypes => Helpers.ParseMultivalueHeader(Headers[HttpKnownHeaderNames.Accept]!);
+
+        public string[]? UserLanguages => Helpers.ParseMultivalueHeader(Headers[HttpKnownHeaderNames.AcceptLanguage]!);
+
+        private CookieCollection ParseCookies(Uri? uri, string setCookieHeader)
+        {
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "uri:" + uri + " setCookieHeader:" + setCookieHeader);
+            CookieCollection cookies = new CookieCollection();
+            CookieParser parser = new CookieParser(setCookieHeader);
+            while (true)
+            {
+                Cookie? cookie = parser.GetServer();
+                if (cookie == null)
+                {
+                    // EOF, done.
+                    break;
+                }
+                if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "CookieParser returned cookie: " + cookie.ToString());
+                if (cookie.Name.Length == 0)
+                {
+                    continue;
+                }
+
+                cookies.InternalAdd(cookie, true);
+            }
+            return cookies;
+        }
+
+        public CookieCollection Cookies
+        {
+            get
+            {
+                if (_cookies == null)
+                {
+                    string? cookieString = Headers[HttpKnownHeaderNames.Cookie];
+                    if (!string.IsNullOrEmpty(cookieString))
+                    {
+                        _cookies = ParseCookies(RequestUri, cookieString);
+                    }
+                    if (_cookies == null)
+                    {
+                        _cookies = new CookieCollection();
+                    }
+                }
+                return _cookies;
+            }
+        }
+
+        public Encoding ContentEncoding
+        {
+            get
+            {
+                if (UserAgent != null && CultureInfo.InvariantCulture.CompareInfo.IsPrefix(UserAgent, "UP"))
+                {
+                    string? postDataCharset = Headers["x-up-devcap-post-charset"];
+                    if (postDataCharset != null && postDataCharset.Length > 0)
+                    {
+                        try
+                        {
+                            return Encoding.GetEncoding(postDataCharset);
+                        }
+                        catch (ArgumentException)
+                        {
+                        }
+                    }
+                }
+                if (HasEntityBody)
+                {
+                    if (ContentType != null)
+                    {
+                        string? charSet = Helpers.GetCharSetValueFromHeader(ContentType);
+                        if (charSet != null)
+                        {
+                            try
+                            {
+                                return Encoding.GetEncoding(charSet);
+                            }
+                            catch (ArgumentException)
+                            {
+                            }
+                        }
+                    }
+                }
+                return Encoding.Default;
+            }
+        }
+
+        public string? ContentType => Headers[HttpKnownHeaderNames.ContentType];
+
+        public bool IsLocal => LocalEndPoint!.Address.Equals(RemoteEndPoint!.Address);
+
+        public bool IsWebSocketRequest
+        {
+            get
+            {
+                if (!SupportsWebSockets)
+                {
+                    return false;
+                }
+
+                bool foundConnectionUpgradeHeader = false;
+                if (string.IsNullOrEmpty(Headers[HttpKnownHeaderNames.Connection]) || string.IsNullOrEmpty(Headers[HttpKnownHeaderNames.Upgrade]))
+                {
+                    return false;
+                }
+
+                foreach (string connection in Headers.GetValues(HttpKnownHeaderNames.Connection)!)
+                {
+                    if (string.Equals(connection, HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase))
+                    {
+                        foundConnectionUpgradeHeader = true;
+                        break;
+                    }
+                }
+
+                if (!foundConnectionUpgradeHeader)
+                {
+                    return false;
+                }
+
+                foreach (string upgrade in Headers.GetValues(HttpKnownHeaderNames.Upgrade)!)
+                {
+                    if (string.Equals(upgrade, HttpWebSocket.WebSocketUpgradeToken, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+        }
+
+        public bool KeepAlive
+        {
+            get
+            {
+                if (!_keepAlive.HasValue)
+                {
+                    string? header = Headers[HttpKnownHeaderNames.ProxyConnection];
+                    if (string.IsNullOrEmpty(header))
+                    {
+                        header = Headers[HttpKnownHeaderNames.Connection];
+                    }
+                    if (string.IsNullOrEmpty(header))
+                    {
+                        if (ProtocolVersion >= HttpVersion.Version11)
+                        {
+                            _keepAlive = true;
+                        }
+                        else
+                        {
+                            header = Headers[HttpKnownHeaderNames.KeepAlive];
+                            _keepAlive = !string.IsNullOrEmpty(header);
+                        }
+                    }
+                    else
+                    {
+                        header = header.ToLowerInvariant();
+                        _keepAlive =
+                            header.IndexOf("close", StringComparison.OrdinalIgnoreCase) < 0 ||
+                            header.Contains("keep-alive", StringComparison.OrdinalIgnoreCase);
+                    }
+                }
+
+                if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "_keepAlive=" + _keepAlive);
+                return _keepAlive.Value;
+            }
+        }
+
+        public NameValueCollection QueryString
+        {
+            get
+            {
+                NameValueCollection queryString = new NameValueCollection();
+                Helpers.FillFromString(queryString, Url!.Query, true, ContentEncoding);
+                return queryString;
+            }
+        }
+
+        public string? RawUrl => _rawUrl;
+
+        private string RequestScheme => IsSecureConnection ? UriScheme.Https : UriScheme.Http;
+
+        public string UserAgent => Headers[HttpKnownHeaderNames.UserAgent]!;
+
+        public string UserHostAddress => LocalEndPoint!.ToString();
+
+        public string UserHostName => Headers[HttpKnownHeaderNames.Host]!;
+
+        public Uri? UrlReferrer
+        {
+            get
+            {
+                string? referrer = Headers[HttpKnownHeaderNames.Referer];
+                if (referrer == null)
+                {
+                    return null;
+                }
+
+                bool success = Uri.TryCreate(referrer, UriKind.RelativeOrAbsolute, out Uri? urlReferrer);
+                return success ? urlReferrer : null;
+            }
+        }
+
+        public Uri? Url => RequestUri;
+
+        public Version ProtocolVersion => _version;
+
+        public X509Certificate2? GetClientCertificate()
+        {
+            if (ClientCertState == ListenerClientCertState.InProgress)
+                throw new InvalidOperationException(SR.Format(SR.net_listener_callinprogress, $"{nameof(GetClientCertificate)}()/{nameof(BeginGetClientCertificate)}()"));
+            ClientCertState = ListenerClientCertState.InProgress;
+
+            GetClientCertificateCore();
+
+            ClientCertState = ListenerClientCertState.Completed;
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"_clientCertificate:{ClientCertificate}");
+
+            return ClientCertificate;
+        }
+
+        public IAsyncResult BeginGetClientCertificate(AsyncCallback? requestCallback, object? state)
+        {
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this);
+            if (ClientCertState == ListenerClientCertState.InProgress)
+                throw new InvalidOperationException(SR.Format(SR.net_listener_callinprogress, $"{nameof(GetClientCertificate)}()/{nameof(BeginGetClientCertificate)}()"));
+            ClientCertState = ListenerClientCertState.InProgress;
+
+            return BeginGetClientCertificateCore(requestCallback, state);
+        }
+
+        public Task<X509Certificate2?> GetClientCertificateAsync()
+        {
+            return Task.Factory.FromAsync(
+                (callback, state) => ((HttpListenerRequest)state!).BeginGetClientCertificate(callback, state),
+                iar => ((HttpListenerRequest)iar.AsyncState!).EndGetClientCertificate(iar),
+                this);
+        }
+
+        internal ListenerClientCertState ClientCertState { get; set; } = ListenerClientCertState.NotInitialized;
+        internal X509Certificate2? ClientCertificate { get; set; }
+
+        public int ClientCertificateError
+        {
+            get
+            {
+                if (ClientCertState == ListenerClientCertState.NotInitialized)
+                    throw new InvalidOperationException(SR.Format(SR.net_listener_mustcall, "GetClientCertificate()/BeginGetClientCertificate()"));
+                else if (ClientCertState == ListenerClientCertState.InProgress)
+                    throw new InvalidOperationException(SR.Format(SR.net_listener_mustcompletecall, "GetClientCertificate()/BeginGetClientCertificate()"));
+
+                return GetClientCertificateErrorCore();
+            }
+        }
+
+        private static class Helpers
+        {
+            //
+            // Get attribute off header value
+            //
+            internal static string? GetCharSetValueFromHeader(string headerValue)
+            {
+                const string AttrName = "charset";
+
+                if (headerValue == null)
+                    return null;
+
+                int l = headerValue.Length;
+                int k = AttrName.Length;
+
+                // find properly separated attribute name
+                int i = 1; // start searching from 1
+
+                while (i < l)
+                {
+                    i = CultureInfo.InvariantCulture.CompareInfo.IndexOf(headerValue, AttrName, i, CompareOptions.IgnoreCase);
+                    if (i < 0)
+                        break;
+                    if (i + k >= l)
+                        break;
+
+                    char chPrev = headerValue[i - 1];
+                    char chNext = headerValue[i + k];
+                    if ((chPrev == ';' || chPrev == ',' || char.IsWhiteSpace(chPrev)) && (chNext == '=' || char.IsWhiteSpace(chNext)))
+                        break;
+
+                    i += k;
+                }
+
+                if (i < 0 || i >= l)
+                    return null;
+
+                // skip to '=' and the following whitespace
+                i += k;
+                while (i < l && char.IsWhiteSpace(headerValue[i]))
+                    i++;
+                if (i >= l || headerValue[i] != '=')
+                    return null;
+                i++;
+                while (i < l && char.IsWhiteSpace(headerValue[i]))
+                    i++;
+                if (i >= l)
+                    return null;
+
+                // parse the value
+                string? attrValue = null;
+
+                int j;
+
+                if (i < l && headerValue[i] == '"')
+                {
+                    if (i == l - 1)
+                        return null;
+                    j = headerValue.IndexOf('"', i + 1);
+                    if (j < 0 || j == i + 1)
+                        return null;
+
+                    attrValue = headerValue.AsSpan(i + 1, j - i - 1).Trim().ToString();
+                }
+                else
+                {
+                    for (j = i; j < l; j++)
+                    {
+                        if (headerValue[j] == ';')
+                            break;
+                    }
+
+                    if (j == i)
+                        return null;
+
+                    attrValue = headerValue.AsSpan(i, j - i).Trim().ToString();
+                }
+
+                return attrValue;
+            }
+
+            internal static string[]? ParseMultivalueHeader(string s)
+            {
+                if (s == null)
+                    return null;
+
+                int l = s.Length;
+
+                // collect comma-separated values into list
+
+                List<string> values = new List<string>();
+                int i = 0;
+
+                while (i < l)
+                {
+                    // find next ,
+                    int ci = s.IndexOf(',', i);
+                    if (ci < 0)
+                        ci = l;
+
+                    // append corresponding server value
+                    values.Add(s.Substring(i, ci - i));
+
+                    // move to next
+                    i = ci + 1;
+
+                    // skip leading space
+                    if (i < l && s[i] == ' ')
+                        i++;
+                }
+
+                // return list as array of strings
+
+                int n = values.Count;
+                string[] strings;
+
+                // if n is 0 that means s was empty string
+
+                if (n == 0)
+                {
+                    strings = new string[1];
+                    strings[0] = string.Empty;
+                }
+                else
+                {
+                    strings = new string[n];
+                    values.CopyTo(0, strings, 0, n);
+                }
+                return strings;
+            }
+
+
+            private static string UrlDecodeStringFromStringInternal(string s, Encoding e)
+            {
+                int count = s.Length;
+                UrlDecoder helper = new UrlDecoder(count, e);
+
+                // go through the string's chars collapsing %XX and %uXXXX and
+                // appending each char as char, with exception of %XX constructs
+                // that are appended as bytes
+
+                for (int pos = 0; pos < count; pos++)
+                {
+                    char ch = s[pos];
+
+                    if (ch == '+')
+                    {
+                        ch = ' ';
+                    }
+                    else if (ch == '%' && pos < count - 2)
+                    {
+                        if (s[pos + 1] == 'u' && pos < count - 5)
+                        {
+                            int h1 = HexConverter.FromChar(s[pos + 2]);
+                            int h2 = HexConverter.FromChar(s[pos + 3]);
+                            int h3 = HexConverter.FromChar(s[pos + 4]);
+                            int h4 = HexConverter.FromChar(s[pos + 5]);
+
+                            if ((h1 | h2 | h3 | h4) != 0xFF)
+                            {   // valid 4 hex chars
+                                ch = (char)((h1 << 12) | (h2 << 8) | (h3 << 4) | h4);
+                                pos += 5;
+
+                                // only add as char
+                                helper.AddChar(ch);
+                                continue;
+                            }
+                        }
+                        else
+                        {
+                            int h1 = HexConverter.FromChar(s[pos + 1]);
+                            int h2 = HexConverter.FromChar(s[pos + 2]);
+
+                            if ((h1 | h2) != 0xFF)
+                            {     // valid 2 hex chars
+                                byte b = (byte)((h1 << 4) | h2);
+                                pos += 2;
+
+                                // don't add as char
+                                helper.AddByte(b);
+                                continue;
+                            }
+                        }
+                    }
+
+                    if ((ch & 0xFF80) == 0)
+                        helper.AddByte((byte)ch); // 7 bit have to go as bytes because of Unicode
+                    else
+                        helper.AddChar(ch);
+                }
+
+                return helper.GetString();
+            }
+
+            private sealed class UrlDecoder
+            {
+                private readonly int _bufferSize;
+
+                // Accumulate characters in a special array
+                private int _numChars;
+                private readonly char[] _charBuffer;
+
+                // Accumulate bytes for decoding into characters in a special array
+                private int _numBytes;
+                private byte[]? _byteBuffer;
+
+                // Encoding to convert chars to bytes
+                private readonly Encoding _encoding;
+
+                private void FlushBytes()
+                {
+                    if (_numBytes > 0)
+                    {
+                        _numChars += _encoding.GetChars(_byteBuffer!, 0, _numBytes, _charBuffer, _numChars);
+                        _numBytes = 0;
+                    }
+                }
+
+                internal UrlDecoder(int bufferSize, Encoding encoding)
+                {
+                    _bufferSize = bufferSize;
+                    _encoding = encoding;
+
+                    _charBuffer = new char[bufferSize];
+                    // byte buffer created on demand
+                }
+
+                internal void AddChar(char ch)
+                {
+                    if (_numBytes > 0)
+                        FlushBytes();
+
+                    _charBuffer[_numChars++] = ch;
+                }
+
+                internal void AddByte(byte b)
+                {
+                    {
+                        if (_byteBuffer == null)
+                            _byteBuffer = new byte[_bufferSize];
+
+                        _byteBuffer[_numBytes++] = b;
+                    }
+                }
+
+                internal string GetString()
+                {
+                    if (_numBytes > 0)
+                        FlushBytes();
+
+                    if (_numChars > 0)
+                        return new string(_charBuffer, 0, _numChars);
+                    else
+                        return string.Empty;
+                }
+            }
+
+
+            internal static void FillFromString(NameValueCollection nvc, string s, bool urlencoded, Encoding encoding)
+            {
+                int l = s.Length;
+                int i = (l > 0 && s[0] == '?') ? 1 : 0;
+
+                while (i < l)
+                {
+                    // find next & while noting first = on the way (and if there are more)
+
+                    int si = i;
+                    int ti = -1;
+
+                    while (i < l)
+                    {
+                        char ch = s[i];
+
+                        if (ch == '=')
+                        {
+                            if (ti < 0)
+                                ti = i;
+                        }
+                        else if (ch == '&')
+                        {
+                            break;
+                        }
+
+                        i++;
+                    }
+
+                    // extract the name / value pair
+
+                    string? name = null;
+                    string? value = null;
+
+                    if (ti >= 0)
+                    {
+                        name = s.Substring(si, ti - si);
+                        value = s.Substring(ti + 1, i - ti - 1);
+                    }
+                    else
+                    {
+                        value = s.Substring(si, i - si);
+                    }
+
+                    // add name / value pair to the collection
+
+                    if (urlencoded)
+                        nvc.Add(
+                           name == null ? null : UrlDecodeStringFromStringInternal(name, encoding),
+                           UrlDecodeStringFromStringInternal(value, encoding));
+                    else
+                        nvc.Add(name, value);
+
+                    // trailing '&'
+
+                    if (i == l - 1 && s[i] == '&')
+                        nvc.Add(null, "");
+
+                    i++;
+                }
+            }
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerRequestUriBuilder.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerRequestUriBuilder.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerRequestUriBuilder.cs	(revision 376)
@@ -0,0 +1,456 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics;
+using System.Text;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Net;
+
+namespace SpaceWizards.HttpListener
+{
+    // We don't use the cooked URL because http.sys unescapes all percent-encoded values. However,
+    // we also can't just use the raw Uri, since http.sys supports not only Utf-8, but also ANSI/DBCS and
+    // Unicode code points. System.Uri only supports Utf-8.
+    // The purpose of this class is to convert all ANSI, DBCS, and Unicode code points into percent encoded
+    // Utf-8 characters.
+    internal sealed class HttpListenerRequestUriBuilder
+    {
+        private static readonly Encoding s_utf8Encoding = new UTF8Encoding(false, true);
+        private static readonly Encoding s_ansiEncoding = Encoding.GetEncoding(0, new EncoderExceptionFallback(), new DecoderExceptionFallback());
+
+        private readonly string _rawUri;
+        private readonly string _cookedUriScheme;
+        private readonly string _cookedUriHost;
+        private readonly string _cookedUriPath;
+        private readonly string _cookedUriQuery;
+
+        // This field is used to build the final request Uri string from the Uri parts passed to the ctor.
+        private StringBuilder? _requestUriString;
+
+        // The raw path is parsed by looping through all characters from left to right. 'rawOctets'
+        // is used to store consecutive percent encoded octets as actual byte values: e.g. for path /pa%C3%84th%2F/
+        // rawOctets will be set to { 0xC3, 0x84 } when we reach character 't' and it will be { 0x2F } when
+        // we reach the final '/'. I.e. after a sequence of percent encoded octets ends, we use rawOctets as
+        // input to the encoding and percent encode the resulting string into UTF-8 octets.
+        //
+        // When parsing ANSI (Latin 1) encoded path '/pa%C4th/', %C4 will be added to rawOctets and when
+        // we reach 't', the content of rawOctets { 0xC4 } will be fed into the ANSI encoding. The resulting
+        // string '\u00C4' will be percent encoded into UTF-8 octets and appended to requestUriString. The final
+        // path will be '/pa%C3%84th/', where '%C3%84' is the UTF-8 percent encoded character.
+        private List<byte>? _rawOctets;
+        private string? _rawPath;
+
+        // Holds the final request Uri.
+        private Uri? _requestUri;
+
+        private HttpListenerRequestUriBuilder(string rawUri, string cookedUriScheme, string cookedUriHost,
+            string cookedUriPath, string cookedUriQuery)
+        {
+            Debug.Assert(!string.IsNullOrEmpty(rawUri), "Empty raw URL.");
+            Debug.Assert(!string.IsNullOrEmpty(cookedUriScheme), "Empty cooked URL scheme.");
+            Debug.Assert(!string.IsNullOrEmpty(cookedUriHost), "Empty cooked URL host.");
+            Debug.Assert(!string.IsNullOrEmpty(cookedUriPath), "Empty cooked URL path.");
+
+            _rawUri = rawUri;
+            _cookedUriScheme = cookedUriScheme;
+            _cookedUriHost = cookedUriHost;
+            _cookedUriPath = AddSlashToAsteriskOnlyPath(cookedUriPath);
+            _cookedUriQuery = cookedUriQuery ?? string.Empty;
+        }
+
+        public static Uri GetRequestUri(string rawUri, string cookedUriScheme, string cookedUriHost,
+            string cookedUriPath, string cookedUriQuery)
+        {
+            HttpListenerRequestUriBuilder builder = new HttpListenerRequestUriBuilder(rawUri,
+                cookedUriScheme, cookedUriHost, cookedUriPath, cookedUriQuery);
+
+            return builder.Build();
+        }
+
+        private Uri Build()
+        {
+            BuildRequestUriUsingRawPath();
+
+            if (_requestUri == null)
+            {
+                BuildRequestUriUsingCookedPath();
+            }
+
+            return _requestUri!;
+        }
+
+        private void BuildRequestUriUsingCookedPath()
+        {
+            bool isValid = Uri.TryCreate(_cookedUriScheme + Uri.SchemeDelimiter + _cookedUriHost + _cookedUriPath +
+                _cookedUriQuery, UriKind.Absolute, out _requestUri);
+
+            // Creating a Uri from the cooked Uri should really always work: If not, we log at least.
+            if (!isValid)
+            {
+                if (NetEventSource.Log.IsEnabled())
+                    NetEventSource.Error(this, SR.Format(SR.net_log_listener_cant_create_uri, _cookedUriScheme, _cookedUriHost, _cookedUriPath, _cookedUriQuery));
+            }
+        }
+
+        private void BuildRequestUriUsingRawPath()
+        {
+            bool isValid = false;
+
+            // Initialize 'rawPath' only if really needed; i.e. if we build the request Uri from the raw Uri.
+            _rawPath = GetPath(_rawUri);
+
+            // Try to check the raw path using first the primary encoding (according to http.sys settings);
+            // if it fails try the secondary encoding.
+            ParsingResult result = BuildRequestUriUsingRawPath(GetEncoding(EncodingType.Primary));
+            if (result == ParsingResult.EncodingError)
+            {
+                Encoding secondaryEncoding = GetEncoding(EncodingType.Secondary);
+                result = BuildRequestUriUsingRawPath(secondaryEncoding);
+            }
+            isValid = (result == ParsingResult.Success) ? true : false;
+
+            // Log that we weren't able to create a Uri from the raw string.
+            if (!isValid)
+            {
+                if (NetEventSource.Log.IsEnabled())
+                    NetEventSource.Error(this, SR.Format(SR.net_log_listener_cant_create_uri, _cookedUriScheme, _cookedUriHost, _rawPath, _cookedUriQuery));
+            }
+        }
+
+        private static Encoding GetEncoding(EncodingType type)
+        {
+            Debug.Assert((type == EncodingType.Primary) || (type == EncodingType.Secondary),
+                $"Unknown 'EncodingType' value: {type}");
+
+            if (type == EncodingType.Secondary)
+            {
+                return s_ansiEncoding;
+            }
+            else
+            {
+                return s_utf8Encoding;
+            }
+        }
+
+        private ParsingResult BuildRequestUriUsingRawPath(Encoding encoding)
+        {
+            Debug.Assert(encoding != null, "'encoding' must be assigned.");
+            Debug.Assert(!string.IsNullOrEmpty(_rawPath), "'rawPath' must have at least one character.");
+
+            _rawOctets = new List<byte>();
+            _requestUriString = new StringBuilder();
+            _requestUriString.Append(_cookedUriScheme);
+            _requestUriString.Append(Uri.SchemeDelimiter);
+            _requestUriString.Append(_cookedUriHost);
+
+            ParsingResult result = ParseRawPath(encoding);
+            if (result == ParsingResult.Success)
+            {
+                _requestUriString.Append(_cookedUriQuery);
+
+                Debug.Assert(_rawOctets.Count == 0,
+                    "Still raw octets left. They must be added to the result path.");
+
+                if (!Uri.TryCreate(_requestUriString.ToString(), UriKind.Absolute, out _requestUri))
+                {
+                    // If we can't create a Uri from the string, this is an invalid string and it doesn't make
+                    // sense to try another encoding.
+                    result = ParsingResult.InvalidString;
+                }
+            }
+
+            if (result != ParsingResult.Success)
+            {
+                if (NetEventSource.Log.IsEnabled())
+                    NetEventSource.Error(this, SR.Format(SR.net_log_listener_cant_convert_raw_path, _rawPath, encoding.EncodingName));
+            }
+
+            return result;
+        }
+
+        private ParsingResult ParseRawPath(Encoding encoding)
+        {
+            Debug.Assert(encoding != null, "'encoding' must be assigned.");
+
+            int index = 0;
+            char current = '\0';
+            Debug.Assert(_rawPath != null);
+            while (index < _rawPath.Length)
+            {
+                current = _rawPath[index];
+                if (current == '%')
+                {
+                    // Assert is enough, since http.sys accepted the request string already. This should never happen.
+                    Debug.Assert(index + 2 < _rawPath.Length, "Expected >=2 characters after '%' (e.g. %2F)");
+
+                    index++;
+                    current = _rawPath[index];
+                    if (current == 'u' || current == 'U')
+                    {
+                        // We found "%u" which means, we have a Unicode code point of the form "%uXXXX".
+                        Debug.Assert(index + 4 < _rawPath.Length, "Expected >=4 characters after '%u' (e.g. %u0062)");
+
+                        // Decode the content of rawOctets into percent encoded UTF-8 characters and append them
+                        // to requestUriString.
+                        if (!EmptyDecodeAndAppendRawOctetsList(encoding))
+                        {
+                            return ParsingResult.EncodingError;
+                        }
+                        if (!AppendUnicodeCodePointValuePercentEncoded(_rawPath.Substring(index + 1, 4)))
+                        {
+                            return ParsingResult.InvalidString;
+                        }
+                        index += 5;
+                    }
+                    else
+                    {
+                        // We found '%', but not followed by 'u', i.e. we have a percent encoded octed: %XX
+                        if (!AddPercentEncodedOctetToRawOctetsList(encoding, _rawPath.Substring(index, 2)))
+                        {
+                            return ParsingResult.InvalidString;
+                        }
+                        index += 2;
+                    }
+                }
+                else
+                {
+                    // We found a non-'%' character: decode the content of rawOctets into percent encoded
+                    // UTF-8 characters and append it to the result.
+                    if (!EmptyDecodeAndAppendRawOctetsList(encoding))
+                    {
+                        return ParsingResult.EncodingError;
+                    }
+                    // Append the current character to the result.
+                    Debug.Assert(_requestUriString != null);
+                    _requestUriString!.Append(current);
+                    index++;
+                }
+            }
+
+            // if the raw path ends with a sequence of percent encoded octets, make sure those get added to the
+            // result (requestUriString).
+            if (!EmptyDecodeAndAppendRawOctetsList(encoding))
+            {
+                return ParsingResult.EncodingError;
+            }
+
+            return ParsingResult.Success;
+        }
+
+        private bool AppendUnicodeCodePointValuePercentEncoded(string codePoint)
+        {
+            // http.sys only supports %uXXXX (4 hex-digits), even though unicode code points could have up to
+            // 6 hex digits. Therefore we parse always 4 characters after %u and convert them to an int.
+            int codePointValue;
+            if (!int.TryParse(codePoint, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePointValue))
+            {
+                if (NetEventSource.Log.IsEnabled())
+                    NetEventSource.Error(this, SR.Format(SR.net_log_listener_cant_convert_percent_value, codePoint));
+                return false;
+            }
+
+            string? unicodeString = null;
+            try
+            {
+                unicodeString = char.ConvertFromUtf32(codePointValue);
+                AppendOctetsPercentEncoded(_requestUriString!, s_utf8Encoding.GetBytes(unicodeString));
+
+                return true;
+            }
+            catch (ArgumentOutOfRangeException)
+            {
+                if (NetEventSource.Log.IsEnabled())
+                    NetEventSource.Error(this, SR.Format(SR.net_log_listener_cant_convert_percent_value, codePoint));
+            }
+            catch (EncoderFallbackException e)
+            {
+                // If utf8Encoding.GetBytes() fails
+                if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, SR.Format(SR.net_log_listener_cant_convert_to_utf8, unicodeString, e.Message));
+            }
+
+            return false;
+        }
+
+        private bool AddPercentEncodedOctetToRawOctetsList(Encoding encoding, string escapedCharacter)
+        {
+            byte encodedValue;
+            if (!byte.TryParse(escapedCharacter, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out encodedValue))
+            {
+                if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, SR.Format(SR.net_log_listener_cant_convert_percent_value, escapedCharacter));
+                return false;
+            }
+
+            Debug.Assert(_rawOctets != null);
+            _rawOctets!.Add(encodedValue);
+
+            return true;
+        }
+
+        private bool EmptyDecodeAndAppendRawOctetsList(Encoding encoding)
+        {
+            if (_rawOctets!.Count == 0)
+            {
+                return true;
+            }
+
+            string? decodedString = null;
+            try
+            {
+                // If the encoding can get a string out of the byte array, this is a valid string in the
+                // 'encoding' encoding.
+                decodedString = encoding.GetString(_rawOctets.ToArray());
+
+                if (encoding == s_utf8Encoding)
+                {
+                    AppendOctetsPercentEncoded(_requestUriString!, _rawOctets.ToArray());
+                }
+                else
+                {
+                    AppendOctetsPercentEncoded(_requestUriString!, s_utf8Encoding.GetBytes(decodedString));
+                }
+
+                _rawOctets.Clear();
+
+                return true;
+            }
+            catch (DecoderFallbackException e)
+            {
+                if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, SR.Format(SR.net_log_listener_cant_convert_bytes, GetOctetsAsString(_rawOctets), e.Message));
+            }
+            catch (EncoderFallbackException e)
+            {
+                // If utf8Encoding.GetBytes() fails
+                if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, SR.Format(SR.net_log_listener_cant_convert_to_utf8, decodedString, e.Message));
+            }
+
+            return false;
+        }
+
+        private static void AppendOctetsPercentEncoded(StringBuilder target, IEnumerable<byte> octets)
+        {
+            foreach (byte octet in octets)
+            {
+                target.Append($"%{octet:X2}");
+            }
+        }
+
+        private static string GetOctetsAsString(IEnumerable<byte> octets)
+        {
+            StringBuilder octetString = new StringBuilder();
+
+            bool first = true;
+            foreach (byte octet in octets)
+            {
+                if (first)
+                {
+                    first = false;
+                }
+                else
+                {
+                    octetString.Append(' ');
+                }
+                octetString.Append($"{octet:X2}");
+            }
+
+            return octetString.ToString();
+        }
+
+        private static string GetPath(string uriString)
+        {
+            Debug.Assert(uriString != null, "uriString must not be null");
+            Debug.Assert(uriString.Length > 0, "uriString must not be empty");
+
+            int pathStartIndex = 0;
+
+            // Perf. improvement: nearly all strings are relative Uris. So just look if the
+            // string starts with '/'. If so, we have a relative Uri and the path starts at position 0.
+            // (http.sys already trimmed leading whitespaces)
+            if (uriString[0] != '/')
+            {
+                // We can't check against cookedUriScheme, since http.sys allows for request http://myserver/ to
+                // use a request line 'GET https://myserver/' (note http vs. https). Therefore check if the
+                // Uri starts with either http:// or https://.
+                int authorityStartIndex = 0;
+                if (uriString.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
+                {
+                    authorityStartIndex = 7;
+                }
+                else if (uriString.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
+                {
+                    authorityStartIndex = 8;
+                }
+
+                if (authorityStartIndex > 0)
+                {
+                    // we have an absolute Uri. Find out where the authority ends and the path begins.
+                    // Note that Uris like "http://server?query=value/1/2" are invalid according to RFC2616
+                    // and http.sys behavior: If the Uri contains a query, there must be at least one '/'
+                    // between the authority and the '?' character: It's safe to just look for the first
+                    // '/' after the authority to determine the beginning of the path.
+                    pathStartIndex = uriString.IndexOf('/', authorityStartIndex);
+                    if (pathStartIndex == -1)
+                    {
+                        // e.g. for request lines like: 'GET http://myserver' (no final '/')
+                        pathStartIndex = uriString.Length;
+                    }
+                }
+                else
+                {
+                    // RFC2616: Request-URI = "*" | absoluteURI | abs_path | authority
+                    // 'authority' can only be used with CONNECT which is never received by HttpListener.
+                    // I.e. if we don't have an absolute path (must start with '/') and we don't have
+                    // an absolute Uri (must start with http:// or https://), then 'uriString' must be '*'.
+                    Debug.Assert((uriString.Length == 1) && (uriString[0] == '*'), "Unknown request Uri string format",
+                        "Request Uri string is not an absolute Uri, absolute path, or '*': {0}", uriString);
+
+                    // Should we ever get here, be consistent with 2.0/3.5 behavior: just add an initial
+                    // slash to the string and treat it as a path:
+                    uriString = "/" + uriString;
+                }
+            }
+
+            // Find end of path: The path is terminated by
+            // - the first '?' character
+            // - the first '#' character: This is never the case here, since http.sys won't accept
+            //   Uris containing fragments. Also, RFC2616 doesn't allow fragments in request Uris.
+            // - end of Uri string
+            int queryIndex = uriString.IndexOf('?');
+            if (queryIndex == -1)
+            {
+                queryIndex = uriString.Length;
+            }
+
+            // will always return a != null string.
+            return AddSlashToAsteriskOnlyPath(uriString.Substring(pathStartIndex, queryIndex - pathStartIndex));
+        }
+
+        private static string AddSlashToAsteriskOnlyPath(string path)
+        {
+            Debug.Assert(path != null, "'path' must not be null");
+
+            // If a request like "OPTIONS * HTTP/1.1" is sent to the listener, then the request Uri
+            // should be "http[s]://server[:port]/*" to be compatible with pre-4.0 behavior.
+            if ((path.Length == 1) && (path[0] == '*'))
+            {
+                return "/*";
+            }
+
+            return path;
+        }
+
+        private enum ParsingResult
+        {
+            Success,
+            InvalidString,
+            EncodingError
+        }
+
+        private enum EncodingType
+        {
+            Primary,
+            Secondary
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerResponse.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerResponse.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpListenerResponse.cs	(revision 376)
@@ -0,0 +1,316 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Net;
+using System.Text;
+
+namespace SpaceWizards.HttpListener
+{
+    public sealed unsafe partial class HttpListenerResponse : IDisposable
+    {
+        private BoundaryType _boundaryType = BoundaryType.None;
+        private CookieCollection? _cookies;
+        private readonly HttpListenerContext? _httpContext;
+        private bool _keepAlive = true;
+        private HttpResponseStream? _responseStream;
+        private string? _statusDescription;
+        private WebHeaderCollection _webHeaders = new WebHeaderCollection();
+
+        public WebHeaderCollection Headers
+        {
+            get => _webHeaders;
+            set
+            {
+                _webHeaders = new WebHeaderCollection();
+                foreach (string headerName in value.AllKeys)
+                {
+                    _webHeaders.Add(headerName, value[headerName]);
+                }
+            }
+        }
+
+        public Encoding? ContentEncoding { get; set; }
+
+        public string? ContentType
+        {
+            get => Headers[HttpKnownHeaderNames.ContentType];
+            set
+            {
+                CheckDisposed();
+                if (string.IsNullOrEmpty(value))
+                {
+                    Headers.Remove(HttpKnownHeaderNames.ContentType);
+                }
+                else
+                {
+                    Headers.Set(HttpKnownHeaderNames.ContentType, value);
+                }
+            }
+        }
+
+        private HttpListenerContext HttpListenerContext => _httpContext!;
+
+        private HttpListenerRequest HttpListenerRequest => HttpListenerContext!.Request;
+
+        internal EntitySendFormat EntitySendFormat
+        {
+            get => (EntitySendFormat)_boundaryType;
+            set
+            {
+                CheckDisposed();
+                CheckSentHeaders();
+                if (value == EntitySendFormat.Chunked && HttpListenerRequest.ProtocolVersion.Minor == 0)
+                {
+                    throw new ProtocolViolationException(SR.net_nochunkuploadonhttp10);
+                }
+                _boundaryType = (BoundaryType)value;
+                if (value != EntitySendFormat.ContentLength)
+                {
+                    _contentLength = -1;
+                }
+            }
+        }
+
+        public bool SendChunked
+        {
+            get => EntitySendFormat == EntitySendFormat.Chunked;
+            set => EntitySendFormat = value ? EntitySendFormat.Chunked : EntitySendFormat.ContentLength;
+        }
+
+        // We MUST NOT send message-body when we send responses with these Status codes
+        private static readonly int[] s_noResponseBody = { 100, 101, 204, 205, 304 };
+
+        private static bool CanSendResponseBody(int responseCode)
+        {
+            for (int i = 0; i < s_noResponseBody.Length; i++)
+            {
+                if (responseCode == s_noResponseBody[i])
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public long ContentLength64
+        {
+            get => _contentLength;
+            set
+            {
+                CheckDisposed();
+                CheckSentHeaders();
+                if (value >= 0)
+                {
+                    _contentLength = value;
+                    _boundaryType = BoundaryType.ContentLength;
+                }
+                else
+                {
+                    throw new ArgumentOutOfRangeException(nameof(value), SR.net_clsmall);
+                }
+            }
+        }
+
+        public CookieCollection Cookies
+        {
+            get => _cookies ?? (_cookies = new CookieCollection());
+            set => _cookies = value;
+        }
+
+        public bool KeepAlive
+        {
+            get => _keepAlive;
+            set
+            {
+                CheckDisposed();
+                _keepAlive = value;
+            }
+        }
+
+        public Stream OutputStream
+        {
+            get
+            {
+                CheckDisposed();
+                EnsureResponseStream();
+                return _responseStream!;
+            }
+        }
+
+        public string? RedirectLocation
+        {
+            get => Headers[HttpResponseHeader.Location];
+            set
+            {
+                // note that this doesn't set the status code to a redirect one
+                CheckDisposed();
+                if (string.IsNullOrEmpty(value))
+                {
+                    Headers.Remove(HttpKnownHeaderNames.Location);
+                }
+                else
+                {
+                    Headers.Set(HttpKnownHeaderNames.Location, value);
+                }
+            }
+        }
+
+        public string StatusDescription
+        {
+            get
+            {
+                if (_statusDescription == null)
+                {
+                    // if the user hasn't set this, generated on the fly, if possible.
+                    // We know this one is safe, no need to verify it as in the setter.
+                    _statusDescription = HttpStatusDescription.Get(StatusCode);
+                }
+                if (_statusDescription == null)
+                {
+                    _statusDescription = string.Empty;
+                }
+                return _statusDescription;
+            }
+            set
+            {
+                CheckDisposed();
+                if (value == null)
+                {
+                    throw new ArgumentNullException(nameof(value));
+                }
+
+                // Need to verify the status description doesn't contain any control characters except HT.  We mask off the high
+                // byte since that's how it's encoded.
+                for (int i = 0; i < value.Length; i++)
+                {
+                    char c = (char)(0x000000ff & (uint)value[i]);
+                    if ((c <= 31 && c != (byte)'\t') || c == 127)
+                    {
+                        throw new ArgumentException(SR.net_WebHeaderInvalidControlChars, nameof(value));
+                    }
+                }
+
+                _statusDescription = value;
+            }
+        }
+
+        public void AddHeader(string name, string value)
+        {
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"name={name}, value={value}");
+            Headers.Set(name, value);
+        }
+
+        public void AppendHeader(string name, string value)
+        {
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"name={name}, value={value}");
+            Headers.Add(name, value);
+        }
+
+        public void AppendCookie(Cookie cookie)
+        {
+            if (cookie == null)
+            {
+                throw new ArgumentNullException(nameof(cookie));
+            }
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"cookie: {cookie}");
+            Cookies.Add(cookie);
+        }
+
+        private void ComputeCookies()
+        {
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"Entering Set-Cookie: {Headers[HttpResponseHeader.SetCookie]}, Set-Cookie2: {Headers[HttpKnownHeaderNames.SetCookie2]}");
+
+            if (_cookies != null)
+            {
+                // now go through the collection, and concatenate all the cookies in per-variant strings
+                string? setCookie2 = null, setCookie = null;
+                for (int index = 0; index < _cookies.Count; index++)
+                {
+                    Cookie cookie = _cookies[index];
+                    string cookieString = cookie.ToServerString();
+                    if (cookieString == null || cookieString.Length == 0)
+                    {
+                        continue;
+                    }
+                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"Now looking at index:{index} cookie: {cookie}");
+                    if (cookie.IsRfc2965Variant())
+                    {
+                        setCookie2 = setCookie2 == null ? cookieString : setCookie2 + ", " + cookieString;
+                    }
+                    else
+                    {
+                        setCookie = setCookie == null ? cookieString : setCookie + ", " + cookieString;
+                    }
+                }
+
+                if (!string.IsNullOrEmpty(setCookie))
+                {
+                    Headers.Set(HttpKnownHeaderNames.SetCookie, setCookie);
+                    if (string.IsNullOrEmpty(setCookie2))
+                    {
+                        Headers.Remove(HttpKnownHeaderNames.SetCookie2);
+                    }
+                }
+
+                if (!string.IsNullOrEmpty(setCookie2))
+                {
+                    Headers.Set(HttpKnownHeaderNames.SetCookie2, setCookie2);
+                    if (string.IsNullOrEmpty(setCookie))
+                    {
+                        Headers.Remove(HttpKnownHeaderNames.SetCookie);
+                    }
+                }
+            }
+
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"Exiting Set-Cookie: {Headers[HttpResponseHeader.SetCookie]} Set-Cookie2: {Headers[HttpKnownHeaderNames.SetCookie2]}");
+        }
+
+        public void Redirect(string url)
+        {
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"url={url}");
+            Headers[HttpResponseHeader.Location] = url;
+            StatusCode = (int)HttpStatusCode.Redirect;
+            StatusDescription = HttpStatusDescription.Get(StatusCode)!;
+        }
+
+        public void SetCookie(Cookie cookie)
+        {
+            if (cookie == null)
+            {
+                throw new ArgumentNullException(nameof(cookie));
+            }
+
+            Cookie newCookie = cookie.Clone();
+            int added = Cookies.InternalAdd(newCookie, true);
+
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"cookie: {cookie}");
+
+            if (added != 1)
+            {
+                // The Cookie already existed and couldn't be replaced.
+                throw new ArgumentException(SR.net_cookie_exists, nameof(cookie));
+            }
+        }
+
+        void IDisposable.Dispose() => Dispose();
+
+        private void CheckDisposed()
+        {
+            if (Disposed)
+            {
+                throw new ObjectDisposedException(GetType().FullName);
+            }
+        }
+
+        private void CheckSentHeaders()
+        {
+            if (SentHeaders)
+            {
+                throw new InvalidOperationException(SR.net_rspsubmitted);
+            }
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpRequestStream.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpRequestStream.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpRequestStream.cs	(revision 376)
@@ -0,0 +1,75 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace SpaceWizards.HttpListener
+{
+    internal partial class HttpRequestStream : Stream
+    {
+        public override bool CanSeek => false;
+        public override bool CanWrite => false;
+        public override bool CanRead => true;
+
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "buffer.Length:" + buffer.Length + " count:" + count + " offset:" + offset);
+
+            StreamHelper.ValidateBufferArguments(buffer, offset, count);
+
+            if (count == 0 || _closed)
+            {
+                return 0;
+            }
+
+            return ReadCore(buffer, offset, count);
+        }
+
+        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
+        {
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "buffer.Length:" + buffer.Length + " count:" + count + " offset:" + offset);
+
+            StreamHelper.ValidateBufferArguments(buffer, offset, count);
+
+            return BeginReadCore(buffer, offset, count, callback, state)!;
+        }
+
+        public override void Flush() { }
+        public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask;
+
+        public override long Length => throw new NotSupportedException(SR.net_noseek);
+
+        public override long Position
+        {
+            get => throw new NotSupportedException(SR.net_noseek);
+            set => throw new NotSupportedException(SR.net_noseek);
+        }
+
+        public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(SR.net_noseek);
+
+        public override void SetLength(long value) => throw new NotSupportedException(SR.net_noseek);
+
+        public override void Write(byte[] buffer, int offset, int count) => throw new InvalidOperationException(SR.net_readonlystream);
+
+        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
+        {
+            throw new InvalidOperationException(SR.net_readonlystream);
+        }
+
+        public override void EndWrite(IAsyncResult asyncResult) => throw new InvalidOperationException(SR.net_readonlystream);
+
+        internal bool Closed => _closed;
+
+        protected override void Dispose(bool disposing)
+        {
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "_closed:" + _closed);
+
+            _closed = true;
+            base.Dispose(disposing);
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpResponseStream.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpResponseStream.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpResponseStream.cs	(revision 376)
@@ -0,0 +1,101 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace SpaceWizards.HttpListener
+{
+    internal sealed partial class HttpResponseStream : Stream
+    {
+        private bool _closed;
+        internal bool Closed => _closed;
+
+        public override bool CanRead => false;
+        public override bool CanSeek => false;
+        public override bool CanWrite => true;
+
+        public override void Flush() { }
+        public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask;
+
+        public override long Length => throw new NotSupportedException(SR.net_noseek);
+
+        public override long Position
+        {
+            get => throw new NotSupportedException(SR.net_noseek);
+            set => throw new NotSupportedException(SR.net_noseek);
+        }
+
+        public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(SR.net_noseek);
+
+        public override void SetLength(long value) => throw new NotSupportedException(SR.net_noseek);
+
+        public override int Read(byte[] buffer, int offset, int size) => throw new InvalidOperationException(SR.net_writeonlystream);
+
+        public override IAsyncResult BeginRead(byte[] buffer, int offset, int size, AsyncCallback? callback, object? state)
+        {
+            throw new InvalidOperationException(SR.net_writeonlystream);
+        }
+
+        public override int EndRead(IAsyncResult asyncResult) => throw new InvalidOperationException(SR.net_writeonlystream);
+
+        public override void Write(byte[] buffer, int offset, int count)
+        {
+            StreamHelper.ValidateBufferArguments(buffer, offset, count);
+
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "buffer.Length:" + buffer.Length + " count:" + count + " offset:" + offset);
+
+            if (_closed)
+            {
+                return;
+            }
+
+            WriteCore(buffer, offset, count);
+        }
+
+        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
+        {
+            StreamHelper.ValidateBufferArguments(buffer, offset, count);
+
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "buffer.Length:" + buffer.Length + " count:" + count + " offset:" + offset);
+
+            return BeginWriteCore(buffer, offset, count, callback, state);
+        }
+
+        public override void EndWrite(IAsyncResult asyncResult)
+        {
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"asyncResult:{asyncResult}");
+
+            if (asyncResult == null)
+            {
+                throw new ArgumentNullException(nameof(asyncResult));
+            }
+
+            EndWriteCore(asyncResult);
+        }
+
+        protected override void Dispose(bool disposing)
+        {
+            try
+            {
+                if (disposing)
+                {
+                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "_closed:" + _closed);
+                    if (_closed)
+                    {
+                        return;
+                    }
+                    _closed = true;
+                    DisposeCore();
+                }
+            }
+            finally
+            {
+                base.Dispose(disposing);
+            }
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpStatusDescription.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpStatusDescription.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/HttpStatusDescription.cs	(revision 376)
@@ -0,0 +1,83 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#nullable enable
+namespace System.Net
+{
+    internal static class HttpStatusDescription
+    {
+        internal static string? Get(HttpStatusCode code) =>
+            Get((int)code);
+
+        internal static string? Get(int code) =>
+            code switch
+            {
+                100 => "Continue",
+                101 => "Switching Protocols",
+                102 => "Processing",
+                103 => "Early Hints",
+
+                200 => "OK",
+                201 => "Created",
+                202 => "Accepted",
+                203 => "Non-Authoritative Information",
+                204 => "No Content",
+                205 => "Reset Content",
+                206 => "Partial Content",
+                207 => "Multi-Status",
+                208 => "Already Reported",
+                226 => "IM Used",
+
+                300 => "Multiple Choices",
+                301 => "Moved Permanently",
+                302 => "Found",
+                303 => "See Other",
+                304 => "Not Modified",
+                305 => "Use Proxy",
+                307 => "Temporary Redirect",
+                308 => "Permanent Redirect",
+
+                400 => "Bad Request",
+                401 => "Unauthorized",
+                402 => "Payment Required",
+                403 => "Forbidden",
+                404 => "Not Found",
+                405 => "Method Not Allowed",
+                406 => "Not Acceptable",
+                407 => "Proxy Authentication Required",
+                408 => "Request Timeout",
+                409 => "Conflict",
+                410 => "Gone",
+                411 => "Length Required",
+                412 => "Precondition Failed",
+                413 => "Request Entity Too Large",
+                414 => "Request-Uri Too Long",
+                415 => "Unsupported Media Type",
+                416 => "Requested Range Not Satisfiable",
+                417 => "Expectation Failed",
+                421 => "Misdirected Request",
+                422 => "Unprocessable Entity",
+                423 => "Locked",
+                424 => "Failed Dependency",
+                426 => "Upgrade Required", // RFC 2817
+                428 => "Precondition Required",
+                429 => "Too Many Requests",
+                431 => "Request Header Fields Too Large",
+                451 => "Unavailable For Legal Reasons",
+
+                500 => "Internal Server Error",
+                501 => "Not Implemented",
+                502 => "Bad Gateway",
+                503 => "Service Unavailable",
+                504 => "Gateway Timeout",
+                505 => "Http Version Not Supported",
+                506 => "Variant Also Negotiates",
+                507 => "Insufficient Storage",
+                508 => "Loop Detected",
+                510 => "Not Extended",
+                511 => "Network Authentication Required",
+
+                _ => null,
+            };
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/LazyAsyncResult.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/LazyAsyncResult.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/LazyAsyncResult.cs	(revision 376)
@@ -0,0 +1,496 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#nullable enable
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.Net
+{
+    // LazyAsyncResult - Base class for all IAsyncResult classes that want to take advantage of
+    // lazily-allocated event handles.
+    internal class LazyAsyncResult : IAsyncResult
+    {
+        private const int HighBit = unchecked((int)0x80000000);
+        private const int ForceAsyncCount = 50;
+
+        // This is to avoid user mistakes when they queue another async op from a callback the completes sync.
+        [ThreadStatic]
+        private static ThreadContext? t_threadContext;
+
+        private static ThreadContext CurrentThreadContext
+        {
+            get
+            {
+                ThreadContext? threadContext = t_threadContext;
+                if (threadContext == null)
+                {
+                    threadContext = new ThreadContext();
+                    t_threadContext = threadContext;
+                }
+
+                return threadContext;
+            }
+        }
+
+        private class ThreadContext
+        {
+            internal int _nestedIOCount;
+        }
+
+#if DEBUG
+        private bool _protectState;                 // Used by ContextAwareResult to prevent some calls.
+#endif
+
+        private readonly object? _asyncObject;              // Caller's async object.
+        private readonly object? _asyncState;               // Caller's state object.
+        private AsyncCallback? _asyncCallback;     // Caller's callback method.
+        private object? _result;                   // Final IO result to be returned byt the End*() method.
+        private int _errorCode;                    // Win32 error code for Win32 IO async calls (that want to throw).
+        private int _intCompleted;                 // Sign bit indicates synchronous completion if set.
+                                                   // Remaining bits count the number of InvokeCallbak() calls.
+
+        private bool _endCalled;                   // True if the user called the End*() method.
+        private bool _userEvent;                   // True if the event has been (or is about to be) handed to the user
+
+        private object? _event;                    // Lazy allocated event to be returned in the IAsyncResult for the client to wait on.
+
+        internal LazyAsyncResult(object? myObject, object? myState, AsyncCallback? myCallBack)
+        {
+            _asyncObject = myObject;
+            _asyncState = myState;
+            _asyncCallback = myCallBack;
+            _result = DBNull.Value;
+            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this);
+        }
+
+        // Interface method to return the original async object.
+        internal object? AsyncObject
+        {
+            get
+            {
+                return _asyncObject;
+            }
+        }
+
+        // Interface method to return the caller's state object.
+        public object? AsyncState
+        {
+            get
+            {
+                return _asyncState;
+            }
+        }
+
+        protected AsyncCallback? AsyncCallback
+        {
+            get
+            {
+                return _asyncCallback;
+            }
+
+            set
+            {
+                _asyncCallback = value;
+            }
+        }
+
+        // Interface property to return a WaitHandle that can be waited on for I/O completion.
+        //
+        // This property implements lazy event creation.
+        //
+        // If this is used, the event cannot be disposed because it is under the control of the
+        // application.  Internal should use InternalWaitForCompletion instead - never AsyncWaitHandle.
+        public WaitHandle AsyncWaitHandle
+        {
+            get
+            {
+#if DEBUG
+                // Can't be called when state is protected.
+                if (_protectState)
+                {
+                    throw new InvalidOperationException("get_AsyncWaitHandle called in protected state");
+                }
+#endif
+
+                // Indicates that the user has seen the event; it can't be disposed.
+                _userEvent = true;
+
+                // The user has access to this object.  Lock-in CompletedSynchronously.
+                if (_intCompleted == 0)
+                {
+                    Interlocked.CompareExchange(ref _intCompleted, HighBit, 0);
+                }
+
+                // Because InternalWaitForCompletion() tries to dispose this event, it's
+                // possible for _event to become null immediately after being set, but only if
+                // IsCompleted has become true.  Therefore it's possible for this property
+                // to give different (set) events to different callers when IsCompleted is true.
+                ManualResetEvent? asyncEvent = (ManualResetEvent?)_event;
+                while (asyncEvent == null)
+                {
+                    LazilyCreateEvent(out asyncEvent);
+                }
+
+                return asyncEvent;
+            }
+        }
+
+        // Returns true if this call created the event.
+        // May return with a null handle.  That means it thought it got one, but it was disposed in the mean time.
+        private bool LazilyCreateEvent(out ManualResetEvent waitHandle)
+        {
+            waitHandle = new ManualResetEvent(false);
+            try
+            {
+                if (Interlocked.CompareExchange(ref _event, waitHandle, null) == null)
+                {
+                    if (InternalPeekCompleted)
+                    {
+                        waitHandle.Set();
+                    }
+                    return true;
+                }
+                else
+                {
+                    waitHandle.Dispose();
+                    waitHandle = (ManualResetEvent)_event;
+
+                    // There's a chance here that _event became null.  But the only way is if another thread completed
+                    // in InternalWaitForCompletion and disposed it.  If we're in InternalWaitForCompletion, we now know
+                    // IsCompleted is set, so we can avoid the wait when waitHandle comes back null.  AsyncWaitHandle
+                    // will try again in this case.
+                    return false;
+                }
+            }
+            catch
+            {
+                // This should be very rare, but doing this will reduce the chance of deadlock.
+                _event = null;
+                waitHandle?.Dispose();
+
+                throw;
+            }
+        }
+
+        // This allows ContextAwareResult to not let anyone trigger the CompletedSynchronously tripwire while the context is being captured.
+        [Conditional("DEBUG")]
+        protected void DebugProtectState(bool protect)
+        {
+#if DEBUG
+            _protectState = protect;
+#endif
+        }
+
+        // Interface property, returning synchronous completion status.
+        public bool CompletedSynchronously
+        {
+            get
+            {
+#if DEBUG
+                // Can't be called when state is protected.
+                if (_protectState)
+                {
+                    throw new InvalidOperationException("get_CompletedSynchronously called in protected state");
+                }
+#endif
+
+                // If this returns greater than zero, it means it was incremented by InvokeCallback before anyone ever saw it.
+                int result = _intCompleted;
+                if (result == 0)
+                {
+                    result = Interlocked.CompareExchange(ref _intCompleted, HighBit, 0);
+                }
+
+                return result > 0;
+            }
+        }
+
+        // Interface property, returning completion status.
+        public bool IsCompleted
+        {
+            get
+            {
+#if DEBUG
+                // Can't be called when state is protected.
+                if (_protectState)
+                {
+                    throw new InvalidOperationException("get_IsCompleted called in protected state");
+                }
+#endif
+
+                // Verify low bits to see if it's been incremented.  If it hasn't, set the high bit
+                // to show that it's been looked at.
+                int result = _intCompleted;
+                if (result == 0)
+                {
+                    result = Interlocked.CompareExchange(ref _intCompleted, HighBit, 0);
+                }
+
+                return (result & ~HighBit) != 0;
+            }
+        }
+
+        // Use to see if something's completed without fixing CompletedSynchronously.
+        internal bool InternalPeekCompleted
+        {
+            get
+            {
+                return (_intCompleted & ~HighBit) != 0;
+            }
+        }
+
+        // Internal property for setting the IO result.
+        internal object? Result
+        {
+            get
+            {
+                return _result == DBNull.Value ? null : _result;
+            }
+            set
+            {
+                // Ideally this should never be called, since setting
+                // the result object really makes sense when the IO completes.
+                //
+                // But if the result was set here (as a preemptive error or for some other reason),
+                // then the "result" parameter passed to InvokeCallback() will be ignored.
+
+                // It's an error to call after the result has been completed or with DBNull.
+                Debug.Assert(value != DBNull.Value, "Result can't be set to DBNull - it's a special internal value.");
+
+                Debug.Assert(!InternalPeekCompleted, "Called on completed result.");
+                _result = value;
+            }
+        }
+
+        internal bool EndCalled
+        {
+            get
+            {
+                return _endCalled;
+            }
+            set
+            {
+                _endCalled = value;
+            }
+        }
+
+        // Internal property for setting the Win32 IO async error code.
+        internal int ErrorCode
+        {
+            get
+            {
+                return _errorCode;
+            }
+            set
+            {
+                _errorCode = value;
+            }
+        }
+
+        // A method for completing the IO with a result and invoking the user's callback.
+        // Used by derived classes to pass context into an overridden Complete().  Useful
+        // for determining the 'winning' thread in case several may simultaneously call
+        // the equivalent of InvokeCallback().
+        protected void ProtectedInvokeCallback(object? result, IntPtr userToken)
+        {
+            // Critical to disallow DBNull here - it could result in a stuck spinlock in WaitForCompletion.
+            if (result == DBNull.Value)
+            {
+                throw new ArgumentNullException(nameof(result));
+            }
+
+#if DEBUG
+            // Always safe to ask for the state now.
+            _protectState = false;
+#endif
+
+            if ((_intCompleted & ~HighBit) == 0 && (Interlocked.Increment(ref _intCompleted) & ~HighBit) == 1)
+            {
+                // DBNull.Value is used to guarantee that the first caller wins,
+                // even if the result was set to null.
+                if (_result == DBNull.Value)
+                {
+                    _result = result;
+                }
+
+                ManualResetEvent? asyncEvent = (ManualResetEvent?)_event;
+                if (asyncEvent != null)
+                {
+                    try
+                    {
+                        asyncEvent.Set();
+                    }
+                    catch (ObjectDisposedException)
+                    {
+                        // Simply ignore this exception - There is apparently a rare race condition
+                        // where the event is disposed before the completion method is called.
+                    }
+                }
+
+                Complete(userToken);
+            }
+        }
+
+        // Completes the IO with a result and invoking the user's callback.
+        internal void InvokeCallback(object? result)
+        {
+            ProtectedInvokeCallback(result, IntPtr.Zero);
+        }
+
+        // Completes the IO without a result and invoking the user's callback.
+        internal void InvokeCallback()
+        {
+            ProtectedInvokeCallback(null, IntPtr.Zero);
+        }
+
+        // NOTE: THIS METHOD MUST NOT BE CALLED DIRECTLY.
+        //
+        // This method does the callback's job and is guaranteed to be called exactly once.
+        // A derived overriding method must call the base class somewhere or the completion is lost.
+        protected virtual void Complete(IntPtr userToken)
+        {
+            bool offloaded = false;
+            ThreadContext threadContext = CurrentThreadContext;
+            try
+            {
+                ++threadContext._nestedIOCount;
+                if (_asyncCallback != null)
+                {
+                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "Invoking callback");
+
+                    if (threadContext._nestedIOCount >= ForceAsyncCount)
+                    {
+                        if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "*** OFFLOADED the user callback ****");
+
+                        Task.Factory.StartNew(
+                            s => WorkerThreadComplete(s!),
+                            this,
+                            CancellationToken.None,
+                            TaskCreationOptions.DenyChildAttach,
+                            TaskScheduler.Default);
+
+                        offloaded = true;
+                    }
+                    else
+                    {
+                        _asyncCallback(this);
+                    }
+                }
+                else
+                {
+                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "No callback to invoke");
+                }
+            }
+            finally
+            {
+                --threadContext._nestedIOCount;
+
+                // Never call this method unless interlocked _intCompleted check has succeeded (like in this case).
+                if (!offloaded)
+                {
+                    Cleanup();
+                }
+            }
+        }
+
+        // Only called by the above method.
+        private static void WorkerThreadComplete(object state)
+        {
+            Debug.Assert(state is LazyAsyncResult);
+            LazyAsyncResult thisPtr = (LazyAsyncResult)state;
+
+            try
+            {
+                thisPtr._asyncCallback!(thisPtr);
+            }
+            finally
+            {
+                thisPtr.Cleanup();
+            }
+        }
+
+        // Custom instance cleanup method.
+        //
+        // Derived types override this method to release unmanaged resources associated with an IO request.
+        protected virtual void Cleanup()
+        {
+        }
+
+        internal object? InternalWaitForCompletion()
+        {
+            return WaitForCompletion(true);
+        }
+
+        private object? WaitForCompletion(bool snap)
+        {
+            ManualResetEvent? waitHandle = null;
+            bool createdByMe = false;
+            bool complete = snap ? IsCompleted : InternalPeekCompleted;
+
+            if (!complete)
+            {
+                // Not done yet, so wait:
+                waitHandle = (ManualResetEvent?)_event;
+                if (waitHandle == null)
+                {
+                    createdByMe = LazilyCreateEvent(out waitHandle);
+                }
+            }
+
+            if (waitHandle != null)
+            {
+                try
+                {
+                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"Waiting for completion event {waitHandle}");
+                    waitHandle.WaitOne(Timeout.Infinite);
+                }
+                catch (ObjectDisposedException)
+                {
+                    // This can occur if this method is called from two different threads.
+                    // This possibility is the trade-off for not locking.
+                }
+                finally
+                {
+                    // We also want to dispose the event although we can't unless we did wait on it here.
+                    if (createdByMe && !_userEvent)
+                    {
+                        // Does _userEvent need to be volatile (or _event set via Interlocked) in order
+                        // to avoid giving a user a disposed event?
+                        ManualResetEvent? oldEvent = (ManualResetEvent?)_event;
+                        _event = null;
+                        if (!_userEvent)
+                        {
+                            oldEvent?.Dispose();
+                        }
+                    }
+                }
+            }
+
+            // A race condition exists because InvokeCallback sets _intCompleted before _result (so that _result
+            // can benefit from the synchronization of _intCompleted).  That means you can get here before _result got
+            // set (although rarely - once every eight hours of stress).  Handle that case with a spin-lock.
+
+            SpinWait sw = default;
+            while (_result == DBNull.Value)
+            {
+                sw.SpinOnce();
+            }
+
+            return _result;
+        }
+
+        // A general interface that is called to release unmanaged resources associated with the class.
+        // It completes the result but doesn't do any of the notifications.
+        internal void InternalCleanup()
+        {
+            if ((_intCompleted & ~HighBit) == 0 && (Interlocked.Increment(ref _intCompleted) & ~HighBit) == 1)
+            {
+                // Set no result so that just in case there are waiters, they don't get stuck in the spin lock.
+                _result = null;
+                Cleanup();
+            }
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/ListenerClientCertState.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/ListenerClientCertState.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/ListenerClientCertState.cs	(revision 376)
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace SpaceWizards.HttpListener
+{
+    internal enum ListenerClientCertState
+    {
+        NotInitialized,
+        InProgress,
+        Completed
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Logging/NetEventSource.Common.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Logging/NetEventSource.Common.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Logging/NetEventSource.Common.cs	(revision 376)
@@ -0,0 +1,722 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#if DEBUG
+// Uncomment to enable runtime checks to help validate that NetEventSource isn't being misused
+// in a way that will cause performance problems, e.g. unexpected boxing of value types.
+//#define DEBUG_NETEVENTSOURCE_MISUSE
+#endif
+
+#nullable enable
+using System.Collections;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Tracing;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+#if NET46
+using System.Security;
+#endif
+
+#pragma warning disable CA1823 // not all IDs are used by all partial providers
+
+namespace System.Net
+{
+    // Implementation:
+    // This partial file is meant to be consumed into each System.Net.* assembly that needs to log.  Each such assembly also provides
+    // its own NetEventSource partial class that adds an appropriate [EventSource] attribute, giving it a unique name for that assembly.
+    // Those partials can then also add additional events if needed, starting numbering from the NextAvailableEventId defined by this partial.
+
+    // Usage:
+    // - Operations that may allocate (e.g. boxing a value type, using string interpolation, etc.) or that may have computations
+    //   at call sites should guard access like:
+    //       if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(null, $"Found certificate: {cert}"); // info logging with a formattable string
+    // - Operations that have zero allocations / measurable computations at call sites can use a simpler pattern, calling methods like:
+    //       NetEventSource.Info(this, "literal string");  // arbitrary message with a literal string
+    //   Debug.Asserts inside the logging methods will help to flag some misuse if the DEBUG_NETEVENTSOURCE_MISUSE compilation constant is defined.
+    //   However, because it can be difficult by observation to understand all of the costs involved, guarding can be done everywhere.
+    // - Messages can be strings, formattable strings, or any other object.  Objects (including those used in formattable strings) have special
+    //   formatting applied, controlled by the Format method.  Partial specializations can also override this formatting by implementing a partial
+    //   method that takes an object and optionally provides a string representation of it, in case a particular library wants to customize further.
+
+    /// <summary>Provides logging facilities for System.Net libraries.</summary>
+#if NET46
+    [SecuritySafeCritical]
+#endif
+    internal sealed partial class NetEventSource : EventSource
+    {
+        /// <summary>The single event source instance to use for all logging.</summary>
+        public static readonly NetEventSource Log = new NetEventSource();
+
+        #region Metadata
+        public class Keywords
+        {
+            public const EventKeywords Default = (EventKeywords)0x0001;
+            public const EventKeywords Debug = (EventKeywords)0x0002;
+
+            // No longer used:
+            // EnterExit = (EventKeywords)0x0004;
+        }
+
+        private const string MissingMember = "(?)";
+        private const string NullInstance = "(null)";
+        private const string StaticMethodObject = "(static)";
+        private const string NoParameters = "";
+        private const int MaxDumpSize = 1024;
+
+        // No longer used:
+        // EnterEventId = 1;
+        // ExitEventId = 2;
+
+        private const int AssociateEventId = 3;
+        private const int InfoEventId = 4;
+        private const int ErrorEventId = 5;
+        private const int DumpArrayEventId = 7;
+
+        // These events are implemented in NetEventSource.Security.cs.
+        // Define the ids here so that projects that include NetEventSource.Security.cs will not have conflicts.
+        private const int EnumerateSecurityPackagesId = 8;
+        private const int SspiPackageNotFoundId = 9;
+        private const int AcquireDefaultCredentialId = 10;
+        private const int AcquireCredentialsHandleId = 11;
+        private const int InitializeSecurityContextId = 12;
+        private const int SecurityContextInputBufferId = 13;
+        private const int SecurityContextInputBuffersId = 14;
+        private const int AcceptSecuritContextId = 15;
+        private const int OperationReturnedSomethingId = 16;
+
+        private const int NextAvailableEventId = 17; // Update this value whenever new events are added.  Derived types should base all events off of this to avoid conflicts.
+        #endregion
+
+        #region Events
+        #region Info
+        /// <summary>Logs an information message.</summary>
+        /// <param name="thisOrContextObject">`this`, or another object that serves to provide context for the operation.</param>
+        /// <param name="formattableString">The message to be logged.</param>
+        /// <param name="memberName">The calling member.</param>
+        [NonEvent]
+        public static void Info(object? thisOrContextObject, FormattableString? formattableString = null, [CallerMemberName] string? memberName = null)
+        {
+            DebugValidateArg(thisOrContextObject);
+            DebugValidateArg(formattableString);
+            if (IsEnabled) Log.Info(IdOf(thisOrContextObject), memberName, formattableString != null ? Format(formattableString) : NoParameters);
+        }
+
+        /// <summary>Logs an information message.</summary>
+        /// <param name="thisOrContextObject">`this`, or another object that serves to provide context for the operation.</param>
+        /// <param name="message">The message to be logged.</param>
+        /// <param name="memberName">The calling member.</param>
+        [NonEvent]
+        public static void Info(object? thisOrContextObject, object? message, [CallerMemberName] string? memberName = null)
+        {
+            DebugValidateArg(thisOrContextObject);
+            DebugValidateArg(message);
+            if (IsEnabled) Log.Info(IdOf(thisOrContextObject), memberName, Format(message).ToString());
+        }
+
+        [Event(InfoEventId, Level = EventLevel.Informational, Keywords = Keywords.Default)]
+        private void Info(string thisOrContextObject, string? memberName, string? message) =>
+            WriteEvent(InfoEventId, thisOrContextObject, memberName ?? MissingMember, message);
+        #endregion
+
+        #region Error
+        /// <summary>Logs an error message.</summary>
+        /// <param name="thisOrContextObject">`this`, or another object that serves to provide context for the operation.</param>
+        /// <param name="formattableString">The message to be logged.</param>
+        /// <param name="memberName">The calling member.</param>
+        [NonEvent]
+        public static void Error(object? thisOrContextObject, FormattableString formattableString, [CallerMemberName] string? memberName = null)
+        {
+            DebugValidateArg(thisOrContextObject);
+            DebugValidateArg(formattableString);
+            if (IsEnabled) Log.ErrorMessage(IdOf(thisOrContextObject), memberName, Format(formattableString));
+        }
+
+        /// <summary>Logs an error message.</summary>
+        /// <param name="thisOrContextObject">`this`, or another object that serves to provide context for the operation.</param>
+        /// <param name="message">The message to be logged.</param>
+        /// <param name="memberName">The calling member.</param>
+        [NonEvent]
+        public static void Error(object? thisOrContextObject, object message, [CallerMemberName] string? memberName = null)
+        {
+            DebugValidateArg(thisOrContextObject);
+            DebugValidateArg(message);
+            if (IsEnabled) Log.ErrorMessage(IdOf(thisOrContextObject), memberName, Format(message).ToString());
+        }
+
+        [Event(ErrorEventId, Level = EventLevel.Error, Keywords = Keywords.Default)]
+        private void ErrorMessage(string thisOrContextObject, string? memberName, string? message) =>
+            WriteEvent(ErrorEventId, thisOrContextObject, memberName ?? MissingMember, message);
+        #endregion
+
+        #region Verbose
+        /// <summary>Logs an info message at verbose mode.</summary>
+        /// <param name="thisOrContextObject">`this`, or another object that serves to provide context for the operation.</param>
+        /// <param name="formattableString">The message to be logged.</param>
+        /// <param name="memberName">The calling member.</param>
+        [NonEvent]
+        public static void Verbose(object? thisOrContextObject, FormattableString formattableString, [CallerMemberName] string? memberName = null)
+        {
+            DebugValidateArg(thisOrContextObject);
+            DebugValidateArg(formattableString);
+            if (IsEnabled) Log.ErrorMessage(IdOf(thisOrContextObject), memberName, Format(formattableString));
+        }
+
+        /// <summary>Logs an info at verbose mode.</summary>
+        /// <param name="thisOrContextObject">`this`, or another object that serves to provide context for the operation.</param>
+        /// <param name="message">The message to be logged.</param>
+        /// <param name="memberName">The calling member.</param>
+        [NonEvent]
+        public static void Verbose(object? thisOrContextObject, object message, [CallerMemberName] string? memberName = null)
+        {
+            DebugValidateArg(thisOrContextObject);
+            DebugValidateArg(message);
+            if (IsEnabled) Log.VerboseMessage(IdOf(thisOrContextObject), memberName, Format(message).ToString());
+        }
+
+        [Event(ErrorEventId, Level = EventLevel.Verbose, Keywords = Keywords.Default)]
+        private void VerboseMessage(string thisOrContextObject, string? memberName, string? message) =>
+            WriteEvent(ErrorEventId, thisOrContextObject, memberName ?? MissingMember, message);
+        #endregion
+
+        #region DumpBuffer
+        /// <summary>Logs the contents of a buffer.</summary>
+        /// <param name="thisOrContextObject">`this`, or another object that serves to provide context for the operation.</param>
+        /// <param name="buffer">The buffer to be logged.</param>
+        /// <param name="memberName">The calling member.</param>
+        [NonEvent]
+        public static void DumpBuffer(object? thisOrContextObject, byte[] buffer, [CallerMemberName] string? memberName = null)
+        {
+            DumpBuffer(thisOrContextObject, buffer, 0, buffer.Length, memberName);
+        }
+
+        /// <summary>Logs the contents of a buffer.</summary>
+        /// <param name="thisOrContextObject">`this`, or another object that serves to provide context for the operation.</param>
+        /// <param name="buffer">The buffer to be logged.</param>
+        /// <param name="offset">The starting offset from which to log.</param>
+        /// <param name="count">The number of bytes to log.</param>
+        /// <param name="memberName">The calling member.</param>
+        [NonEvent]
+        public static void DumpBuffer(object? thisOrContextObject, byte[] buffer, int offset, int count, [CallerMemberName] string? memberName = null)
+        {
+            if (IsEnabled && offset >= 0 && offset <= buffer.Length - count)
+            {
+                count = Math.Min(count, MaxDumpSize);
+
+                byte[] slice = buffer;
+                if (offset != 0 || count != buffer.Length)
+                {
+                    slice = new byte[count];
+                    Buffer.BlockCopy(buffer, offset, slice, 0, count);
+                }
+
+                Log.DumpBuffer(IdOf(thisOrContextObject), memberName, slice);
+            }
+        }
+
+        /// <summary>Logs the contents of a buffer.</summary>
+        /// <param name="thisOrContextObject">`this`, or another object that serves to provide context for the operation.</param>
+        /// <param name="bufferPtr">The starting location of the buffer to be logged.</param>
+        /// <param name="count">The number of bytes to log.</param>
+        /// <param name="memberName">The calling member.</param>
+        [NonEvent]
+        public static unsafe void DumpBuffer(object? thisOrContextObject, IntPtr bufferPtr, int count, [CallerMemberName] string? memberName = null)
+        {
+            Debug.Assert(bufferPtr != IntPtr.Zero);
+            Debug.Assert(count >= 0);
+
+            if (IsEnabled)
+            {
+                var buffer = new byte[Math.Min(count, MaxDumpSize)];
+                fixed (byte* targetPtr = buffer)
+                {
+                    Buffer.MemoryCopy((byte*)bufferPtr, targetPtr, buffer.Length, buffer.Length);
+                }
+                Log.DumpBuffer(IdOf(thisOrContextObject), memberName, buffer);
+            }
+        }
+
+        [Event(DumpArrayEventId, Level = EventLevel.Verbose, Keywords = Keywords.Debug)]
+        private void DumpBuffer(string thisOrContextObject, string? memberName, byte[] buffer) =>
+            WriteEvent(DumpArrayEventId, thisOrContextObject, memberName ?? MissingMember, buffer);
+        #endregion
+
+        #region Associate
+        /// <summary>Logs a relationship between two objects.</summary>
+        /// <param name="first">The first object.</param>
+        /// <param name="second">The second object.</param>
+        /// <param name="memberName">The calling member.</param>
+        [NonEvent]
+        public static void Associate(object first, object second, [CallerMemberName] string? memberName = null)
+        {
+            DebugValidateArg(first);
+            DebugValidateArg(second);
+            if (IsEnabled) Log.Associate(IdOf(first), memberName, IdOf(first), IdOf(second));
+        }
+
+        /// <summary>Logs a relationship between two objects.</summary>
+        /// <param name="thisOrContextObject">`this`, or another object that serves to provide context for the operation.</param>
+        /// <param name="first">The first object.</param>
+        /// <param name="second">The second object.</param>
+        /// <param name="memberName">The calling member.</param>
+        [NonEvent]
+        public static void Associate(object? thisOrContextObject, object first, object second, [CallerMemberName] string? memberName = null)
+        {
+            DebugValidateArg(thisOrContextObject);
+            DebugValidateArg(first);
+            DebugValidateArg(second);
+            if (IsEnabled) Log.Associate(IdOf(thisOrContextObject), memberName, IdOf(first), IdOf(second));
+        }
+
+        [Event(AssociateEventId, Level = EventLevel.Informational, Keywords = Keywords.Default, Message = "[{2}]<-->[{3}]")]
+        private void Associate(string thisOrContextObject, string? memberName, string first, string second) =>
+            WriteEvent(AssociateEventId, thisOrContextObject, memberName ?? MissingMember, first, second);
+        #endregion
+        #endregion
+
+        #region Helpers
+        [Conditional("DEBUG_NETEVENTSOURCE_MISUSE")]
+        private static void DebugValidateArg(object? arg)
+        {
+            if (!IsEnabled)
+            {
+                Debug.Assert(!(arg is ValueType), $"Should not be passing value type {arg?.GetType()} to logging without IsEnabled check");
+                Debug.Assert(!(arg is FormattableString), $"Should not be formatting FormattableString \"{arg}\" if tracing isn't enabled");
+            }
+        }
+
+        [Conditional("DEBUG_NETEVENTSOURCE_MISUSE")]
+        private static void DebugValidateArg(FormattableString? arg)
+        {
+            Debug.Assert(IsEnabled || arg == null, $"Should not be formatting FormattableString \"{arg}\" if tracing isn't enabled");
+        }
+
+        public static new bool IsEnabled =>
+            Log.IsEnabled();
+
+        [NonEvent]
+        public static string IdOf(object? value) => value != null ? value.GetType().Name + "#" + GetHashCode(value) : NullInstance;
+
+        [NonEvent]
+        public static int GetHashCode(object? value) => value?.GetHashCode() ?? 0;
+
+        [NonEvent]
+        public static object Format(object? value)
+        {
+            // If it's null, return a known string for null values
+            if (value == null)
+            {
+                return NullInstance;
+            }
+
+            // Give another partial implementation a chance to provide its own string representation
+            string? result = null;
+            AdditionalCustomizedToString(value, ref result);
+            if (result != null)
+            {
+                return result;
+            }
+
+            // Format arrays with their element type name and length
+            if (value is Array arr)
+            {
+                return $"{arr.GetType().GetElementType()}[{((Array)value).Length}]";
+            }
+
+            // Format ICollections as the name and count
+            if (value is ICollection c)
+            {
+                return $"{c.GetType().Name}({c.Count})";
+            }
+
+            // Format SafeHandles as their type, hash code, and pointer value
+            if (value is SafeHandle handle)
+            {
+                return $"{handle.GetType().Name}:{handle.GetHashCode()}(0x{handle.DangerousGetHandle():X})";
+            }
+
+            // Format IntPtrs as hex
+            if (value is IntPtr)
+            {
+                return $"0x{value:X}";
+            }
+
+            // If the string representation of the instance would just be its type name,
+            // use its id instead.
+            string? toString = value.ToString();
+            if (toString == null || toString == value.GetType().FullName)
+            {
+                return IdOf(value);
+            }
+
+            // Otherwise, return the original object so that the caller does default formatting.
+            return value;
+        }
+
+        [NonEvent]
+        private static string Format(FormattableString s)
+        {
+            switch (s.ArgumentCount)
+            {
+                case 0: return s.Format;
+                case 1: return string.Format(s.Format, Format(s.GetArgument(0)));
+                case 2: return string.Format(s.Format, Format(s.GetArgument(0)), Format(s.GetArgument(1)));
+                case 3: return string.Format(s.Format, Format(s.GetArgument(0)), Format(s.GetArgument(1)), Format(s.GetArgument(2)));
+                default:
+                    object?[] args = s.GetArguments();
+                    object[] formattedArgs = new object[args.Length];
+                    for (int i = 0; i < args.Length; i++)
+                    {
+                        formattedArgs[i] = Format(args[i]);
+                    }
+                    return string.Format(s.Format, formattedArgs);
+            }
+        }
+
+        static partial void AdditionalCustomizedToString<T>(T value, ref string? result);
+        #endregion
+
+        #region Custom WriteEvent overloads
+
+        [NonEvent]
+        private unsafe void WriteEvent(int eventId, string? arg1, string? arg2, string? arg3, string? arg4)
+        {
+            if (IsEnabled())
+            {
+                if (arg1 == null) arg1 = "";
+                if (arg2 == null) arg2 = "";
+                if (arg3 == null) arg3 = "";
+                if (arg4 == null) arg4 = "";
+
+                fixed (char* string1Bytes = arg1)
+                fixed (char* string2Bytes = arg2)
+                fixed (char* string3Bytes = arg3)
+                fixed (char* string4Bytes = arg4)
+                {
+                    const int NumEventDatas = 4;
+                    var descrs = stackalloc EventData[NumEventDatas];
+
+                    descrs[0] = new EventData
+                    {
+                        DataPointer = (IntPtr)string1Bytes,
+                        Size = ((arg1.Length + 1) * 2)
+                    };
+                    descrs[1] = new EventData
+                    {
+                        DataPointer = (IntPtr)string2Bytes,
+                        Size = ((arg2.Length + 1) * 2)
+                    };
+                    descrs[2] = new EventData
+                    {
+                        DataPointer = (IntPtr)string3Bytes,
+                        Size = ((arg3.Length + 1) * 2)
+                    };
+                    descrs[3] = new EventData
+                    {
+                        DataPointer = (IntPtr)string4Bytes,
+                        Size = ((arg4.Length + 1) * 2)
+                    };
+
+                    WriteEventCore(eventId, NumEventDatas, descrs);
+                }
+            }
+        }
+
+        [NonEvent]
+        private unsafe void WriteEvent(int eventId, string? arg1, string? arg2, byte[]? arg3)
+        {
+            if (IsEnabled())
+            {
+                if (arg1 == null) arg1 = "";
+                if (arg2 == null) arg2 = "";
+                if (arg3 == null) arg3 = Array.Empty<byte>();
+
+                fixed (char* arg1Ptr = arg1)
+                fixed (char* arg2Ptr = arg2)
+                fixed (byte* arg3Ptr = arg3)
+                {
+                    int bufferLength = arg3.Length;
+                    const int NumEventDatas = 4;
+                    var descrs = stackalloc EventData[NumEventDatas];
+
+                    descrs[0] = new EventData
+                    {
+                        DataPointer = (IntPtr)arg1Ptr,
+                        Size = (arg1.Length + 1) * sizeof(char)
+                    };
+                    descrs[1] = new EventData
+                    {
+                        DataPointer = (IntPtr)arg2Ptr,
+                        Size = (arg2.Length + 1) * sizeof(char)
+                    };
+                    descrs[2] = new EventData
+                    {
+                        DataPointer = (IntPtr)(&bufferLength),
+                        Size = 4
+                    };
+                    descrs[3] = new EventData
+                    {
+                        DataPointer = (IntPtr)arg3Ptr,
+                        Size = bufferLength
+                    };
+
+                    WriteEventCore(eventId, NumEventDatas, descrs);
+                }
+            }
+        }
+
+        [NonEvent]
+        private unsafe void WriteEvent(int eventId, string? arg1, int arg2, int arg3, int arg4)
+        {
+            if (IsEnabled())
+            {
+                if (arg1 == null) arg1 = "";
+
+                fixed (char* arg1Ptr = arg1)
+                {
+                    const int NumEventDatas = 4;
+                    var descrs = stackalloc EventData[NumEventDatas];
+
+                    descrs[0] = new EventData
+                    {
+                        DataPointer = (IntPtr)(arg1Ptr),
+                        Size = (arg1.Length + 1) * sizeof(char)
+                    };
+                    descrs[1] = new EventData
+                    {
+                        DataPointer = (IntPtr)(&arg2),
+                        Size = sizeof(int)
+                    };
+                    descrs[2] = new EventData
+                    {
+                        DataPointer = (IntPtr)(&arg3),
+                        Size = sizeof(int)
+                    };
+                    descrs[3] = new EventData
+                    {
+                        DataPointer = (IntPtr)(&arg4),
+                        Size = sizeof(int)
+                    };
+
+                    WriteEventCore(eventId, NumEventDatas, descrs);
+                }
+            }
+        }
+
+        [NonEvent]
+        private unsafe void WriteEvent(int eventId, string? arg1, int arg2, string? arg3)
+        {
+            if (IsEnabled())
+            {
+                if (arg1 == null) arg1 = "";
+                if (arg3 == null) arg3 = "";
+
+                fixed (char* arg1Ptr = arg1)
+                fixed (char* arg3Ptr = arg3)
+                {
+                    const int NumEventDatas = 3;
+                    var descrs = stackalloc EventData[NumEventDatas];
+
+                    descrs[0] = new EventData
+                    {
+                        DataPointer = (IntPtr)(arg1Ptr),
+                        Size = (arg1.Length + 1) * sizeof(char)
+                    };
+                    descrs[1] = new EventData
+                    {
+                        DataPointer = (IntPtr)(&arg2),
+                        Size = sizeof(int)
+                    };
+                    descrs[2] = new EventData
+                    {
+                        DataPointer = (IntPtr)(arg3Ptr),
+                        Size = (arg3.Length + 1) * sizeof(char)
+                    };
+
+                    WriteEventCore(eventId, NumEventDatas, descrs);
+                }
+            }
+        }
+
+        [NonEvent]
+        private unsafe void WriteEvent(int eventId, string? arg1, string? arg2, int arg3)
+        {
+            if (IsEnabled())
+            {
+                if (arg1 == null) arg1 = "";
+                if (arg2 == null) arg2 = "";
+
+                fixed (char* arg1Ptr = arg1)
+                fixed (char* arg2Ptr = arg2)
+                {
+                    const int NumEventDatas = 3;
+                    var descrs = stackalloc EventData[NumEventDatas];
+
+                    descrs[0] = new EventData
+                    {
+                        DataPointer = (IntPtr)(arg1Ptr),
+                        Size = (arg1.Length + 1) * sizeof(char)
+                    };
+                    descrs[1] = new EventData
+                    {
+                        DataPointer = (IntPtr)(arg2Ptr),
+                        Size = (arg2.Length + 1) * sizeof(char)
+                    };
+                    descrs[2] = new EventData
+                    {
+                        DataPointer = (IntPtr)(&arg3),
+                        Size = sizeof(int)
+                    };
+
+                    WriteEventCore(eventId, NumEventDatas, descrs);
+                }
+            }
+        }
+
+        [NonEvent]
+        private unsafe void WriteEvent(int eventId, string? arg1, string? arg2, string? arg3, int arg4)
+        {
+            if (IsEnabled())
+            {
+                if (arg1 == null) arg1 = "";
+                if (arg2 == null) arg2 = "";
+                if (arg3 == null) arg3 = "";
+
+                fixed (char* arg1Ptr = arg1)
+                fixed (char* arg2Ptr = arg2)
+                fixed (char* arg3Ptr = arg3)
+                {
+                    const int NumEventDatas = 4;
+                    var descrs = stackalloc EventData[NumEventDatas];
+
+                    descrs[0] = new EventData
+                    {
+                        DataPointer = (IntPtr)(arg1Ptr),
+                        Size = (arg1.Length + 1) * sizeof(char)
+                    };
+                    descrs[1] = new EventData
+                    {
+                        DataPointer = (IntPtr)(arg2Ptr),
+                        Size = (arg2.Length + 1) * sizeof(char)
+                    };
+                    descrs[2] = new EventData
+                    {
+                        DataPointer = (IntPtr)(arg3Ptr),
+                        Size = (arg3.Length + 1) * sizeof(char)
+                    };
+                    descrs[3] = new EventData
+                    {
+                        DataPointer = (IntPtr)(&arg4),
+                        Size = sizeof(int)
+                    };
+
+                    WriteEventCore(eventId, NumEventDatas, descrs);
+                }
+            }
+        }
+
+        [NonEvent]
+        private unsafe void WriteEvent(int eventId, string arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8)
+        {
+            if (IsEnabled())
+            {
+                if (arg1 == null) arg1 = "";
+
+                fixed (char* arg1Ptr = arg1)
+                {
+                    const int NumEventDatas = 8;
+                    var descrs = stackalloc EventData[NumEventDatas];
+
+                    descrs[0] = new EventData
+                    {
+                        DataPointer = (IntPtr)(arg1Ptr),
+                        Size = (arg1.Length + 1) * sizeof(char)
+                    };
+                    descrs[1] = new EventData
+                    {
+                        DataPointer = (IntPtr)(&arg2),
+                        Size = sizeof(int)
+                    };
+                    descrs[2] = new EventData
+                    {
+                        DataPointer = (IntPtr)(&arg3),
+                        Size = sizeof(int)
+                    };
+                    descrs[3] = new EventData
+                    {
+                        DataPointer = (IntPtr)(&arg4),
+                        Size = sizeof(int)
+                    };
+                    descrs[4] = new EventData
+                    {
+                        DataPointer = (IntPtr)(&arg5),
+                        Size = sizeof(int)
+                    };
+                    descrs[5] = new EventData
+                    {
+                        DataPointer = (IntPtr)(&arg6),
+                        Size = sizeof(int)
+                    };
+                    descrs[6] = new EventData
+                    {
+                        DataPointer = (IntPtr)(&arg7),
+                        Size = sizeof(int)
+                    };
+                    descrs[7] = new EventData
+                    {
+                        DataPointer = (IntPtr)(&arg8),
+                        Size = sizeof(int)
+                    };
+
+                    WriteEventCore(eventId, NumEventDatas, descrs);
+                }
+            }
+        }
+
+        [NonEvent]
+        private unsafe void WriteEvent(int eventId, string arg1, string arg2, int arg3, int arg4, int arg5)
+        {
+            if (IsEnabled())
+            {
+                if (arg1 == null) arg1 = "";
+                if (arg2 == null) arg2 = "";
+
+                fixed (char* arg1Ptr = arg1)
+                fixed (char* arg2Ptr = arg2)
+                {
+                    const int NumEventDatas = 5;
+                    var descrs = stackalloc EventData[NumEventDatas];
+
+                    descrs[0] = new EventData
+                    {
+                        DataPointer = (IntPtr)(arg1Ptr),
+                        Size = (arg1.Length + 1) * sizeof(char)
+                    };
+                    descrs[1] = new EventData
+                    {
+                        DataPointer = (IntPtr)(arg2Ptr),
+                        Size = (arg2.Length + 1) * sizeof(char)
+                    };
+                    descrs[2] = new EventData
+                    {
+                        DataPointer = (IntPtr)(&arg3),
+                        Size = sizeof(int)
+                    };
+                    descrs[3] = new EventData
+                    {
+                        DataPointer = (IntPtr)(&arg4),
+                        Size = sizeof(int)
+                    };
+                    descrs[4] = new EventData
+                    {
+                        DataPointer = (IntPtr)(&arg5),
+                        Size = sizeof(int)
+                    };
+
+                    WriteEventCore(eventId, NumEventDatas, descrs);
+                }
+            }
+        }
+        #endregion
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/ChunkStream.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/ChunkStream.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/ChunkStream.cs	(revision 376)
@@ -0,0 +1,400 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+// System.Net.ChunkStream
+//
+// Authors:
+//  Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//
+// (C) 2003 Ximian, Inc (http://www.ximian.com)
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Net;
+using System.Text;
+
+namespace SpaceWizards.HttpListener
+{
+    internal sealed class ChunkStream
+    {
+        private enum State
+        {
+            None,
+            PartialSize,
+            Body,
+            BodyFinished,
+            Trailer
+        }
+
+        private sealed class Chunk
+        {
+            public byte[] Bytes;
+            public int Offset;
+
+            public Chunk(byte[] chunk)
+            {
+                Bytes = chunk;
+            }
+
+            public int Read(byte[] buffer, int offset, int size)
+            {
+                int nread = (size > Bytes.Length - Offset) ? Bytes.Length - Offset : size;
+                Buffer.BlockCopy(Bytes, Offset, buffer, offset, nread);
+                Offset += nread;
+                return nread;
+            }
+        }
+
+        internal WebHeaderCollection _headers;
+        private int _chunkSize;
+        private int _chunkRead;
+        private int _totalWritten;
+        private State _state;
+        private StringBuilder _saved;
+        private bool _sawCR;
+        private bool _gotit;
+        private int _trailerState;
+        private List<Chunk> _chunks;
+
+        public ChunkStream(WebHeaderCollection headers)
+        {
+            _headers = headers;
+            _saved = new StringBuilder();
+            _chunks = new List<Chunk>();
+            _chunkSize = -1;
+            _totalWritten = 0;
+        }
+
+        public void ResetBuffer()
+        {
+            _chunkSize = -1;
+            _chunkRead = 0;
+            _totalWritten = 0;
+            _chunks.Clear();
+        }
+
+        public int Read(byte[] buffer, int offset, int size)
+        {
+            return ReadFromChunks(buffer, offset, size);
+        }
+
+        private int ReadFromChunks(byte[] buffer, int offset, int size)
+        {
+            int count = _chunks.Count;
+            int nread = 0;
+
+            var chunksForRemoving = new List<Chunk>(count);
+            for (int i = 0; i < count; i++)
+            {
+                Chunk chunk = _chunks[i];
+
+                if (chunk.Offset == chunk.Bytes.Length)
+                {
+                    chunksForRemoving.Add(chunk);
+                    continue;
+                }
+
+                nread += chunk.Read(buffer, offset + nread, size - nread);
+                if (nread == size)
+                    break;
+            }
+
+            foreach (var chunk in chunksForRemoving)
+                _chunks.Remove(chunk);
+
+            return nread;
+        }
+
+        public void Write(byte[] buffer, int offset, int size)
+        {
+            // Note, the logic here only works when offset is 0 here.
+            // Otherwise, it would treat "size" as the end offset instead of an actual byte count from offset.
+            Debug.Assert(offset == 0);
+
+            if (offset < size)
+                InternalWrite(buffer, ref offset, size);
+        }
+
+        private void InternalWrite(byte[] buffer, ref int offset, int size)
+        {
+            if (_state == State.None || _state == State.PartialSize)
+            {
+                _state = GetChunkSize(buffer, ref offset, size);
+                if (_state == State.PartialSize)
+                    return;
+
+                _saved.Length = 0;
+                _sawCR = false;
+                _gotit = false;
+            }
+
+            if (_state == State.Body && offset < size)
+            {
+                _state = ReadBody(buffer, ref offset, size);
+                if (_state == State.Body)
+                    return;
+            }
+
+            if (_state == State.BodyFinished && offset < size)
+            {
+                _state = ReadCRLF(buffer, ref offset, size);
+                if (_state == State.BodyFinished)
+                    return;
+
+                _sawCR = false;
+            }
+
+            if (_state == State.Trailer && offset < size)
+            {
+                _state = ReadTrailer(buffer, ref offset, size);
+                if (_state == State.Trailer)
+                    return;
+
+                _saved.Length = 0;
+                _sawCR = false;
+                _gotit = false;
+            }
+
+            if (offset < size)
+                InternalWrite(buffer, ref offset, size);
+        }
+
+        public bool WantMore
+        {
+            get { return (_chunkRead != _chunkSize || _chunkSize != 0 || _state != State.None); }
+        }
+
+        public bool DataAvailable
+        {
+            get
+            {
+                int count = _chunks.Count;
+                for (int i = 0; i < count; i++)
+                {
+                    Chunk ch = _chunks[i];
+                    if (ch == null || ch.Bytes == null)
+                        continue;
+                    if (ch.Bytes.Length > 0 && ch.Offset < ch.Bytes.Length)
+                        return (_state != State.Body);
+                }
+                return false;
+            }
+        }
+
+        public int TotalDataSize
+        {
+            get { return _totalWritten; }
+        }
+
+        public int ChunkLeft
+        {
+            get { return _chunkSize - _chunkRead; }
+        }
+
+        private State ReadBody(byte[] buffer, ref int offset, int size)
+        {
+            if (_chunkSize == 0)
+                return State.BodyFinished;
+
+            int diff = size - offset;
+            if (diff + _chunkRead > _chunkSize)
+                diff = _chunkSize - _chunkRead;
+
+            byte[] chunk = new byte[diff];
+            Buffer.BlockCopy(buffer, offset, chunk, 0, diff);
+            _chunks.Add(new Chunk(chunk));
+            offset += diff;
+            _chunkRead += diff;
+            _totalWritten += diff;
+
+            return (_chunkRead == _chunkSize) ? State.BodyFinished : State.Body;
+        }
+
+        private State GetChunkSize(byte[] buffer, ref int offset, int size)
+        {
+            _chunkRead = 0;
+            _chunkSize = 0;
+            char c = '\0';
+            while (offset < size)
+            {
+                c = (char)buffer[offset++];
+                if (c == '\r')
+                {
+                    if (_sawCR)
+                        ThrowProtocolViolation("2 CR found");
+
+                    _sawCR = true;
+                    continue;
+                }
+
+                if (_sawCR && c == '\n')
+                    break;
+
+                if (c == ' ')
+                    _gotit = true;
+
+                if (!_gotit)
+                    _saved.Append(c);
+
+                if (_saved.Length > 20)
+                    ThrowProtocolViolation("chunk size too long.");
+            }
+
+            if (!_sawCR || c != '\n')
+            {
+                if (offset < size)
+                    ThrowProtocolViolation("Missing \\n");
+
+                try
+                {
+                    if (_saved.Length > 0)
+                    {
+                        _chunkSize = int.Parse(RemoveChunkExtension(_saved.ToString()), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
+                    }
+                }
+                catch (Exception)
+                {
+                    ThrowProtocolViolation("Cannot parse chunk size.");
+                }
+
+                return State.PartialSize;
+            }
+
+            _chunkRead = 0;
+            try
+            {
+                _chunkSize = int.Parse(RemoveChunkExtension(_saved.ToString()), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
+            }
+            catch (Exception)
+            {
+                ThrowProtocolViolation("Cannot parse chunk size.");
+            }
+
+            if (_chunkSize == 0)
+            {
+                _trailerState = 2;
+                return State.Trailer;
+            }
+
+            return State.Body;
+        }
+
+        private static string RemoveChunkExtension(string input)
+        {
+            int idx = input.IndexOf(';');
+            if (idx == -1)
+                return input;
+            return input.Substring(0, idx);
+        }
+
+        private State ReadCRLF(byte[] buffer, ref int offset, int size)
+        {
+            if (!_sawCR)
+            {
+                if ((char)buffer[offset++] != '\r')
+                    ThrowProtocolViolation("Expecting \\r");
+
+                _sawCR = true;
+                if (offset == size)
+                    return State.BodyFinished;
+            }
+
+            if (_sawCR && (char)buffer[offset++] != '\n')
+                ThrowProtocolViolation("Expecting \\n");
+
+            return State.None;
+        }
+
+        private State ReadTrailer(byte[] buffer, ref int offset, int size)
+        {
+            char c = '\0';
+
+            // short path
+            if (_trailerState == 2 && (char)buffer[offset] == '\r' && _saved.Length == 0)
+            {
+                offset++;
+                if (offset < size && (char)buffer[offset] == '\n')
+                {
+                    offset++;
+                    return State.None;
+                }
+                offset--;
+            }
+
+            int st = _trailerState;
+            string stString = "\r\n\r";
+            while (offset < size && st < 4)
+            {
+                c = (char)buffer[offset++];
+                if ((st == 0 || st == 2) && c == '\r')
+                {
+                    st++;
+                    continue;
+                }
+
+                if ((st == 1 || st == 3) && c == '\n')
+                {
+                    st++;
+                    continue;
+                }
+
+                if (st > 0)
+                {
+                    _saved.Append(stString.AsSpan(0, _saved.Length == 0 ? st - 2 : st));
+                    st = 0;
+                    if (_saved.Length > 4196)
+                        ThrowProtocolViolation("Error reading trailer (too long).");
+                }
+            }
+
+            if (st < 4)
+            {
+                _trailerState = st;
+                if (offset < size)
+                    ThrowProtocolViolation("Error reading trailer.");
+
+                return State.Trailer;
+            }
+
+            StringReader reader = new StringReader(_saved.ToString());
+            string? line;
+            while ((line = reader.ReadLine()) != null && line != "")
+                _headers.Add(line);
+
+            return State.None;
+        }
+
+        private static void ThrowProtocolViolation(string message)
+        {
+            WebException we = new WebException(message, null, WebExceptionStatus.ServerProtocolViolation, null);
+            throw we;
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/ChunkedInputStream.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/ChunkedInputStream.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/ChunkedInputStream.cs	(revision 376)
@@ -0,0 +1,179 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+// System.Net.ChunkedInputStream
+//
+// Authors:
+//  Gonzalo Paniagua Javier (gonzalo@novell.com)
+//
+// Copyright (c) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.IO;
+using System.Net;
+using System.Runtime.InteropServices;
+
+namespace SpaceWizards.HttpListener
+{
+    internal sealed class ChunkedInputStream : HttpRequestStream
+    {
+        private ChunkStream _decoder;
+        private readonly HttpListenerContext _context;
+        private bool _no_more_data;
+
+        private sealed class ReadBufferState
+        {
+            public byte[] Buffer;
+            public int Offset;
+            public int Count;
+            public int InitialCount;
+            public HttpStreamAsyncResult Ares;
+            public ReadBufferState(byte[] buffer, int offset, int count, HttpStreamAsyncResult ares)
+            {
+                Buffer = buffer;
+                Offset = offset;
+                Count = count;
+                InitialCount = count;
+                Ares = ares;
+            }
+        }
+
+        public ChunkedInputStream(HttpListenerContext context, Stream stream, byte[] buffer, int offset, int length)
+                    : base(stream, buffer, offset, length)
+        {
+            _context = context;
+            WebHeaderCollection coll = (WebHeaderCollection)context.Request.Headers;
+            _decoder = new ChunkStream(coll);
+        }
+
+        public ChunkStream Decoder
+        {
+            get { return _decoder; }
+            set { _decoder = value; }
+        }
+
+        protected override int ReadCore(byte[] buffer, int offset, int count)
+        {
+            IAsyncResult ares = BeginReadCore(buffer, offset, count, null, null);
+            return EndRead(ares);
+        }
+
+        protected override IAsyncResult BeginReadCore(byte[] buffer, int offset, int size, AsyncCallback? cback, object? state)
+        {
+            HttpStreamAsyncResult ares = new HttpStreamAsyncResult(this);
+            ares._callback = cback;
+            ares._state = state;
+            if (_no_more_data || size == 0 || _closed)
+            {
+                ares.Complete();
+                return ares;
+            }
+            int nread = _decoder.Read(buffer, offset, size);
+            offset += nread;
+            size -= nread;
+            if (size == 0)
+            {
+                // got all we wanted, no need to bother the decoder yet
+                ares._count = nread;
+                ares.Complete();
+                return ares;
+            }
+            if (!_decoder.WantMore)
+            {
+                _no_more_data = nread == 0;
+                ares._count = nread;
+                ares.Complete();
+                return ares;
+            }
+            ares._buffer = new byte[8192];
+            ares._offset = 0;
+            ares._count = 8192;
+            ReadBufferState rb = new ReadBufferState(buffer, offset, size, ares);
+            rb.InitialCount += nread;
+            base.BeginReadCore(ares._buffer, ares._offset, ares._count, OnRead, rb);
+            return ares;
+        }
+
+        private void OnRead(IAsyncResult base_ares)
+        {
+            ReadBufferState rb = (ReadBufferState)base_ares.AsyncState!;
+            HttpStreamAsyncResult ares = rb.Ares;
+            try
+            {
+                int nread = base.EndRead(base_ares);
+                if (nread == 0)
+                {
+                    _no_more_data = true;
+                    ares._count = rb.InitialCount - rb.Count;
+                    ares.Complete();
+                    return;
+                }
+
+                _decoder.Write(ares._buffer!, ares._offset, nread);
+                nread = _decoder.Read(rb.Buffer, rb.Offset, rb.Count);
+                rb.Offset += nread;
+                rb.Count -= nread;
+                if (rb.Count == 0 || !_decoder.WantMore)
+                {
+                    _no_more_data = !_decoder.WantMore && nread == 0;
+                    ares._count = rb.InitialCount - rb.Count;
+                    ares.Complete();
+                    return;
+                }
+                ares._offset = 0;
+                ares._count = Math.Min(8192, _decoder.ChunkLeft + 6);
+                base.BeginReadCore(ares._buffer!, ares._offset, ares._count, OnRead, rb);
+            }
+            catch (Exception e)
+            {
+                _context.Connection.SendError(e.Message, 400);
+                ares.Complete(e);
+            }
+        }
+
+        public override int EndRead(IAsyncResult asyncResult)
+        {
+            if (asyncResult == null)
+                throw new ArgumentNullException(nameof(asyncResult));
+
+            HttpStreamAsyncResult? ares = asyncResult as HttpStreamAsyncResult;
+            if (ares == null || !ReferenceEquals(this, ares._parent))
+            {
+                throw new ArgumentException(SR.net_io_invalidasyncresult, nameof(asyncResult));
+            }
+            if (ares._endCalled)
+            {
+                throw new InvalidOperationException(SR.Format(SR.net_io_invalidendcall, nameof(EndRead)));
+            }
+            ares._endCalled = true;
+
+            if (!asyncResult.IsCompleted)
+                asyncResult.AsyncWaitHandle.WaitOne();
+
+            if (ares._error != null)
+                throw new HttpListenerException((int)HttpStatusCode.BadRequest, SR.Format(SR.net_io_operation_aborted, ares._error.Message));
+
+            return ares._count;
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpConnection.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpConnection.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpConnection.cs	(revision 376)
@@ -0,0 +1,566 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+// System.Net.HttpConnection
+//
+// Author:
+//  Gonzalo Paniagua Javier (gonzalo.mono@gmail.com)
+//
+// Copyright (c) 2005-2009 Novell, Inc. (http://www.novell.com)
+// Copyright (c) 2012 Xamarin, Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Net;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace SpaceWizards.HttpListener
+{
+    internal sealed class HttpConnection
+    {
+        private static readonly Action<Task<int>, object?> s_onreadCallback = OnRead;
+        private const int BufferSize = 8192;
+        private Socket? _socket;
+        private Stream _stream;
+        private HttpEndPointListener _epl;
+        private MemoryStream? _memoryStream;
+        private byte[]? _buffer;
+        private HttpListenerContext _context;
+        private StringBuilder? _currentLine;
+        private ListenerPrefix? _prefix;
+        private HttpRequestStream? _requestStream;
+        private HttpResponseStream? _responseStream;
+        private bool _chunked;
+        private int _reuses;
+        private bool _contextBound;
+        private bool _secure;
+        private X509Certificate _cert;
+        private int _timeout = 90000; // 90k ms for first request, 15k ms from then on
+        private Timer _timer;
+        private IPEndPoint? _localEndPoint;
+        private HttpListener? _lastListener;
+        private int[]? _clientCertErrors;
+        private X509Certificate2? _clientCert;
+        private SslStream? _sslStream;
+        private InputState _inputState = InputState.RequestLine;
+        private LineState _lineState = LineState.None;
+        private int _position;
+
+        public HttpConnection(Socket sock, HttpEndPointListener epl, bool secure, X509Certificate cert)
+        {
+            _socket = sock;
+            _epl = epl;
+            _secure = secure;
+            _cert = cert;
+            if (secure == false)
+            {
+                _stream = new NetworkStream(sock, false);
+            }
+            else
+            {
+#pragma warning disable CA5359
+                _sslStream = epl.Listener.CreateSslStream(new NetworkStream(sock, false), false, (t, c, ch, e) =>
+                {
+                    if (c == null)
+                    {
+                        return true;
+                    }
+
+                    var c2 = c as X509Certificate2;
+                    if (c2 == null)
+                    {
+                        c2 = new X509Certificate2(c.GetRawCertData());
+                    }
+
+                    _clientCert = c2;
+                    _clientCertErrors = new int[] { (int)e };
+                    return true;
+                });
+#pragma warning restore CA5359
+
+                _stream = _sslStream;
+            }
+
+            _timer = new Timer(OnTimeout, null, Timeout.Infinite, Timeout.Infinite);
+            if (_sslStream != null) {
+                _sslStream.AuthenticateAsServer (_cert, true, (SslProtocols)ServicePointManager.SecurityProtocol, false);
+            }
+            Init();
+        }
+
+        internal int[]? ClientCertificateErrors
+        {
+            get { return _clientCertErrors; }
+        }
+
+        internal X509Certificate2? ClientCertificate
+        {
+            get { return _clientCert; }
+        }
+
+        internal SslStream? SslStream
+        {
+            get { return _sslStream; }
+        }
+
+        [MemberNotNull(nameof(_memoryStream))]
+        [MemberNotNull(nameof(_context))]
+        private void Init()
+        {
+            _contextBound = false;
+            _requestStream = null;
+            _responseStream = null;
+            _prefix = null;
+            _chunked = false;
+            _memoryStream = new MemoryStream();
+            _position = 0;
+            _inputState = InputState.RequestLine;
+            _lineState = LineState.None;
+            _context = new HttpListenerContext(this);
+        }
+
+        public Stream ConnectedStream => _stream;
+
+        public bool IsClosed
+        {
+            get { return (_socket == null); }
+        }
+
+        public int Reuses
+        {
+            get { return _reuses; }
+        }
+
+        public IPEndPoint? LocalEndPoint
+        {
+            get
+            {
+                if (_localEndPoint != null)
+                    return _localEndPoint;
+
+                _localEndPoint = (IPEndPoint?)_socket!.LocalEndPoint;
+                return _localEndPoint;
+            }
+        }
+
+        public IPEndPoint? RemoteEndPoint
+        {
+            get { return (IPEndPoint?)_socket!.RemoteEndPoint; }
+        }
+
+        public bool IsSecure
+        {
+            get { return _secure; }
+        }
+
+        public ListenerPrefix? Prefix
+        {
+            get { return _prefix; }
+            set { _prefix = value; }
+        }
+
+        private void OnTimeout(object? unused)
+        {
+            CloseSocket();
+            Unbind();
+        }
+
+        public void BeginReadRequest()
+        {
+            if (_buffer == null)
+                _buffer = new byte[BufferSize];
+            try
+            {
+                if (_reuses == 1)
+                    _timeout = 15000;
+                _timer.Change(_timeout, Timeout.Infinite);
+                _stream.ReadAsync(_buffer, 0, BufferSize).ContinueWith(s_onreadCallback, this);
+            }
+            catch
+            {
+                _timer.Change(Timeout.Infinite, Timeout.Infinite);
+                CloseSocket();
+                Unbind();
+            }
+        }
+
+        public HttpRequestStream GetRequestStream(bool chunked, long contentlength)
+        {
+            if (_requestStream == null)
+            {
+                byte[] buffer = _memoryStream!.GetBuffer();
+                int length = (int)_memoryStream!.Length;
+                _memoryStream = null;
+                if (chunked)
+                {
+                    _chunked = true;
+                    _context.Response.SendChunked = true;
+                    _requestStream = new ChunkedInputStream(_context, _stream, buffer, _position, length - _position);
+                }
+                else
+                {
+                    _requestStream = new HttpRequestStream(_stream, buffer, _position, length - _position, contentlength);
+                }
+            }
+            return _requestStream;
+        }
+
+        public HttpResponseStream GetResponseStream()
+        {
+            if (_responseStream == null)
+            {
+                HttpListener? listener = _context._listener;
+
+                if (listener == null)
+                    return new HttpResponseStream(_stream, _context.Response, true);
+
+                _responseStream = new HttpResponseStream(_stream, _context.Response, listener.IgnoreWriteExceptions);
+            }
+            return _responseStream;
+        }
+
+        private static void OnRead(Task<int> task, object? state)
+        {
+            HttpConnection cnc = (HttpConnection)state!;
+            cnc.OnReadInternal(task);
+        }
+
+        private void OnReadInternal(Task<int> ares)
+        {
+            _timer.Change(Timeout.Infinite, Timeout.Infinite);
+            int nread = -1;
+            try
+            {
+                nread = ares.Result;
+                _memoryStream!.Write(_buffer!, 0, nread);
+                if (_memoryStream.Length > 32768)
+                {
+                    SendError(HttpStatusDescription.Get(400), 400);
+                    Close(true);
+                    return;
+                }
+            }
+            catch
+            {
+                if (_memoryStream != null && _memoryStream.Length > 0)
+                    SendError();
+                if (_socket != null)
+                {
+                    CloseSocket();
+                    Unbind();
+                }
+                return;
+            }
+
+            if (nread == 0)
+            {
+                CloseSocket();
+                Unbind();
+                return;
+            }
+
+            if (ProcessInput(_memoryStream))
+            {
+                if (!_context.HaveError)
+                    _context.Request.FinishInitialization();
+
+                if (_context.HaveError)
+                {
+                    SendError();
+                    Close(true);
+                    return;
+                }
+
+                if (!_epl.BindContext(_context))
+                {
+                    const int NotFoundErrorCode = 404;
+                    SendError(HttpStatusDescription.Get(NotFoundErrorCode), NotFoundErrorCode);
+                    Close(true);
+                    return;
+                }
+                HttpListener listener = _context._listener!;
+                if (_lastListener != listener)
+                {
+                    RemoveConnection();
+                    listener.AddConnection(this);
+                    _lastListener = listener;
+                }
+
+                _contextBound = true;
+                listener.RegisterContext(_context);
+                return;
+            }
+
+            _stream.ReadAsync(_buffer!, 0, BufferSize).ContinueWith(s_onreadCallback, this);
+        }
+
+        private void RemoveConnection()
+        {
+            if (_lastListener == null)
+                _epl.RemoveConnection(this);
+            else
+                _lastListener.RemoveConnection(this);
+        }
+
+        private enum InputState
+        {
+            RequestLine,
+            Headers
+        }
+
+        private enum LineState
+        {
+            None,
+            CR,
+            LF
+        }
+
+        // true -> done processing
+        // false -> need more input
+        private bool ProcessInput(MemoryStream ms)
+        {
+            byte[] buffer = ms.GetBuffer();
+            int len = (int)ms.Length;
+            int used = 0;
+            string? line;
+
+            while (true)
+            {
+                if (_context.HaveError)
+                    return true;
+
+                if (_position >= len)
+                    break;
+
+                try
+                {
+                    line = ReadLine(buffer, _position, len - _position, ref used);
+                    _position += used;
+                }
+                catch
+                {
+                    _context.ErrorMessage = HttpStatusDescription.Get(400)!;
+                    _context.ErrorStatus = 400;
+                    return true;
+                }
+
+                if (line == null)
+                    break;
+
+                if (line == "")
+                {
+                    if (_inputState == InputState.RequestLine)
+                        continue;
+                    _currentLine = null;
+                    ms = null!;
+                    return true;
+                }
+
+                if (_inputState == InputState.RequestLine)
+                {
+                    _context.Request.SetRequestLine(line);
+                    _inputState = InputState.Headers;
+                }
+                else
+                {
+                    try
+                    {
+                        _context.Request.AddHeader(line);
+                    }
+                    catch (Exception e)
+                    {
+                        _context.ErrorMessage = e.Message;
+                        _context.ErrorStatus = 400;
+                        return true;
+                    }
+                }
+            }
+
+            if (used == len)
+            {
+                ms.SetLength(0);
+                _position = 0;
+            }
+            return false;
+        }
+
+        private string? ReadLine(byte[] buffer, int offset, int len, ref int used)
+        {
+            if (_currentLine == null)
+                _currentLine = new StringBuilder(128);
+            int last = offset + len;
+            used = 0;
+            for (int i = offset; i < last && _lineState != LineState.LF; i++)
+            {
+                used++;
+                byte b = buffer[i];
+                if (b == 13)
+                {
+                    _lineState = LineState.CR;
+                }
+                else if (b == 10)
+                {
+                    _lineState = LineState.LF;
+                }
+                else
+                {
+                    _currentLine.Append((char)b);
+                }
+            }
+
+            string? result = null;
+            if (_lineState == LineState.LF)
+            {
+                _lineState = LineState.None;
+                result = _currentLine.ToString();
+                _currentLine.Length = 0;
+            }
+
+            return result;
+        }
+
+        public void SendError(string? msg, int status)
+        {
+            try
+            {
+                HttpListenerResponse response = _context.Response;
+                response.StatusCode = status;
+                response.ContentType = "text/html";
+                string? description = HttpStatusDescription.Get(status);
+                var str = !string.IsNullOrEmpty(msg) ?
+                    $"<h1>{description} ({msg})</h1>" :
+                    $"<h1>{description}</h1>";
+
+                byte[] error = Encoding.Default.GetBytes(str);
+                response.Close(error, false);
+            }
+            catch
+            {
+                // response was already closed
+            }
+        }
+
+        public void SendError()
+        {
+            SendError(_context.ErrorMessage, _context.ErrorStatus);
+        }
+
+        private void Unbind()
+        {
+            if (_contextBound)
+            {
+                _epl.UnbindContext(_context);
+                _contextBound = false;
+            }
+        }
+
+        public void Close()
+        {
+            Close(false);
+        }
+
+        private void CloseSocket()
+        {
+            if (_socket == null)
+                return;
+
+            try
+            {
+                _socket.Shutdown(SocketShutdown.Both);
+                _socket.Close();
+            }
+            catch { }
+            finally
+            {
+                _socket = null;
+            }
+
+            RemoveConnection();
+        }
+
+        internal void Close(bool force)
+        {
+            if (_socket != null)
+            {
+                Stream st = GetResponseStream();
+                if (st != null)
+                    st.Close();
+
+                _responseStream = null;
+            }
+
+            if (_socket != null)
+            {
+                force |= !_context.Request.KeepAlive;
+                if (!force)
+                    force = (_context.Response.Headers[HttpKnownHeaderNames.Connection] == HttpHeaderStrings.Close);
+
+                if (!force && _context.Request.FlushInput())
+                {
+                    if (_chunked && _context.Response.ForceCloseChunked == false)
+                    {
+                        // Don't close. Keep working.
+                        _reuses++;
+                        Unbind();
+                        Init();
+                        BeginReadRequest();
+                        return;
+                    }
+
+                    _reuses++;
+                    Unbind();
+                    Init();
+                    BeginReadRequest();
+                    return;
+                }
+
+                Socket s = _socket;
+                _socket = null;
+                try
+                {
+                    if (s != null)
+                        s.Shutdown(SocketShutdown.Both);
+                }
+                catch
+                {
+                }
+                finally
+                {
+                    if (s != null)
+                        s.Close();
+                }
+                Unbind();
+                RemoveConnection();
+                return;
+            }
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpEndPointListener.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpEndPointListener.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpEndPointListener.cs	(revision 376)
@@ -0,0 +1,404 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+// System.Net.HttpEndPointListener
+//
+// Author:
+//  Gonzalo Paniagua Javier (gonzalo.mono@gmail.com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+// Copyright (c) 2012 Xamarin, Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Sockets;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+
+namespace SpaceWizards.HttpListener
+{
+    internal sealed class HttpEndPointListener
+    {
+        private readonly HttpListener _listener;
+        private readonly IPEndPoint _endpoint;
+        private readonly Socket _socket;
+        private readonly Dictionary<HttpConnection, HttpConnection> _unregisteredConnections;
+        private Dictionary<ListenerPrefix, HttpListener> _prefixes;
+        private List<ListenerPrefix>? _unhandledPrefixes; // host = '*'
+        private List<ListenerPrefix>? _allPrefixes;       // host = '+'
+        private X509Certificate? _cert;
+        private bool _secure;
+
+        public HttpEndPointListener(HttpListener listener, IPAddress addr, int port, bool secure)
+        {
+            _listener = listener;
+
+            if (secure)
+            {
+                _secure = secure;
+                _cert = _listener.LoadCertificateAndKey (addr, port);
+            }
+
+            _endpoint = new IPEndPoint(addr, port);
+            _socket = new Socket(addr.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+            _socket.Bind(_endpoint);
+            _socket.Listen(500);
+
+            SocketAsyncEventArgs args = new SocketAsyncEventArgs();
+            args.UserToken = this;
+            args.Completed += OnAccept;
+            Accept(args);
+
+            _prefixes = new Dictionary<ListenerPrefix, HttpListener>();
+            _unregisteredConnections = new Dictionary<HttpConnection, HttpConnection>();
+        }
+
+        internal HttpListener Listener
+        {
+            get { return _listener; }
+        }
+
+        private void Accept(SocketAsyncEventArgs e)
+        {
+            e.AcceptSocket = null;
+            bool asyn;
+            try
+            {
+                asyn = _socket.AcceptAsync(e);
+            }
+            catch (ObjectDisposedException)
+            {
+                // Once the listener starts running, it kicks off an async accept,
+                // and each subsequent accept initiates the next async accept.  At
+                // point if the listener is torn down, the socket will be disposed
+                // and the AcceptAsync on the socket can fail with an ODE.  Far from
+                // ideal, but for now just eat such exceptions.
+                return;
+            }
+            if (!asyn)
+            {
+                ProcessAccept(e);
+            }
+        }
+
+        private static void ProcessAccept(SocketAsyncEventArgs args)
+        {
+            HttpEndPointListener epl = (HttpEndPointListener)args.UserToken!;
+
+            Socket? accepted = args.SocketError == SocketError.Success ? args.AcceptSocket : null;
+            epl.Accept(args);
+
+            if (accepted == null)
+                return;
+
+            if (epl._secure && epl._cert == null)
+            {
+                accepted.Close();
+                return;
+            }
+
+            HttpConnection conn;
+            try
+            {
+                conn = new HttpConnection(accepted, epl, epl._secure, epl._cert!);
+            }
+            catch
+            {
+                accepted.Close();
+                return;
+            }
+
+            lock (epl._unregisteredConnections)
+            {
+                epl._unregisteredConnections[conn] = conn;
+            }
+            conn.BeginReadRequest();
+        }
+
+        private static void OnAccept(object? sender, SocketAsyncEventArgs e)
+        {
+            ProcessAccept(e);
+        }
+
+        internal void RemoveConnection(HttpConnection conn)
+        {
+            lock (_unregisteredConnections)
+            {
+                _unregisteredConnections.Remove(conn);
+            }
+        }
+
+        public bool BindContext(HttpListenerContext context)
+        {
+            HttpListenerRequest req = context.Request;
+            ListenerPrefix? prefix;
+            HttpListener? listener = SearchListener(req.Url, out prefix);
+            if (listener == null)
+                return false;
+
+            context._listener = listener;
+            context.Connection.Prefix = prefix;
+            return true;
+        }
+
+        public void UnbindContext(HttpListenerContext context)
+        {
+            if (context == null || context.Request == null)
+                return;
+
+            context._listener!.UnregisterContext(context);
+        }
+
+        private HttpListener? SearchListener(Uri? uri, out ListenerPrefix? prefix)
+        {
+            prefix = null;
+            if (uri == null)
+                return null;
+
+            string host = uri.Host;
+            int port = uri.Port;
+            string path = WebUtility.UrlDecode(uri.AbsolutePath);
+            string pathSlash = path[path.Length - 1] == '/' ? path : path + "/";
+
+            HttpListener? bestMatch = null;
+            int bestLength = -1;
+
+            if (host != null && host != "")
+            {
+                Dictionary<ListenerPrefix, HttpListener> localPrefixes = _prefixes;
+                foreach (ListenerPrefix p in localPrefixes.Keys)
+                {
+                    string ppath = p.Path!;
+                    if (ppath.Length < bestLength)
+                        continue;
+
+                    if (p.Host != host || p.Port != port)
+                        continue;
+
+                    if (path.StartsWith(ppath, StringComparison.Ordinal) || pathSlash.StartsWith(ppath, StringComparison.Ordinal))
+                    {
+                        bestLength = ppath.Length;
+                        bestMatch = localPrefixes[p];
+                        prefix = p;
+                    }
+                }
+                if (bestLength != -1)
+                    return bestMatch;
+            }
+
+            List<ListenerPrefix>? list = _unhandledPrefixes;
+            bestMatch = MatchFromList(host, path, list, out prefix);
+
+            if (path != pathSlash && bestMatch == null)
+                bestMatch = MatchFromList(host, pathSlash, list, out prefix);
+
+            if (bestMatch != null)
+                return bestMatch;
+
+            list = _allPrefixes;
+            bestMatch = MatchFromList(host, path, list, out prefix);
+
+            if (path != pathSlash && bestMatch == null)
+                bestMatch = MatchFromList(host, pathSlash, list, out prefix);
+
+            if (bestMatch != null)
+                return bestMatch;
+
+            return null;
+        }
+
+        private HttpListener? MatchFromList(string? host, string path, List<ListenerPrefix>? list, out ListenerPrefix? prefix)
+        {
+            prefix = null;
+            if (list == null)
+                return null;
+
+            HttpListener? bestMatch = null;
+            int bestLength = -1;
+
+            foreach (ListenerPrefix p in list)
+            {
+                string ppath = p.Path!;
+                if (ppath.Length < bestLength)
+                    continue;
+
+                if (path.StartsWith(ppath, StringComparison.Ordinal))
+                {
+                    bestLength = ppath.Length;
+                    bestMatch = p._listener;
+                    prefix = p;
+                }
+            }
+
+            return bestMatch;
+        }
+
+        private void AddSpecial(List<ListenerPrefix> list, ListenerPrefix prefix)
+        {
+            if (list == null)
+                return;
+
+            foreach (ListenerPrefix p in list)
+            {
+                if (p.Path == prefix.Path)
+                    throw new HttpListenerException((int)HttpStatusCode.BadRequest, SR.Format(SR.net_listener_already, prefix));
+            }
+            list.Add(prefix);
+        }
+
+        private bool RemoveSpecial(List<ListenerPrefix> list, ListenerPrefix prefix)
+        {
+            if (list == null)
+                return false;
+
+            int c = list.Count;
+            for (int i = 0; i < c; i++)
+            {
+                ListenerPrefix p = list[i];
+                if (p.Path == prefix.Path)
+                {
+                    list.RemoveAt(i);
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private void CheckIfRemove()
+        {
+            if (_prefixes.Count > 0)
+                return;
+
+            List<ListenerPrefix>? list = _unhandledPrefixes;
+            if (list != null && list.Count > 0)
+                return;
+
+            list = _allPrefixes;
+            if (list != null && list.Count > 0)
+                return;
+
+            HttpEndPointManager.RemoveEndPoint(this, _endpoint);
+        }
+
+        public void Close()
+        {
+            _socket.Close();
+            lock (_unregisteredConnections)
+            {
+                // Clone the list because RemoveConnection can be called from Close
+                var connections = new List<HttpConnection>(_unregisteredConnections.Keys);
+
+                foreach (HttpConnection c in connections)
+                    c.Close(true);
+                _unregisteredConnections.Clear();
+            }
+        }
+
+        public void AddPrefix(ListenerPrefix prefix, HttpListener listener)
+        {
+            List<ListenerPrefix>? current;
+            List<ListenerPrefix> future;
+            if (prefix.Host == "*")
+            {
+                do
+                {
+                    current = _unhandledPrefixes;
+                    future = current != null ? new List<ListenerPrefix>(current) : new List<ListenerPrefix>();
+                    prefix._listener = listener;
+                    AddSpecial(future, prefix);
+                } while (Interlocked.CompareExchange(ref _unhandledPrefixes, future, current) != current);
+                return;
+            }
+
+            if (prefix.Host == "+")
+            {
+                do
+                {
+                    current = _allPrefixes;
+                    future = current != null ? new List<ListenerPrefix>(current) : new List<ListenerPrefix>();
+                    prefix._listener = listener;
+                    AddSpecial(future, prefix);
+                } while (Interlocked.CompareExchange(ref _allPrefixes, future, current) != current);
+                return;
+            }
+
+            Dictionary<ListenerPrefix, HttpListener> prefs, p2;
+            do
+            {
+                prefs = _prefixes;
+                if (prefs.ContainsKey(prefix))
+                {
+                    throw new HttpListenerException((int)HttpStatusCode.BadRequest, SR.Format(SR.net_listener_already, prefix));
+                }
+                p2 = new Dictionary<ListenerPrefix, HttpListener>(prefs);
+                p2[prefix] = listener;
+            } while (Interlocked.CompareExchange(ref _prefixes, p2, prefs) != prefs);
+        }
+
+        public void RemovePrefix(ListenerPrefix prefix, HttpListener listener)
+        {
+            List<ListenerPrefix>? current;
+            List<ListenerPrefix> future;
+            if (prefix.Host == "*")
+            {
+                do
+                {
+                    current = _unhandledPrefixes;
+                    future = current != null ? new List<ListenerPrefix>(current) : new List<ListenerPrefix>();
+                    if (!RemoveSpecial(future, prefix))
+                        break; // Prefix not found
+                } while (Interlocked.CompareExchange(ref _unhandledPrefixes, future, current) != current);
+
+                CheckIfRemove();
+                return;
+            }
+
+            if (prefix.Host == "+")
+            {
+                do
+                {
+                    current = _allPrefixes;
+                    future = current != null ? new List<ListenerPrefix>(current) : new List<ListenerPrefix>();
+                    if (!RemoveSpecial(future, prefix))
+                        break; // Prefix not found
+                } while (Interlocked.CompareExchange(ref _allPrefixes, future, current) != current);
+                CheckIfRemove();
+                return;
+            }
+
+            Dictionary<ListenerPrefix, HttpListener> prefs, p2;
+            do
+            {
+                prefs = _prefixes;
+                if (!prefs.ContainsKey(prefix))
+                    break;
+
+                p2 = new Dictionary<ListenerPrefix, HttpListener>(prefs);
+                p2.Remove(prefix);
+            } while (Interlocked.CompareExchange(ref _prefixes, p2, prefs) != prefs);
+            CheckIfRemove();
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpEndPointManager.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpEndPointManager.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpEndPointManager.cs	(revision 376)
@@ -0,0 +1,217 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+// System.Net.HttpEndPointManager
+//
+// Author:
+//  Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+
+namespace SpaceWizards.HttpListener
+{
+    internal sealed class HttpEndPointManager
+    {
+        private static Dictionary<IPAddress, Dictionary<int, HttpEndPointListener>> s_ipEndPoints = new Dictionary<IPAddress, Dictionary<int, HttpEndPointListener>>();
+
+        private HttpEndPointManager()
+        {
+        }
+
+        public static void AddListener(HttpListener listener)
+        {
+            List<string> added = new List<string>();
+            try
+            {
+                lock ((s_ipEndPoints as ICollection).SyncRoot)
+                {
+                    foreach (string prefix in listener.Prefixes)
+                    {
+                        AddPrefixInternal(prefix, listener);
+                        added.Add(prefix);
+                    }
+                }
+            }
+            catch
+            {
+                foreach (string prefix in added)
+                {
+                    RemovePrefix(prefix, listener);
+                }
+                throw;
+            }
+        }
+
+        public static void AddPrefix(string prefix, HttpListener listener)
+        {
+            lock ((s_ipEndPoints as ICollection).SyncRoot)
+            {
+                AddPrefixInternal(prefix, listener);
+            }
+        }
+
+        private static void AddPrefixInternal(string p, HttpListener listener)
+        {
+            ListenerPrefix lp = new ListenerPrefix(p);
+            if (lp.Host != "*" && lp.Host != "+" && Uri.CheckHostName(lp.Host) == UriHostNameType.Unknown)
+                throw new HttpListenerException((int)HttpStatusCode.BadRequest, SR.net_listener_host);
+
+            if (lp.Port <= 0 || lp.Port >= 65536)
+                throw new HttpListenerException((int)HttpStatusCode.BadRequest, SR.net_invalid_port);
+
+            if (lp.Path!.Contains('%'))
+                throw new HttpListenerException((int)HttpStatusCode.BadRequest, SR.net_invalid_path);
+
+            if (lp.Path.IndexOf("//", StringComparison.Ordinal) != -1)
+                throw new HttpListenerException((int)HttpStatusCode.BadRequest, SR.net_invalid_path);
+
+            // listens on all the interfaces if host name cannot be parsed by IPAddress.
+            foreach (HttpEndPointListener epl in GetEPListener(lp.Host!, lp.Port, listener, lp.Secure))
+            {
+                epl.AddPrefix(lp, listener);
+            }
+        }
+
+        private static IEnumerable<HttpEndPointListener> GetEPListener(string host, int port, HttpListener listener, bool secure)
+        {
+            IPAddress[] addresses;
+            if (host == "*" || host == "+")
+            {
+                addresses = new []{IPAddress.Any, IPAddress.IPv6Any};
+            }
+            else
+            {
+                if (host.StartsWith('[') && host.EndsWith(']'))
+                    host = host[1..^1];
+
+                const int NotSupportedErrorCode = 50;
+                try
+                {
+                    addresses = Dns.GetHostAddresses(host);
+                }
+                catch
+                {
+                    // Throw same error code as windows, request is not supported.
+                    throw new HttpListenerException(NotSupportedErrorCode, SR.net_listener_not_supported);
+                }
+
+                if (addresses.Any(a => a.Equals(IPAddress.IPv6Any) || a.Equals(IPAddress.Any)))
+                {
+                    // Don't support listening to 0.0.0.0, match windows behavior.
+                    throw new HttpListenerException(NotSupportedErrorCode, SR.net_listener_not_supported);
+                }
+            }
+
+            foreach (var addr in addresses)
+            {
+                Dictionary<int, HttpEndPointListener>? p = null;
+                if (s_ipEndPoints.ContainsKey(addr))
+                {
+                    p = s_ipEndPoints[addr];
+                }
+                else
+                {
+                    p = new Dictionary<int, HttpEndPointListener>();
+                    s_ipEndPoints[addr] = p;
+                }
+
+                HttpEndPointListener? epl = null;
+                if (p.ContainsKey(port))
+                {
+                    epl = p[port];
+                }
+                else
+                {
+                    try
+                    {
+                        epl = new HttpEndPointListener(listener, addr, port, secure);
+                    }
+                    catch (SocketException ex)
+                    {
+                        throw new HttpListenerException(ex.ErrorCode, ex.Message);
+                    }
+                    p[port] = epl;
+                }
+
+                yield return epl;
+            }
+        }
+
+        public static void RemoveEndPoint(HttpEndPointListener epl, IPEndPoint ep)
+        {
+            lock ((s_ipEndPoints as ICollection).SyncRoot)
+            {
+                Dictionary<int, HttpEndPointListener>? p = null;
+                p = s_ipEndPoints[ep.Address];
+                p.Remove(ep.Port);
+                if (p.Count == 0)
+                {
+                    s_ipEndPoints.Remove(ep.Address);
+                }
+                epl.Close();
+            }
+        }
+
+        public static void RemoveListener(HttpListener listener)
+        {
+            lock ((s_ipEndPoints as ICollection).SyncRoot)
+            {
+                foreach (string prefix in listener.Prefixes)
+                {
+                    RemovePrefixInternal(prefix, listener);
+                }
+            }
+        }
+
+        public static void RemovePrefix(string prefix, HttpListener listener)
+        {
+            lock ((s_ipEndPoints as ICollection).SyncRoot)
+            {
+                RemovePrefixInternal(prefix, listener);
+            }
+        }
+
+        private static void RemovePrefixInternal(string prefix, HttpListener listener)
+        {
+            ListenerPrefix lp = new ListenerPrefix(prefix);
+            if (lp.Path!.Contains('%'))
+                return;
+
+            if (lp.Path.IndexOf("//", StringComparison.Ordinal) != -1)
+                return;
+
+            foreach (HttpEndPointListener epl in GetEPListener(lp.Host!, lp.Port, listener, lp.Secure))
+            {
+                epl.RemovePrefix(lp, listener);
+            }
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpHeaderStrings.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpHeaderStrings.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpHeaderStrings.cs	(revision 376)
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace SpaceWizards.HttpListener
+{
+    internal sealed class HttpHeaderStrings
+    {
+        internal const string Charset = "charset=";
+        internal const string NetCoreServerName = "Microsoft-NetCore/2.0";
+        internal const string Close = "close";
+        internal const string Chunked = "chunked";
+        internal const string KeepAlive = "keep-alive";
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListener.Certificates.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListener.Certificates.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListener.Certificates.cs	(revision 376)
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
+
+namespace SpaceWizards.HttpListener
+{
+    public partial class HttpListener
+    {
+        internal SslStream CreateSslStream(Stream innerStream, bool ownsStream, RemoteCertificateValidationCallback callback)
+        {
+            return new SslStream(innerStream, ownsStream, callback);
+        }
+
+        internal X509Certificate? LoadCertificateAndKey(IPAddress addr, int port)
+        {
+            // TODO https://github.com/dotnet/runtime/issues/19752: Implement functionality to read SSL certificate.
+            return null;
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListener.Managed.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListener.Managed.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListener.Managed.cs	(revision 376)
@@ -0,0 +1,365 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Net.Security;
+
+namespace SpaceWizards.HttpListener
+{
+    public sealed unsafe partial class HttpListener
+    {
+        public static bool IsSupported => true;
+
+        private Dictionary<HttpListenerContext, HttpListenerContext> _listenerContexts = new Dictionary<HttpListenerContext, HttpListenerContext>();
+        private List<HttpListenerContext> _contextQueue = new List<HttpListenerContext>();
+        private List<ListenerAsyncResult> _asyncWaitQueue = new List<ListenerAsyncResult>();
+        private Dictionary<HttpConnection, HttpConnection> _connections = new Dictionary<HttpConnection, HttpConnection>();
+        private bool _unsafeConnectionNtlmAuthentication;
+
+        public HttpListenerTimeoutManager TimeoutManager
+        {
+            get
+            {
+                CheckDisposed();
+                return _timeoutManager;
+            }
+        }
+
+        private void AddPrefixCore(string uriPrefix) => HttpEndPointManager.AddPrefix(uriPrefix, this);
+
+        private void RemovePrefixCore(string uriPrefix) => HttpEndPointManager.RemovePrefix(uriPrefix, this);
+
+        public void Start()
+        {
+            lock (_internalLock)
+            {
+                try
+                {
+                    CheckDisposed();
+                    if (_state == State.Started)
+                        return;
+
+                    HttpEndPointManager.AddListener(this);
+
+                    _state = State.Started;
+                }
+                catch (Exception exception)
+                {
+                    _state = State.Closed;
+                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, $"Start {exception}");
+                    throw;
+                }
+            }
+        }
+
+        public bool UnsafeConnectionNtlmAuthentication
+        {
+            // NTLM isn't currently supported, so this is a nop anyway and we can just roundtrip the value
+            get => _unsafeConnectionNtlmAuthentication;
+            set
+            {
+                CheckDisposed();
+                _unsafeConnectionNtlmAuthentication = value;
+            }
+        }
+
+        public void Stop()
+        {
+
+            lock (_internalLock)
+            {
+                try
+                {
+                    CheckDisposed();
+                    if (_state == State.Stopped)
+                    {
+                        return;
+                    }
+
+                    Close(false);
+                }
+                catch (Exception exception)
+                {
+                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, $"Stop {exception}");
+                    throw;
+                }
+                finally
+                {
+                    _state = State.Stopped;
+                }
+            }
+        }
+
+        public void Abort()
+        {
+
+            lock (_internalLock)
+            {
+                try
+                {
+                    if (_state == State.Closed)
+                    {
+                        return;
+                    }
+
+                    // Just detach and free resources. Don't call Stop (which may throw).
+                    if (_state == State.Started)
+                    {
+                        Close(true);
+                    }
+                }
+                catch (Exception exception)
+                {
+                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, $"Abort {exception}");
+                    throw;
+                }
+                finally
+                {
+                    _state = State.Closed;
+                }
+            }
+        }
+
+        private void Dispose()
+        {
+
+            lock (_internalLock)
+            {
+                try
+                {
+                    if (_state == State.Closed)
+                    {
+                        return;
+                    }
+
+                    Close(true);
+                }
+                catch (Exception exception)
+                {
+                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, $"Dispose {exception}");
+                    throw;
+                }
+                finally
+                {
+                    _state = State.Closed;
+                }
+            }
+        }
+
+        private void Close(bool force)
+        {
+            CheckDisposed();
+            HttpEndPointManager.RemoveListener(this);
+            Cleanup(force);
+        }
+
+        internal void UnregisterContext(HttpListenerContext context)
+        {
+            lock ((_listenerContexts as ICollection).SyncRoot)
+            {
+                _listenerContexts.Remove(context);
+            }
+            lock ((_contextQueue as ICollection).SyncRoot)
+            {
+                int idx = _contextQueue.IndexOf(context);
+                if (idx >= 0)
+                    _contextQueue.RemoveAt(idx);
+            }
+        }
+
+        internal void AddConnection(HttpConnection cnc)
+        {
+            lock ((_connections as ICollection).SyncRoot)
+            {
+                _connections[cnc] = cnc;
+            }
+        }
+
+        internal void RemoveConnection(HttpConnection cnc)
+        {
+            lock ((_connections as ICollection).SyncRoot)
+            {
+                _connections.Remove(cnc);
+            }
+        }
+
+        internal void RegisterContext(HttpListenerContext context)
+        {
+            lock ((_listenerContexts as ICollection).SyncRoot)
+            {
+                _listenerContexts[context] = context;
+            }
+
+            ListenerAsyncResult? ares = null;
+            lock ((_asyncWaitQueue as ICollection).SyncRoot)
+            {
+                if (_asyncWaitQueue.Count == 0)
+                {
+                    lock ((_contextQueue as ICollection).SyncRoot)
+                        _contextQueue.Add(context);
+                }
+                else
+                {
+                    ares = _asyncWaitQueue[0];
+                    _asyncWaitQueue.RemoveAt(0);
+                }
+            }
+
+            if (ares != null)
+            {
+                ares.Complete(context);
+            }
+        }
+
+        private void Cleanup(bool close_existing)
+        {
+            lock ((_listenerContexts as ICollection).SyncRoot)
+            {
+                if (close_existing)
+                {
+                    // Need to copy this since closing will call UnregisterContext
+                    ICollection keys = _listenerContexts.Keys;
+                    var all = new HttpListenerContext[keys.Count];
+                    keys.CopyTo(all, 0);
+                    _listenerContexts.Clear();
+                    for (int i = all.Length - 1; i >= 0; i--)
+                        all[i].Connection.Close(true);
+                }
+
+                lock ((_connections as ICollection).SyncRoot)
+                {
+                    ICollection keys = _connections.Keys;
+                    var conns = new HttpConnection[keys.Count];
+                    keys.CopyTo(conns, 0);
+                    _connections.Clear();
+                    for (int i = conns.Length - 1; i >= 0; i--)
+                        conns[i].Close(true);
+                }
+                lock ((_contextQueue as ICollection).SyncRoot)
+                {
+                    var ctxs = (HttpListenerContext[])_contextQueue.ToArray();
+                    _contextQueue.Clear();
+                    for (int i = ctxs.Length - 1; i >= 0; i--)
+                        ctxs[i].Connection.Close(true);
+                }
+
+                lock ((_asyncWaitQueue as ICollection).SyncRoot)
+                {
+                    Exception exc = new ObjectDisposedException("listener");
+                    foreach (ListenerAsyncResult ares in _asyncWaitQueue)
+                    {
+                        ares.Complete(exc);
+                    }
+                    _asyncWaitQueue.Clear();
+                }
+            }
+        }
+
+        private HttpListenerContext? GetContextFromQueue()
+        {
+            lock ((_contextQueue as ICollection).SyncRoot)
+            {
+                if (_contextQueue.Count == 0)
+                {
+                    return null;
+                }
+
+                HttpListenerContext context = _contextQueue[0];
+                _contextQueue.RemoveAt(0);
+
+                return context;
+            }
+        }
+
+        public IAsyncResult BeginGetContext(AsyncCallback? callback, object? state)
+        {
+            CheckDisposed();
+            if (_state != State.Started)
+            {
+                throw new InvalidOperationException(SR.Format(SR.net_listener_mustcall, "Start()"));
+            }
+
+            ListenerAsyncResult ares = new ListenerAsyncResult(this, callback, state);
+
+            // lock wait_queue early to avoid race conditions
+            lock ((_asyncWaitQueue as ICollection).SyncRoot)
+            {
+                lock ((_contextQueue as ICollection).SyncRoot)
+                {
+                    HttpListenerContext? ctx = GetContextFromQueue();
+                    if (ctx != null)
+                    {
+                        ares.Complete(ctx, true);
+                        return ares;
+                    }
+                }
+
+                _asyncWaitQueue.Add(ares);
+            }
+
+            return ares;
+        }
+
+        public HttpListenerContext EndGetContext(IAsyncResult asyncResult)
+        {
+            CheckDisposed();
+            if (asyncResult == null)
+            {
+                throw new ArgumentNullException(nameof(asyncResult));
+            }
+
+            ListenerAsyncResult? ares = asyncResult as ListenerAsyncResult;
+            if (ares == null || !ReferenceEquals(this, ares._parent))
+            {
+                throw new ArgumentException(SR.net_io_invalidasyncresult, nameof(asyncResult));
+            }
+            if (ares._endCalled)
+            {
+                throw new InvalidOperationException(SR.Format(SR.net_io_invalidendcall, nameof(EndGetContext)));
+            }
+
+            ares._endCalled = true;
+
+            if (!ares.IsCompleted)
+                ares.AsyncWaitHandle.WaitOne();
+
+            lock ((_asyncWaitQueue as ICollection).SyncRoot)
+            {
+                int idx = _asyncWaitQueue.IndexOf(ares);
+                if (idx >= 0)
+                    _asyncWaitQueue.RemoveAt(idx);
+            }
+
+            HttpListenerContext context = ares.GetContext()!;
+            context.ParseAuthentication(context.AuthenticationSchemes);
+            return context;
+        }
+
+        internal AuthenticationSchemes SelectAuthenticationScheme(HttpListenerContext context)
+        {
+            return AuthenticationSchemeSelectorDelegate != null ? AuthenticationSchemeSelectorDelegate(context.Request) : _authenticationScheme;
+        }
+
+        public HttpListenerContext GetContext()
+        {
+            CheckDisposed();
+            if (_state == State.Stopped)
+            {
+                throw new InvalidOperationException(SR.Format(SR.net_listener_mustcall, "Start()"));
+            }
+            if (_prefixes.Count == 0)
+            {
+                throw new InvalidOperationException(SR.Format(SR.net_listener_mustcall, "AddPrefix()"));
+            }
+
+            ListenerAsyncResult ares = (ListenerAsyncResult)BeginGetContext(null, null);
+            ares._inGet = true;
+
+            return EndGetContext(ares);
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListenerContext.Managed.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListenerContext.Managed.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListenerContext.Managed.cs	(revision 376)
@@ -0,0 +1,107 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Net;
+using System.Net.WebSockets;
+using System.Security.Principal;
+using System.Text;
+using System.Threading.Tasks;
+using SpaceWizards.HttpListener.WebSockets;
+using HttpListenerWebSocketContext = SpaceWizards.HttpListener.WebSockets.HttpListenerWebSocketContext;
+using HttpWebSocket = SpaceWizards.HttpListener.WebSockets.HttpWebSocket;
+
+namespace SpaceWizards.HttpListener
+{
+    public sealed unsafe partial class HttpListenerContext
+    {
+        private HttpConnection _connection;
+
+        internal HttpListenerContext(HttpConnection connection)
+        {
+            _connection = connection;
+            _response = new HttpListenerResponse(this);
+            Request = new HttpListenerRequest(this);
+            ErrorStatus = 400;
+        }
+
+        internal int ErrorStatus { get; set; }
+
+        internal string? ErrorMessage { get; set; }
+
+        internal bool HaveError => ErrorMessage != null;
+
+        internal HttpConnection Connection => _connection;
+
+        internal void ParseAuthentication(AuthenticationSchemes expectedSchemes)
+        {
+            if (expectedSchemes == AuthenticationSchemes.Anonymous)
+                return;
+
+            string? header = Request.Headers[HttpKnownHeaderNames.Authorization];
+            if (string.IsNullOrEmpty(header))
+                return;
+
+            if (IsBasicHeader(header))
+            {
+                _user = ParseBasicAuthentication(header.Substring(AuthenticationTypes.Basic.Length + 1));
+            }
+        }
+
+        internal IPrincipal? ParseBasicAuthentication(string authData) =>
+            TryParseBasicAuth(authData, out HttpStatusCode errorCode, out string? username, out string? password) ?
+                new GenericPrincipal(new HttpListenerBasicIdentity(username, password), Array.Empty<string>()) :
+                null;
+
+        internal static bool IsBasicHeader(string header) =>
+            header.Length >= 6 &&
+            header[5] == ' ' &&
+            string.Compare(header, 0, AuthenticationTypes.Basic, 0, 5, StringComparison.OrdinalIgnoreCase) == 0;
+
+        internal static bool TryParseBasicAuth(string headerValue, out HttpStatusCode errorCode, [NotNullWhen(true)] out string? username, [NotNullWhen(true)] out string? password)
+        {
+            errorCode = HttpStatusCode.OK;
+            username = password = null;
+            try
+            {
+                if (string.IsNullOrWhiteSpace(headerValue))
+                {
+                    return false;
+                }
+
+                string authString = Encoding.UTF8.GetString(Convert.FromBase64String(headerValue));
+                int colonPos = authString.IndexOf(':');
+                if (colonPos < 0)
+                {
+                    // username must be at least 1 char
+                    errorCode = HttpStatusCode.BadRequest;
+                    return false;
+                }
+
+                username = authString.Substring(0, colonPos);
+                password = authString.Substring(colonPos + 1);
+                return true;
+            }
+            catch
+            {
+                errorCode = HttpStatusCode.InternalServerError;
+                return false;
+            }
+        }
+
+        public Task<WebSockets.HttpListenerWebSocketContext> AcceptWebSocketAsync(string? subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval)
+        {
+            return HttpWebSocket.AcceptWebSocketAsyncCore(this, subProtocol, receiveBufferSize, keepAliveInterval);
+        }
+
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Task<WebSockets.HttpListenerWebSocketContext> AcceptWebSocketAsync(string? subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval, ArraySegment<byte> internalBuffer)
+        {
+            WebSocketValidate.ValidateArraySegment(internalBuffer, nameof(internalBuffer));
+            HttpWebSocket.ValidateOptions(subProtocol, receiveBufferSize, HttpWebSocket.MinSendBufferSize, keepAliveInterval);
+            return HttpWebSocket.AcceptWebSocketAsyncCore(this, subProtocol, receiveBufferSize, keepAliveInterval, internalBuffer);
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListenerRequest.Managed.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListenerRequest.Managed.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListenerRequest.Managed.cs	(revision 376)
@@ -0,0 +1,428 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+// System.Net.HttpListenerRequest
+//
+// Authors:
+//  Gonzalo Paniagua Javier (gonzalo.mono@gmail.com)
+//  Marek Safar (marek.safar@gmail.com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+// Copyright (c) 2011-2012 Xamarin, Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Net;
+using System.Security.Authentication.ExtendedProtection;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+
+namespace SpaceWizards.HttpListener
+{
+    public sealed partial class HttpListenerRequest
+    {
+        private sealed class Context : TransportContext
+        {
+            public override ChannelBinding? GetChannelBinding(ChannelBindingKind kind)
+            {
+                if (kind != ChannelBindingKind.Endpoint)
+                {
+                    throw new NotSupportedException(SR.Format(SR.net_listener_invalid_cbt_type, kind.ToString()));
+                }
+
+                return null;
+            }
+        }
+
+        private long _contentLength;
+        private bool _clSet;
+        private WebHeaderCollection _headers;
+        private string? _method;
+        private Stream? _inputStream;
+        private HttpListenerContext _context;
+        private bool _isChunked;
+
+        private static byte[] s_100continue = Encoding.ASCII.GetBytes("HTTP/1.1 100 Continue\r\n\r\n");
+
+        internal HttpListenerRequest(HttpListenerContext context)
+        {
+            _context = context;
+            _headers = new WebHeaderCollection();
+            _version = HttpVersion.Version10;
+        }
+
+        private static readonly char[] s_separators = new char[] { ' ' };
+
+        internal void SetRequestLine(string req)
+        {
+            string[] parts = req.Split(s_separators, 3);
+            if (parts.Length != 3)
+            {
+                _context.ErrorMessage = "Invalid request line (parts).";
+                return;
+            }
+
+            _method = parts[0];
+            foreach (char c in _method)
+            {
+                int ic = (int)c;
+
+                if ((ic >= 'A' && ic <= 'Z') ||
+                    (ic > 32 && c < 127 && c != '(' && c != ')' && c != '<' &&
+                     c != '<' && c != '>' && c != '@' && c != ',' && c != ';' &&
+                     c != ':' && c != '\\' && c != '"' && c != '/' && c != '[' &&
+                     c != ']' && c != '?' && c != '=' && c != '{' && c != '}'))
+                    continue;
+
+                _context.ErrorMessage = "(Invalid verb)";
+                return;
+            }
+
+            _rawUrl = parts[1];
+            if (parts[2].Length != 8 || !parts[2].StartsWith("HTTP/", StringComparison.Ordinal))
+            {
+                _context.ErrorMessage = "Invalid request line (version).";
+                return;
+            }
+
+            try
+            {
+                _version = new Version(parts[2].Substring(5));
+            }
+            catch
+            {
+                _context.ErrorMessage = "Invalid request line (version).";
+                return;
+            }
+
+            if (_version.Major < 1)
+            {
+                _context.ErrorMessage = "Invalid request line (version).";
+                return;
+            }
+            if (_version.Major > 1)
+            {
+                _context.ErrorStatus = (int)HttpStatusCode.HttpVersionNotSupported;
+                _context.ErrorMessage = HttpStatusDescription.Get(HttpStatusCode.HttpVersionNotSupported);
+                return;
+            }
+        }
+
+        private static bool MaybeUri(string s)
+        {
+            int p = s.IndexOf(':');
+            if (p == -1)
+                return false;
+
+            if (p >= 10)
+                return false;
+
+            return IsPredefinedScheme(s.Substring(0, p));
+        }
+
+        private static bool IsPredefinedScheme(string scheme)
+        {
+            if (scheme == null || scheme.Length < 3)
+                return false;
+
+            char c = scheme[0];
+            if (c == 'h')
+                return (scheme == UriScheme.Http ||  scheme == UriScheme.Https);
+            if (c == 'f')
+                return (scheme == UriScheme.File || scheme == UriScheme.Ftp);
+
+            if (c == 'n')
+            {
+                c = scheme[1];
+                if (c == 'e')
+                    return (scheme == UriScheme.News || scheme == UriScheme.NetPipe || scheme == UriScheme.NetTcp);
+                if (scheme == UriScheme.Nntp)
+                    return true;
+                return false;
+            }
+            if ((c == 'g' && scheme == UriScheme.Gopher) || (c == 'm' && scheme == UriScheme.Mailto))
+                return true;
+
+            return false;
+        }
+
+        internal void FinishInitialization()
+        {
+            string host = UserHostName;
+            if (_version > HttpVersion.Version10 && (host == null || host.Length == 0))
+            {
+                _context.ErrorMessage = "Invalid host name";
+                return;
+            }
+
+            string path;
+            Uri? raw_uri = null;
+            Debug.Assert(_rawUrl != null);
+            if (MaybeUri(_rawUrl!.ToLowerInvariant()) && Uri.TryCreate(_rawUrl, UriKind.Absolute, out raw_uri))
+                path = raw_uri.PathAndQuery;
+            else
+                path = _rawUrl;
+
+            if ((host == null || host.Length == 0))
+                host = UserHostAddress;
+
+            if (raw_uri != null)
+                host = raw_uri.Host;
+
+            host = host.Substring(0, ServiceNameStore.FindEndOfHostname(host, 0));
+
+            string base_uri = $"{RequestScheme}://{host}:{LocalEndPoint!.Port}";
+
+            if (!Uri.TryCreate(base_uri + path, UriKind.Absolute, out _requestUri))
+            {
+                _context.ErrorMessage = WebUtility.HtmlEncode("Invalid url: " + base_uri + path);
+                return;
+            }
+
+            _requestUri = HttpListenerRequestUriBuilder.GetRequestUri(_rawUrl, _requestUri.Scheme,
+                                _requestUri.Authority, _requestUri.LocalPath, _requestUri.Query);
+
+            if (_version >= HttpVersion.Version11)
+            {
+                string? t_encoding = Headers[HttpKnownHeaderNames.TransferEncoding];
+                _isChunked = (t_encoding != null && string.Equals(t_encoding, "chunked", StringComparison.OrdinalIgnoreCase));
+                // 'identity' is not valid!
+                if (t_encoding != null && !_isChunked)
+                {
+                    _context.ErrorStatus = 501;
+                    _context.ErrorMessage = "";
+                    return;
+                }
+            }
+
+            if (!_isChunked && !_clSet)
+            {
+                if (string.Equals(_method, "POST", StringComparison.OrdinalIgnoreCase) ||
+                    string.Equals(_method, "PUT", StringComparison.OrdinalIgnoreCase))
+                {
+                    _context.ErrorStatus = 411;
+                    _context.ErrorMessage = "";
+                    return;
+                }
+            }
+
+            if (string.Equals(Headers[HttpKnownHeaderNames.Expect], "100-continue", StringComparison.OrdinalIgnoreCase))
+            {
+                HttpResponseStream output = _context.Connection.GetResponseStream();
+                output.InternalWrite(s_100continue, 0, s_100continue.Length);
+            }
+        }
+
+        internal static string Unquote(string str)
+        {
+            int start = str.IndexOf('\"');
+            int end = str.LastIndexOf('\"');
+            if (start >= 0 && end >= 0)
+                str = str.Substring(start + 1, end - 1);
+            return str.Trim();
+        }
+
+        internal void AddHeader(string header)
+        {
+            int colon = header.IndexOf(':');
+            if (colon == -1 || colon == 0)
+            {
+                _context.ErrorMessage = HttpStatusDescription.Get(400);
+                _context.ErrorStatus = 400;
+                return;
+            }
+
+            string name = header.AsSpan(0, colon).Trim().ToString();
+            string val = header.AsSpan(colon + 1).Trim().ToString();
+            if (name.Equals("content-length", StringComparison.OrdinalIgnoreCase))
+            {
+                // To match Windows behavior:
+                // Content lengths >= 0 and <= long.MaxValue are accepted as is.
+                // Content lengths > long.MaxValue and <= ulong.MaxValue are treated as 0.
+                // Content lengths < 0 cause the requests to fail.
+                // Other input is a failure, too.
+                long parsedContentLength =
+                    ulong.TryParse(val, out ulong parsedUlongContentLength) ? (parsedUlongContentLength <= long.MaxValue ? (long)parsedUlongContentLength : 0) :
+                    long.Parse(val);
+                if (parsedContentLength < 0 || (_clSet && parsedContentLength != _contentLength))
+                {
+                    _context.ErrorMessage = "Invalid Content-Length.";
+                }
+                else
+                {
+                    _contentLength = parsedContentLength;
+                    _clSet = true;
+                }
+            }
+            else if (name.Equals("transfer-encoding", StringComparison.OrdinalIgnoreCase))
+            {
+                if (Headers[HttpKnownHeaderNames.TransferEncoding] != null)
+                {
+                    _context.ErrorStatus = (int)HttpStatusCode.NotImplemented;
+                    _context.ErrorMessage = HttpStatusDescription.Get(HttpStatusCode.NotImplemented);
+                }
+            }
+
+            if (_context.ErrorMessage == null)
+            {
+                _headers.Set(name, val);
+            }
+        }
+
+        // returns true is the stream could be reused.
+        internal bool FlushInput()
+        {
+            if (!HasEntityBody)
+                return true;
+
+            int length = 2048;
+            if (_contentLength > 0)
+                length = (int)Math.Min(_contentLength, (long)length);
+
+            byte[] bytes = new byte[length];
+            while (true)
+            {
+                try
+                {
+                    IAsyncResult ares = InputStream.BeginRead(bytes, 0, length, null, null);
+                    if (!ares.IsCompleted && !ares.AsyncWaitHandle.WaitOne(1000))
+                        return false;
+                    if (InputStream.EndRead(ares) <= 0)
+                        return true;
+                }
+                catch (ObjectDisposedException)
+                {
+                    _inputStream = null;
+                    return true;
+                }
+                catch
+                {
+                    return false;
+                }
+            }
+        }
+
+        private X509Certificate2? GetClientCertificateCore() => ClientCertificate = _context.Connection.ClientCertificate;
+
+        private int GetClientCertificateErrorCore()
+        {
+            HttpConnection cnc = _context.Connection;
+            if (cnc.ClientCertificate == null)
+                return 0;
+            int[]? errors = cnc.ClientCertificateErrors;
+            if (errors != null && errors.Length > 0)
+                return errors[0];
+            return 0;
+        }
+
+        public long ContentLength64
+        {
+            get
+            {
+                if (_isChunked)
+                    _contentLength = -1;
+
+                return _contentLength;
+            }
+        }
+
+        public bool HasEntityBody => (_contentLength > 0 || _isChunked);
+
+        public NameValueCollection Headers => _headers;
+
+        public string? HttpMethod => _method;
+
+        public Stream InputStream
+        {
+            get
+            {
+                if (_inputStream == null)
+                {
+                    if (_isChunked || _contentLength > 0)
+                        _inputStream = _context.Connection.GetRequestStream(_isChunked, _contentLength);
+                    else
+                        _inputStream = Stream.Null;
+                }
+
+                return _inputStream;
+            }
+        }
+
+        public bool IsAuthenticated => false;
+
+        public bool IsSecureConnection => _context.Connection.IsSecure;
+
+        public IPEndPoint? LocalEndPoint => _context.Connection.LocalEndPoint;
+
+        public IPEndPoint? RemoteEndPoint => _context.Connection.RemoteEndPoint;
+
+        public Guid RequestTraceIdentifier { get; } = Guid.NewGuid();
+
+        private IAsyncResult BeginGetClientCertificateCore(AsyncCallback? requestCallback, object? state)
+        {
+            var asyncResult = new GetClientCertificateAsyncResult(this, state, requestCallback);
+
+            // The certificate is already retrieved by the time this method is called. GetClientCertificateCore() evaluates to
+            // a simple member access, so this will always complete immediately.
+            ClientCertState = ListenerClientCertState.Completed;
+            asyncResult.InvokeCallback(GetClientCertificateCore());
+
+            return asyncResult;
+        }
+
+        public X509Certificate2? EndGetClientCertificate(IAsyncResult asyncResult)
+        {
+            if (asyncResult == null)
+                throw new ArgumentNullException(nameof(asyncResult));
+
+            GetClientCertificateAsyncResult? clientCertAsyncResult = asyncResult as GetClientCertificateAsyncResult;
+            if (clientCertAsyncResult == null || clientCertAsyncResult.AsyncObject != this)
+            {
+                throw new ArgumentException(SR.net_io_invalidasyncresult, nameof(asyncResult));
+            }
+            if (clientCertAsyncResult.EndCalled)
+            {
+                throw new InvalidOperationException(SR.Format(SR.net_io_invalidendcall, nameof(EndGetClientCertificate)));
+            }
+            clientCertAsyncResult.EndCalled = true;
+
+            return (X509Certificate2?)clientCertAsyncResult.Result;
+        }
+
+        public string? ServiceName => null;
+
+        public TransportContext TransportContext => new Context();
+
+        private Uri? RequestUri => _requestUri;
+        private bool SupportsWebSockets => true;
+
+        private sealed class GetClientCertificateAsyncResult : LazyAsyncResult
+        {
+            public GetClientCertificateAsyncResult(object myObject, object? myState, AsyncCallback? myCallBack) : base(myObject, myState, myCallBack) { }
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListenerResponse.Managed.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListenerResponse.Managed.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListenerResponse.Managed.cs	(revision 376)
@@ -0,0 +1,354 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+// System.Net.HttpListenerResponse
+//
+// Author:
+//  Gonzalo Paniagua Javier (gonzalo@novell.com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Globalization;
+using System.IO;
+using System.Net;
+using System.Text;
+
+namespace SpaceWizards.HttpListener
+{
+    public sealed partial class HttpListenerResponse : IDisposable
+    {
+        private long _contentLength;
+        private Version _version = HttpVersion.Version11;
+        private int _statusCode = 200;
+        internal object _headersLock = new object();
+        private bool _forceCloseChunked;
+
+        internal HttpListenerResponse(HttpListenerContext context)
+        {
+            _httpContext = context;
+        }
+
+        internal bool ForceCloseChunked => _forceCloseChunked;
+
+        private void EnsureResponseStream()
+        {
+            if (_responseStream == null)
+            {
+                _responseStream = _httpContext!.Connection.GetResponseStream();
+            }
+        }
+
+        public Version ProtocolVersion
+        {
+            get => _version;
+            set
+            {
+                CheckDisposed();
+                if (value == null)
+                {
+                    throw new ArgumentNullException(nameof(value));
+                }
+                if (value.Major != 1 || (value.Minor != 0 && value.Minor != 1))
+                {
+                    throw new ArgumentException(SR.net_wrongversion, nameof(value));
+                }
+
+                _version = new Version(value.Major, value.Minor); // match Windows behavior, trimming to just Major.Minor
+            }
+        }
+
+        public int StatusCode
+        {
+            get => _statusCode;
+            set
+            {
+                CheckDisposed();
+
+                if (value < 100 || value > 999)
+                    throw new ProtocolViolationException(SR.net_invalidstatus);
+
+                _statusCode = value;
+            }
+        }
+
+        private void Dispose() => Close(true);
+
+        public void Close()
+        {
+            if (Disposed)
+                return;
+
+            Close(false);
+        }
+
+        public void Abort()
+        {
+            if (Disposed)
+                return;
+
+            Close(true);
+        }
+
+        private void Close(bool force)
+        {
+            Disposed = true;
+            _httpContext!.Connection.Close(force);
+        }
+
+        public void Close(byte[] responseEntity, bool willBlock)
+        {
+            CheckDisposed();
+
+            if (responseEntity == null)
+            {
+                throw new ArgumentNullException(nameof(responseEntity));
+            }
+
+            if (!SentHeaders && _boundaryType != BoundaryType.Chunked)
+            {
+                ContentLength64 = responseEntity.Length;
+            }
+
+            if (willBlock)
+            {
+                try
+                {
+                    OutputStream.Write(responseEntity, 0, responseEntity.Length);
+                }
+                finally
+                {
+                    Close(false);
+                }
+            }
+            else
+            {
+                OutputStream.BeginWrite(responseEntity, 0, responseEntity.Length, iar =>
+                {
+                    var thisRef = (HttpListenerResponse)iar.AsyncState!;
+                    try
+                    {
+                        thisRef.OutputStream.EndWrite(iar);
+                    }
+                    finally
+                    {
+                        thisRef.Close(false);
+                    }
+                }, this);
+            }
+        }
+
+        public void CopyFrom(HttpListenerResponse templateResponse)
+        {
+            _webHeaders.Clear();
+            _webHeaders.Add(templateResponse._webHeaders);
+            _contentLength = templateResponse._contentLength;
+            _statusCode = templateResponse._statusCode;
+            _statusDescription = templateResponse._statusDescription;
+            _keepAlive = templateResponse._keepAlive;
+            _version = templateResponse._version;
+        }
+
+        internal void SendHeaders(bool closing, MemoryStream ms, bool isWebSocketHandshake = false)
+        {
+            if (!isWebSocketHandshake)
+            {
+                if (_webHeaders[HttpKnownHeaderNames.Server] == null)
+                {
+                    _webHeaders.Set(HttpKnownHeaderNames.Server, HttpHeaderStrings.NetCoreServerName);
+                }
+
+                if (_webHeaders[HttpKnownHeaderNames.Date] == null)
+                {
+                    _webHeaders.Set(HttpKnownHeaderNames.Date, DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture));
+                }
+
+                if (_boundaryType == BoundaryType.None)
+                {
+                    if (HttpListenerRequest.ProtocolVersion <= HttpVersion.Version10)
+                    {
+                        _keepAlive = false;
+                    }
+                    else
+                    {
+                        _boundaryType = BoundaryType.Chunked;
+                    }
+
+                    if (CanSendResponseBody(_httpContext!.Response.StatusCode))
+                    {
+                        _contentLength = -1;
+                    }
+                    else
+                    {
+                        _boundaryType = BoundaryType.ContentLength;
+                        _contentLength = 0;
+                    }
+                }
+
+                if (_boundaryType != BoundaryType.Chunked)
+                {
+                    if (_boundaryType != BoundaryType.ContentLength && closing)
+                    {
+                        _contentLength = CanSendResponseBody(_httpContext!.Response.StatusCode) ? -1 : 0;
+                    }
+
+                    if (_boundaryType == BoundaryType.ContentLength)
+                    {
+                        _webHeaders.Set(HttpKnownHeaderNames.ContentLength, _contentLength.ToString("D", CultureInfo.InvariantCulture));
+                    }
+                }
+
+                /* Apache forces closing the connection for these status codes:
+                 *    HttpStatusCode.BadRequest                 400
+                 *    HttpStatusCode.RequestTimeout             408
+                 *    HttpStatusCode.LengthRequired             411
+                 *    HttpStatusCode.RequestEntityTooLarge     413
+                 *    HttpStatusCode.RequestUriTooLong         414
+                 *    HttpStatusCode.InternalServerError      500
+                 *    HttpStatusCode.ServiceUnavailable         503
+                 */
+                bool conn_close = (_statusCode == (int)HttpStatusCode.BadRequest || _statusCode == (int)HttpStatusCode.RequestTimeout
+                        || _statusCode == (int)HttpStatusCode.LengthRequired || _statusCode == (int)HttpStatusCode.RequestEntityTooLarge
+                        || _statusCode == (int)HttpStatusCode.RequestUriTooLong || _statusCode == (int)HttpStatusCode.InternalServerError
+                        || _statusCode == (int)HttpStatusCode.ServiceUnavailable);
+
+                if (!conn_close)
+                {
+                    conn_close = !_httpContext!.Request.KeepAlive;
+                }
+
+                // They sent both KeepAlive: true and Connection: close
+                if (!_keepAlive || conn_close)
+                {
+                    _webHeaders.Set(HttpKnownHeaderNames.Connection, HttpHeaderStrings.Close);
+                    conn_close = true;
+                }
+
+                if (SendChunked)
+                {
+                    _webHeaders.Set(HttpKnownHeaderNames.TransferEncoding, HttpHeaderStrings.Chunked);
+                }
+
+                int reuses = _httpContext!.Connection.Reuses;
+                if (reuses >= 100)
+                {
+                    _forceCloseChunked = true;
+                    if (!conn_close)
+                    {
+                        _webHeaders.Set(HttpKnownHeaderNames.Connection, HttpHeaderStrings.Close);
+                        conn_close = true;
+                    }
+                }
+
+                if (HttpListenerRequest.ProtocolVersion <= HttpVersion.Version10)
+                {
+                    if (_keepAlive)
+                    {
+                        Headers[HttpResponseHeader.KeepAlive] = "true";
+                    }
+
+                    if (!conn_close)
+                    {
+                        _webHeaders.Set(HttpKnownHeaderNames.Connection, HttpHeaderStrings.KeepAlive);
+                    }
+                }
+
+                ComputeCookies();
+            }
+
+            Encoding encoding = Encoding.Default;
+            StreamWriter writer = new StreamWriter(ms, encoding, 256);
+            writer.Write("HTTP/1.1 {0} ", _statusCode); // "1.1" matches Windows implementation, which ignores the response version
+            writer.Flush();
+            byte[] statusDescriptionBytes = WebHeaderEncoding.GetBytes(StatusDescription);
+            ms.Write(statusDescriptionBytes, 0, statusDescriptionBytes.Length);
+            writer.Write("\r\n");
+
+            writer.Write(FormatHeaders(_webHeaders));
+            writer.Flush();
+            int preamble = encoding.Preamble.Length;
+            EnsureResponseStream();
+
+            /* Assumes that the ms was at position 0 */
+            ms.Position = preamble;
+            SentHeaders = !isWebSocketHandshake;
+        }
+
+        private static bool HeaderCanHaveEmptyValue(string name) =>
+            !string.Equals(name, HttpKnownHeaderNames.Location, StringComparison.OrdinalIgnoreCase);
+
+        private static string FormatHeaders(WebHeaderCollection headers)
+        {
+            var sb = new StringBuilder();
+
+            for (int i = 0; i < headers.Count; i++)
+            {
+                string key = headers.GetKey(i);
+                string[] values = headers.GetValues(i)!;
+
+                int startingLength = sb.Length;
+
+                sb.Append(key).Append(": ");
+                bool anyValues = false;
+                for (int j = 0; j < values.Length; j++)
+                {
+                    string value = values[j];
+                    if (!string.IsNullOrWhiteSpace(value))
+                    {
+                        if (anyValues)
+                        {
+                            if (key.Equals(HttpKnownHeaderNames.SetCookie, StringComparison.OrdinalIgnoreCase) ||
+                                key.Equals(HttpKnownHeaderNames.SetCookie2, StringComparison.OrdinalIgnoreCase))
+                            {
+                                sb.Append("\r\n").Append(key).Append(": ");
+                            }
+                            else
+                            {
+                                sb.Append(", ");
+                            }
+                        }
+                        sb.Append(value);
+                        anyValues = true;
+                    }
+                }
+
+                if (anyValues || HeaderCanHaveEmptyValue(key))
+                {
+                    // Complete the header
+                    sb.Append("\r\n");
+                }
+                else
+                {
+                    // Empty header; remove it.
+                    sb.Length = startingLength;
+                }
+            }
+
+            return sb.Append("\r\n").ToString();
+        }
+
+        private bool Disposed { get; set; }
+        internal bool SentHeaders { get; set; }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListenerTimeoutManager.Managed.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListenerTimeoutManager.Managed.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListenerTimeoutManager.Managed.cs	(revision 376)
@@ -0,0 +1,96 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.Versioning;
+
+namespace SpaceWizards.HttpListener
+{
+    public class HttpListenerTimeoutManager
+    {
+        private TimeSpan _drainEntityBody = TimeSpan.Zero;
+        private TimeSpan _idleConnection = TimeSpan.Zero;
+
+        internal HttpListenerTimeoutManager(HttpListener listener) { }
+
+        public TimeSpan DrainEntityBody
+        {
+            get => _drainEntityBody;
+            set
+            {
+                // Managed implementation currently doesn't pool connections,
+                // so this is a nop other than roundtripping the value.
+                ValidateTimeout(value);
+                _drainEntityBody = value;
+            }
+        }
+
+        public TimeSpan IdleConnection
+        {
+            get => _idleConnection;
+            set
+            {
+                // Managed implementation currently doesn't pool connections,
+                // so this is a nop other than roundtripping the value.
+                ValidateTimeout(value);
+                _idleConnection = value;
+            }
+        }
+
+        public TimeSpan EntityBody
+        {
+            get => TimeSpan.Zero;
+            [SupportedOSPlatform("windows")]
+            set
+            {
+                ValidateTimeout(value);
+                throw new PlatformNotSupportedException(); // low usage, not currently implemented
+            }
+        }
+
+        public TimeSpan HeaderWait
+        {
+            get => TimeSpan.Zero;
+            [SupportedOSPlatform("windows")]
+            set
+            {
+                ValidateTimeout(value);
+                throw new PlatformNotSupportedException(); // low usage, not currently implemented
+            }
+        }
+
+        public long MinSendBytesPerSecond
+        {
+            get => 0;
+            [SupportedOSPlatform("windows")]
+            set
+            {
+                if (value < 0 || value > uint.MaxValue)
+                {
+                    throw new ArgumentOutOfRangeException(nameof(value));
+                }
+                throw new PlatformNotSupportedException(); // low usage, not currently implemented
+            }
+        }
+
+        public TimeSpan RequestQueue
+        {
+            get => TimeSpan.Zero;
+            [SupportedOSPlatform("windows")]
+            set
+            {
+                ValidateTimeout(value);
+                throw new PlatformNotSupportedException(); // low usage, not currently implemented
+            }
+        }
+
+        private void ValidateTimeout(TimeSpan value)
+        {
+            long timeoutValue = Convert.ToInt64(value.TotalSeconds);
+            if (timeoutValue < 0 || timeoutValue > ushort.MaxValue)
+            {
+                throw new ArgumentOutOfRangeException(nameof(value));
+            }
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpRequestStream.Managed.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpRequestStream.Managed.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpRequestStream.Managed.cs	(revision 376)
@@ -0,0 +1,218 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+// System.Net.RequestStream
+//
+// Author:
+//  Gonzalo Paniagua Javier (gonzalo@novell.com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Runtime.ExceptionServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace SpaceWizards.HttpListener
+{
+    internal partial class HttpRequestStream : Stream
+    {
+        private byte[] _buffer;
+        private int _offset;
+        private int _length;
+        private long _remainingBody;
+        protected bool _closed;
+        private Stream _stream;
+
+        internal HttpRequestStream(Stream stream, byte[] buffer, int offset, int length)
+            : this(stream, buffer, offset, length, -1)
+        {
+        }
+
+        internal HttpRequestStream(Stream stream, byte[] buffer, int offset, int length, long contentlength)
+        {
+            _stream = stream;
+            _buffer = buffer;
+            _offset = offset;
+            _length = length;
+            _remainingBody = contentlength;
+        }
+
+        // Returns 0 if we can keep reading from the base stream,
+        // > 0 if we read something from the buffer.
+        // -1 if we had a content length set and we finished reading that many bytes.
+        private int FillFromBuffer(byte[] buffer, int offset, int count)
+        {
+            if (_remainingBody == 0)
+                return -1;
+
+            if (_length == 0)
+                return 0;
+
+            int size = Math.Min(_length, count);
+            if (_remainingBody > 0)
+                size = (int)Math.Min(size, _remainingBody);
+
+            if (_offset > _buffer.Length - size)
+            {
+                size = Math.Min(size, _buffer.Length - _offset);
+            }
+            if (size == 0)
+                return 0;
+
+            Buffer.BlockCopy(_buffer, _offset, buffer, offset, size);
+            _offset += size;
+            _length -= size;
+            if (_remainingBody > 0)
+                _remainingBody -= size;
+            return size;
+        }
+
+        protected virtual int ReadCore(byte[] buffer, int offset, int size)
+        {
+            // Call FillFromBuffer to check for buffer boundaries even when remaining_body is 0
+            int nread = FillFromBuffer(buffer, offset, size);
+            if (nread == -1)
+            { // No more bytes available (Content-Length)
+                return 0;
+            }
+            else if (nread > 0)
+            {
+                return nread;
+            }
+
+            if (_remainingBody > 0)
+            {
+                size = (int)Math.Min(_remainingBody, (long)size);
+            }
+
+            nread = _stream.Read(buffer, offset, size);
+
+            if (_remainingBody > 0)
+            {
+                if (nread == 0)
+                {
+                    throw new HttpListenerException((int)HttpStatusCode.BadRequest);
+                }
+
+                Debug.Assert(nread <= _remainingBody);
+                _remainingBody -= nread;
+            }
+
+            return nread;
+        }
+
+        protected virtual IAsyncResult BeginReadCore(byte[] buffer, int offset, int size, AsyncCallback? cback, object? state)
+        {
+            if (size == 0 || _closed)
+            {
+                HttpStreamAsyncResult ares = new HttpStreamAsyncResult(this);
+                ares._callback = cback;
+                ares._state = state;
+                ares.Complete();
+                return ares;
+            }
+
+            int nread = FillFromBuffer(buffer, offset, size);
+            if (nread > 0 || nread == -1)
+            {
+                HttpStreamAsyncResult ares = new HttpStreamAsyncResult(this);
+                ares._buffer = buffer;
+                ares._offset = offset;
+                ares._count = size;
+                ares._callback = cback;
+                ares._state = state;
+                ares._synchRead = Math.Max(0, nread);
+                ares.Complete();
+                return ares;
+            }
+
+            // Avoid reading past the end of the request to allow
+            // for HTTP pipelining
+            if (_remainingBody >= 0 && size > _remainingBody)
+            {
+                size = (int)Math.Min(_remainingBody, (long)size);
+            }
+
+            return _stream.BeginRead(buffer, offset, size, cback, state);
+        }
+
+        public override int EndRead(IAsyncResult asyncResult)
+        {
+            if (asyncResult == null)
+                throw new ArgumentNullException(nameof(asyncResult));
+
+            if (asyncResult is HttpStreamAsyncResult r)
+            {
+                if (!ReferenceEquals(this, r._parent))
+                {
+                    throw new ArgumentException(SR.net_io_invalidasyncresult, nameof(asyncResult));
+                }
+                if (r._endCalled)
+                {
+                    throw new InvalidOperationException(SR.Format(SR.net_io_invalidendcall, nameof(EndRead)));
+                }
+                r._endCalled = true;
+
+                if (!asyncResult.IsCompleted)
+                {
+                    asyncResult.AsyncWaitHandle.WaitOne();
+                }
+
+                return r._synchRead;
+            }
+
+            if (_closed)
+                return 0;
+
+            int nread = 0;
+            try
+            {
+                nread = _stream.EndRead(asyncResult);
+            }
+            catch (IOException e) when (e.InnerException is ArgumentException || e.InnerException is InvalidOperationException)
+            {
+                ExceptionDispatchInfo.Throw(e.InnerException);
+            }
+
+            if (_remainingBody > 0)
+            {
+                if (nread == 0)
+                {
+                    throw new HttpListenerException((int)HttpStatusCode.BadRequest);
+                }
+
+                Debug.Assert(nread <= _remainingBody);
+                _remainingBody -= nread;
+            }
+
+            return nread;
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpResponseStream.Managed.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpResponseStream.Managed.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpResponseStream.Managed.cs	(revision 376)
@@ -0,0 +1,320 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+// System.Net.ResponseStream
+//
+// Author:
+//  Gonzalo Paniagua Javier (gonzalo@novell.com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Net.Sockets;
+using System.Text;
+using System.Runtime.ExceptionServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace SpaceWizards.HttpListener
+{
+    internal sealed partial class HttpResponseStream : Stream
+    {
+        private HttpListenerResponse _response;
+        private bool _ignore_errors;
+        private bool _trailer_sent;
+        private Stream _stream;
+
+        internal HttpResponseStream(Stream stream, HttpListenerResponse response, bool ignore_errors)
+        {
+            _response = response;
+            _ignore_errors = ignore_errors;
+            _stream = stream;
+        }
+
+        private void DisposeCore()
+        {
+            byte[]? bytes = null;
+            MemoryStream? ms = GetHeaders(true);
+            bool chunked = _response.SendChunked;
+            if (_stream.CanWrite)
+            {
+                try
+                {
+                    if (ms != null)
+                    {
+                        long start = ms.Position;
+                        if (chunked && !_trailer_sent)
+                        {
+                            bytes = GetChunkSizeBytes(0, true);
+                            ms.Position = ms.Length;
+                            ms.Write(bytes, 0, bytes.Length);
+                        }
+                        InternalWrite(ms.GetBuffer(), (int)start, (int)(ms.Length - start));
+                        _trailer_sent = true;
+                    }
+                    else if (chunked && !_trailer_sent)
+                    {
+                        bytes = GetChunkSizeBytes(0, true);
+                        InternalWrite(bytes, 0, bytes.Length);
+                        _trailer_sent = true;
+                    }
+                }
+                catch (HttpListenerException)
+                {
+                    // Ignore error due to connection reset by peer
+                }
+            }
+            _response.Close();
+        }
+
+        internal async Task WriteWebSocketHandshakeHeadersAsync()
+        {
+            if (_closed)
+                throw new ObjectDisposedException(GetType().ToString());
+
+            if (_stream.CanWrite)
+            {
+                MemoryStream ms = GetHeaders(closing: false, isWebSocketHandshake: true)!;
+                bool chunked = _response.SendChunked;
+
+                long start = ms.Position;
+                if (chunked)
+                {
+                    byte[] bytes = GetChunkSizeBytes(0, true);
+                    ms.Position = ms.Length;
+                    ms.Write(bytes, 0, bytes.Length);
+                }
+
+                await InternalWriteAsync(ms.GetBuffer(), (int)start, (int)(ms.Length - start)).ConfigureAwait(false);
+                await _stream.FlushAsync().ConfigureAwait(false);
+            }
+        }
+
+        private MemoryStream? GetHeaders(bool closing, bool isWebSocketHandshake = false)
+        {
+            // SendHeaders works on shared headers
+            lock (_response._headersLock)
+            {
+                if (_response.SentHeaders)
+                {
+                    return null;
+                }
+
+                MemoryStream ms = new MemoryStream();
+                _response.SendHeaders(closing, ms, isWebSocketHandshake);
+                return ms;
+            }
+        }
+
+        private static byte[] s_crlf = new byte[] { 13, 10 };
+        private static byte[] GetChunkSizeBytes(int size, bool final)
+        {
+            Debug.Assert(size != 0 || final, "Only final chunks can have a size of 0");
+
+            string str = $"{size:x}\r\n{(final ? "\r\n" : "")}";
+            return Encoding.ASCII.GetBytes(str);
+        }
+
+        internal void InternalWrite(byte[] buffer, int offset, int count)
+        {
+            if (_ignore_errors)
+            {
+                try
+                {
+                    _stream.Write(buffer, offset, count);
+                }
+                catch { }
+            }
+            else
+            {
+                try
+                {
+                    _stream.Write(buffer, offset, count);
+                }
+                catch (IOException ex)
+                {
+                    throw new HttpListenerException(ex.HResult, ex.Message);
+                }
+            }
+        }
+
+        internal Task InternalWriteAsync(byte[] buffer, int offset, int count) =>
+            _ignore_errors ? InternalWriteIgnoreErrorsAsync(buffer, offset, count) : _stream.WriteAsync(buffer, offset, count);
+
+        private async Task InternalWriteIgnoreErrorsAsync(byte[] buffer, int offset, int count)
+        {
+            try { await _stream.WriteAsync(buffer.AsMemory(offset, count)).ConfigureAwait(false); }
+            catch { }
+        }
+
+        private void WriteCore(byte[] buffer, int offset, int size)
+        {
+            if (size == 0)
+                return;
+
+            byte[]? bytes = null;
+            MemoryStream? ms = GetHeaders(false);
+            bool chunked = _response.SendChunked;
+            if (ms != null)
+            {
+                long start = ms.Position; // After the possible preamble for the encoding
+                ms.Position = ms.Length;
+                if (chunked)
+                {
+                    bytes = GetChunkSizeBytes(size, false);
+                    ms.Write(bytes, 0, bytes.Length);
+                }
+
+                int new_count = Math.Min(size, 16384 - (int)ms.Position + (int)start);
+                ms.Write(buffer, offset, new_count);
+                size -= new_count;
+                offset += new_count;
+                InternalWrite(ms.GetBuffer(), (int)start, (int)(ms.Length - start));
+                ms.SetLength(0);
+                ms.Capacity = 0; // 'dispose' the buffer in ms.
+            }
+            else if (chunked)
+            {
+                bytes = GetChunkSizeBytes(size, false);
+                InternalWrite(bytes, 0, bytes.Length);
+            }
+
+            if (size > 0)
+                InternalWrite(buffer, offset, size);
+            if (chunked)
+                InternalWrite(s_crlf, 0, 2);
+        }
+
+        private IAsyncResult BeginWriteCore(byte[] buffer, int offset, int size, AsyncCallback? cback, object? state)
+        {
+            if (_closed)
+            {
+                HttpStreamAsyncResult ares = new HttpStreamAsyncResult(this);
+                ares._callback = cback;
+                ares._state = state;
+                ares.Complete();
+                return ares;
+            }
+
+            if (size == 0)
+            {
+                HttpStreamAsyncResult ares = new HttpStreamAsyncResult(this);
+                ares._callback = cback;
+                ares._state = state;
+                ares._count = size;
+                ares._offset = offset;
+                ares._buffer = buffer;
+                ares.Complete();
+                return ares;
+            }
+
+            byte[]? bytes = null;
+            MemoryStream? ms = GetHeaders(false);
+            bool chunked = _response.SendChunked;
+            if (ms != null)
+            {
+                long start = ms.Position;
+                ms.Position = ms.Length;
+                if (chunked)
+                {
+                    bytes = GetChunkSizeBytes(size, false);
+                    ms.Write(bytes, 0, bytes.Length);
+                }
+                ms.Write(buffer, offset, size);
+                buffer = ms.GetBuffer();
+                offset = (int)start;
+                size = (int)(ms.Position - start);
+            }
+            else if (chunked)
+            {
+                bytes = GetChunkSizeBytes(size, false);
+                InternalWrite(bytes, 0, bytes.Length);
+            }
+
+            try
+            {
+                return _stream.BeginWrite(buffer, offset, size, cback, state);
+            }
+            catch (IOException ex)
+            {
+                if (_ignore_errors)
+                {
+                    HttpStreamAsyncResult ares = new HttpStreamAsyncResult(this);
+                    ares._callback = cback;
+                    ares._state = state;
+                    ares.Complete();
+                    return ares;
+                }
+                else
+                {
+                    throw new HttpListenerException(ex.HResult, ex.Message);
+                }
+            }
+        }
+
+        private void EndWriteCore(IAsyncResult asyncResult)
+        {
+            if (_closed)
+                return;
+
+            if (asyncResult is HttpStreamAsyncResult { _buffer: not null, _count: 0 })
+                return;
+
+            if (_ignore_errors)
+            {
+                try
+                {
+                    _stream.EndWrite(asyncResult);
+                    if (_response.SendChunked)
+                        _stream.Write(s_crlf, 0, 2);
+                }
+                catch { }
+            }
+            else
+            {
+                try
+                {
+                    _stream.EndWrite(asyncResult);
+                    if (_response.SendChunked)
+                        _stream.Write(s_crlf, 0, 2);
+                }
+                catch (IOException ex)
+                {
+                    // NetworkStream wraps exceptions in IOExceptions; if the underlying socket operation
+                    // failed because of invalid arguments or usage, propagate that error.  Otherwise
+                    // wrap the whole thing in an HttpListenerException.  This is all to match Windows behavior.
+                    if (ex.InnerException is ArgumentException || ex.InnerException is InvalidOperationException)
+                    {
+                        ExceptionDispatchInfo.Throw(ex.InnerException);
+                    }
+
+                    throw new HttpListenerException(ex.HResult, ex.Message);
+                }
+            }
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpStreamAsyncResult.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpStreamAsyncResult.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/HttpStreamAsyncResult.cs	(revision 376)
@@ -0,0 +1,113 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+// System.Net.HttpStreamAsyncResult
+//
+// Authors:
+//  Gonzalo Paniagua Javier (gonzalo@novell.com)
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace SpaceWizards.HttpListener
+{
+    internal sealed class HttpStreamAsyncResult : IAsyncResult
+    {
+        private object _locker = new object();
+        private ManualResetEvent? _handle;
+        private bool _completed;
+
+        internal readonly object _parent;
+        internal byte[]? _buffer;
+        internal int _offset;
+        internal int _count;
+        internal AsyncCallback? _callback;
+        internal object? _state;
+        internal int _synchRead;
+        internal Exception? _error;
+        internal bool _endCalled;
+
+        internal HttpStreamAsyncResult(object parent)
+        {
+            _parent = parent;
+        }
+
+        public void Complete(Exception e)
+        {
+            _error = e;
+            Complete();
+        }
+
+        public void Complete()
+        {
+            lock (_locker)
+            {
+                if (_completed)
+                    return;
+
+                _completed = true;
+                if (_handle != null)
+                    _handle.Set();
+
+                if (_callback != null)
+                    Task.Run(() => _callback(this));
+            }
+        }
+
+        public object? AsyncState
+        {
+            get { return _state; }
+        }
+
+        public WaitHandle AsyncWaitHandle
+        {
+            get
+            {
+                lock (_locker)
+                {
+                    if (_handle == null)
+                        _handle = new ManualResetEvent(_completed);
+                }
+
+                return _handle;
+            }
+        }
+
+        public bool CompletedSynchronously => false;
+
+        public bool IsCompleted
+        {
+            get
+            {
+                lock (_locker)
+                {
+                    return _completed;
+                }
+            }
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/ListenerAsyncResult.Managed.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/ListenerAsyncResult.Managed.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/ListenerAsyncResult.Managed.cs	(revision 376)
@@ -0,0 +1,257 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+// System.Net.ListenerAsyncResult
+//
+// Authors:
+//  Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//
+// Copyright (c) 2005 Ximian, Inc (http://www.ximian.com)
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Net;
+using System.Runtime.ExceptionServices;
+using System.Threading;
+
+namespace SpaceWizards.HttpListener
+{
+    internal sealed class ListenerAsyncResult : IAsyncResult
+    {
+        private ManualResetEvent? _handle;
+        private bool _synch;
+        private bool _completed;
+        private AsyncCallback? _cb;
+        private object? _state;
+        private Exception? _exception;
+        private HttpListenerContext? _context;
+        private object _locker = new object();
+        private ListenerAsyncResult? _forward;
+        internal readonly HttpListener _parent;
+        internal bool _endCalled;
+        internal bool _inGet;
+
+        public ListenerAsyncResult(HttpListener parent, AsyncCallback? cb, object? state)
+        {
+            _parent = parent;
+            _cb = cb;
+            _state = state;
+        }
+
+        internal void Complete(Exception exc)
+        {
+            if (_forward != null)
+            {
+                _forward.Complete(exc);
+                return;
+            }
+            _exception = exc;
+            if (_inGet && (exc is ObjectDisposedException))
+                _exception = new HttpListenerException((int)HttpStatusCode.InternalServerError, SR.net_listener_close);
+            lock (_locker)
+            {
+                _completed = true;
+                if (_handle != null)
+                    _handle.Set();
+
+                if (_cb != null)
+                    ThreadPool.UnsafeQueueUserWorkItem(s_invokeCB, this);
+            }
+        }
+
+        private static WaitCallback s_invokeCB = new WaitCallback(InvokeCallback!);
+        private static void InvokeCallback(object o)
+        {
+            ListenerAsyncResult ares = (ListenerAsyncResult)o!;
+            if (ares._forward != null)
+            {
+                InvokeCallback(ares._forward);
+                return;
+            }
+            try
+            {
+                ares._cb!(ares);
+            }
+            catch
+            {
+            }
+        }
+
+        internal void Complete(HttpListenerContext context)
+        {
+            Complete(context, false);
+        }
+
+        internal void Complete(HttpListenerContext context, bool synch)
+        {
+            if (_forward != null)
+            {
+                _forward.Complete(context, synch);
+                return;
+            }
+            _synch = synch;
+            _context = context;
+            lock (_locker)
+            {
+                bool authFailure = false;
+                try
+                {
+                    context.AuthenticationSchemes = context._listener!.SelectAuthenticationScheme(context);
+                }
+                catch (OutOfMemoryException oom)
+                {
+                    context.AuthenticationSchemes = AuthenticationSchemes.Anonymous;
+                    _exception = oom;
+                }
+                catch
+                {
+                    authFailure = true;
+                    context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
+                }
+
+                if (context.AuthenticationSchemes != AuthenticationSchemes.None &&
+                    (context.AuthenticationSchemes & AuthenticationSchemes.Anonymous) != AuthenticationSchemes.Anonymous &&
+                    (context.AuthenticationSchemes & AuthenticationSchemes.Basic) != AuthenticationSchemes.Basic)
+                {
+                    authFailure = true;
+                    context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
+                }
+                else if (context.AuthenticationSchemes == AuthenticationSchemes.Basic)
+                {
+                    HttpStatusCode errorCode = HttpStatusCode.Unauthorized;
+                    string? authHeader = context.Request.Headers["Authorization"];
+                    if (authHeader == null ||
+                        !HttpListenerContext.IsBasicHeader(authHeader) ||
+                        authHeader.Length < AuthenticationTypes.Basic.Length + 2 ||
+                        !HttpListenerContext.TryParseBasicAuth(authHeader.Substring(AuthenticationTypes.Basic.Length + 1), out errorCode, out _, out _))
+                    {
+                        authFailure = true;
+                        context.Response.StatusCode = (int)errorCode;
+                        if (errorCode == HttpStatusCode.Unauthorized)
+                        {
+                            context.Response.Headers["WWW-Authenticate"] = context.AuthenticationSchemes + " realm=\"" + context._listener!.Realm + "\"";
+                        }
+                    }
+                }
+
+                if (authFailure)
+                {
+                    context.Response.OutputStream.Close();
+                    IAsyncResult ares = context._listener!.BeginGetContext(_cb, _state);
+                    _forward = (ListenerAsyncResult)ares;
+                    lock (_forward._locker)
+                    {
+                        if (_handle != null)
+                            _forward._handle = _handle;
+                    }
+                    ListenerAsyncResult next = _forward;
+                    for (int i = 0; next._forward != null; i++)
+                    {
+                        if (i > 20)
+                            Complete(new HttpListenerException((int)HttpStatusCode.Unauthorized, SR.net_listener_auth_errors));
+                        next = next._forward;
+                    }
+                }
+                else
+                {
+                    _completed = true;
+                    _synch = false;
+
+                    if (_handle != null)
+                        _handle.Set();
+
+                    if (_cb != null)
+                        ThreadPool.UnsafeQueueUserWorkItem(s_invokeCB, this);
+                }
+            }
+        }
+
+        internal HttpListenerContext? GetContext()
+        {
+            if (_forward != null)
+            {
+                return _forward.GetContext();
+            }
+
+            if (_exception != null)
+            {
+                ExceptionDispatchInfo.Throw(_exception);
+            }
+
+            return _context;
+        }
+
+        public object? AsyncState
+        {
+            get
+            {
+                if (_forward != null)
+                    return _forward.AsyncState;
+                return _state;
+            }
+        }
+
+        public WaitHandle AsyncWaitHandle
+        {
+            get
+            {
+                if (_forward != null)
+                    return _forward.AsyncWaitHandle;
+
+                lock (_locker)
+                {
+                    if (_handle == null)
+                        _handle = new ManualResetEvent(_completed);
+                }
+
+                return _handle;
+            }
+        }
+
+        public bool CompletedSynchronously
+        {
+            get
+            {
+                if (_forward != null)
+                    return _forward.CompletedSynchronously;
+                return _synch;
+            }
+        }
+
+        public bool IsCompleted
+        {
+            get
+            {
+                if (_forward != null)
+                    return _forward.IsCompleted;
+
+                lock (_locker)
+                {
+                    return _completed;
+                }
+            }
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/ListenerPrefix.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/ListenerPrefix.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/ListenerPrefix.cs	(revision 376)
@@ -0,0 +1,134 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+// System.Net.ListenerPrefix
+//
+// Author:
+//  Gonzalo Paniagua Javier (gonzalo@novell.com)
+//  Oleg Mihailik (mihailik gmail co_m)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Net;
+
+namespace SpaceWizards.HttpListener
+{
+    internal sealed class ListenerPrefix
+    {
+        private string _original;
+        private string? _host;
+        private ushort _port;
+        private string? _path;
+        private bool _secure;
+        private IPAddress[]? _addresses;
+        internal HttpListener? _listener;
+
+        public ListenerPrefix(string prefix)
+        {
+            _original = prefix;
+            Parse(prefix);
+        }
+
+        public override string ToString()
+        {
+            return _original;
+        }
+
+        public IPAddress[]? Addresses
+        {
+            get { return _addresses; }
+            set { _addresses = value; }
+        }
+        public bool Secure
+        {
+            get { return _secure; }
+        }
+
+        public string? Host
+        {
+            get { return _host; }
+        }
+
+        public int Port
+        {
+            get { return _port; }
+        }
+
+        public string? Path
+        {
+            get { return _path; }
+        }
+
+        // Equals and GetHashCode are required to detect duplicates in HttpListenerPrefixCollection.
+        public override bool Equals([NotNullWhen(true)] object? o)
+        {
+            ListenerPrefix? other = o as ListenerPrefix;
+            if (other == null)
+                return false;
+
+            return (_original == other._original);
+        }
+
+        public override int GetHashCode()
+        {
+            return _original.GetHashCode();
+        }
+
+        private void Parse(string uri)
+        {
+            ushort default_port = 80;
+            if (uri.StartsWith("https://", StringComparison.Ordinal))
+            {
+                default_port = 443;
+                _secure = true;
+            }
+
+            int length = uri.Length;
+            int start_host = uri.IndexOf(':') + 3;
+            if (start_host >= length)
+                throw new ArgumentException(SR.net_listener_host);
+
+            int end_host = ServiceNameStore.FindEndOfHostname(uri, start_host);
+            int root;
+            if (uri[end_host] == ':')
+            {
+                _host = uri.Substring(start_host, end_host - start_host);
+                root = uri.IndexOf('/', end_host, length - end_host);
+                _port = (ushort)int.Parse(uri.AsSpan(end_host + 1, root - end_host - 1));
+                _path = uri.Substring(root);
+            }
+            else
+            {
+                root = uri.IndexOf('/', start_host, length - start_host);
+                _host = uri.Substring(start_host, root - start_host);
+                _port = default_port;
+                _path = uri.Substring(root);
+            }
+            if (_path.Length != 1)
+                _path = _path.Substring(0, _path.Length - 1);
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/WebSockets/HttpWebSocket.Managed.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/WebSockets/HttpWebSocket.Managed.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Managed/WebSockets/HttpWebSocket.Managed.cs	(revision 376)
@@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Net;
+using System.Net.WebSockets;
+using System.Threading.Tasks;
+
+namespace SpaceWizards.HttpListener.WebSockets
+{
+    internal static partial class HttpWebSocket
+    {
+        private const string SupportedVersion = "13";
+
+        internal static async Task<HttpListenerWebSocketContext> AcceptWebSocketAsyncCore(HttpListenerContext context,
+            string? subProtocol,
+            int receiveBufferSize,
+            TimeSpan keepAliveInterval,
+            ArraySegment<byte>? internalBuffer = null)
+        {
+            ValidateOptions(subProtocol, receiveBufferSize, MinSendBufferSize, keepAliveInterval);
+
+            // get property will create a new response if one doesn't exist.
+            HttpListenerResponse response = context.Response;
+            HttpListenerRequest request = context.Request;
+            ValidateWebSocketHeaders(context);
+
+            string? secWebSocketVersion = request.Headers[HttpKnownHeaderNames.SecWebSocketVersion];
+
+            // Optional for non-browser client
+            string? origin = request.Headers[HttpKnownHeaderNames.Origin];
+
+            string[]? secWebSocketProtocols = null;
+            string outgoingSecWebSocketProtocolString;
+            bool shouldSendSecWebSocketProtocolHeader =
+                ProcessWebSocketProtocolHeader(
+                    request.Headers[HttpKnownHeaderNames.SecWebSocketProtocol],
+                    subProtocol,
+                    out outgoingSecWebSocketProtocolString);
+
+            if (shouldSendSecWebSocketProtocolHeader)
+            {
+                secWebSocketProtocols = new string[] { outgoingSecWebSocketProtocolString };
+                response.Headers.Add(HttpKnownHeaderNames.SecWebSocketProtocol, outgoingSecWebSocketProtocolString);
+            }
+
+            // negotiate the websocket key return value
+            string? secWebSocketKey = request.Headers[HttpKnownHeaderNames.SecWebSocketKey];
+            string secWebSocketAccept = HttpWebSocket.GetSecWebSocketAcceptString(secWebSocketKey);
+
+            response.Headers.Add(HttpKnownHeaderNames.Connection, HttpKnownHeaderNames.Upgrade);
+            response.Headers.Add(HttpKnownHeaderNames.Upgrade, WebSocketUpgradeToken);
+            response.Headers.Add(HttpKnownHeaderNames.SecWebSocketAccept, secWebSocketAccept);
+
+            response.StatusCode = (int)HttpStatusCode.SwitchingProtocols; // HTTP 101
+            response.StatusDescription = HttpStatusDescription.Get(HttpStatusCode.SwitchingProtocols)!;
+
+            HttpResponseStream responseStream = (response.OutputStream as HttpResponseStream)!;
+
+            // Send websocket handshake headers
+            await responseStream.WriteWebSocketHandshakeHeadersAsync().ConfigureAwait(false);
+
+            WebSocket webSocket = WebSocket.CreateFromStream(context.Connection.ConnectedStream, isServer:true, subProtocol, keepAliveInterval);
+
+            HttpListenerWebSocketContext webSocketContext = new HttpListenerWebSocketContext(
+                                                                request.Url!,
+                                                                request.Headers,
+                                                                request.Cookies,
+                                                                context.User!,
+                                                                request.IsAuthenticated,
+                                                                request.IsLocal,
+                                                                request.IsSecureConnection,
+                                                                origin!,
+                                                                secWebSocketProtocols != null ? secWebSocketProtocols : Array.Empty<string>(),
+                                                                secWebSocketVersion!,
+                                                                secWebSocketKey!,
+                                                                webSocket);
+
+            return webSocketContext;
+        }
+
+        private const bool WebSocketsSupported = true;
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/NetEventSource.HttpListener.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/NetEventSource.HttpListener.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/NetEventSource.HttpListener.cs	(revision 376)
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.Tracing;
+
+namespace System.Net
+{
+    [EventSource(Name = "Private.InternalDiagnostics.SpaceWizards.HttpListener", LocalizationResources = "FxResources.SpaceWizards.HttpListener.SR")]
+    internal sealed partial class NetEventSource : EventSource
+    {
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/ServiceNameStore.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/ServiceNameStore.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/ServiceNameStore.cs	(revision 376)
@@ -0,0 +1,337 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Security.Authentication.ExtendedProtection;
+using System.Diagnostics;
+using System.Globalization;
+using System.Net;
+
+namespace SpaceWizards.HttpListener
+{
+    internal sealed class ServiceNameStore
+    {
+        private readonly List<string> _serviceNames;
+        private ServiceNameCollection? _serviceNameCollection;
+
+        public ServiceNameCollection ServiceNames
+        {
+            get
+            {
+                if (_serviceNameCollection == null)
+                {
+                    _serviceNameCollection = new ServiceNameCollection(_serviceNames);
+                }
+                return _serviceNameCollection;
+            }
+        }
+
+        public ServiceNameStore()
+        {
+            _serviceNames = new List<string>();
+            _serviceNameCollection = null; // set only when needed (due to expensive item-by-item copy)
+        }
+
+        private static string? NormalizeServiceName(string? inputServiceName)
+        {
+            if (string.IsNullOrWhiteSpace(inputServiceName))
+            {
+                return inputServiceName;
+            }
+
+            // Separate out the prefix
+            int shashIndex = inputServiceName.IndexOf('/');
+            if (shashIndex < 0)
+            {
+                return inputServiceName;
+            }
+            string prefix = inputServiceName.Substring(0, shashIndex + 1); // Includes slash
+            string hostPortAndDistinguisher = inputServiceName.Substring(shashIndex + 1); // Excludes slash
+
+            if (string.IsNullOrWhiteSpace(hostPortAndDistinguisher))
+            {
+                return inputServiceName;
+            }
+
+            string host = hostPortAndDistinguisher;
+            string port = string.Empty;
+            string distinguisher = string.Empty;
+
+            // Check for the absence of a port or distinguisher.
+            UriHostNameType hostType = Uri.CheckHostName(hostPortAndDistinguisher);
+            if (hostType == UriHostNameType.Unknown)
+            {
+                string hostAndPort = hostPortAndDistinguisher;
+
+                // Check for distinguisher
+                int nextSlashIndex = hostPortAndDistinguisher.IndexOf('/');
+                if (nextSlashIndex >= 0)
+                {
+                    // host:port/distinguisher or host/distinguisher
+                    hostAndPort = hostPortAndDistinguisher.Substring(0, nextSlashIndex); // Excludes Slash
+                    distinguisher = hostPortAndDistinguisher.Substring(nextSlashIndex); // Includes Slash
+                    host = hostAndPort; // We don't know if there is a port yet.
+
+                    // No need to validate the distinguisher
+                }
+
+                // Check for port
+                int colonIndex = hostAndPort.LastIndexOf(':'); // Allow IPv6 addresses
+                if (colonIndex >= 0)
+                {
+                    // host:port
+                    host = hostAndPort.Substring(0, colonIndex); // Excludes colon
+                    port = hostAndPort.Substring(colonIndex + 1); // Excludes colon
+
+                    // Loosely validate the port just to make sure it was a port and not something else
+                    ushort portValue;
+                    if (!ushort.TryParse(port, NumberStyles.Integer, CultureInfo.InvariantCulture, out portValue))
+                    {
+                        return inputServiceName;
+                    }
+
+                    // Re-include the colon for the final output.  Do not change the port format.
+                    port = hostAndPort.Substring(colonIndex);
+                }
+
+                hostType = Uri.CheckHostName(host); // Revaidate the host
+            }
+
+            if (hostType != UriHostNameType.Dns)
+            {
+                // UriHostNameType.IPv4, UriHostNameType.IPv6: Do not normalize IPv4/6 hosts.
+                // UriHostNameType.Basic: This is never returned by CheckHostName today
+                // UriHostNameType.Unknown: Nothing recognizable to normalize
+                // default Some new UriHostNameType?
+                return inputServiceName;
+            }
+
+            // Now we have a valid DNS host, normalize it.
+
+            Uri? constructedUri;
+            // This shouldn't fail, but we need to avoid any unexpected exceptions on this code path.
+            if (!Uri.TryCreate(Uri.UriSchemeHttp + Uri.SchemeDelimiter + host, UriKind.Absolute, out constructedUri))
+            {
+                return inputServiceName;
+            }
+
+            string normalizedHost = constructedUri.GetComponents(
+                UriComponents.NormalizedHost, UriFormat.SafeUnescaped);
+
+            string normalizedServiceName = prefix + normalizedHost + port + distinguisher;
+
+            // Don't return the new one unless we absolutely have to.  It may have only changed casing.
+            if (inputServiceName.Equals(normalizedServiceName, StringComparison.OrdinalIgnoreCase))
+            {
+                return inputServiceName;
+            }
+
+            return normalizedServiceName;
+        }
+
+        private bool AddSingleServiceName(string spn)
+        {
+            spn = NormalizeServiceName(spn)!;
+            if (Contains(spn))
+            {
+                return false;
+            }
+            else
+            {
+                _serviceNames.Add(spn);
+                return true;
+            }
+        }
+
+        public bool Add(string uriPrefix)
+        {
+            Debug.Assert(!string.IsNullOrEmpty(uriPrefix));
+
+            string[] newServiceNames = BuildServiceNames(uriPrefix);
+
+            bool addedAny = false;
+            foreach (string spn in newServiceNames)
+            {
+                if (AddSingleServiceName(spn))
+                {
+                    addedAny = true;
+
+                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, SR.Format(SR.net_log_listener_spn_add, spn, uriPrefix));
+                }
+            }
+
+            if (addedAny)
+            {
+                _serviceNameCollection = null;
+            }
+            else if (NetEventSource.Log.IsEnabled())
+            {
+                NetEventSource.Info(this, SR.Format(SR.net_log_listener_spn_not_add, uriPrefix));
+            }
+
+            return addedAny;
+        }
+
+        public bool Remove(string uriPrefix)
+        {
+            Debug.Assert(!string.IsNullOrEmpty(uriPrefix));
+
+            string? newServiceName = BuildSimpleServiceName(uriPrefix);
+            newServiceName = NormalizeServiceName(newServiceName);
+            bool needToRemove = Contains(newServiceName);
+
+            if (needToRemove)
+            {
+                _serviceNames.Remove(newServiceName!);
+                _serviceNameCollection = null; //invalidate (readonly) ServiceNameCollection
+            }
+
+            if (NetEventSource.Log.IsEnabled())
+            {
+                if (needToRemove)
+                {
+                    NetEventSource.Info(this, SR.Format(SR.net_log_listener_spn_remove, newServiceName, uriPrefix));
+                }
+                else
+                {
+                    NetEventSource.Info(this, SR.Format(SR.net_log_listener_spn_not_remove, uriPrefix));
+                }
+            }
+
+            return needToRemove;
+        }
+
+        // Assumes already normalized
+        private bool Contains(string? newServiceName)
+        {
+            if (newServiceName == null)
+            {
+                return false;
+            }
+
+            foreach (string serviceName in _serviceNames)
+            {
+                if (serviceName.Equals(newServiceName, StringComparison.OrdinalIgnoreCase))
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        public void Clear()
+        {
+            _serviceNames.Clear();
+            _serviceNameCollection = null; //invalidate (readonly) ServiceNameCollection
+        }
+
+        private string? ExtractHostname(string uriPrefix, bool allowInvalidUriStrings)
+        {
+            if (Uri.IsWellFormedUriString(uriPrefix, UriKind.Absolute))
+            {
+                Uri hostUri = new Uri(uriPrefix);
+                return hostUri.Host;
+            }
+            else if (allowInvalidUriStrings)
+            {
+                int i = uriPrefix.IndexOf("://", StringComparison.Ordinal) + 3;
+                int j = FindEndOfHostname(uriPrefix, i);
+
+                return uriPrefix.Substring(i, j - i);
+            }
+
+            return null;
+        }
+
+        internal static int FindEndOfHostname(ReadOnlySpan<char> uriPrefix, int i)
+        {
+            int j = i;
+
+            bool inSquareBrackets = false;
+            while (j < uriPrefix.Length && uriPrefix[j] != '/' && (uriPrefix[j] != ':' || inSquareBrackets))
+            {
+                if (uriPrefix[j] == '[')
+                {
+                    if (inSquareBrackets)
+                    {
+                        j = i;
+                        break;
+                    }
+                    inSquareBrackets = true;
+                }
+                if (inSquareBrackets && uriPrefix[j] == ']')
+                {
+                    inSquareBrackets = false;
+                }
+                j++;
+            }
+
+            return j;
+        }
+
+        public string? BuildSimpleServiceName(string uriPrefix)
+        {
+            string? hostname = ExtractHostname(uriPrefix, false);
+
+            if (hostname != null)
+            {
+                return "HTTP/" + hostname;
+            }
+            else
+            {
+                return null;
+            }
+        }
+
+        public string[] BuildServiceNames(string uriPrefix)
+        {
+            string hostname = ExtractHostname(uriPrefix, true)!;
+
+            IPAddress? ipAddress = null;
+            if (string.Equals(hostname, "*", StringComparison.OrdinalIgnoreCase) ||
+                string.Equals(hostname, "+", StringComparison.OrdinalIgnoreCase) ||
+                IPAddress.TryParse(hostname, out ipAddress))
+            {
+                // for a wildcard, register the machine name.  If the caller doesn't have DNS permission
+                // or the query fails for some reason, don't add an SPN.
+                try
+                {
+                    string machineName = Dns.GetHostEntry(string.Empty).HostName;
+                    return new string[] { "HTTP/" + machineName };
+                }
+                catch (System.Net.Sockets.SocketException)
+                {
+                    return Array.Empty<string>();
+                }
+                catch (System.Security.SecurityException)
+                {
+                    return Array.Empty<string>();
+                }
+            }
+            else if (!hostname.Contains('.'))
+            {
+                // for a dotless name, try to resolve the FQDN.  If the caller doesn't have DNS permission
+                // or the query fails for some reason, add only the dotless name.
+                try
+                {
+                    string fqdn = Dns.GetHostEntry(hostname).HostName;
+                    return new string[] { "HTTP/" + hostname, "HTTP/" + fqdn };
+                }
+                catch (System.Net.Sockets.SocketException)
+                {
+                    return new string[] { "HTTP/" + hostname };
+                }
+                catch (System.Security.SecurityException)
+                {
+                    return new string[] { "HTTP/" + hostname };
+                }
+            }
+            else
+            {
+                return new string[] { "HTTP/" + hostname };
+            }
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/StreamHelper.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/StreamHelper.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/StreamHelper.cs	(revision 376)
@@ -0,0 +1,27 @@
+﻿using System;
+using System.Runtime.CompilerServices;
+
+namespace SpaceWizards.HttpListener
+{
+    internal static class StreamHelper
+    {
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static void ValidateBufferArguments(byte[] buffer, int offset, int count)
+        {
+            if (buffer is null)
+            {
+                throw new ArgumentNullException(nameof(buffer));
+            }
+
+            if (offset < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(offset), "Argument cannot be negative");
+            }
+
+            if ((uint)count > buffer.Length - offset)
+            {
+                throw new ArgumentOutOfRangeException(nameof(offset), "Invalid offset length");
+            }
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/UriScheme.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/UriScheme.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/UriScheme.cs	(revision 376)
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Net
+{
+    internal static class UriScheme
+    {
+        public const string File = "file";
+        public const string Ftp = "ftp";
+        public const string Gopher = "gopher";
+        public const string Http = "http";
+        public const string Https = "https";
+        public const string News = "news";
+        public const string NetPipe = "net.pipe";
+        public const string NetTcp = "net.tcp";
+        public const string Nntp = "nntp";
+        public const string Mailto = "mailto";
+        public const string Ws = "ws";
+        public const string Wss = "wss";
+
+        public const string SchemeDelimiter = "://";
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/WebHeaderEncoding.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/WebHeaderEncoding.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/WebHeaderEncoding.cs	(revision 376)
@@ -0,0 +1,85 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Text;
+
+namespace System.Net
+{
+    // we use this static class as a helper class to encode/decode HTTP headers.
+    // what we need is a 1-1 correspondence between a char in the range U+0000-U+00FF
+    // and a byte in the range 0x00-0xFF (which is the range that can hit the network).
+    // The Latin-1 encoding (ISO-88591-1) (GetEncoding(28591)) works for byte[] to string, but is a little slow.
+    // It doesn't work for string -> byte[] because of best-fit-mapping problems.
+    internal static class WebHeaderEncoding
+    {
+        internal static unsafe string GetString(byte[] bytes, int byteIndex, int byteCount)
+        {
+            if (byteCount < 1)
+            {
+                return string.Empty;
+            }
+
+            Debug.Assert(bytes != null && (uint)byteIndex <= (uint)bytes.Length && (uint)(byteIndex + byteCount) <= (uint)bytes.Length);
+
+            return string.Create(byteCount, (bytes, byteIndex), (buffer, state) =>
+            {
+                fixed (byte* pByt = &state.bytes[state.byteIndex])
+                fixed (char* pStr = buffer)
+                {
+                    byte* pBytes = pByt;
+                    char* pString = pStr;
+                    int byteCount = buffer.Length;
+
+                    while (byteCount >= 8)
+                    {
+                        pString[0] = (char)pBytes[0];
+                        pString[1] = (char)pBytes[1];
+                        pString[2] = (char)pBytes[2];
+                        pString[3] = (char)pBytes[3];
+                        pString[4] = (char)pBytes[4];
+                        pString[5] = (char)pBytes[5];
+                        pString[6] = (char)pBytes[6];
+                        pString[7] = (char)pBytes[7];
+                        pString += 8;
+                        pBytes += 8;
+                        byteCount -= 8;
+                    }
+                    for (int i = 0; i < byteCount; i++)
+                    {
+                        pString[i] = (char)pBytes[i];
+                    }
+                }
+            });
+        }
+
+        internal static int GetByteCount(string myString) => myString.Length;
+
+        internal static unsafe void GetBytes(string myString, int charIndex, int charCount, byte[] bytes, int byteIndex)
+        {
+            if (myString.Length == 0)
+            {
+                return;
+            }
+
+            fixed (byte* bufferPointer = bytes)
+            {
+                byte* newBufferPointer = bufferPointer + byteIndex;
+                int finalIndex = charIndex + charCount;
+                while (charIndex < finalIndex)
+                {
+                    *newBufferPointer++ = (byte)myString[charIndex++];
+                }
+            }
+        }
+        internal static byte[] GetBytes(string myString)
+        {
+            byte[] bytes = new byte[myString.Length];
+            if (myString.Length != 0)
+            {
+                GetBytes(myString, 0, myString.Length, bytes, 0);
+            }
+            return bytes;
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/WebSockets/HttpListenerWebSocketContext.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/WebSockets/HttpListenerWebSocketContext.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/WebSockets/HttpListenerWebSocketContext.cs	(revision 376)
@@ -0,0 +1,114 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Net;
+using System.Net.WebSockets;
+using System.Security.Principal;
+
+namespace SpaceWizards.HttpListener.WebSockets
+{
+    public class HttpListenerWebSocketContext : WebSocketContext
+    {
+        private readonly Uri _requestUri;
+        private readonly NameValueCollection _headers;
+        private readonly CookieCollection _cookieCollection;
+        private readonly IPrincipal _user;
+        private readonly bool _isAuthenticated;
+        private readonly bool _isLocal;
+        private readonly bool _isSecureConnection;
+
+        private readonly string _origin;
+        private readonly IEnumerable<string> _secWebSocketProtocols;
+        private readonly string _secWebSocketVersion;
+        private readonly string _secWebSocketKey;
+
+        private readonly WebSocket _webSocket;
+
+        internal HttpListenerWebSocketContext(
+            Uri requestUri,
+            NameValueCollection headers,
+            CookieCollection cookieCollection,
+            IPrincipal user,
+            bool isAuthenticated,
+            bool isLocal,
+            bool isSecureConnection,
+            string origin,
+            IEnumerable<string> secWebSocketProtocols,
+            string secWebSocketVersion,
+            string secWebSocketKey,
+            WebSocket webSocket)
+        {
+            Debug.Assert(requestUri != null, "requestUri shouldn't be null");
+            Debug.Assert(headers != null, "headers shouldn't be null");
+            Debug.Assert(cookieCollection != null, "cookieCollection shouldn't be null");
+            Debug.Assert(secWebSocketProtocols != null, "secWebSocketProtocols shouldn't be null");
+            Debug.Assert(webSocket != null, "webSocket shouldn't be null");
+
+            _cookieCollection = new CookieCollection();
+            _cookieCollection.Add(cookieCollection);
+
+            _headers = new NameValueCollection(headers);
+            _user = CopyPrincipal(user)!;
+
+            _requestUri = requestUri;
+            _isAuthenticated = isAuthenticated;
+            _isLocal = isLocal;
+            _isSecureConnection = isSecureConnection;
+            _origin = origin;
+            _secWebSocketProtocols = secWebSocketProtocols;
+            _secWebSocketVersion = secWebSocketVersion;
+            _secWebSocketKey = secWebSocketKey;
+            _webSocket = webSocket;
+        }
+
+        public override Uri RequestUri => _requestUri;
+
+        public override NameValueCollection Headers => _headers;
+
+        public override string Origin => _origin;
+
+        public override IEnumerable<string> SecWebSocketProtocols => _secWebSocketProtocols;
+
+        public override string SecWebSocketVersion => _secWebSocketVersion;
+
+        public override string SecWebSocketKey => _secWebSocketKey;
+
+        public override CookieCollection CookieCollection => _cookieCollection;
+
+        public override IPrincipal User => _user;
+
+        public override bool IsAuthenticated => _isAuthenticated;
+
+        public override bool IsLocal => _isLocal;
+
+        public override bool IsSecureConnection => _isSecureConnection;
+
+        public override WebSocket WebSocket => _webSocket;
+
+        private static IPrincipal? CopyPrincipal(IPrincipal user)
+        {
+            if (user != null)
+            {
+                /*if (!(user is WindowsPrincipal))*/
+                {
+                    // AuthenticationSchemes.Basic.
+                    if (user.Identity is HttpListenerBasicIdentity basicIdentity)
+                    {
+                        return new GenericPrincipal(new HttpListenerBasicIdentity(basicIdentity.Name, basicIdentity.Password), null);
+                    }
+                }
+                /*else
+                {
+                    // AuthenticationSchemes.Digest, AuthenticationSchemes.Negotiate, AuthenticationSchemes.NTLM.
+                    throw new PlatformNotSupportedException();
+                }*/
+            }
+
+            return null;
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/WebSockets/HttpWebSocket.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/WebSockets/HttpWebSocket.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/WebSockets/HttpWebSocket.cs	(revision 376)
@@ -0,0 +1,189 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Net;
+using System.Net.WebSockets;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading;
+
+namespace SpaceWizards.HttpListener.WebSockets
+{
+    internal static partial class HttpWebSocket
+    {
+        internal const string SecWebSocketKeyGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+        internal const string WebSocketUpgradeToken = "websocket";
+        internal const int DefaultReceiveBufferSize = 16 * 1024;
+        internal const int DefaultClientSendBufferSize = 16 * 1024;
+
+        [SuppressMessage("Microsoft.Security", "CA5350", Justification = "SHA1 used only for hashing purposes, not for crypto.")]
+        internal static string GetSecWebSocketAcceptString(string? secWebSocketKey)
+        {
+            string acceptString = string.Concat(secWebSocketKey, HttpWebSocket.SecWebSocketKeyGuid);
+            byte[] toHash = Encoding.UTF8.GetBytes(acceptString);
+
+            // SHA1 used only for hashing purposes, not for crypto. Check here for FIPS compat.
+            byte[] hash = SHA1.HashData(toHash);
+            return Convert.ToBase64String(hash);
+        }
+
+        // return value here signifies if a Sec-WebSocket-Protocol header should be returned by the server.
+        internal static bool ProcessWebSocketProtocolHeader(string? clientSecWebSocketProtocol,
+            string? subProtocol,
+            out string acceptProtocol)
+        {
+            acceptProtocol = string.Empty;
+            if (string.IsNullOrEmpty(clientSecWebSocketProtocol))
+            {
+                // client hasn't specified any Sec-WebSocket-Protocol header
+                if (subProtocol != null)
+                {
+                    // If the server specified _anything_ this isn't valid.
+                    throw new WebSocketException(WebSocketError.UnsupportedProtocol,
+                        SR.Format(SR.net_WebSockets_ClientAcceptingNoProtocols, subProtocol));
+                }
+                // Treat empty and null from the server as the same thing here, server should not send headers.
+                return false;
+            }
+
+            // here, we know the client specified something and it's non-empty.
+
+            if (subProtocol == null)
+            {
+                // client specified some protocols, server specified 'null'. So server should send headers.
+                return true;
+            }
+
+            // here, we know that the client has specified something, it's not empty
+            // and the server has specified exactly one protocol
+
+            string[] requestProtocols = clientSecWebSocketProtocol.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
+            acceptProtocol = subProtocol;
+
+            // client specified protocols, serverOptions has exactly 1 non-empty entry. Check that
+            // this exists in the list the client specified.
+            for (int i = 0; i < requestProtocols.Length; i++)
+            {
+                string currentRequestProtocol = requestProtocols[i];
+                if (string.Equals(acceptProtocol, currentRequestProtocol, StringComparison.OrdinalIgnoreCase))
+                {
+                    return true;
+                }
+            }
+
+            throw new WebSocketException(WebSocketError.UnsupportedProtocol,
+                SR.Format(SR.net_WebSockets_AcceptUnsupportedProtocol,
+                    clientSecWebSocketProtocol,
+                    subProtocol));
+        }
+
+        internal static void ValidateOptions(string? subProtocol, int receiveBufferSize, int sendBufferSize, TimeSpan keepAliveInterval)
+        {
+            if (subProtocol != null)
+            {
+                WebSocketValidate.ValidateSubprotocol(subProtocol);
+            }
+
+            if (receiveBufferSize < MinReceiveBufferSize)
+            {
+                throw new ArgumentOutOfRangeException(nameof(receiveBufferSize), receiveBufferSize,
+                    SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, MinReceiveBufferSize));
+            }
+
+            if (sendBufferSize < MinSendBufferSize)
+            {
+                throw new ArgumentOutOfRangeException(nameof(sendBufferSize), sendBufferSize,
+                    SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, MinSendBufferSize));
+            }
+
+            if (receiveBufferSize > MaxBufferSize)
+            {
+                throw new ArgumentOutOfRangeException(nameof(receiveBufferSize), receiveBufferSize,
+                    SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooBig,
+                        nameof(receiveBufferSize),
+                        receiveBufferSize,
+                        MaxBufferSize));
+            }
+
+            if (sendBufferSize > MaxBufferSize)
+            {
+                throw new ArgumentOutOfRangeException(nameof(sendBufferSize), sendBufferSize,
+                    SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooBig,
+                        nameof(sendBufferSize),
+                        sendBufferSize,
+                        MaxBufferSize));
+            }
+
+            if (keepAliveInterval < Timeout.InfiniteTimeSpan) // -1 millisecond
+            {
+                throw new ArgumentOutOfRangeException(nameof(keepAliveInterval), keepAliveInterval,
+                    SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, Timeout.InfiniteTimeSpan.ToString()));
+            }
+        }
+
+        internal const int MinSendBufferSize = 16;
+        internal const int MinReceiveBufferSize = 256;
+        internal const int MaxBufferSize = 64 * 1024;
+
+        private static void ValidateWebSocketHeaders(HttpListenerContext context)
+        {
+            if (!WebSocketsSupported)
+            {
+                throw new PlatformNotSupportedException(SR.net_WebSockets_UnsupportedPlatform);
+            }
+
+            if (!context.Request.IsWebSocketRequest)
+            {
+                throw new WebSocketException(WebSocketError.NotAWebSocket,
+                    SR.Format(SR.net_WebSockets_AcceptNotAWebSocket,
+                    nameof(ValidateWebSocketHeaders),
+                    HttpKnownHeaderNames.Connection,
+                    HttpKnownHeaderNames.Upgrade,
+                    HttpWebSocket.WebSocketUpgradeToken,
+                    context.Request.Headers[HttpKnownHeaderNames.Upgrade]));
+            }
+
+            string? secWebSocketVersion = context.Request.Headers[HttpKnownHeaderNames.SecWebSocketVersion];
+            if (string.IsNullOrEmpty(secWebSocketVersion))
+            {
+                throw new WebSocketException(WebSocketError.HeaderError,
+                    SR.Format(SR.net_WebSockets_AcceptHeaderNotFound,
+                    nameof(ValidateWebSocketHeaders),
+                    HttpKnownHeaderNames.SecWebSocketVersion));
+            }
+
+            if (!string.Equals(secWebSocketVersion, SupportedVersion, StringComparison.OrdinalIgnoreCase))
+            {
+                throw new WebSocketException(WebSocketError.UnsupportedVersion,
+                    SR.Format(SR.net_WebSockets_AcceptUnsupportedWebSocketVersion,
+                    nameof(ValidateWebSocketHeaders),
+                    secWebSocketVersion,
+                    SupportedVersion));
+            }
+
+            string? secWebSocketKey = context.Request.Headers[HttpKnownHeaderNames.SecWebSocketKey];
+            bool isSecWebSocketKeyInvalid = string.IsNullOrWhiteSpace(secWebSocketKey);
+            if (!isSecWebSocketKeyInvalid)
+            {
+                try
+                {
+                    // key must be 16 bytes then base64-encoded
+                    isSecWebSocketKeyInvalid = Convert.FromBase64String(secWebSocketKey!).Length != 16;
+                }
+                catch
+                {
+                    isSecWebSocketKeyInvalid = true;
+                }
+            }
+            if (isSecWebSocketKeyInvalid)
+            {
+                throw new WebSocketException(WebSocketError.HeaderError,
+                    SR.Format(SR.net_WebSockets_AcceptHeaderNotFound,
+                    nameof(ValidateWebSocketHeaders),
+                    HttpKnownHeaderNames.SecWebSocketKey));
+            }
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/WebSockets/WebSocketValidate.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/WebSockets/WebSocketValidate.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/WebSockets/WebSocketValidate.cs	(revision 376)
@@ -0,0 +1,157 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Globalization;
+using System.Text;
+
+namespace System.Net.WebSockets
+{
+    internal static partial class WebSocketValidate
+    {
+        internal const int MaxControlFramePayloadLength = 123;
+        private const int CloseStatusCodeAbort = 1006;
+        private const int CloseStatusCodeFailedTLSHandshake = 1015;
+        private const int InvalidCloseStatusCodesFrom = 0;
+        private const int InvalidCloseStatusCodesTo = 999;
+        private const string Separators = "()<>@,;:\\\"/[]?={} ";
+
+        internal static void ThrowIfInvalidState(WebSocketState currentState, bool isDisposed, WebSocketState[] validStates)
+        {
+            string validStatesText = string.Empty;
+
+            if (validStates != null && validStates.Length > 0)
+            {
+                foreach (WebSocketState validState in validStates)
+                {
+                    if (currentState == validState)
+                    {
+                        // Ordering is important to maintain .NET 4.5 WebSocket implementation exception behavior.
+                        if (isDisposed)
+                        {
+                            throw new ObjectDisposedException(nameof(WebSocket));
+                        }
+
+                        return;
+                    }
+                }
+
+                validStatesText = string.Join(", ", validStates);
+            }
+
+            throw new WebSocketException(
+                WebSocketError.InvalidState,
+                SR.Format(SR.net_WebSockets_InvalidState, currentState, validStatesText));
+        }
+
+        internal static void ValidateSubprotocol(string subProtocol)
+        {
+            if (string.IsNullOrWhiteSpace(subProtocol))
+            {
+                throw new ArgumentException(SR.net_WebSockets_InvalidEmptySubProtocol, nameof(subProtocol));
+            }
+
+            string? invalidChar = null;
+            int i = 0;
+            while (i < subProtocol.Length)
+            {
+                char ch = subProtocol[i];
+                if (ch < 0x21 || ch > 0x7e)
+                {
+                    invalidChar = string.Format(CultureInfo.InvariantCulture, "[{0}]", (int)ch);
+                    break;
+                }
+
+                if (!char.IsLetterOrDigit(ch) &&
+#pragma warning disable CA2249 // Consider using 'string.Contains' instead of 'string.IndexOf'.  This file is built into a project that doesn't have string.Contains(char).
+                    Separators.IndexOf(ch) >= 0)
+#pragma warning restore CA2249
+                {
+                    invalidChar = ch.ToString();
+                    break;
+                }
+
+                i++;
+            }
+
+            if (invalidChar != null)
+            {
+                throw new ArgumentException(SR.Format(SR.net_WebSockets_InvalidCharInProtocolString, subProtocol, invalidChar), nameof(subProtocol));
+            }
+        }
+
+        internal static void ValidateCloseStatus(WebSocketCloseStatus closeStatus, string? statusDescription)
+        {
+            if (closeStatus == WebSocketCloseStatus.Empty && !string.IsNullOrEmpty(statusDescription))
+            {
+                throw new ArgumentException(SR.Format(SR.net_WebSockets_ReasonNotNull,
+                    statusDescription,
+                    WebSocketCloseStatus.Empty),
+                    nameof(statusDescription));
+            }
+
+            int closeStatusCode = (int)closeStatus;
+
+            if ((closeStatusCode >= InvalidCloseStatusCodesFrom &&
+                closeStatusCode <= InvalidCloseStatusCodesTo) ||
+                closeStatusCode == CloseStatusCodeAbort ||
+                closeStatusCode == CloseStatusCodeFailedTLSHandshake)
+            {
+                // CloseStatus 1006 means Aborted - this will never appear on the wire and is reflected by calling WebSocket.Abort
+                throw new ArgumentException(SR.Format(SR.net_WebSockets_InvalidCloseStatusCode,
+                    closeStatusCode),
+                    nameof(closeStatus));
+            }
+
+            int length = 0;
+            if (!string.IsNullOrEmpty(statusDescription))
+            {
+                length = Encoding.UTF8.GetByteCount(statusDescription);
+            }
+
+            if (length > MaxControlFramePayloadLength)
+            {
+                throw new ArgumentException(SR.Format(SR.net_WebSockets_InvalidCloseStatusDescription,
+                    statusDescription,
+                    MaxControlFramePayloadLength),
+                    nameof(statusDescription));
+            }
+        }
+
+        internal static void ValidateArraySegment(ArraySegment<byte> arraySegment, string parameterName)
+        {
+            Debug.Assert(!string.IsNullOrEmpty(parameterName), "'parameterName' MUST NOT be NULL or string.Empty");
+
+            if (arraySegment.Array == null)
+            {
+                throw new ArgumentNullException(parameterName + "." + nameof(arraySegment.Array));
+            }
+            if (arraySegment.Offset < 0 || arraySegment.Offset > arraySegment.Array.Length)
+            {
+                throw new ArgumentOutOfRangeException(parameterName + "." + nameof(arraySegment.Offset));
+            }
+            if (arraySegment.Count < 0 || arraySegment.Count > (arraySegment.Array.Length - arraySegment.Offset))
+            {
+                throw new ArgumentOutOfRangeException(parameterName + "." + nameof(arraySegment.Count));
+            }
+        }
+
+        internal static void ValidateBuffer(byte[] buffer, int offset, int count)
+        {
+            if (buffer == null)
+            {
+                throw new ArgumentNullException(nameof(buffer));
+            }
+
+            if (offset < 0 || offset > buffer.Length)
+            {
+                throw new ArgumentOutOfRangeException(nameof(offset));
+            }
+
+            if (count < 0 || count > (buffer.Length - offset))
+            {
+                throw new ArgumentOutOfRangeException(nameof(count));
+            }
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Windows/CookieExtensions.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Windows/CookieExtensions.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Net/Windows/CookieExtensions.cs	(revision 376)
@@ -0,0 +1,63 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics;
+using System.Reflection;
+using System.Diagnostics.CodeAnalysis;
+using System.Net;
+
+namespace SpaceWizards.HttpListener
+{
+    // TODO https://github.com/dotnet/runtime/issues/19348
+    internal static class CookieExtensions
+    {
+        private static Func<Cookie, string>? s_toServerStringFunc;
+
+        public static string ToServerString(this Cookie cookie)
+        {
+            s_toServerStringFunc ??= (Func<Cookie, string>)typeof(Cookie).GetMethod("ToServerString", BindingFlags.Instance | BindingFlags.NonPublic)!.CreateDelegate(typeof(Func<Cookie, string>));
+            Debug.Assert(s_toServerStringFunc != null, "Reflection failed for Cookie.ToServerString().");
+            return s_toServerStringFunc(cookie);
+        }
+
+        private static Func<Cookie, Cookie>? s_cloneFunc;
+
+        public static Cookie Clone(this Cookie cookie)
+        {
+            s_cloneFunc ??= (Func<Cookie, Cookie>)typeof(Cookie).GetMethod("Clone", BindingFlags.Instance | BindingFlags.NonPublic)!.CreateDelegate(typeof(Func<Cookie, Cookie>));
+            Debug.Assert(s_cloneFunc != null, "Reflection failed for Cookie.Clone().");
+            return s_cloneFunc(cookie);
+        }
+
+        private enum CookieVariant
+        {
+            Unknown,
+            Plain,
+            Rfc2109,
+            Rfc2965,
+            Default = Rfc2109
+        }
+
+        private static Func<Cookie, CookieVariant>? s_getVariantFunc;
+
+        public static bool IsRfc2965Variant(this Cookie cookie)
+        {
+            s_getVariantFunc ??= (Func<Cookie, CookieVariant>)typeof(Cookie).GetProperty("Variant", BindingFlags.Instance | BindingFlags.NonPublic)!.GetGetMethod(true)!.CreateDelegate(typeof(Func<Cookie, CookieVariant>));
+            Debug.Assert(s_getVariantFunc != null, "Reflection failed for Cookie.Variant.");
+            return s_getVariantFunc(cookie) == CookieVariant.Rfc2965;
+        }
+    }
+
+    internal static class CookieCollectionExtensions
+    {
+        private static Func<CookieCollection, Cookie, bool, int>? s_internalAddFunc;
+
+        public static int InternalAdd(this CookieCollection cookieCollection, Cookie cookie, bool isStrict)
+        {
+            s_internalAddFunc ??= (Func<CookieCollection, Cookie, bool, int>)typeof(CookieCollection).GetMethod("InternalAdd", BindingFlags.Instance | BindingFlags.NonPublic)!.CreateDelegate(typeof(Func<CookieCollection, Cookie, bool, int>));
+            Debug.Assert(s_internalAddFunc != null, "Reflection failed for CookieCollection.InternalAdd().");
+            return s_internalAddFunc(cookieCollection, cookie, isStrict);
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/StringExtensions.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/StringExtensions.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/StringExtensions.cs	(revision 376)
@@ -0,0 +1,43 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+
+namespace System
+{
+    internal static class StringExtensions
+    {
+        internal static string SubstringTrim(this string value, int startIndex, int length)
+        {
+            Debug.Assert(value != null, "string must be non-null");
+            Debug.Assert(startIndex >= 0, "startIndex must be non-negative");
+            Debug.Assert(length >= 0, "length must be non-negative");
+            Debug.Assert(startIndex <= value.Length - length, "startIndex + length must be <= value.Length");
+
+            if (length == 0)
+            {
+                return string.Empty;
+            }
+
+            int endIndex = startIndex + length - 1;
+
+            while (startIndex <= endIndex && char.IsWhiteSpace(value[startIndex]))
+            {
+                startIndex++;
+            }
+
+            while (endIndex >= startIndex && char.IsWhiteSpace(value[endIndex]))
+            {
+                endIndex--;
+            }
+
+            int newLength = endIndex - startIndex + 1;
+            Debug.Assert(newLength >= 0 && newLength <= value.Length, "Expected resulting length to be within value's length");
+
+            return
+                newLength == 0 ? string.Empty :
+                newLength == value.Length ? value :
+                value.Substring(startIndex, newLength);
+        }
+    }
+}
Index: /binary-improvements2/SpaceWizards.HttpListener/src/System/Threading/Tasks/TaskToApm.cs
===================================================================
--- /binary-improvements2/SpaceWizards.HttpListener/src/System/Threading/Tasks/TaskToApm.cs	(revision 376)
+++ /binary-improvements2/SpaceWizards.HttpListener/src/System/Threading/Tasks/TaskToApm.cs	(revision 376)
@@ -0,0 +1,132 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// Helper methods for using Tasks to implement the APM pattern.
+//
+// Example usage, wrapping a Task<int>-returning FooAsync method with Begin/EndFoo methods:
+//
+//     public IAsyncResult BeginFoo(..., AsyncCallback? callback, object? state) =>
+//         TaskToApm.Begin(FooAsync(...), callback, state);
+//
+//     public int EndFoo(IAsyncResult asyncResult) =>
+//         TaskToApm.End<int>(asyncResult);
+
+#nullable enable
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+
+namespace System.Threading.Tasks
+{
+    /// <summary>
+    /// Provides support for efficiently using Tasks to implement the APM (Begin/End) pattern.
+    /// </summary>
+    internal static class TaskToApm
+    {
+        /// <summary>
+        /// Marshals the Task as an IAsyncResult, using the supplied callback and state
+        /// to implement the APM pattern.
+        /// </summary>
+        /// <param name="task">The Task to be marshaled.</param>
+        /// <param name="callback">The callback to be invoked upon completion.</param>
+        /// <param name="state">The state to be stored in the IAsyncResult.</param>
+        /// <returns>An IAsyncResult to represent the task's asynchronous operation.</returns>
+        public static IAsyncResult Begin(Task task, AsyncCallback? callback, object? state) =>
+            new TaskAsyncResult(task, state, callback);
+
+        /// <summary>Processes an IAsyncResult returned by Begin.</summary>
+        /// <param name="asyncResult">The IAsyncResult to unwrap.</param>
+        public static void End(IAsyncResult asyncResult)
+        {
+            if (GetTask(asyncResult) is Task t)
+            {
+                t.GetAwaiter().GetResult();
+                return;
+            }
+
+            ThrowArgumentException(asyncResult);
+        }
+
+        /// <summary>Processes an IAsyncResult returned by Begin.</summary>
+        /// <param name="asyncResult">The IAsyncResult to unwrap.</param>
+        public static TResult End<TResult>(IAsyncResult asyncResult)
+        {
+            if (GetTask(asyncResult) is Task<TResult> task)
+            {
+                return task.GetAwaiter().GetResult();
+            }
+
+            ThrowArgumentException(asyncResult);
+            return default!; // unreachable
+        }
+
+        /// <summary>Gets the task represented by the IAsyncResult.</summary>
+        public static Task? GetTask(IAsyncResult asyncResult) => (asyncResult as TaskAsyncResult)?._task;
+
+        /// <summary>Throws an argument exception for the invalid <paramref name="asyncResult"/>.</summary>
+        [DoesNotReturn]
+        private static void ThrowArgumentException(IAsyncResult asyncResult) =>
+            throw (asyncResult is null ?
+                new ArgumentNullException(nameof(asyncResult)) :
+                new ArgumentException(null, nameof(asyncResult)));
+
+        /// <summary>Provides a simple IAsyncResult that wraps a Task.</summary>
+        /// <remarks>
+        /// We could use the Task as the IAsyncResult if the Task's AsyncState is the same as the object state,
+        /// but that's very rare, in particular in a situation where someone cares about allocation, and always
+        /// using TaskAsyncResult simplifies things and enables additional optimizations.
+        /// </remarks>
+        internal sealed class TaskAsyncResult : IAsyncResult
+        {
+            /// <summary>The wrapped Task.</summary>
+            internal readonly Task _task;
+            /// <summary>Callback to invoke when the wrapped task completes.</summary>
+            private readonly AsyncCallback? _callback;
+
+            /// <summary>Initializes the IAsyncResult with the Task to wrap and the associated object state.</summary>
+            /// <param name="task">The Task to wrap.</param>
+            /// <param name="state">The new AsyncState value.</param>
+            /// <param name="callback">Callback to invoke when the wrapped task completes.</param>
+            internal TaskAsyncResult(Task task, object? state, AsyncCallback? callback)
+            {
+                Debug.Assert(task != null);
+                _task = task;
+                AsyncState = state;
+
+                if (task.IsCompleted)
+                {
+                    // Synchronous completion.  Invoke the callback.  No need to store it.
+                    CompletedSynchronously = true;
+                    callback?.Invoke(this);
+                }
+                else if (callback != null)
+                {
+                    // Asynchronous completion, and we have a callback; schedule it. We use OnCompleted rather than ContinueWith in
+                    // order to avoid running synchronously if the task has already completed by the time we get here but still run
+                    // synchronously as part of the task's completion if the task completes after (the more common case).
+                    _callback = callback;
+                    _task.ConfigureAwait(continueOnCapturedContext: false)
+                         .GetAwaiter()
+                         .OnCompleted(InvokeCallback); // allocates a delegate, but avoids a closure
+                }
+            }
+
+            /// <summary>Invokes the callback.</summary>
+            private void InvokeCallback()
+            {
+                Debug.Assert(!CompletedSynchronously);
+                Debug.Assert(_callback != null);
+                _callback.Invoke(this);
+            }
+
+            /// <summary>Gets a user-defined object that qualifies or contains information about an asynchronous operation.</summary>
+            public object? AsyncState { get; }
+            /// <summary>Gets a value that indicates whether the asynchronous operation completed synchronously.</summary>
+            /// <remarks>This is set lazily based on whether the <see cref="_task"/> has completed by the time this object is created.</remarks>
+            public bool CompletedSynchronously { get; }
+            /// <summary>Gets a value that indicates whether the asynchronous operation has completed.</summary>
+            public bool IsCompleted => _task.IsCompleted;
+            /// <summary>Gets a <see cref="WaitHandle"/> that is used to wait for an asynchronous operation to complete.</summary>
+            public WaitHandle AsyncWaitHandle => ((IAsyncResult)_task).AsyncWaitHandle;
+        }
+    }
+}
