Ejemplo n.º 1
0
        private async Task doFetch(TaskCompletionSource <HttpResponseMessage> tcs, HttpRequestMessage request, CancellationToken cancellationToken)
        {
            try {
                var requestObject = new JSObject();
                requestObject.SetObjectProperty("method", request.Method.Method);

                // See https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials for
                // standard values and meanings
                requestObject.SetObjectProperty("credentials", DefaultCredentials);

                // See https://developer.mozilla.org/en-US/docs/Web/API/Request/cache for
                // standard values and meanings
                requestObject.SetObjectProperty("cache", Cache);

                // See https://developer.mozilla.org/en-US/docs/Web/API/Request/mode for
                // standard values and meanings
                requestObject.SetObjectProperty("mode", Mode);

                // We need to check for body content
                if (request.Content != null)
                {
                    if (request.Content is StringContent)
                    {
                        requestObject.SetObjectProperty("body", await request.Content.ReadAsStringAsync());
                    }
                    else
                    {
                        using (var uint8Buffer = Uint8Array.From(await request.Content.ReadAsByteArrayAsync()))
                        {
                            requestObject.SetObjectProperty("body", uint8Buffer);
                        }
                    }
                }

                // Process headers
                // Cors has it's own restrictions on headers.
                // https://developer.mozilla.org/en-US/docs/Web/API/Headers
                using (var jsHeaders = new HostObject("Headers")) {
                    if (request.Headers != null)
                    {
                        foreach (var header in request.Headers)
                        {
                            foreach (var value in header.Value)
                            {
                                jsHeaders.Invoke("append", header.Key, value);
                            }
                        }
                    }
                    if (request.Content?.Headers != null)
                    {
                        foreach (var header in request.Content.Headers)
                        {
                            foreach (var value in header.Value)
                            {
                                jsHeaders.Invoke("append", header.Key, value);
                            }
                        }
                    }
                    requestObject.SetObjectProperty("headers", jsHeaders);
                }

                JSObject           abortController    = null;
                JSObject           signal             = null;
                WasmHttpReadStream wasmHttpReadStream = null;

                CancellationTokenRegistration abortRegistration = default(CancellationTokenRegistration);
                if (cancellationToken.CanBeCanceled)
                {
                    abortController = new HostObject("AbortController");

                    signal = (JSObject)abortController.GetObjectProperty("signal");
                    requestObject.SetObjectProperty("signal", signal);
                    abortRegistration = cancellationToken.Register((Action)(() => {
                        if (abortController.JSHandle != -1)
                        {
                            abortController.Invoke((string)"abort");
                            abortController?.Dispose();
                        }
                        wasmHttpReadStream?.Dispose();
                    }));
                }

                var args = new Core.Array();
                args.Push(request.RequestUri.ToString());
                args.Push(requestObject);

                requestObject.Dispose();

                var response = (Task <object>)fetch.Invoke("apply", window, args);
                args.Dispose();

                var t = await response;

                var status = new WasmFetchResponse((JSObject)t, abortController, abortRegistration);

                //Console.WriteLine($"bodyUsed: {status.IsBodyUsed}");
                //Console.WriteLine($"ok: {status.IsOK}");
                //Console.WriteLine($"redirected: {status.IsRedirected}");
                //Console.WriteLine($"status: {status.Status}");
                //Console.WriteLine($"statusText: {status.StatusText}");
                //Console.WriteLine($"type: {status.ResponseType}");
                //Console.WriteLine($"url: {status.Url}");

                HttpResponseMessage httpresponse = new HttpResponseMessage((HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), status.Status.ToString()));

                httpresponse.Content = StreamingSupported && StreamingEnabled
                                    ? new StreamContent(wasmHttpReadStream = new WasmHttpReadStream(status))
                                    : (HttpContent) new WasmHttpContent(status);

                // Fill the response headers
                // CORS will only allow access to certain headers.
                // If a request is made for a resource on another origin which returns the CORs headers, then the type is cors.
                // cors and basic responses are almost identical except that a cors response restricts the headers you can view to
                // `Cache-Control`, `Content-Language`, `Content-Type`, `Expires`, `Last-Modified`, and `Pragma`.
                // View more information https://developers.google.com/web/updates/2015/03/introduction-to-fetch#response_types
                //
                // Note: Some of the headers may not even be valid header types in .NET thus we use TryAddWithoutValidation
                using (var respHeaders = (JSObject)status.Headers) {
                    if (respHeaders != null)
                    {
                        using (var entriesIterator = (JSObject)respHeaders.Invoke("entries")) {
                            JSObject nextResult = null;
                            try {
                                nextResult = (JSObject)entriesIterator.Invoke("next");
                                while (!(bool)nextResult.GetObjectProperty("done"))
                                {
                                    using (var resultValue = (WebAssembly.Core.Array)nextResult.GetObjectProperty("value")) {
                                        var name  = (string)resultValue [0];
                                        var value = (string)resultValue [1];
                                        if (!httpresponse.Headers.TryAddWithoutValidation(name, value))
                                        {
                                            if (httpresponse.Content != null)
                                            {
                                                if (!httpresponse.Content.Headers.TryAddWithoutValidation(name, value))
                                                {
                                                    Console.WriteLine($"Warning: Can not add response header for name: {name} value: {value}");
                                                }
                                            }
                                        }
                                    }
                                    nextResult?.Dispose();
                                    nextResult = (JSObject)entriesIterator.Invoke("next");
                                }
                            } finally {
                                nextResult?.Dispose();
                            }
                        }
                    }
                }

                tcs.SetResult(httpresponse);

                signal?.Dispose();
            } catch (Exception exception) {
                tcs.SetException(exception);
            }
        }
