diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs
new file mode 100644
index 00000000000000..e3b0398841fa28
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+
+using JSObject = System.Runtime.InteropServices.JavaScript.JSObject;
+using JSException = System.Runtime.InteropServices.JavaScript.JSException;
+
+internal static partial class Interop
+{
+ internal static partial class Runtime
+ {
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern string InvokeJS(string str, out int exceptionalResult);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object CompileFunction(string str, out int exceptionalResult);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object InvokeJSWithArgs(int jsObjHandle, string method, object?[] parms, out int exceptionalResult);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object GetObjectProperty(int jsObjHandle, string propertyName, out int exceptionalResult);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object SetObjectProperty(int jsObjHandle, string propertyName, object value, bool createIfNotExists, bool hasOwnProperty, out int exceptionalResult);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object GetByIndex(int jsObjHandle, int index, out int exceptionalResult);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object SetByIndex(int jsObjHandle, int index, object? value, out int exceptionalResult);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object GetGlobalObject(string? globalName, out int exceptionalResult);
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object ReleaseHandle(int jsObjHandle, out int exceptionalResult);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object ReleaseObject(int jsObjHandle, out int exceptionalResult);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object NewObjectJS(int jsObjHandle, object[] parms, out int exceptionalResult);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object BindCoreObject(int jsObjHandle, int gcHandle, out int exceptionalResult);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object BindHostObject(int jsObjHandle, int gcHandle, out int exceptionalResult);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object New(string className, object[] parms, out int exceptionalResult);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object TypedArrayToArray(int jsObjHandle, out int exceptionalResult);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object TypedArrayCopyTo(int jsObjHandle, int arrayPtr, int begin, int end, int bytesPerElement, out int exceptionalResult);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object TypedArrayFrom(int arrayPtr, int begin, int end, int bytesPerElement, int type, out int exceptionalResult);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern object TypedArrayCopyFrom(int jsObjHandle, int arrayPtr, int begin, int end, int bytesPerElement, out int exceptionalResult);
+
+ // /
+ // / Execute the provided string in the JavaScript context
+ // /
+ // / The js.
+ // / String.
+ public static string InvokeJS(string str)
+ {
+ string res = InvokeJS(str, out int exception);
+ if (exception != 0)
+ throw new JSException(res);
+ return res;
+ }
+
+ public static System.Runtime.InteropServices.JavaScript.Function? CompileFunction(string snippet)
+ {
+ object res = CompileFunction(snippet, out int exception);
+ if (exception != 0)
+ throw new JSException((string)res);
+ return res as System.Runtime.InteropServices.JavaScript.Function;
+ }
+
+ public static int New(params object[] parms)
+ {
+ object res = New(typeof(T).Name, parms, out int exception);
+ if (exception != 0)
+ throw new JSException((string)res);
+ return (int)res;
+ }
+
+ public static int New(string hostClassName, params object[] parms)
+ {
+ object res = New(hostClassName, parms, out int exception);
+ if (exception != 0)
+ throw new JSException((string)res);
+ return (int)res;
+ }
+
+ public static JSObject? NewJSObject(JSObject? jsFuncPtr = null, params object[] parms)
+ {
+ object res = NewObjectJS(jsFuncPtr?.JSHandle ?? 0, parms, out int exception);
+ if (exception != 0)
+ throw new JSException((string)res);
+ return res as JSObject;
+ }
+ public static object GetGlobalObject(string? str = null)
+ {
+ int exception;
+ object globalHandle = Runtime.GetGlobalObject(str, out exception);
+
+ if (exception != 0)
+ throw new JSException($"Error obtaining a handle to global {str}");
+
+ return globalHandle;
+ }
+
+ }
+}
diff --git a/src/libraries/Common/tests/System/Net/Http/TestHelper.cs b/src/libraries/Common/tests/System/Net/Http/TestHelper.cs
index c3f63a84ee3c73..3c88a428908999 100644
--- a/src/libraries/Common/tests/System/Net/Http/TestHelper.cs
+++ b/src/libraries/Common/tests/System/Net/Http/TestHelper.cs
@@ -120,7 +120,7 @@ public static void EnableUnencryptedHttp2IfNecessary(HttpClientHandler handler)
return;
}
- FieldInfo socketsHttpHandlerField = typeof(HttpClientHandler).GetField("_socketsHttpHandler", BindingFlags.NonPublic | BindingFlags.Instance);
+ FieldInfo socketsHttpHandlerField = typeof(HttpClientHandler).GetField("_underlyingHandler", BindingFlags.NonPublic | BindingFlags.Instance);
if (socketsHttpHandlerField == null)
{
// Not using .NET Core implementation, i.e. could be .NET Framework.
diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj
index 2f7eb9dc48f43e..1bd8d89fc682a2 100644
--- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj
+++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj
@@ -1,4 +1,4 @@
-
+
Library
System.Net.Http
@@ -7,12 +7,15 @@
$(NoWarn);0436;CS1573
true
$(DefineConstants);HTTP_DLL
- $(NetCoreAppCurrent)-Windows_NT;$(NetCoreAppCurrent)-Linux;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS;$(NetCoreAppCurrent)-FreeBSD
+ $(NetCoreAppCurrent)-Windows_NT;$(NetCoreAppCurrent)-Linux;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS;$(NetCoreAppCurrent)-FreeBSD;$(NetCoreAppCurrent)-Browser
enable
$(DefineConstants);SYSNETHTTP_NO_OPENSSL
+
+ $(DefineConstants);TARGETS_BROWSER
+
@@ -116,7 +119,7 @@
-
+
@@ -166,56 +169,6 @@
Link="Common\System\Net\NTAuthentication.Common.cs" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Common\System\Net\Http\aspnetcore\IHttpHeadersHandler.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http2\Hpack\DynamicTable.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http2\Hpack\HeaderField.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http2\Hpack\HPackDecoder.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http2\Hpack\HPackDecodingException.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http2\Hpack\HPackEncoder.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http2\Hpack\HPackEncodingException.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http2\Hpack\Huffman.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http2\Hpack\HuffmanDecodingException.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http2\Hpack\IntegerDecoder.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http2\Hpack\IntegerEncoder.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http2\Hpack\H2StaticTable.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http2\Hpack\StatusCodes.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http3\Http3SettingType.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http3\Http3StreamType.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http3\Helpers\VariableLengthIntegerHelper.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http3\Frames\Http3ErrorCode.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http3\Frames\Http3Frame.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http3\Frames\Http3FrameType.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http3\QPack\HeaderField.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http3\QPack\QPackDecoder.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http3\QPack\QPackDecodingException.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http3\QPack\QPackEncoder.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http3\QPack\QPackEncodingException.cs
+
+
+ Common\System\Net\Http\aspnetcore\Http3\QPack\H3StaticTable.cs
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs
new file mode 100644
index 00000000000000..a046f0a1fe300e
--- /dev/null
+++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs
@@ -0,0 +1,534 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Http;
+using System.Net.Security;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+using JSObject = System.Runtime.InteropServices.JavaScript.JSObject;
+using JSException = System.Runtime.InteropServices.JavaScript.JSException;
+using HostObject = System.Runtime.InteropServices.JavaScript.HostObject;
+using Uint8Array = System.Runtime.InteropServices.JavaScript.Uint8Array;
+using Function = System.Runtime.InteropServices.JavaScript.Function;
+
+namespace System.Net.Http
+{
+ // **Note** on `Task.ConfigureAwait(continueOnCapturedContext: true)` for the WebAssembly Browser.
+ // The current implementation of WebAssembly for the Browser does not have a SynchronizationContext nor a Scheduler
+ // thus forcing the callbacks to run on the main browser thread. When threading is eventually implemented using
+ // emscripten's threading model of remote worker threads, via SharedArrayBuffer, any API calls will have to be
+ // remoted back to the main thread. Most APIs only work on the main browser thread.
+ // During discussions the concensus has been that it will not matter right now which value is used for ConfigureAwait
+ // we should put this in place now.
+ internal sealed class BrowserHttpHandler : HttpMessageHandler
+ {
+ // This partial implementation contains members common to Browser WebAssembly running on .NET Core.
+ private static readonly JSObject? s_fetch = (JSObject)System.Runtime.InteropServices.JavaScript.Runtime.GetGlobalObject("fetch");
+ private static readonly JSObject? s_window = (JSObject)System.Runtime.InteropServices.JavaScript.Runtime.GetGlobalObject("window");
+
+ ///
+ /// Gets whether the current Browser supports streaming responses
+ ///
+ private static bool StreamingSupported { get; }
+
+ static BrowserHttpHandler()
+ {
+ using (var streamingSupported = new Function("return typeof Response !== 'undefined' && 'body' in Response.prototype && typeof ReadableStream === 'function'"))
+ StreamingSupported = (bool)streamingSupported.Call();
+ }
+
+ public bool UseCookies
+ {
+ get => throw new PlatformNotSupportedException();
+ set => throw new PlatformNotSupportedException();
+ }
+
+ public CookieContainer CookieContainer
+ {
+ get => throw new PlatformNotSupportedException();
+ set => throw new PlatformNotSupportedException();
+ }
+
+ public DecompressionMethods AutomaticDecompression
+ {
+ get => throw new PlatformNotSupportedException();
+ set => throw new PlatformNotSupportedException();
+ }
+
+ public bool UseProxy
+ {
+ get => throw new PlatformNotSupportedException();
+ set => throw new PlatformNotSupportedException();
+ }
+
+ public IWebProxy? Proxy
+ {
+ get => throw new PlatformNotSupportedException();
+ set => throw new PlatformNotSupportedException();
+ }
+
+ public ICredentials? DefaultProxyCredentials
+ {
+ get => throw new PlatformNotSupportedException();
+ set => throw new PlatformNotSupportedException();
+ }
+
+ public bool PreAuthenticate
+ {
+ get => throw new PlatformNotSupportedException();
+ set => throw new PlatformNotSupportedException();
+ }
+
+ public ICredentials? Credentials
+ {
+ get => throw new PlatformNotSupportedException();
+ set => throw new PlatformNotSupportedException();
+ }
+
+ public bool AllowAutoRedirect
+ {
+ get => throw new PlatformNotSupportedException();
+ set => throw new PlatformNotSupportedException();
+ }
+
+ public int MaxAutomaticRedirections
+ {
+ get => throw new PlatformNotSupportedException();
+ set => throw new PlatformNotSupportedException();
+ }
+
+ public int MaxConnectionsPerServer
+ {
+ get => throw new PlatformNotSupportedException();
+ set => throw new PlatformNotSupportedException();
+ }
+
+ public int MaxResponseHeadersLength
+ {
+ get => throw new PlatformNotSupportedException();
+ set => throw new PlatformNotSupportedException();
+ }
+
+ public SslClientAuthenticationOptions SslOptions
+ {
+ get => throw new PlatformNotSupportedException();
+ set => throw new PlatformNotSupportedException();
+ }
+
+ public IDictionary Properties => throw new PlatformNotSupportedException();
+
+ protected internal override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ try
+ {
+ var requestObject = new JSObject();
+
+ if (request.Properties.TryGetValue("WebAssemblyFetchOptions", out object? fetchOptionsValue) &&
+ fetchOptionsValue is IDictionary fetchOptions)
+ {
+ foreach (KeyValuePair item in fetchOptions)
+ {
+ requestObject.SetObjectProperty(item.Key, item.Value);
+ }
+ }
+
+ requestObject.SetObjectProperty("method", request.Method.Method);
+
+ // We need to check for body content
+ if (request.Content != null)
+ {
+ if (request.Content is StringContent)
+ {
+ requestObject.SetObjectProperty("body", await request.Content.ReadAsStringAsync().ConfigureAwait(continueOnCapturedContext: true));
+ }
+ else
+ {
+ using (Uint8Array uint8Buffer = Uint8Array.From(await request.Content.ReadAsByteArrayAsync().ConfigureAwait(continueOnCapturedContext: true)))
+ {
+ requestObject.SetObjectProperty("body", uint8Buffer);
+ }
+ }
+ }
+
+ // Process headers
+ // Cors has its own restrictions on headers.
+ // https://developer.mozilla.org/en-US/docs/Web/API/Headers
+ using (HostObject jsHeaders = new HostObject("Headers"))
+ {
+ foreach (KeyValuePair> header in request.Headers)
+ {
+ foreach (string value in header.Value)
+ {
+ jsHeaders.Invoke("append", header.Key, value);
+ }
+ }
+ if (request.Content != null)
+ {
+ foreach (KeyValuePair> header in request.Content.Headers)
+ {
+ foreach (string value in header.Value)
+ {
+ jsHeaders.Invoke("append", header.Key, value);
+ }
+ }
+ }
+ requestObject.SetObjectProperty("headers", jsHeaders);
+ }
+
+
+ WasmHttpReadStream? wasmHttpReadStream = null;
+
+ JSObject abortController = new HostObject("AbortController");
+ JSObject signal = (JSObject)abortController.GetObjectProperty("signal");
+ requestObject.SetObjectProperty("signal", signal);
+ signal.Dispose();
+
+ CancellationTokenSource abortCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+ CancellationTokenRegistration abortRegistration = abortCts.Token.Register((Action)(() =>
+ {
+ if (abortController.JSHandle != -1)
+ {
+ abortController.Invoke("abort");
+ abortController?.Dispose();
+ }
+ wasmHttpReadStream?.Dispose();
+ abortCts.Dispose();
+ }));
+
+ var args = new System.Runtime.InteropServices.JavaScript.Array();
+ if (request.RequestUri != null)
+ {
+ args.Push(request.RequestUri.ToString());
+ args.Push(requestObject);
+ }
+
+ requestObject.Dispose();
+
+ var response = s_fetch?.Invoke("apply", s_window, args) as Task