PrintOptions( System.Collections.Generic.IEnumerable<System.Type> optionTypes) { var options = new Core.Array<Core.ICommandLineArgument>(); foreach (var optType in optionTypes) { var arg = System.Activator.CreateInstance(optType) as Core.ICommandLineArgument; options.Add(arg); } foreach (var arg in options.OrderBy(key => key.LongName)) { if (arg is Core.ICustomHelpText) { Core.Log.Info("{0}: {1}", (arg as Core.ICustomHelpText).OptionHelp, arg.ContextHelp); } else { if (null == arg.ShortName) { Core.Log.Info("{0}: {1}", arg.LongName, arg.ContextHelp); } else { Core.Log.Info("{0} (or {1}): {2}", arg.LongName, arg.ShortName, arg.ContextHelp); } } Core.Log.Info(""); } }
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); } }
private async Task ConnectAsyncJavaScript(Uri uri, CancellationToken cancellationToken) { var tcsConnect = new TaskCompletionSource <bool> (); // For Abort/Dispose. Calling Abort on the request at any point will close the connection. cts.Token.Register(AbortRequest); // Wrap the cancellationToken in a using so that it can be disposed of whether // we successfully connected or failed trying. // Otherwise any timeout/cancellation would apply to the full session. // In the failure case we need to release the references and dispose of the objects. using (cancellationToken.Register(() => tcsConnect.TrySetCanceled())) { try { Core.Array subProtocols = null; if (Options.RequestedSubProtocols.Count > 0) { subProtocols = new Core.Array(); foreach (var item in Options.RequestedSubProtocols) { subProtocols.Push(item); } } innerWebSocket = new HostObject("WebSocket", uri.ToString(), subProtocols); subProtocols?.Dispose(); // Setup the onError callback onError = new Action <JSObject> ((errorEvt) => { errorEvt.Dispose(); }); // Attach the onError callback innerWebSocket.SetObjectProperty("onerror", onError); // Setup the onClose callback onClose = new Action <JSObject> ((closeEvt) => { innerWebSocketCloseStatus = (WebSocketCloseStatus)closeEvt.GetObjectProperty("code"); innerWebSocketCloseStatusDescription = closeEvt.GetObjectProperty("reason")?.ToString(); var mess = new ReceivePayload(WebSocketHelpers.EmptyPayload, WebSocketMessageType.Close); receiveMessageQueue.BufferPayload(mess); if (!tcsConnect.Task.IsCanceled && !tcsConnect.Task.IsCompleted && !tcsConnect.Task.IsFaulted) { tcsConnect.SetException(new WebSocketException(WebSocketError.NativeError)); } else { tcsClose?.SetResult(true); } closeEvt.Dispose(); }); // Attach the onClose callback innerWebSocket.SetObjectProperty("onclose", onClose); // Setup the onOpen callback onOpen = new Action <JSObject> ((evt) => { if (!cancellationToken.IsCancellationRequested) { // Change internal state to 'connected' to enable the other methods if (Interlocked.CompareExchange(ref state, connected, connecting) != connecting) { // Aborted/Disposed during connect. throw new ObjectDisposedException(GetType().FullName); } tcsConnect.SetResult(true); } evt.Dispose(); }); // Attach the onOpen callback innerWebSocket.SetObjectProperty("onopen", onOpen); // Setup the onMessage callback onMessage = new Action <JSObject> ((messageEvent) => { ThrowIfNotConnected(); // get the events "data" var eventData = messageEvent.GetObjectProperty("data"); // If the messageEvent's data property is marshalled as a JSObject then we are dealing with // binary data if (eventData is JSObject) { // TODO: Handle ArrayBuffer binary type but have only seen 'blob' so far without // changing the default websocket binary type manually. if (innerWebSocket.GetObjectProperty("binaryType").ToString() == "blob") { Action <JSObject> loadend = null; // Create a new "FileReader" object using (var reader = new HostObject("FileReader")) { loadend = new Action <JSObject> ((loadEvent) => { using (var target = (JSObject)loadEvent.GetObjectProperty("target")) { if ((int)target.GetObjectProperty("readyState") == 2) { using (var binResult = (ArrayBuffer)target.GetObjectProperty("result")) { var mess = new ReceivePayload(binResult, WebSocketMessageType.Binary); receiveMessageQueue.BufferPayload(mess); Runtime.FreeObject(loadend); } } } loadEvent.Dispose(); }); reader.Invoke("addEventListener", "loadend", loadend); using (var blobData = (JSObject)messageEvent.GetObjectProperty("data")) reader.Invoke("readAsArrayBuffer", blobData); } } else { throw new NotImplementedException($"WebSocket bynary type '{innerWebSocket.GetObjectProperty ("binaryType").ToString ()}' not supported."); } } else if (eventData is string) { var mess = new ReceivePayload(Encoding.UTF8.GetBytes(((string)eventData).ToString()), WebSocketMessageType.Text); receiveMessageQueue.BufferPayload(mess); } messageEvent.Dispose(); }); // Attach the onMessage callaback innerWebSocket.SetObjectProperty("onmessage", onMessage); await tcsConnect.Task; } catch (Exception wse) { ConnectExceptionCleanup(); WebSocketException wex = new WebSocketException("WebSocket connection failure.", wse); throw wex; } } }
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 = 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); } }
Main( string[] args) { #if false // take control of Ctrl+C System.Console.CancelKeyPress += new System.ConsoleCancelEventHandler(HandleCancellation); #endif try { var totalTimeProfile = new Core.TimeProfile(Core.ETimingProfiles.TimedTotal); var processCommandLineProfile = new Core.TimeProfile(Core.ETimingProfiles.ProcessCommandLine); totalTimeProfile.StartProfile(); processCommandLineProfile.StartProfile(); var verbosityLevel = (Core.EVerboseLevel)Core.CommandLineProcessor.Evaluate(new Core.Options.VerbosityLevel()); switch (verbosityLevel) { case Core.EVerboseLevel.None: case Core.EVerboseLevel.Info: case Core.EVerboseLevel.Detail: case Core.EVerboseLevel.Full: Core.Graph.Instance.VerbosityLevel = verbosityLevel; break; default: throw new Core.Exception("Unrecognized verbosity level, {0}", verbosityLevel); } if (Core.CommandLineProcessor.Evaluate(new Core.Options.PrintHelp())) { CommandLineArgumentHelper.PrintHelp(); return; } if (Core.CommandLineProcessor.Evaluate(new Core.Options.PrintVersion())) { CommandLineArgumentHelper.PrintVersion(); return; } if (Core.CommandLineProcessor.Evaluate(new Core.Options.CreateDebugProject())) { DebugProject.Create(); return; } if (Core.CommandLineProcessor.Evaluate(new Core.Options.MakePackage())) { Core.PackageUtilities.MakePackage(); return; } if (Core.CommandLineProcessor.Evaluate(new Core.Options.AddDependentPackage())) { Core.PackageUtilities.AddDependentPackage(); return; } if (Core.CommandLineProcessor.Evaluate(new Core.Options.ShowDefinitionFile())) { Core.PackageUtilities.IdentifyAllPackages(allowDuplicates: true, enforceBamAssemblyVersions: false); Core.Graph.Instance.MasterPackage.Show(); return; } // configure Core.Graph.Instance.BuildRoot = Core.CommandLineProcessor.Evaluate(new Core.Options.BuildRoot()); Core.Graph.Instance.Mode = Core.CommandLineProcessor.Evaluate(new Core.Options.BuildMode()); if (null == Core.Graph.Instance.Mode) { throw new Core.Exception("No build mode specified"); } var configs = new Core.Array<Core.Environment>(); var requestedConfigs = Core.CommandLineProcessor.Evaluate(new Core.Options.BuildConfigurations()); if (0 == requestedConfigs.Count) { // default requestedConfigs.Add(new Core.StringArray("debug")); } foreach (var configOption in requestedConfigs) { foreach (var config in configOption) { var env = new Core.Environment(); env.Configuration = Core.Configuration.FromString(config); configs.Add(env); } } processCommandLineProfile.StopProfile(); Core.EntryPoint.Execute(configs); totalTimeProfile.StopProfile(); } catch (Core.Exception exception) { Core.Exception.DisplayException(exception); System.Environment.ExitCode = -1; } catch (System.Exception exception) { var message = new System.Text.StringBuilder(); message.AppendFormat("{0} not handled: {1}", exception.GetType().ToString(), exception.Message); message.AppendLine(); message.AppendLine(exception.StackTrace); Core.Log.ErrorMessage(message.ToString()); System.Environment.ExitCode = -2; } finally { if (Core.Graph.Instance.BuildEnvironments.Count > 0) { Core.Log.Info((0 == System.Environment.ExitCode) ? "\nBuild Succeeded" : "\nBuild Failed"); if (Core.CommandLineProcessor.Evaluate(new Core.Options.PrintStatistics())) { Core.Statistics.Display(); } Core.Log.DebugMessage("Exit code {0}", System.Environment.ExitCode); } } }