Ejemplo n.º 2
0
        private async Task doFetch(TaskCompletionSource <HttpResponseMessage> tcs, HttpRequestMessage request, CancellationToken cancellationToken)
        {
            try {
                var requestObject = Runtime.NewJSObject();
                requestObject.SetObjectProperty("method", request.Method.Method);

                // See https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials for
                // standard values and meanings
                requestObject.SetObjectProperty("credentials", DefaultCredentials);

                // See https://developer.mozilla.org/en-US/docs/Web/API/Request/cache for
                // standard values and meanings
                requestObject.SetObjectProperty("cache", Cache);

                // See https://developer.mozilla.org/en-US/docs/Web/API/Request/mode for
                // standard values and meanings
                requestObject.SetObjectProperty("mode", Mode);

                // We need to check for body content
                if (request.Content != null)
                {
                    if (request.Content is StringContent)
                    {
                        requestObject.SetObjectProperty("body", await request.Content.ReadAsStringAsync());
                    }
                    else
                    {
                        requestObject.SetObjectProperty("body", await request.Content.ReadAsByteArrayAsync());
                    }
                }

                // Process headers
                // Cors has it's own restrictions on headers.
                // https://developer.mozilla.org/en-US/docs/Web/API/Headers
                var requestHeaders = GetHeadersAsStringArray(request);

                if (requestHeaders != null && requestHeaders.Length > 0)
                {
                    using (var headersObj = (JSObject)WebAssembly.Runtime.GetGlobalObject("Headers"))
                        using (var jsHeaders = Runtime.NewJSObject(headersObj)) {
                            for (int i = 0; i < requestHeaders.Length; i++)
                            {
                                //Console.WriteLine($"append: {requestHeaders[i][0]} / {requestHeaders[i][1]}");
                                jsHeaders.Invoke("append", requestHeaders [i] [0], requestHeaders [i] [1]);
                            }
                            requestObject.SetObjectProperty("headers", jsHeaders);
                        }
                }

                JSObject           abortController    = null;
                JSObject           signal             = null;
                WasmHttpReadStream wasmHttpReadStream = null;

                CancellationTokenRegistration abortRegistration = default(CancellationTokenRegistration);
                if (cancellationToken.CanBeCanceled)
                {
                    using (var abortObj = (JSObject)WebAssembly.Runtime.GetGlobalObject("AbortController"))
                        abortController = Runtime.NewJSObject(abortObj);

                    signal = (JSObject)abortController.GetObjectProperty("signal");
                    requestObject.SetObjectProperty("signal", signal);
                    abortRegistration = cancellationToken.Register(() => {
                        if (abortController.JSHandle != -1)
                        {
                            abortController.Invoke("abort");
                            abortController?.Dispose();
                        }
                        wasmHttpReadStream?.Dispose();
                    });
                }

                var args = Runtime.NewJSArray();
                args.Invoke("push", request.RequestUri.ToString());
                args.Invoke("push", requestObject);

                requestObject.Dispose();

                var response = (Task <object>)fetch.Invoke("apply", window, args);
                args.Dispose();

                var t = await response;

                var status = new WasmFetchResponse((JSObject)t, abortController, abortRegistration);

                //Console.WriteLine($"bodyUsed: {status.IsBodyUsed}");
                //Console.WriteLine($"ok: {status.IsOK}");
                //Console.WriteLine($"redirected: {status.IsRedirected}");
                //Console.WriteLine($"status: {status.Status}");
                //Console.WriteLine($"statusText: {status.StatusText}");
                //Console.WriteLine($"type: {status.ResponseType}");
                //Console.WriteLine($"url: {status.Url}");

                HttpResponseMessage httpresponse = new HttpResponseMessage((HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), status.Status.ToString()));

                httpresponse.Content = StreamingSupported && StreamingEnabled
                                    ? new StreamContent(wasmHttpReadStream = new WasmHttpReadStream(status))
                                    : (HttpContent) new WasmHttpContent(status);

                // Fill the response headers
                // CORS will only allow access to certain headers.
                // If a request is made for a resource on another origin which returns the CORs headers, then the type is cors.
                // cors and basic responses are almost identical except that a cors response restricts the headers you can view to
                // `Cache-Control`, `Content-Language`, `Content-Type`, `Expires`, `Last-Modified`, and `Pragma`.
                // View more information https://developers.google.com/web/updates/2015/03/introduction-to-fetch#response_types
                //
                // Note: Some of the headers may not even be valid header types in .NET thus we use TryAddWithoutValidation
                using (var respHeaders = status.Headers) {
                    // Here we invoke the forEach on the headers object
                    // Note: the Action takes 3 objects and not two.  The other seems to be the Header object.
                    var foreachAction = new Action <object, object, object> ((value, name, other) => {
                        if (!httpresponse.Headers.TryAddWithoutValidation((string)name, (string)value))
                        {
                            if (httpresponse.Content != null)
                            {
                                if (!httpresponse.Content.Headers.TryAddWithoutValidation((string)name, (string)value))
                                {
                                    Console.WriteLine($"Warning: Can not add response header for name: {name} value: {value}");
                                }
                            }
                        }
                        ((JSObject)other).Dispose();
                    });

                    try {
                        respHeaders.Invoke("forEach", foreachAction);
                    } finally {
                        // Do not remove the following line of code.  The httpresponse is used in the lambda above when parsing the Headers.
                        // if a local is captured (used) by a lambda it becomes heap memory as we translate them into fields on an object.
                        // The foreachAction is allocated when marshalled to JavaScript.  Since we do not know when JS is finished with the
                        // Action we need to tell the Runtime to de-allocate the object and remove the instance from JS as well.
                        WebAssembly.Runtime.FreeObject(foreachAction);
                    }
                }

                tcs.SetResult(httpresponse);

                signal?.Dispose();
            } catch (Exception exception) {
                tcs.SetException(exception);
            }
        }
