protected override async Task InvokeCore(object[] parameters, FunctionInvocationContext context) { string scriptHostArguments; switch (Metadata.ScriptType) { case ScriptType.WindowsBatch: scriptHostArguments = string.Format("/c \"{0}\"", _scriptFilePath); await ExecuteScriptAsync("cmd", scriptHostArguments, parameters, context); break; case ScriptType.Python: // Passing -u forces stdout to be unbuffered so we can log messages as they happen. scriptHostArguments = string.Format("-u \"{0}\"", _scriptFilePath); await ExecuteScriptAsync("python.exe", scriptHostArguments, parameters, context); break; case ScriptType.PHP: scriptHostArguments = string.Format("\"{0}\"", _scriptFilePath); await ExecuteScriptAsync("php.exe", scriptHostArguments, parameters, context); break; case ScriptType.Bash: scriptHostArguments = string.Format("\"{0}\"", _scriptFilePath); string bashPath = ResolveBashPath(); await ExecuteScriptAsync(bashPath, scriptHostArguments, parameters, context); break; } }
protected override async Task <object> InvokeCore(object[] parameters, FunctionInvocationContext context) { string invocationId = context.ExecutionContext.InvocationId.ToString(); // TODO: fix extensions and remove object triggerValue = TransformInput(parameters[0], context.Binder.BindingData); var triggerInput = (_trigger.Name, _trigger.DataType ?? DataType.String, triggerValue); var inputs = new[] { triggerInput }.Concat(await BindInputsAsync(context.Binder)); ScriptInvocationContext invocationContext = new ScriptInvocationContext() { FunctionMetadata = Metadata, BindingData = context.Binder.BindingData, ExecutionContext = context.ExecutionContext, Inputs = inputs, ResultSource = new TaskCompletionSource <ScriptInvocationResult>(), AsyncExecutionContext = System.Threading.ExecutionContext.Capture(), // TODO: link up cancellation token to parameter descriptors CancellationToken = CancellationToken.None, Logger = context.Logger }; ScriptInvocationResult result; _invocationBuffer.Post(invocationContext); result = await invocationContext.ResultSource.Task; await BindOutputsAsync(triggerValue, context.Binder, result); return(result.Return); }
protected override async Task InvokeCore(object[] parameters, FunctionInvocationContext context) { object input = parameters[0]; string invocationId = context.ExecutionContext.InvocationId.ToString(); object convertedInput = ConvertInput(input); Utility.ApplyBindingData(convertedInput, context.Binder.BindingData); Dictionary<string, object> bindingData = context.Binder.BindingData; bindingData["InvocationId"] = invocationId; Dictionary<string, string> environmentVariables = new Dictionary<string, string>(); string functionInstanceOutputPath = Path.Combine(Path.GetTempPath(), "Functions", "Binding", invocationId); await ProcessInputBindingsAsync(convertedInput, functionInstanceOutputPath, context.Binder, _inputBindings, _outputBindings, bindingData, environmentVariables); InitializeEnvironmentVariables(environmentVariables, functionInstanceOutputPath, input, _outputBindings, context.ExecutionContext); var userTraceWriter = CreateUserTraceWriter(context.TraceWriter); PSDataCollection<ErrorRecord> errors = await InvokePowerShellScript(environmentVariables, userTraceWriter); await ProcessOutputBindingsAsync(functionInstanceOutputPath, _outputBindings, input, context.Binder, bindingData); ErrorRecord error = errors.FirstOrDefault(); if (error != null) { throw new RuntimeException("PowerShell script error", error.Exception, error); } }
protected override async Task <object> InvokeCore(object[] parameters, FunctionInvocationContext context) { MethodInfo function = await GetFunctionTargetAsync(isInvocation : true); // Separate system parameters from the actual method parameters object[] originalParameters = parameters; int actualParameterCount = function.GetParameters().Length; parameters = parameters.Take(actualParameterCount).ToArray(); object result = function.Invoke(null, parameters); // after the function executes, we have to copy values back into the original // array to ensure object references are maintained (since we took a copy above) for (int i = 0; i < parameters.Length; i++) { originalParameters[i] = parameters[i]; } // unwrap the task if (result is Task) { result = await((Task)result).ContinueWith(t => GetTaskResult(t), TaskContinuationOptions.ExecuteSynchronously); } return(result); }
protected override async Task InvokeCore(object[] parameters, FunctionInvocationContext context) { object requestObj = (parameters != null && parameters.Any()) ? parameters[0] : null; if (requestObj == null || !(requestObj is HttpRequestMessage)) { throw new Exception("Could not find parameter of type HttpRequestMessage while executing a Proxy Request"); } await _proxyClient.Execute(requestObj as HttpRequestMessage, context.Logger); }
private static IDisposable BeginFunctionScope(FunctionInvocationContext context) { // Node captures the AsyncLocal value of the first invocation, which means that logs // are correlated incorrectly. Here we'll overwrite that value with the correct value // immediately before logging. return(context.Logger.BeginScope( new Dictionary <string, object> { ["MS_FunctionName"] = context.ExecutionContext.FunctionName, ["MS_FunctionInvocationId"] = context.ExecutionContext.InvocationId.ToString() })); }
protected override async Task <object> InvokeCore(object[] parameters, FunctionInvocationContext context) { HttpRequest requestObj = parameters?.FirstOrDefault() as HttpRequest; if (requestObj == null) { throw new Exception("Could not find parameter of type HttpRequest while executing a Proxy Request"); } await _proxyClient.Execute(requestObj, context.Logger); return(requestObj.HttpContext.Items[ScriptConstants.AzureFunctionsHttpResponseKey]); }
protected override async Task <object> InvokeCore(object[] parameters, FunctionInvocationContext context) { // Need to wait for at least one language worker process to be initialized before accepting invocations if (!IsDispatcherReady()) { await DelayUntilFunctionDispatcherInitializedOrShutdown(); } var bindingData = context.Binder.BindingData; object triggerValue = TransformInput(parameters[0], bindingData); var triggerInput = (_bindingMetadata.Name, _bindingMetadata.DataType ?? DataType.String, triggerValue); IEnumerable <(string, DataType, object)> inputs = new[] { triggerInput }; if (_inputBindings.Count > 1) { var nonTriggerInputs = await BindInputsAsync(context.Binder); inputs = inputs.Concat(nonTriggerInputs); } var invocationContext = new ScriptInvocationContext { FunctionMetadata = Metadata, BindingData = bindingData, ExecutionContext = context.ExecutionContext, Inputs = inputs, ResultSource = new TaskCompletionSource <ScriptInvocationResult>(), AsyncExecutionContext = System.Threading.ExecutionContext.Capture(), Traceparent = Activity.Current?.Id, Tracestate = Activity.Current?.TraceStateString, Attributes = Activity.Current?.Tags, // TODO: link up cancellation token to parameter descriptors CancellationToken = CancellationToken.None, Logger = context.Logger }; string invocationId = context.ExecutionContext.InvocationId.ToString(); _logger.LogTrace($"Sending invocation id:{invocationId}"); await _functionDispatcher.InvokeAsync(invocationContext); var result = await invocationContext.ResultSource.Task; await BindOutputsAsync(triggerValue, context.Binder, result); return(result.Return); }
protected override async Task InvokeCore(object[] parameters, FunctionInvocationContext context) { object input = parameters[0]; string invocationId = context.ExecutionContext.InvocationId.ToString(); DataType dataType = _trigger.DataType ?? DataType.String; var userTraceWriter = CreateUserTraceWriter(context.TraceWriter); var scriptExecutionContext = CreateScriptExecutionContext(input, dataType, userTraceWriter, context); var bindingData = (Dictionary <string, object>)scriptExecutionContext["bindingData"]; await ProcessInputBindingsAsync(context.Binder, scriptExecutionContext, bindingData); object functionResult = await ScriptFunc(scriptExecutionContext); await ProcessOutputBindingsAsync(_outputBindings, input, context.Binder, bindingData, scriptExecutionContext, functionResult); }
internal async Task ExecuteScriptAsync(string path, string arguments, object[] invocationParameters, FunctionInvocationContext context) { object input = invocationParameters[0]; string invocationId = context.ExecutionContext.InvocationId.ToString(); string workingDirectory = Path.GetDirectoryName(_scriptFilePath); string functionInstanceOutputPath = Path.Combine(Path.GetTempPath(), "Functions", "Binding", invocationId); Dictionary<string, string> environmentVariables = new Dictionary<string, string>(); InitializeEnvironmentVariables(environmentVariables, functionInstanceOutputPath, input, _outputBindings, context.ExecutionContext); object convertedInput = ConvertInput(input); Utility.ApplyBindingData(convertedInput, context.Binder.BindingData); Dictionary<string, object> bindingData = context.Binder.BindingData; bindingData["InvocationId"] = invocationId; await ProcessInputBindingsAsync(convertedInput, functionInstanceOutputPath, context.Binder, _inputBindings, _outputBindings, bindingData, environmentVariables); Process process = CreateProcess(path, workingDirectory, arguments, environmentVariables); var userTraceWriter = CreateUserTraceWriter(context.TraceWriter); process.OutputDataReceived += (s, e) => { if (e.Data != null) { userTraceWriter.Info(e.Data); } }; var tcs = new TaskCompletionSource<object>(); process.Exited += (sender, args) => { tcs.TrySetResult(null); }; process.Start(); process.BeginOutputReadLine(); await tcs.Task; if (process.ExitCode != 0) { string error = process.StandardError.ReadToEnd(); throw new ApplicationException(error); } await ProcessOutputBindingsAsync(functionInstanceOutputPath, _outputBindings, input, context.Binder, bindingData); }
private static FunctionInvocationContext GetContextFromParameters(object[] parameters, FunctionMetadata metadata) { ExecutionContext functionExecutionContext = null; Binder binder = null; ILogger logger = null; for (var i = 0; i < parameters.Length; i++) { switch (parameters[i]) { case ExecutionContext fc: functionExecutionContext ??= fc; break; case Binder b: binder ??= b; break; case ILogger l: logger ??= l; break; } } // We require the ExecutionContext, so this will throw if one is not found. if (functionExecutionContext == null) { throw new ArgumentException("Function ExecutionContext was not found"); } functionExecutionContext.FunctionDirectory = metadata.FunctionDirectory; functionExecutionContext.FunctionName = metadata.Name; FunctionInvocationContext context = new FunctionInvocationContext { ExecutionContext = functionExecutionContext, Binder = binder, Logger = logger }; return(context); }
protected override async Task<object> InvokeCore(object[] parameters, FunctionInvocationContext context) { MethodInfo function = await GetFunctionTargetAsync(isInvocation: true); // Separate system parameters from the actual method parameters object[] originalParameters = parameters; int actualParameterCount = function.GetParameters().Length; parameters = parameters.Take(actualParameterCount).ToArray(); object result = await _methodInvoker.InvokeAsync(null, parameters); // after the function executes, we have to copy values back into the original // array to ensure object references are maintained (since we took a copy above) for (int i = 0; i < parameters.Length; i++) { originalParameters[i] = parameters[i]; } return result; }
protected override async Task InvokeCore(object[] parameters, FunctionInvocationContext context) { // Separate system parameters from the actual method parameters object[] originalParameters = parameters; MethodInfo function = await GetFunctionTargetAsync(); int actualParameterCount = function.GetParameters().Length; object[] systemParameters = parameters.Skip(actualParameterCount).ToArray(); parameters = parameters.Take(actualParameterCount).ToArray(); parameters = ProcessInputParameters(parameters); object result = function.Invoke(null, parameters); // after the function executes, we have to copy values back into the original // array to ensure object references are maintained (since we took a copy above) for (int i = 0; i < parameters.Length; i++) { originalParameters[i] = parameters[i]; } if (result is Task) { result = await((Task)result).ContinueWith(t => GetTaskResult(t), TaskContinuationOptions.ExecuteSynchronously); } if (result != null) { _resultProcessor(function, parameters, systemParameters, result); } // if a return value binding was specified, copy the return value // into the output binding slot (by convention the last parameter) var returnValueBinding = Metadata.Bindings.SingleOrDefault(p => p.IsReturn); if (returnValueBinding != null && !(returnValueBinding is IResultProcessingBinding)) { originalParameters[originalParameters.Length - 1] = result; } }
private static FunctionInvocationContext GetContextFromParameters(object[] parameters, FunctionMetadata metadata) { // We require the ExecutionContext, so this will throw if one is not found. ExecutionContext functionExecutionContext = parameters.OfType <ExecutionContext>().First(); functionExecutionContext.FunctionDirectory = metadata.FunctionDirectory; functionExecutionContext.FunctionName = metadata.Name; // These may not be present, so null is okay. Binder binder = parameters.OfType <Binder>().FirstOrDefault(); ILogger logger = parameters.OfType <ILogger>().FirstOrDefault(); FunctionInvocationContext context = new FunctionInvocationContext { ExecutionContext = functionExecutionContext, Binder = binder, Logger = logger }; return(context); }
protected override async Task InvokeCore(object[] parameters, FunctionInvocationContext context) { // Ensure we're properly initialized await _initializer.Value.ConfigureAwait(false); object input = parameters[0]; string invocationId = context.ExecutionContext.InvocationId.ToString(); DataType dataType = _trigger.DataType ?? DataType.String; var userTraceWriter = CreateUserTraceWriter(context.TraceWriter); var scriptExecutionContext = await CreateScriptExecutionContextAsync(input, dataType, userTraceWriter, context).ConfigureAwait(false); var bindingData = (Dictionary <string, object>)scriptExecutionContext["bindingData"]; await ProcessInputBindingsAsync(context.Binder, scriptExecutionContext, bindingData); ScriptFunc function = await GetFunctionTargetAsync(); object functionResult = await function(scriptExecutionContext); await ProcessOutputBindingsAsync(_outputBindings, input, context.Binder, bindingData, scriptExecutionContext, functionResult); }
protected override async Task <object> InvokeCore(object[] parameters, FunctionInvocationContext context) { // Need to wait for atleast one language worker process to be initialized before accepting invocations await DelayUntilFunctionDispatcherInitialized(); string invocationId = context.ExecutionContext.InvocationId.ToString(); // TODO: fix extensions and remove object triggerValue = TransformInput(parameters[0], context.Binder.BindingData); var triggerInput = (_bindingMetadata.Name, _bindingMetadata.DataType ?? DataType.String, triggerValue); var inputs = new[] { triggerInput }.Concat(await BindInputsAsync(context.Binder)); ScriptInvocationContext invocationContext = new ScriptInvocationContext() { FunctionMetadata = Metadata, BindingData = context.Binder.BindingData, // This has duplicates too (of type DefaultHttpRequest). Needs to be removed after verifying this can indeed be constructed by the workers from the rest of the data being passed (https://github.com/Azure/azure-functions-host/issues/4735). ExecutionContext = context.ExecutionContext, Inputs = inputs, ResultSource = new TaskCompletionSource <ScriptInvocationResult>(), AsyncExecutionContext = System.Threading.ExecutionContext.Capture(), Traceparent = Activity.Current?.Id, Attributes = Activity.Current?.Tags, // TODO: link up cancellation token to parameter descriptors CancellationToken = CancellationToken.None, Logger = context.Logger }; ScriptInvocationResult result; _logger.LogDebug($"Sending invocation id:{invocationId}"); await _functionDispatcher.InvokeAsync(invocationContext); result = await invocationContext.ResultSource.Task; await BindOutputsAsync(triggerValue, context.Binder, result); return(result.Return); }
protected override async Task <object> InvokeCore(object[] parameters, FunctionInvocationContext context) { // Need to wait for atleast one language worker process to be initialized before accepting invocations await DelayUntilFunctionDispatcherInitialized(); string invocationId = context.ExecutionContext.InvocationId.ToString(); // TODO: fix extensions and remove object triggerValue = TransformInput(parameters[0], context.Binder.BindingData); var triggerInput = (_bindingMetadata.Name, _bindingMetadata.DataType ?? DataType.String, triggerValue); var inputs = new[] { triggerInput }.Concat(await BindInputsAsync(context.Binder)); ScriptInvocationContext invocationContext = new ScriptInvocationContext() { FunctionMetadata = Metadata, BindingData = context.Binder.BindingData, ExecutionContext = context.ExecutionContext, Inputs = inputs, ResultSource = new TaskCompletionSource <ScriptInvocationResult>(), AsyncExecutionContext = System.Threading.ExecutionContext.Capture(), // TODO: link up cancellation token to parameter descriptors CancellationToken = CancellationToken.None, Logger = context.Logger }; ScriptInvocationResult result; _logger.LogDebug($"Sending invocation id:{invocationId}"); await _functionDispatcher.InvokeAsync(invocationContext); result = await invocationContext.ResultSource.Task; await BindOutputsAsync(triggerValue, context.Binder, result); return(result.Return); }
protected abstract Task InvokeCore(object[] parameters, FunctionInvocationContext context);
public async Task Invoke(object[] parameters) { // We require the ExecutionContext, so this will throw if one is not found. ExecutionContext functionExecutionContext = parameters.OfType <ExecutionContext>().First(); PopulateExecutionContext(functionExecutionContext); // These may not be present, so null is okay. TraceWriter functionTraceWriter = parameters.OfType <TraceWriter>().FirstOrDefault(); Binder binder = parameters.OfType <Binder>().FirstOrDefault(); ILogger logger = parameters.OfType <ILogger>().FirstOrDefault(); string invocationId = functionExecutionContext.InvocationId.ToString(); var startedEvent = new FunctionStartedEvent(functionExecutionContext.InvocationId, Metadata); _metrics.BeginEvent(startedEvent); var invokeLatencyEvent = LogInvocationMetrics(_metrics, Metadata); var invocationStopWatch = new Stopwatch(); invocationStopWatch.Start(); try { string startMessage = $"Function started (Id={invocationId})"; TraceWriter.Info(startMessage); Logger?.LogInformation(startMessage); FunctionInvocationContext context = new FunctionInvocationContext { ExecutionContext = functionExecutionContext, Binder = binder, TraceWriter = functionTraceWriter, Logger = logger }; await InvokeCore(parameters, context); invocationStopWatch.Stop(); LogFunctionResult(startedEvent, true, invocationId, invocationStopWatch.ElapsedMilliseconds); } catch (AggregateException ex) { ExceptionDispatchInfo exInfo = null; // If there's only a single exception, rethrow it by itself Exception singleEx = ex.Flatten().InnerExceptions.SingleOrDefault(); if (singleEx != null) { exInfo = ExceptionDispatchInfo.Capture(singleEx); } else { exInfo = ExceptionDispatchInfo.Capture(ex); } invocationStopWatch.Stop(); LogFunctionResult(startedEvent, false, invocationId, invocationStopWatch.ElapsedMilliseconds); exInfo.Throw(); } catch { invocationStopWatch.Stop(); LogFunctionResult(startedEvent, false, invocationId, invocationStopWatch.ElapsedMilliseconds); throw; } finally { if (startedEvent != null) { _metrics.EndEvent(startedEvent); } if (invokeLatencyEvent != null) { _metrics.EndEvent(invokeLatencyEvent); } } }
private async Task <Dictionary <string, object> > CreateScriptExecutionContextAsync(object input, DataType dataType, TraceWriter traceWriter, FunctionInvocationContext invocationContext) { // create a TraceWriter wrapper that can be exposed to Node.js var log = (ScriptFunc)(p => { var logData = (IDictionary <string, object>)p; string message = (string)logData["msg"]; if (message != null) { try { TraceLevel level = (TraceLevel)logData["lvl"]; var evt = new TraceEvent(level, message); // Node captures the AsyncLocal value of the first invocation, which means that logs // are correlated incorrectly. Here we'll overwrite that value with the correct value // immediately before logging. using (invocationContext.Logger.BeginScope( new Dictionary <string, object> { ["MS_FunctionInvocationId"] = invocationContext.ExecutionContext.InvocationId })) { // TraceWriter already logs to ILogger traceWriter.Trace(evt); } } catch (ObjectDisposedException) { // if a function attempts to write to a disposed // TraceWriter. Might happen if a function tries to // log after calling done() } } return(Task.FromResult <object>(null)); }); var bindings = new Dictionary <string, object>(); var bind = (ScriptFunc)(p => { IDictionary <string, object> bindValues = (IDictionary <string, object>)p; foreach (var bindValue in bindValues) { bindings[bindValue.Key] = bindValue.Value; } return(Task.FromResult <object>(null)); }); var executionContext = new Dictionary <string, object> { ["invocationId"] = invocationContext.ExecutionContext.InvocationId, ["functionName"] = invocationContext.ExecutionContext.FunctionName, ["functionDirectory"] = invocationContext.ExecutionContext.FunctionDirectory, }; var context = new Dictionary <string, object>() { { "invocationId", invocationContext.ExecutionContext.InvocationId }, { "executionContext", executionContext }, { "log", log }, { "bindings", bindings }, { "bind", bind } }; if (!string.IsNullOrEmpty(_entryPoint)) { context["_entryPoint"] = _entryPoint; } // convert the request to a json object if (input is HttpRequestMessage request) { var requestObject = await CreateRequestObjectAsync(request).ConfigureAwait(false); input = requestObject; // If this is a WebHook function, the input should be the // request body var httpTrigger = _inputBindings.OfType <ExtensionBinding>().SingleOrDefault(p => p.Metadata.IsTrigger)? .Attributes.OfType <HttpTriggerAttribute>().SingleOrDefault(); if (httpTrigger != null && !string.IsNullOrEmpty(httpTrigger.WebHookType)) { requestObject.TryGetValue("body", out input); } // make the entire request object available as well // this is symmetric with context.res which we also support context["req"] = requestObject; } else if (input is TimerInfo) { // TODO: Need to generalize this model rather than hardcode // so other extensions can also express their Node.js object model TimerInfo timerInfo = (TimerInfo)input; var inputValues = new Dictionary <string, object>() { { "isPastDue", timerInfo.IsPastDue } }; if (timerInfo.ScheduleStatus != null) { inputValues["last"] = timerInfo.ScheduleStatus.Last.ToString("s", CultureInfo.InvariantCulture); inputValues["next"] = timerInfo.ScheduleStatus.Next.ToString("s", CultureInfo.InvariantCulture); } input = inputValues; } else if (input is Stream) { FunctionBinding.ConvertStreamToValue((Stream)input, dataType, ref input); } Utility.ApplyBindingData(input, invocationContext.Binder.BindingData); var bindingData = NormalizeBindingData(invocationContext.Binder.BindingData); bindingData["invocationId"] = invocationContext.ExecutionContext.InvocationId.ToString(); context["bindingData"] = bindingData; // if the input is json, try converting to an object or array object converted; if (TryConvertJson(input, out converted)) { input = converted; } bindings.Add(_trigger.Name, input); context.Add("_triggerType", _trigger.Type); return(context); }
public async Task Invoke(object[] parameters) { // We require the ExecutionContext, so this will throw if one is not found. ExecutionContext functionExecutionContext = parameters.OfType<ExecutionContext>().First(); // These may not be present, so null is okay. TraceWriter functionTraceWriter = parameters.OfType<TraceWriter>().FirstOrDefault(); Binder binder = parameters.OfType<Binder>().FirstOrDefault(); string invocationId = functionExecutionContext.InvocationId.ToString(); FunctionStartedEvent startedEvent = new FunctionStartedEvent(functionExecutionContext.InvocationId, Metadata); _metrics.BeginEvent(startedEvent); LogInvocationMetrics(_metrics, Metadata.Bindings); try { TraceWriter.Info($"Function started (Id={invocationId})"); FunctionInvocationContext context = new FunctionInvocationContext { ExecutionContext = functionExecutionContext, Binder = binder, TraceWriter = functionTraceWriter }; await InvokeCore(parameters, context); TraceWriter.Info($"Function completed (Success, Id={invocationId})"); } catch (AggregateException ex) { ExceptionDispatchInfo exInfo = null; // If there's only a single exception, rethrow it by itself Exception singleEx = ex.Flatten().InnerExceptions.SingleOrDefault(); if (singleEx != null) { exInfo = ExceptionDispatchInfo.Capture(singleEx); } else { exInfo = ExceptionDispatchInfo.Capture(ex); } LogFunctionFailed(startedEvent, "Failure", invocationId); exInfo.Throw(); } catch { LogFunctionFailed(startedEvent, "Failure", invocationId); throw; } finally { if (startedEvent != null) { _metrics.EndEvent(startedEvent); } } }
protected override Task InvokeCore(object[] parameters, FunctionInvocationContext context) { throw new NotImplementedException(); }
private Dictionary <string, object> CreateScriptExecutionContext(object input, DataType dataType, TraceWriter traceWriter, FunctionInvocationContext invocationContext) { // create a TraceWriter wrapper that can be exposed to Node.js var log = (Func <object, Task <object> >)(p => { string text = p as string; if (text != null) { try { traceWriter.Info(text); } catch (ObjectDisposedException) { // if a function attempts to write to a disposed // TraceWriter. Might happen if a function tries to // log after calling done() } } return(Task.FromResult <object>(null)); }); var bindings = new Dictionary <string, object>(); var bind = (Func <object, Task <object> >)(p => { IDictionary <string, object> bindValues = (IDictionary <string, object>)p; foreach (var bindValue in bindValues) { bindings[bindValue.Key] = bindValue.Value; } return(Task.FromResult <object>(null)); }); var context = new Dictionary <string, object>() { { "invocationId", invocationContext.ExecutionContext.InvocationId }, { "log", log }, { "bindings", bindings }, { "bind", bind } }; if (!string.IsNullOrEmpty(_entryPoint)) { context["_entryPoint"] = _entryPoint; } if (input is HttpRequestMessage) { // convert the request to a json object HttpRequestMessage request = (HttpRequestMessage)input; string rawBody = null; var requestObject = CreateRequestObject(request, out rawBody); input = requestObject; if (rawBody != null) { requestObject["rawBody"] = rawBody; } // If this is a WebHook function, the input should be the // request body HttpTriggerBindingMetadata httpBinding = _trigger as HttpTriggerBindingMetadata; if (httpBinding != null && !string.IsNullOrEmpty(httpBinding.WebHookType)) { requestObject.TryGetValue("body", out input); } // make the entire request object available as well // this is symmetric with context.res which we also support context["req"] = requestObject; } else if (input is TimerInfo) { // TODO: Need to generalize this model rather than hardcode // so other extensions can also express their Node.js object model TimerInfo timerInfo = (TimerInfo)input; var inputValues = new Dictionary <string, object>() { { "isPastDue", timerInfo.IsPastDue } }; if (timerInfo.ScheduleStatus != null) { inputValues["last"] = timerInfo.ScheduleStatus.Last.ToString("s", CultureInfo.InvariantCulture); inputValues["next"] = timerInfo.ScheduleStatus.Next.ToString("s", CultureInfo.InvariantCulture); } input = inputValues; } else if (input is Stream) { FunctionBinding.ConvertStreamToValue((Stream)input, dataType, ref input); } Utility.ApplyBindingData(input, invocationContext.Binder.BindingData); var bindingData = NormalizeBindingData(invocationContext.Binder.BindingData); bindingData["invocationId"] = invocationContext.ExecutionContext.InvocationId.ToString(); context["bindingData"] = bindingData; // if the input is json, try converting to an object or array object converted; if (TryConvertJson(input, out converted)) { input = converted; } bindings.Add(_trigger.Name, input); context.Add("_triggerType", _trigger.Type); return(context); }
public async Task Invoke(object[] parameters) { // We require the ExecutionContext, so this will throw if one is not found. ExecutionContext functionExecutionContext = parameters.OfType <ExecutionContext>().First(); // These may not be present, so null is okay. TraceWriter functionTraceWriter = parameters.OfType <TraceWriter>().FirstOrDefault(); Binder binder = parameters.OfType <Binder>().FirstOrDefault(); string invocationId = functionExecutionContext.InvocationId.ToString(); FunctionStartedEvent startedEvent = new FunctionStartedEvent(functionExecutionContext.InvocationId, Metadata); _metrics.BeginEvent(startedEvent); LogInvocationMetrics(_metrics, Metadata.Bindings); try { TraceWriter.Info($"Function started (Id={invocationId})"); FunctionInvocationContext context = new FunctionInvocationContext { ExecutionContext = functionExecutionContext, Binder = binder, TraceWriter = functionTraceWriter }; await InvokeCore(parameters, context); TraceWriter.Info($"Function completed (Success, Id={invocationId})"); } catch (AggregateException ex) { ExceptionDispatchInfo exInfo = null; // If there's only a single exception, rethrow it by itself Exception singleEx = ex.Flatten().InnerExceptions.SingleOrDefault(); if (singleEx != null) { exInfo = ExceptionDispatchInfo.Capture(singleEx); } else { exInfo = ExceptionDispatchInfo.Capture(ex); } LogFunctionFailed(startedEvent, "Failure", invocationId); exInfo.Throw(); } catch { LogFunctionFailed(startedEvent, "Failure", invocationId); throw; } finally { if (startedEvent != null) { _metrics.EndEvent(startedEvent); } } }
public async Task Invoke(object[] parameters) { FunctionInvocationContext context = GetContextFromParameters(parameters, Metadata); await InvokeCore(parameters, context); }
internal async Task ExecuteScriptAsync(string path, string arguments, object[] invocationParameters, FunctionInvocationContext context) { object input = invocationParameters[0]; string invocationId = context.ExecutionContext.InvocationId.ToString(); string workingDirectory = Path.GetDirectoryName(_scriptFilePath); string functionInstanceOutputPath = Path.Combine(Path.GetTempPath(), "Functions", "Binding", invocationId); Dictionary <string, string> environmentVariables = new Dictionary <string, string>(); InitializeEnvironmentVariables(environmentVariables, functionInstanceOutputPath, input, _outputBindings, context.ExecutionContext); object convertedInput = ConvertInput(input); Utility.ApplyBindingData(convertedInput, context.Binder.BindingData); Dictionary <string, object> bindingData = context.Binder.BindingData; bindingData["InvocationId"] = invocationId; await ProcessInputBindingsAsync(convertedInput, functionInstanceOutputPath, context.Binder, _inputBindings, _outputBindings, bindingData, environmentVariables); Process process = CreateProcess(path, workingDirectory, arguments, environmentVariables); var userTraceWriter = CreateUserTraceWriter(context.TraceWriter); process.OutputDataReceived += (s, e) => { if (e.Data != null) { // the user's TraceWriter will automatically log to ILogger as well userTraceWriter.Info(e.Data); } }; var tcs = new TaskCompletionSource <object>(); process.Exited += (sender, args) => { tcs.TrySetResult(null); }; process.Start(); process.BeginOutputReadLine(); await tcs.Task; if (process.ExitCode != 0) { string error = process.StandardError.ReadToEnd(); throw new ApplicationException(error); } await ProcessOutputBindingsAsync(functionInstanceOutputPath, _outputBindings, input, context.Binder, bindingData); }
protected override async Task InvokeCore(object[] parameters, FunctionInvocationContext context) { // Separate system parameters from the actual method parameters object[] originalParameters = parameters; MethodInfo function = await GetFunctionTargetAsync(); int actualParameterCount = function.GetParameters().Length; object[] systemParameters = parameters.Skip(actualParameterCount).ToArray(); parameters = parameters.Take(actualParameterCount).ToArray(); parameters = ProcessInputParameters(parameters); object result = function.Invoke(null, parameters); // after the function executes, we have to copy values back into the original // array to ensure object references are maintained (since we took a copy above) for (int i = 0; i < parameters.Length; i++) { originalParameters[i] = parameters[i]; } if (result is Task) { result = await ((Task)result).ContinueWith(t => GetTaskResult(t), TaskContinuationOptions.ExecuteSynchronously); } if (result != null) { _resultProcessor(function, parameters, systemParameters, result); } // if a return value binding was specified, copy the return value // into the output binding slot (by convention the last parameter) var returnValueBinding = Metadata.Bindings.SingleOrDefault(p => p.IsReturn); if (returnValueBinding != null && !(returnValueBinding is IResultProcessingBinding)) { originalParameters[originalParameters.Length - 1] = result; } }