protected RpcServerBase( IRpcServicePublisher servicePublisher, IRpcServiceActivator serviceActivator, IRpcServiceDefinitionsProvider definitionsProvider, IRpcServerOptions?options, ILoggerFactory?loggerFactory) { this.ServicePublisher = servicePublisher ?? throw new ArgumentNullException(nameof(servicePublisher)); this.ServiceActivator = serviceActivator ?? throw new ArgumentNullException(nameof(serviceActivator)); this.ServiceDefinitionsProvider = definitionsProvider ?? throw new ArgumentNullException(nameof(definitionsProvider)); this.LoggerFactory = loggerFactory; this.Logger = loggerFactory?.CreateLogger(this.GetType()) ?? RpcLogger.CreateLogger(this.GetType()); if (options != null) { this.serializer = options.Serializer; this.AllowDiscovery = options.AllowDiscovery ?? true; this.AllowAutoPublish = options.AllowAutoPublish ?? false; this.CallInterceptors = options.Interceptors.ToImmutableArrayList(); this.ExceptionConverters = options.ExceptionConverters.ToImmutableArrayList(); } if (this.ExceptionConverters.Count > 0) { this.CustomFaultHandler = new RpcServerFaultHandler(null, this.ExceptionConverters, null); } else { this.CustomFaultHandler = RpcServerFaultHandler.Default; } }
/// <summary> /// Processes the dependency download request /// </summary> /// <param name="msgStream">The protobuf messaging stream</param> /// <param name="request">The StreamingMessage request for function load</param> /// <param name="pwsh">The PowerShell instance used to download modules</param> internal void ProcessDependencyDownload(MessagingStream msgStream, StreamingMessage request, PowerShell pwsh) { if (request.FunctionLoadRequest.ManagedDependencyEnabled) { var rpcLogger = new RpcLogger(msgStream); rpcLogger.SetContext(request.RequestId, null); if (!_shouldUpdateFunctionAppDependencies) { if (!string.IsNullOrEmpty(_dependenciesNotUpdatedMessage)) { // We were not able to update the function app dependencies. // However, there is a previous installation, so continue with the function app execution. rpcLogger.Log(LogLevel.Warning, _dependenciesNotUpdatedMessage, isUserLog: true); } else { // The function app already has the latest dependencies installed. rpcLogger.Log(LogLevel.Trace, PowerShellWorkerStrings.LatestFunctionAppDependenciesAlreadyInstalled, isUserLog: true); } return; } if (Dependencies.Count == 0) { // If there are no dependencies to install, log and return. rpcLogger.Log(LogLevel.Trace, PowerShellWorkerStrings.FunctionAppDoesNotHaveDependentModulesToInstall, isUserLog: true); return; } //Start dependency download on a separate thread _dependencyDownloadTask = Task.Run(() => InstallFunctionAppDependencies(pwsh, rpcLogger)); } }
internal RequestProcessor(MessagingStream msgStream) { _msgStream = msgStream; _logger = new RpcLogger(msgStream); _powerShellManager = new PowerShellManager(_logger); _functionLoader = new FunctionLoader(); }
/// <summary> /// Entry point of the language worker. /// </summary> public async static Task Main(string[] args) { RpcLogger.WriteSystemLog( LogLevel.Information, string.Format(PowerShellWorkerStrings.PowerShellWorkerVersion, typeof(Worker).Assembly.GetName().Version)); WorkerArguments arguments = null; Parser.Default.ParseArguments <WorkerArguments>(args) .WithParsed(ops => arguments = ops) .WithNotParsed(err => Environment.Exit(1)); var msgStream = new MessagingStream(arguments.Host, arguments.Port); var requestProcessor = new RequestProcessor(msgStream); // Send StartStream message var startedMessage = new StreamingMessage() { RequestId = arguments.RequestId, StartStream = new StartStream() { WorkerId = arguments.WorkerId } }; msgStream.Write(startedMessage); await requestProcessor.ProcessRequestLoop(); }
/// <summary> /// This method invokes the FunctionApp's profile.ps1. /// </summary> internal void InvokeProfile(string profilePath) { Exception exception = null; if (profilePath == null) { RpcLogger.WriteSystemLog(string.Format(PowerShellWorkerStrings.FileNotFound, "profile.ps1", FunctionLoader.FunctionAppRootPath)); return; } try { // Import-Module on a .ps1 file will evaluate the script in the global scope. _pwsh.AddCommand(Utils.ImportModuleCmdletInfo) .AddParameter("Name", profilePath) .AddParameter("PassThru", true) .AddCommand(Utils.RemoveModuleCmdletInfo) .AddParameter("Force", true) .AddParameter("ErrorAction", "SilentlyContinue") .InvokeAndClearCommands(); } catch (Exception e) { exception = e; throw; } finally { if (_pwsh.HadErrors) { string errorMsg = string.Format(PowerShellWorkerStrings.FailToRunProfile, profilePath); _logger.Log(LogLevel.Error, errorMsg, exception, isUserLog: true); } } }
private RpcLogger CreateLoggerWithContext(string requestId, string invocationId) { var logger = new RpcLogger(_msgStream); logger.SetContext(requestId, invocationId); return(logger); }
protected override async Task <RpcResponseMessage> SendAsync(RpcRequestMessage request, string route = null, CancellationToken cancellationToken = default(CancellationToken)) { var logger = new RpcLogger(_log); try { lock (_lockingObject) { var rpcRequestJson = JsonConvert.SerializeObject(request, JsonSerializerSettings); var requestBytes = Encoding.UTF8.GetBytes(rpcRequestJson); logger.LogRequest(rpcRequestJson); var client = GetSocket(); client.SendBufferSize = requestBytes.Length; var val = client.SendAsync(new ArraySegment <byte>(requestBytes, 0, requestBytes.Length), SocketFlags.None).Result; using (var memoryStream = ReceiveFullResponse(client)) { memoryStream.Position = 0; using (var streamReader = new StreamReader(memoryStream)) using (var reader = new JsonTextReader(streamReader)) { var serializer = JsonSerializer.Create(JsonSerializerSettings); var message = serializer.Deserialize <RpcResponseMessage>(reader); logger.LogResponse(message); return(message); } } } } catch (Exception ex) { logger.LogException(ex); throw new RpcClientUnknownException("Error occurred when trying to send ipc requests(s)", ex); } }
/// <summary> /// Checkout an idle PowerShellManager instance in a non-blocking asynchronous way. /// </summary> internal PowerShellManager CheckoutIdleWorker(StreamingMessage request, AzFunctionInfo functionInfo) { PowerShellManager psManager = null; string requestId = request.RequestId; string invocationId = request.InvocationRequest?.InvocationId; // If the pool has an idle one, just use it. if (!_pool.TryTake(out psManager)) { // The pool doesn't have an idle one. if (_poolSize < _upperBound && Interlocked.Increment(ref _poolSize) <= _upperBound) { // If the pool hasn't reached its bounded capacity yet, then // we create a new item and return it. var logger = new RpcLogger(_msgStream); logger.SetContext(requestId, invocationId); psManager = new PowerShellManager(logger); } else { // If the pool has reached its bounded capacity, then the thread // should be blocked until an idle one becomes available. psManager = _pool.Take(); } } // Register the function with the Runspace before returning the idle PowerShellManager. FunctionMetadata.RegisterFunctionMetadata(psManager.InstanceId, functionInfo); psManager.Logger.SetContext(requestId, invocationId); return(psManager); }
protected override Task <RpcResponseMessage> SendAsync(RpcRequestMessage request, string route = null) { var logger = new RpcLogger(_log); try { lock (_lockingObject) { var rpcRequestJson = JsonConvert.SerializeObject(request, JsonSerializerSettings); var requestBytes = Encoding.UTF8.GetBytes(rpcRequestJson); logger.LogRequest(rpcRequestJson); GetPipeClient().Write(requestBytes, 0, requestBytes.Length); using (var memoryData = ReceiveFullResponse(GetPipeClient())) { memoryData.Position = 0; using (StreamReader streamReader = new StreamReader(memoryData)) using (JsonTextReader reader = new JsonTextReader(streamReader)) { var serializer = JsonSerializer.Create(JsonSerializerSettings); var message = serializer.Deserialize <RpcResponseMessage>(reader); logger.LogResponse(message); return(Task.FromResult(message)); } } } } catch (Exception ex) { var exception = new RpcClientUnknownException("Error occurred when trying to send ipc requests(s)", ex); logger.LogException(exception); throw exception; } }
internal StreamingMessage ProcessWorkerInitRequest(StreamingMessage request) { var workerInitRequest = request.WorkerInitRequest; Environment.SetEnvironmentVariable("AZUREPS_HOST_ENVIRONMENT", $"AzureFunctions/{workerInitRequest.HostVersion}"); StreamingMessage response = NewStreamingMessageTemplate( request.RequestId, StreamingMessage.ContentOneofCase.WorkerInitResponse, out StatusResult status); response.WorkerInitResponse.Capabilities.Add("RpcHttpBodyOnly", "true"); // If the environment variable is set, spin up the custom named pipe server. // This is typically used for debugging. It will throw a friendly exception if the // pipe name is not a valid pipename. string pipeName = Environment.GetEnvironmentVariable("PSWorkerCustomPipeName"); if (!string.IsNullOrEmpty(pipeName)) { RpcLogger.WriteSystemLog(LogLevel.Trace, string.Format(PowerShellWorkerStrings.SpecifiedCustomPipeName, pipeName)); RemoteSessionNamedPipeServer.CreateCustomNamedPipeServer(pipeName); } return(response); }
private async Task HandleIncomingMessagesAsync() { var logger = new RpcLogger(_log); while (!_cancellationTokenSource.IsCancellationRequested) { try { if (_clientWebSocket != null && _clientWebSocket.State == WebSocketState.Open && AnyQueueRequests()) { using (var memoryData = await ReceiveFullResponseAsync(_clientWebSocket).ConfigureAwait(false)) { if (memoryData.Length == 0) { return; } memoryData.Position = 0; using (var streamReader = new StreamReader(memoryData)) using (var reader = new JsonTextReader(streamReader)) { var serializer = JsonSerializer.Create(JsonSerializerSettings); var message = serializer.Deserialize <RpcStreamingResponseMessage>(reader); HandleResponse(message); logger.LogResponse(message); } } } } catch (Exception ex) { HandleError(ex); logger.LogException(ex); } } }
protected async Task <TResponse> SendAsync <TRequest, TResponse>(TRequest request) where TResponse : RpcResponseMessage { var logger = new RpcLogger(_log); try { //lock (_lockingObject) // { var rpcRequestJson = JsonConvert.SerializeObject(request, JsonSerializerSettings); var requestBytes = new ArraySegment <byte>(Encoding.UTF8.GetBytes(rpcRequestJson)); logger.LogRequest(rpcRequestJson); //add timeout; var webSocket = GetClientWebSocket(); await webSocket.SendAsync(requestBytes, WebSocketMessageType.Text, true, CancellationToken.None).ConfigureAwait(false); using (var memoryData = await ReceiveFullResponseAsync(webSocket)) { memoryData.Position = 0; using (StreamReader streamReader = new StreamReader(memoryData)) using (JsonTextReader reader = new JsonTextReader(streamReader)) { var serializer = JsonSerializer.Create(JsonSerializerSettings); var message = serializer.Deserialize <TResponse>(reader); logger.LogResponse(message); return(message); } } //} } catch (Exception ex) { logger.LogException(ex); throw new RpcClientUnknownException("Error occurred when trying to send ipc requests(s)", ex); } }
internal StreamingMessage ProcessFunctionEnvironmentReloadRequest(StreamingMessage request) { var stopwatch = new Stopwatch(); stopwatch.Start(); var environmentReloadRequest = request.FunctionEnvironmentReloadRequest; var rpcLogger = new RpcLogger(_msgStream); rpcLogger.SetContext(request.RequestId, null); var functionsEnvironmentReloader = new FunctionsEnvironmentReloader(rpcLogger); functionsEnvironmentReloader.ReloadEnvironment( environmentReloadRequest.EnvironmentVariables, environmentReloadRequest.FunctionAppDirectory); rpcLogger.Log(isUserOnlyLog: false, LogLevel.Trace, string.Format(PowerShellWorkerStrings.EnvironmentReloadCompleted, stopwatch.ElapsedMilliseconds)); StreamingMessage response = NewStreamingMessageTemplate( request.RequestId, StreamingMessage.ContentOneofCase.FunctionEnvironmentReloadResponse, out StatusResult status); return(response); }
public async Task SendRequestAsync(RpcRequestMessage request, IRpcStreamingResponseHandler requestResponseHandler, string route = null) { var logger = new RpcLogger(_log); try { await _semaphoreSlim.WaitAsync().ConfigureAwait(false); var rpcRequestJson = JsonConvert.SerializeObject(request, JsonSerializerSettings); var requestBytes = new ArraySegment <byte>(Encoding.UTF8.GetBytes(rpcRequestJson)); logger.LogRequest(rpcRequestJson); var timeoutCancellationTokenSource = new CancellationTokenSource(); timeoutCancellationTokenSource.CancelAfter(ConnectionTimeout); var webSocket = _clientWebSocket; await webSocket.SendAsync(requestBytes, WebSocketMessageType.Text, true, timeoutCancellationTokenSource.Token) .ConfigureAwait(false); HandleRequest(request, requestResponseHandler); } catch (Exception ex) { logger.LogException(ex); throw new RpcClientUnknownException("Error occurred trying to send web socket requests(s)", ex); } finally { _semaphoreSlim.Release(); } }
/// <summary> /// Method to process a InvocationRequest. /// This method checks out a worker from the pool, and then starts the actual invocation in a threadpool thread. /// </summary> internal StreamingMessage ProcessInvocationRequest(StreamingMessage request) { Exception error = null; try { if (_dependencyManager.DependencyDownloadTask != null && !_dependencyManager.DependencyDownloadTask.IsCompleted) { var rpcLogger = new RpcLogger(_msgStream); rpcLogger.SetContext(request.RequestId, request.InvocationRequest?.InvocationId); rpcLogger.Log(LogLevel.Information, PowerShellWorkerStrings.DependencyDownloadInProgress, isUserLog: true); _dependencyManager.WaitOnDependencyDownload(); } if (_dependencyManager.DependencyError != null) { error = _dependencyManager.DependencyError; } else { AzFunctionInfo functionInfo = FunctionLoader.GetFunctionInfo(request.InvocationRequest.FunctionId); PowerShellManager psManager = _powershellPool.CheckoutIdleWorker(request, functionInfo); if (_powershellPool.UpperBound == 1) { // When the concurrency upper bound is 1, we can handle only one invocation at a time anyways, // so it's better to just do it on the current thread to reduce the required synchronization. ProcessInvocationRequestImpl(request, functionInfo, psManager); } else { // When the concurrency upper bound is more than 1, we have to handle the invocation in a worker // thread, so multiple invocations can make progress at the same time, even though by time-sharing. Task.Run(() => ProcessInvocationRequestImpl(request, functionInfo, psManager)); } } } catch (Exception e) { error = e; } if (error != null) { StreamingMessage response = NewStreamingMessageTemplate( request.RequestId, StreamingMessage.ContentOneofCase.InvocationResponse, out StatusResult status); response.InvocationResponse.InvocationId = request.InvocationRequest.InvocationId; status.Status = StatusResult.Types.Status.Failure; status.Exception = error.ToRpcException(); return(response); } return(null); }
/// <summary> /// Populate the pool with the very first PowerShellManager instance. /// We instantiate PowerShellManager instances in a lazy way, starting from size 1 and increase the number of workers as needed. /// </summary> internal void Initialize(PowerShell pwsh) { var logger = new RpcLogger(_msgStream); var psManager = new PowerShellManager(logger, pwsh); _pool.Add(psManager); _poolSize = 1; }
private async Task HandleIncomingMessagesAsync() { var logger = new RpcLogger(_log); while (_cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested) { try { await _semaphoreSlimListener.WaitAsync().ConfigureAwait(false); if (_clientWebSocket != null && _clientWebSocket.State == WebSocketState.Open && AnyQueueRequests()) { using (var memoryData = await ReceiveFullResponseAsync(_clientWebSocket).ConfigureAwait(false)) { if (memoryData.Length == 0) { return; } memoryData.Position = 0; using (var streamReader = new StreamReader(memoryData)) using (var reader = new JsonTextReader(streamReader)) { var serializer = JsonSerializer.Create(JsonSerializerSettings); var message = serializer.Deserialize <RpcStreamingResponseMessage>(reader); //Random random = new Random(); //var x = random.Next(0, 50); //if (x == 5) throw new Exception("Error"); HandleResponse(message); logger.LogResponse(message); } } } } catch (Exception ex) { //HandleError should not be called from any of the functions that are called from this function //which also rethrow the exception to avoid calling the error handlers twice for the same error. //if cancellation requested ignore it as we will end up in a loop if (!_cancellationTokenSource.IsCancellationRequested) { HandleError(ex); logger.LogException(ex); } } finally { //release the semaphore for the listener so it can be disposed _semaphoreSlimListener.Release(); } } }
/// <summary> /// Constructor of the pool. /// </summary> internal PowerShellManagerPool(MessagingStream msgStream) { string upperBound = Environment.GetEnvironmentVariable("PSWorkerInProcConcurrencyUpperBound"); if (string.IsNullOrEmpty(upperBound) || !int.TryParse(upperBound, out _upperBound)) { _upperBound = 1; } _msgStream = msgStream; _pool = new BlockingCollection <PowerShellManager>(_upperBound); RpcLogger.WriteSystemLog(LogLevel.Information, string.Format(PowerShellWorkerStrings.LogConcurrencyUpperBound, _upperBound.ToString())); }
/// <summary> /// Initialize the pool and populate it with PowerShellManager instances. /// We instantiate PowerShellManager instances in a lazy way, starting from size 1 and increase the number of workers as needed. /// </summary> internal void Initialize(string requestId) { var logger = new RpcLogger(_msgStream); try { logger.SetContext(requestId, invocationId: null); _pool.Add(new PowerShellManager(logger)); _poolSize = 1; } finally { logger.ResetContext(); } }
/// <summary> /// Method to process a InvocationRequest. /// This method checks out a worker from the pool, and then starts the actual invocation in a threadpool thread. /// </summary> internal StreamingMessage ProcessInvocationRequest(StreamingMessage request) { try { var stopwatch = new FunctionInvocationPerformanceStopwatch(); stopwatch.OnStart(); // Will block if installing dependencies is required _dependencyManager.WaitForDependenciesAvailability( () => { var rpcLogger = new RpcLogger(_msgStream); rpcLogger.SetContext(request.RequestId, request.InvocationRequest?.InvocationId); return(rpcLogger); }); stopwatch.OnCheckpoint(FunctionInvocationPerformanceStopwatch.Checkpoint.DependenciesAvailable); AzFunctionInfo functionInfo = FunctionLoader.GetFunctionInfo(request.InvocationRequest.FunctionId); PowerShellManager psManager = _powershellPool.CheckoutIdleWorker( request.RequestId, request.InvocationRequest?.InvocationId, functionInfo.FuncName, functionInfo.OutputBindings); stopwatch.OnCheckpoint(FunctionInvocationPerformanceStopwatch.Checkpoint.RunspaceAvailable); // When the concurrency upper bound is more than 1, we have to handle the invocation in a worker // thread, so multiple invocations can make progress at the same time, even though by time-sharing. Task.Run(() => ProcessInvocationRequestImpl(request, functionInfo, psManager, stopwatch)); } catch (Exception e) { StreamingMessage response = NewStreamingMessageTemplate( request.RequestId, StreamingMessage.ContentOneofCase.InvocationResponse, out StatusResult status); response.InvocationResponse.InvocationId = request.InvocationRequest.InvocationId; status.Status = StatusResult.Types.Status.Failure; status.Exception = e.ToRpcException(); return(response); } return(null); }
protected override async Task <RpcResponseMessage> SendAsync(RpcRequestMessage request, string route = null) { RpcLogger rpcLogger = new RpcLogger(_log); RpcResponseMessage rpcResponseMessage; try { var cancellationTokenSource = new CancellationTokenSource(); cancellationTokenSource.CancelAfter(ConnectionTimeout); using (var pipeStream = new NamedPipeClientStream(IpcPath)) { await pipeStream.ConnectAsync(cancellationTokenSource.Token); string str = JsonConvert.SerializeObject(request, JsonSerializerSettings); byte[] bytes = Encoding.UTF8.GetBytes(str); rpcLogger.LogRequest(str); await pipeStream.WriteAsync(bytes, 0, bytes.Length, cancellationTokenSource.Token); using (MemoryStream fullResponse = await ReceiveFullResponseAsync(pipeStream, cancellationTokenSource.Token)) { fullResponse.Position = 0L; using (StreamReader streamReader = new StreamReader(fullResponse)) { using (JsonTextReader jsonTextReader = new JsonTextReader(streamReader)) { RpcResponseMessage responseMessage = JsonSerializer.Create(JsonSerializerSettings).Deserialize <RpcResponseMessage>(jsonTextReader); rpcLogger.LogResponse(responseMessage); rpcResponseMessage = responseMessage; } } } } } catch (TaskCanceledException ex) { var exception = new RpcClientTimeoutException($"Rpc timeout after {ConnectionTimeout.TotalMilliseconds} milliseconds", ex); rpcLogger.LogException(exception); throw exception; } catch (Exception ex) { var unknownException = new RpcClientUnknownException("Error occurred when trying to send ipc requests(s)", ex); rpcLogger.LogException(unknownException); throw unknownException; } return(rpcResponseMessage); }
/// <summary> /// Checkout an idle PowerShellManager instance in a non-blocking asynchronous way. /// </summary> internal PowerShellManager CheckoutIdleWorker( string requestId, string invocationId, string functionName, ReadOnlyDictionary <string, ReadOnlyBindingInfo> outputBindings) { PowerShellManager psManager = null; // If the pool has an idle one, just use it. if (!_pool.TryTake(out psManager)) { // The pool doesn't have an idle one. if (_poolSize < UpperBound) { int id = Interlocked.Increment(ref _poolSize); if (id <= UpperBound) { // If the pool hasn't reached its bounded capacity yet, then // we create a new item and return it. var logger = CreateLoggerWithContext(requestId, invocationId); psManager = new PowerShellManager(logger, id); RpcLogger.WriteSystemLog(LogLevel.Trace, string.Format(PowerShellWorkerStrings.LogNewPowerShellManagerCreated, id.ToString())); } } if (psManager == null) { var logger = CreateLoggerWithContext(requestId, invocationId); logger.Log(isUserOnlyLog: true, LogLevel.Warning, string.Format(PowerShellWorkerStrings.FunctionQueuingRequest, functionName)); // If the pool has reached its bounded capacity, then the thread // should be blocked until an idle one becomes available. psManager = _pool.Take(); } } psManager.Logger.SetContext(requestId, invocationId); // Finish the initialization if not yet. // This applies only to the very first PowerShellManager instance, whose initialization was deferred. psManager.Initialize(); // Register the function with the Runspace before returning the idle PowerShellManager. FunctionMetadata.RegisterFunctionMetadata(psManager.InstanceId, outputBindings); return(psManager); }
/// <summary> /// Method to process a InvocationRequest. /// This method checks out a worker from the pool, and then starts the actual invocation in a threadpool thread. /// </summary> internal StreamingMessage ProcessInvocationRequest(StreamingMessage request) { try { // Will block if installing dependencies is required _dependencyManager.WaitForDependenciesAvailability( () => { var rpcLogger = new RpcLogger(_msgStream); rpcLogger.SetContext(request.RequestId, request.InvocationRequest?.InvocationId); return(rpcLogger); }); AzFunctionInfo functionInfo = FunctionLoader.GetFunctionInfo(request.InvocationRequest.FunctionId); PowerShellManager psManager = _powershellPool.CheckoutIdleWorker(request, functionInfo); if (_powershellPool.UpperBound == 1) { // When the concurrency upper bound is 1, we can handle only one invocation at a time anyways, // so it's better to just do it on the current thread to reduce the required synchronization. ProcessInvocationRequestImpl(request, functionInfo, psManager); } else { // When the concurrency upper bound is more than 1, we have to handle the invocation in a worker // thread, so multiple invocations can make progress at the same time, even though by time-sharing. Task.Run(() => ProcessInvocationRequestImpl(request, functionInfo, psManager)); } } catch (Exception e) { StreamingMessage response = NewStreamingMessageTemplate( request.RequestId, StreamingMessage.ContentOneofCase.InvocationResponse, out StatusResult status); response.InvocationResponse.InvocationId = request.InvocationRequest.InvocationId; status.Status = StatusResult.Types.Status.Failure; status.Exception = e.ToRpcException(); return(response); } return(null); }
protected override async Task <RpcResponseMessage> SendAsync(RpcRequestMessage request, string route = null) { var logger = new RpcLogger(_log); try { await semaphoreSlim.WaitAsync().ConfigureAwait(false); var rpcRequestJson = JsonConvert.SerializeObject(request, JsonSerializerSettings); var requestBytes = new ArraySegment <byte>(Encoding.UTF8.GetBytes(rpcRequestJson)); logger.LogRequest(rpcRequestJson); var cancellationTokenSource = new CancellationTokenSource(); cancellationTokenSource.CancelAfter(ConnectionTimeout); using (var webSocket = await GetClientWebSocketAsync().ConfigureAwait(false)) { await webSocket.SendAsync(requestBytes, WebSocketMessageType.Text, true, cancellationTokenSource.Token) .ConfigureAwait(false); using (var memoryData = await ReceiveFullResponseAsync(webSocket).ConfigureAwait(false)) { memoryData.Position = 0; using (var streamReader = new StreamReader(memoryData)) using (var reader = new JsonTextReader(streamReader)) { var serializer = JsonSerializer.Create(JsonSerializerSettings); var message = serializer.Deserialize <RpcResponseMessage>(reader); logger.LogResponse(message); return(message); } } } } catch (Exception ex) { var exception = new RpcClientUnknownException("Error occurred when trying to web socket requests(s)", ex); logger.LogException(exception); throw exception; } finally { semaphoreSlim.Release(); } }
/// <summary> /// Checkout an idle PowerShellManager instance in a non-blocking asynchronous way. /// </summary> internal PowerShellManager CheckoutIdleWorker(StreamingMessage request, AzFunctionInfo functionInfo) { PowerShellManager psManager = null; string requestId = request.RequestId; string invocationId = request.InvocationRequest?.InvocationId; // If the pool has an idle one, just use it. if (!_pool.TryTake(out psManager)) { // The pool doesn't have an idle one. if (_poolSize < _upperBound) { int id = Interlocked.Increment(ref _poolSize); if (id <= _upperBound) { // If the pool hasn't reached its bounded capacity yet, then // we create a new item and return it. var logger = new RpcLogger(_msgStream); logger.SetContext(requestId, invocationId); psManager = new PowerShellManager(logger, id); RpcLogger.WriteSystemLog(LogLevel.Trace, string.Format(PowerShellWorkerStrings.LogNewPowerShellManagerCreated, id.ToString())); } } if (psManager == null) { // If the pool has reached its bounded capacity, then the thread // should be blocked until an idle one becomes available. psManager = _pool.Take(); } } // Finish the initialization if not yet. // This applies only to the very first PowerShellManager instance, whose initialization was deferred. psManager.Initialize(); // Register the function with the Runspace before returning the idle PowerShellManager. FunctionMetadata.RegisterFunctionMetadata(psManager.InstanceId, functionInfo); psManager.Logger.SetContext(requestId, invocationId); return(psManager); }
internal StreamingMessage ProcessWorkerInitRequest(StreamingMessage request) { StreamingMessage response = NewStreamingMessageTemplate( request.RequestId, StreamingMessage.ContentOneofCase.WorkerInitResponse, out StatusResult status); // If the environment variable is set, spin up the custom named pipe server. // This is typically used for debugging. It will throw a friendly exception if the // pipe name is not a valid pipename. string pipeName = Environment.GetEnvironmentVariable("PSWorkerCustomPipeName"); if (!string.IsNullOrEmpty(pipeName)) { RpcLogger.WriteSystemLog(LogLevel.Trace, string.Format(PowerShellWorkerStrings.SpecifiedCustomPipeName, pipeName)); RemoteSessionNamedPipeServer.CreateCustomNamedPipeServer(pipeName); } return(response); }
protected override async Task SendAsync(RpcRequestMessage request, string route = null) { var logger = new RpcLogger(_log); try { await semaphoreSlim.WaitAsync().ConfigureAwait(false); var rpcRequestJson = JsonConvert.SerializeObject(request, JsonSerializerSettings); var requestBytes = new ArraySegment <byte>(Encoding.UTF8.GetBytes(rpcRequestJson)); logger.LogRequest(rpcRequestJson); var cancellationTokenSource = new CancellationTokenSource(); cancellationTokenSource.CancelAfter(ConnectionTimeout); var webSocket = await GetClientWebSocketAsync().ConfigureAwait(false); await webSocket.SendAsync(requestBytes, WebSocketMessageType.Text, true, cancellationTokenSource.Token) .ConfigureAwait(false); if (listener != null) { cancellationTokenSource.Cancel(); cancellationTokenSource.Dispose(); cancellationTokenSource = new CancellationTokenSource(); } listener = Task.Factory.StartNew(async() => { await HandleIncomingMessagesAsync(_clientWebSocket, CancellationToken.None); }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Current); } catch (Exception ex) { logger.LogException(ex); throw new RpcClientUnknownException("Error occurred trying to send web socket requests(s)", ex); } finally { semaphoreSlim.Release(); } }
internal async Task ProcessRequestLoop() { StreamingMessage request, response; while (await _msgStream.MoveNext()) { request = _msgStream.GetCurrentMessage(); if (_requestHandlers.TryGetValue(request.ContentCase, out Func <StreamingMessage, StreamingMessage> requestFunc)) { response = requestFunc(request); } else { RpcLogger.WriteSystemLog(LogLevel.Warning, string.Format(PowerShellWorkerStrings.UnsupportedMessage, request.ContentCase)); continue; } if (response != null) { _msgStream.Write(response); } } }
internal PowerShellManager(RpcLogger logger) { var initialSessionState = InitialSessionState.CreateDefault(); // Setting the execution policy on macOS and Linux throws an exception so only update it on Windows if (Platform.IsWindows) { // This sets the execution policy on Windows to Unrestricted which is required to run the user's function scripts on // Windows client versions. This is needed if a user is testing their function locally with the func CLI initialSessionState.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.Unrestricted; } _pwsh = PowerShell.Create(initialSessionState); _logger = logger; // Setup Stream event listeners var streamHandler = new StreamHandler(logger); _pwsh.Streams.Debug.DataAdding += streamHandler.DebugDataAdding; _pwsh.Streams.Error.DataAdding += streamHandler.ErrorDataAdding; _pwsh.Streams.Information.DataAdding += streamHandler.InformationDataAdding; _pwsh.Streams.Progress.DataAdding += streamHandler.ProgressDataAdding; _pwsh.Streams.Verbose.DataAdding += streamHandler.VerboseDataAdding; _pwsh.Streams.Warning.DataAdding += streamHandler.WarningDataAdding; }
/// <summary> /// Processes the dependency download request /// </summary> /// <param name="msgStream">The protobuf messaging stream</param> /// <param name="request">The StreamingMessage request for function load</param> /// <param name="pwsh">The PowerShell instance used to download modules</param> internal void ProcessDependencyDownload(MessagingStream msgStream, StreamingMessage request, PowerShell pwsh) { if (request.FunctionLoadRequest.ManagedDependencyEnabled) { var rpcLogger = new RpcLogger(msgStream); rpcLogger.SetContext(request.RequestId, null); if (Dependencies.Count == 0) { // If there are no dependencies to install, log and return. rpcLogger.Log(LogLevel.Trace, PowerShellWorkerStrings.FunctionAppDoesNotHaveDependentModulesToInstall, isUserLog: true); return; } if (!_shouldUpdateFunctionAppDependencies) { // The function app already has the latest dependencies installed. rpcLogger.Log(LogLevel.Trace, PowerShellWorkerStrings.LatestFunctionAppDependenciesAlreadyInstalled, isUserLog: true); return; } //Start dependency download on a separate thread _dependencyDownloadTask = Task.Run(() => InstallFunctionAppDependencies(pwsh, rpcLogger)); } }