Ejemplo n.º 3
0
        private async Task doFetch(TaskCompletionSource <HttpResponseMessage> tcs, HttpRequestMessage request, CancellationToken cancellationToken)
        {
            try {
                var requestObject = new JSObject();

                if (request.Properties.TryGetValue("WebAssemblyFetchOptions", out var fetchOoptionsValue) &&
                    fetchOoptionsValue is IDictionary <string, object> fetchOptions)
                {
                    foreach (var 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());
                    }
                    else
                    {
                        // 2.1.801 seems to have a problem with the line
                        // using (var uint8Buffer = Uint8Array.From(await request.Content.ReadAsByteArrayAsync ()))
                        // so we split it up into two lines.
                        var byteAsync = await request.Content.ReadAsByteArrayAsync();

                        using (var uint8Buffer = Uint8Array.From(byteAsync))
                        {
                            requestObject.SetObjectProperty("body", uint8Buffer);
                        }
                    }
                }

                // Process headers
                // Cors has it's own restrictions on headers.
                // https://developer.mozilla.org/en-US/docs/Web/API/Headers
                using (var jsHeaders = new HostObject("Headers")) {
                    if (request.Headers != null)
                    {
                        foreach (var header in request.Headers)
                        {
                            foreach (var value in header.Value)
                            {
                                jsHeaders.Invoke("append", header.Key, value);
                            }
                        }
                    }
                    if (request.Content?.Headers != null)
                    {
                        foreach (var header in request.Content.Headers)
                        {
                            foreach (var 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();
                }));

                var args = new Runtime.InteropServices.JavaScript.Array();
                args.Push(request.RequestUri.ToString());
                args.Push(requestObject);

                requestObject.Dispose();

                var response = fetch.Invoke("apply", window, args) as Task <object>;
                args.Dispose();
                if (response == null)
                {
                    throw new Exception("Internal error marshalling the response Promise from `fetch`.");
                }

                var t = await response;

                var status = new WasmFetchResponse((JSObject)t, abortController, abortCts, abortRegistration);

                //Console.WriteLine($"bodyUsed: {status.IsBodyUsed}");
                //Console.WriteLine($"ok: {status.IsOK}");
                //Console.WriteLine($"redirected: {status.IsRedirected}");
                //Console.WriteLine($"status: {status.Status}");
                //Console.WriteLine($"statusText: {status.StatusText}");
                //Console.WriteLine($"type: {status.ResponseType}");
                //Console.WriteLine($"url: {status.Url}");

                HttpResponseMessage httpresponse = new HttpResponseMessage((HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), status.Status.ToString()));

                var streamingEnabled = request.Properties.TryGetValue("WebAssemblyEnableStreamingResponse", out var streamingEnabledValue) && (bool)streamingEnabledValue;

                httpresponse.Content = StreamingSupported && streamingEnabled
                                        ? new StreamContent(wasmHttpReadStream = new WasmHttpReadStream(status))
                                        : (HttpContent) new WasmHttpContent(status);

                // Fill the response headers
                // CORS will only allow access to certain headers.
                // If a request is made for a resource on another origin which returns the CORs headers, then the type is cors.
                // cors and basic responses are almost identical except that a cors response restricts the headers you can view to
                // `Cache-Control`, `Content-Language`, `Content-Type`, `Expires`, `Last-Modified`, and `Pragma`.
                // View more information https://developers.google.com/web/updates/2015/03/introduction-to-fetch#response_types
                //
                // Note: Some of the headers may not even be valid header types in .NET thus we use TryAddWithoutValidation
                using (var respHeaders = (JSObject)status.Headers) {
                    if (respHeaders != null)
                    {
                        using (var entriesIterator = (JSObject)respHeaders.Invoke("entries")) {
                            JSObject nextResult = null;
                            try {
                                nextResult = (JSObject)entriesIterator.Invoke("next");
                                while (!(bool)nextResult.GetObjectProperty("done"))
                                {
                                    using (var resultValue = (Runtime.InteropServices.JavaScript.Array)nextResult.GetObjectProperty("value")) {
                                        var name  = (string)resultValue [0];
                                        var value = (string)resultValue [1];
                                        if (!httpresponse.Headers.TryAddWithoutValidation(name, value))
                                        {
                                            if (httpresponse.Content != null)
                                            {
                                                if (!httpresponse.Content.Headers.TryAddWithoutValidation(name, value))
                                                {
                                                    Console.WriteLine($"Warning: Can not add response header for name: {name} value: {value}");
                                                }
                                            }
                                        }
                                    }
                                    nextResult?.Dispose();
                                    nextResult = (JSObject)entriesIterator.Invoke("next");
                                }
                            } finally {
                                nextResult?.Dispose();
                            }
                        }
                    }
                }

                tcs.SetResult(httpresponse);
            } catch (JSException jsExc)  {
                var httpExc = new System.Net.Http.HttpRequestException(jsExc.Message);
                tcs.SetException(httpExc);
            } catch (Exception exception)  {
                tcs.SetException(exception);
            }
        }