internal async Task SendInvocationRequest(ScriptInvocationContext context) { try { if (_functionLoadErrors.ContainsKey(context.FunctionMetadata.GetFunctionId())) { _workerChannelLogger.LogDebug($"Function {context.FunctionMetadata.Name} failed to load"); context.ResultSource.TrySetException(_functionLoadErrors[context.FunctionMetadata.GetFunctionId()]); _executingInvocations.TryRemove(context.ExecutionContext.InvocationId.ToString(), out ScriptInvocationContext _); } else { if (context.CancellationToken.IsCancellationRequested) { context.ResultSource.SetCanceled(); return; } var invocationRequest = await context.ToRpcInvocationRequest(_workerChannelLogger, _workerCapabilities, _isSharedMemoryDataTransferEnabled, _sharedMemoryManager); _executingInvocations.TryAdd(invocationRequest.InvocationId, context); SendStreamingMessage(new StreamingMessage { InvocationRequest = invocationRequest }); } } catch (Exception invokeEx) { context.ResultSource.TrySetException(invokeEx); } }
internal void SendInvocationRequest(ScriptInvocationContext context) { try { if (_functionLoadErrors.ContainsKey(context.FunctionMetadata.FunctionId)) { _workerChannelLogger.LogDebug($"Function {context.FunctionMetadata.Name} failed to load"); context.ResultSource.TrySetException(_functionLoadErrors[context.FunctionMetadata.FunctionId]); _executingInvocations.TryRemove(context.ExecutionContext.InvocationId.ToString(), out ScriptInvocationContext _); } else { if (context.CancellationToken.IsCancellationRequested) { context.ResultSource.SetCanceled(); return; } InvocationRequest invocationRequest = context.ToRpcInvocationRequest(IsTriggerMetadataPopulatedByWorker(), _workerChannelLogger, _workerCapabilities); _executingInvocations.TryAdd(invocationRequest.InvocationId, context); SendStreamingMessage(new StreamingMessage { InvocationRequest = invocationRequest }); } } catch (Exception invokeEx) { context.ResultSource.TrySetException(invokeEx); } }
public async Task ToRpcInvocationRequest_RpcDataTransfer() { var logger = new TestLogger("test"); var httpContext = new DefaultHttpContext(); httpContext.Request.Host = new HostString("local"); httpContext.Request.Path = "/test"; httpContext.Request.Method = "Post"; var poco = new TestPoco { Id = 1, Name = "Test" }; var bindingData = new Dictionary <string, object> { { "req", httpContext.Request }, { "$request", httpContext.Request }, { "headers", httpContext.Request.Headers.ToDictionary(p => p.Key, p => p.Value) }, { "query", httpContext.Request.QueryString.ToString() }, { "sys", new SystemBindingData() } }; const int inputStringLength = 32; string inputString = TestUtils.GetRandomString(inputStringLength); const int inputBytesLength = 32; byte[] inputBytes = TestUtils.GetRandomBytesInArray(inputBytesLength); var inputs = new List <(string name, DataType type, object val)> { ("req", DataType.String, httpContext.Request), ("fooStr", DataType.String, inputString), ("fooBytes", DataType.Binary, inputBytes), }; var invocationContext = new ScriptInvocationContext() { ExecutionContext = new ExecutionContext() { InvocationId = Guid.NewGuid(), FunctionName = "Test", }, BindingData = bindingData, Inputs = inputs, ResultSource = new TaskCompletionSource <ScriptInvocationResult>(), Logger = logger, AsyncExecutionContext = System.Threading.ExecutionContext.Capture() }; var functionMetadata = new FunctionMetadata { Name = "Test" }; var httpTriggerBinding = new BindingMetadata { Name = "req", Type = "httpTrigger", Direction = BindingDirection.In, Raw = new JObject() }; var fooStrInputBinding = new BindingMetadata { Name = "fooStr", Type = "fooStr", Direction = BindingDirection.In }; var fooBytesInputBinding = new BindingMetadata { Name = "fooBytes", Type = "fooBytes", Direction = BindingDirection.In }; var httpOutputBinding = new BindingMetadata { Name = "res", Type = "http", Direction = BindingDirection.Out, Raw = new JObject(), DataType = DataType.String }; functionMetadata.Bindings.Add(httpTriggerBinding); functionMetadata.Bindings.Add(fooStrInputBinding); functionMetadata.Bindings.Add(fooBytesInputBinding); functionMetadata.Bindings.Add(httpOutputBinding); invocationContext.FunctionMetadata = functionMetadata; GrpcCapabilities capabilities = new GrpcCapabilities(logger); var result = await invocationContext.ToRpcInvocationRequest(logger, capabilities, isSharedMemoryDataTransferEnabled : true, _sharedMemoryManager); Assert.Equal(3, result.InputData.Count); Assert.Equal("fooStr", result.InputData[1].Name); Assert.Equal("fooBytes", result.InputData[2].Name); // The input data should be transferred over regular RPC despite enabling shared memory Assert.Equal(inputString, result.InputData[1].Data.String); Assert.Equal(inputBytes, result.InputData[2].Data.Bytes); }
public async Task ToRpcInvocationRequest_RpcSharedMemoryDataTransfer() { var logger = new TestLogger("test"); var httpContext = new DefaultHttpContext(); httpContext.Request.Host = new HostString("local"); httpContext.Request.Path = "/test"; httpContext.Request.Method = "Post"; var poco = new TestPoco { Id = 1, Name = "Test" }; var bindingData = new Dictionary <string, object> { { "req", httpContext.Request }, { "$request", httpContext.Request }, { "headers", httpContext.Request.Headers.ToDictionary(p => p.Key, p => p.Value) }, { "query", httpContext.Request.QueryString.ToString() }, { "sys", new SystemBindingData() } }; const int inputStringLength = 2 * 1024 * 1024; string inputString = TestUtils.GetRandomString(inputStringLength); const int inputBytesLength = 2 * 1024 * 1024; byte[] inputBytes = TestUtils.GetRandomBytesInArray(inputBytesLength); var inputs = new List <(string name, DataType type, object val)> { ("req", DataType.String, httpContext.Request), ("fooStr", DataType.String, inputString), ("fooBytes", DataType.Binary, inputBytes), }; var invocationContext = new ScriptInvocationContext() { ExecutionContext = new ExecutionContext() { InvocationId = Guid.NewGuid(), FunctionName = "Test", }, BindingData = bindingData, Inputs = inputs, ResultSource = new TaskCompletionSource <ScriptInvocationResult>(), Logger = logger, AsyncExecutionContext = System.Threading.ExecutionContext.Capture() }; var functionMetadata = new FunctionMetadata { Name = "Test" }; var httpTriggerBinding = new BindingMetadata { Name = "req", Type = "httpTrigger", Direction = BindingDirection.In, Raw = new JObject() }; var fooStrInputBinding = new BindingMetadata { Name = "fooStr", Type = "fooStr", Direction = BindingDirection.In }; var fooBytesInputBinding = new BindingMetadata { Name = "fooBytes", Type = "fooBytes", Direction = BindingDirection.In }; var httpOutputBinding = new BindingMetadata { Name = "res", Type = "http", Direction = BindingDirection.Out, Raw = new JObject(), DataType = DataType.String }; functionMetadata.Bindings.Add(httpTriggerBinding); functionMetadata.Bindings.Add(fooStrInputBinding); functionMetadata.Bindings.Add(fooBytesInputBinding); functionMetadata.Bindings.Add(httpOutputBinding); invocationContext.FunctionMetadata = functionMetadata; GrpcCapabilities capabilities = new GrpcCapabilities(logger); var result = await invocationContext.ToRpcInvocationRequest(logger, capabilities, isSharedMemoryDataTransferEnabled : true, _sharedMemoryManager); Assert.Equal(3, result.InputData.Count); Assert.Equal("fooStr", result.InputData[1].Name); Assert.Equal("fooBytes", result.InputData[2].Name); // The input data should be transferred over shared memory RpcSharedMemory sharedMem1 = result.InputData[1].RpcSharedMemory; // This is what the expected byte[] representation of the string should be // We use that to find expected length byte[] contentBytes = Encoding.UTF8.GetBytes(inputString); Assert.Equal(contentBytes.Length, sharedMem1.Count); // Check that the name of the shared memory map is a valid GUID Assert.True(Guid.TryParse(sharedMem1.Name, out _)); // Check the type being sent Assert.Equal(sharedMem1.Type, RpcDataType.String); // The input data should be transferred over shared memory RpcSharedMemory sharedMem2 = result.InputData[2].RpcSharedMemory; Assert.Equal(inputBytes.Length, sharedMem2.Count); // Check that the name of the shared memory map is a valid GUID Assert.True(Guid.TryParse(sharedMem2.Name, out _)); // Check the type being sent Assert.Equal(sharedMem2.Type, RpcDataType.Bytes); }
public async Task ToRpcInvocationRequest_MultipleInputBindings() { var logger = new TestLogger("test"); var httpContext = new DefaultHttpContext(); httpContext.Request.Host = new HostString("local"); httpContext.Request.Path = "/test"; httpContext.Request.Method = "Post"; var poco = new TestPoco { Id = 1, Name = "Test" }; var bindingData = new Dictionary <string, object> { { "req", httpContext.Request }, { "$request", httpContext.Request }, { "headers", httpContext.Request.Headers.ToDictionary(p => p.Key, p => p.Value) }, { "query", httpContext.Request.QueryString.ToString() }, { "sys", new SystemBindingData() } }; var inputs = new List <(string name, DataType type, object val)> { ("req", DataType.String, httpContext.Request), ("blob", DataType.String, null), // verify that null values are handled ("foo", DataType.String, "test"), ("bar1", DataType.String, poco), ("bar2", DataType.String, poco) }; var invocationContext = new ScriptInvocationContext() { ExecutionContext = new ExecutionContext() { InvocationId = Guid.NewGuid(), FunctionName = "Test", }, BindingData = bindingData, Inputs = inputs, ResultSource = new TaskCompletionSource <ScriptInvocationResult>(), Logger = logger, AsyncExecutionContext = System.Threading.ExecutionContext.Capture() }; var functionMetadata = new FunctionMetadata { Name = "Test" }; var httpTriggerBinding = new BindingMetadata { Name = "req", Type = "httpTrigger", Direction = BindingDirection.In, Raw = new JObject() }; var blobInputBinding = new BindingMetadata { Name = "blob", Type = "blob", Direction = BindingDirection.In }; var fooInputBinding = new BindingMetadata { Name = "foo", Type = "foo", Direction = BindingDirection.In }; var barInputBinding1 = new BindingMetadata { Name = "bar1", Type = "bar", Direction = BindingDirection.In }; var barInputBinding2 = new BindingMetadata { Name = "bar2", Type = "bar", Direction = BindingDirection.In }; var httpOutputBinding = new BindingMetadata { Name = "res", Type = "http", Direction = BindingDirection.Out, Raw = new JObject(), DataType = DataType.String }; functionMetadata.Bindings.Add(httpTriggerBinding); functionMetadata.Bindings.Add(blobInputBinding); functionMetadata.Bindings.Add(fooInputBinding); functionMetadata.Bindings.Add(barInputBinding1); functionMetadata.Bindings.Add(barInputBinding2); functionMetadata.Bindings.Add(httpOutputBinding); invocationContext.FunctionMetadata = functionMetadata; GrpcCapabilities capabilities = new GrpcCapabilities(logger); var result = await invocationContext.ToRpcInvocationRequest(logger, capabilities, isSharedMemoryDataTransferEnabled : false, _sharedMemoryManager); Assert.Equal(5, result.InputData.Count); Assert.Equal("req", result.InputData[0].Name); var resultHttp = result.InputData[0].Data; Assert.Equal("http://local/test", ((RpcHttp)result.InputData[0].Data.Http).Url); // verify the null input was propagated properly Assert.Equal("blob", result.InputData[1].Name); Assert.Equal(string.Empty, result.InputData[1].Data.String); Assert.Equal("foo", result.InputData[2].Name); Assert.Equal("test", result.InputData[2].Data.String); Assert.Equal("bar1", result.InputData[3].Name); var resultPoco = result.InputData[3].Data; Assert.Equal("{\"Name\":\"Test\",\"Id\":1}", resultPoco.Json); Assert.Equal("bar2", result.InputData[4].Name); Assert.Same(resultPoco, result.InputData[4].Data); Assert.Equal(4, result.TriggerMetadata.Count); Assert.Same(resultHttp, result.TriggerMetadata["req"]); Assert.Same(resultHttp, result.TriggerMetadata["$request"]); Assert.True(result.TriggerMetadata.ContainsKey("headers")); Assert.True(result.TriggerMetadata.ContainsKey("query")); }
public async Task ToRpcInvocationRequest_Http_OmitsDuplicateBodyOfBindingData() { var logger = new TestLogger("test"); var httpContext = new DefaultHttpContext(); httpContext.Request.Host = new HostString("local"); httpContext.Request.Path = "/test"; httpContext.Request.Method = "Post"; var inputs = new List <(string name, DataType type, object val)> { ("req", DataType.String, httpContext.Request) }; var bindingData = new Dictionary <string, object> { { "req", httpContext.Request }, { "$request", httpContext.Request }, { "headers", httpContext.Request.Headers.ToDictionary(p => p.Key, p => p.Value) }, { "query", httpContext.Request.QueryString.ToString() }, { "sys", new SystemBindingData() } }; var invocationContext = new ScriptInvocationContext() { ExecutionContext = new ExecutionContext() { InvocationId = Guid.NewGuid(), FunctionName = "Test", }, BindingData = bindingData, Inputs = inputs, ResultSource = new TaskCompletionSource <ScriptInvocationResult>(), Logger = logger, AsyncExecutionContext = System.Threading.ExecutionContext.Capture() }; var functionMetadata = new FunctionMetadata { Name = "Test" }; var httpTriggerBinding = new BindingMetadata { Name = "req", Type = "httpTrigger", Direction = BindingDirection.In, Raw = new JObject() }; var httpOutputBinding = new BindingMetadata { Name = "res", Type = "http", Direction = BindingDirection.Out, Raw = new JObject(), DataType = DataType.String }; functionMetadata.Bindings.Add(httpTriggerBinding); functionMetadata.Bindings.Add(httpOutputBinding); invocationContext.FunctionMetadata = functionMetadata; GrpcCapabilities capabilities = new GrpcCapabilities(logger); MapField <string, string> addedCapabilities = new MapField <string, string> { { RpcWorkerConstants.RpcHttpTriggerMetadataRemoved, "1" }, { RpcWorkerConstants.RpcHttpBodyOnly, "1" } }; capabilities.UpdateCapabilities(addedCapabilities); var result = await invocationContext.ToRpcInvocationRequest(logger, capabilities, isSharedMemoryDataTransferEnabled : false, _sharedMemoryManager); Assert.Equal(1, result.InputData.Count); Assert.Equal(2, result.TriggerMetadata.Count); Assert.True(result.TriggerMetadata.ContainsKey("headers")); Assert.True(result.TriggerMetadata.ContainsKey("query")); }
public async Task ToRpcInvocationRequest_RpcSharedMemoryDataTransfer_UsingFunctionDataCache_CacheMiss() { var logger = new TestLogger("test"); var httpContext = new DefaultHttpContext(); httpContext.Request.Host = new HostString("local"); httpContext.Request.Path = "/test"; httpContext.Request.Method = "Post"; var poco = new TestPoco { Id = 1, Name = "Test" }; var bindingData = new Dictionary <string, object> { { "req", httpContext.Request }, { "$request", httpContext.Request }, { "headers", httpContext.Request.Headers.ToDictionary(p => p.Key, p => p.Value) }, { "query", httpContext.Request.QueryString.ToString() }, { "sys", new SystemBindingData() } }; const int inputStringLength = 2 * 1024 * 1024; string inputString = TestUtils.GetRandomString(inputStringLength); Stream inputStream1 = new MemoryStream(); StreamWriter inputStreamWriter1 = new StreamWriter(inputStream1); await inputStreamWriter1.WriteAsync(inputString); await inputStreamWriter1.FlushAsync(); inputStream1.Seek(0, SeekOrigin.Begin); FunctionDataCacheKey key1 = new FunctionDataCacheKey("fooStr", "0x1"); MockCacheAwareReadObject cacheObj1 = new MockCacheAwareReadObject(key1, inputStream1, _functionDataCache); const int inputBytesLength = 2 * 1024 * 1024; byte[] inputBytes = TestUtils.GetRandomBytesInArray(inputBytesLength); Stream inputStream2 = new MemoryStream(inputBytes); inputStream2.Seek(0, SeekOrigin.Begin); FunctionDataCacheKey key2 = new FunctionDataCacheKey("fooBytes", "0x1"); MockCacheAwareReadObject cacheObj2 = new MockCacheAwareReadObject(key2, inputStream2, _functionDataCache); var inputs = new List <(string name, DataType type, object val)> { ("req", DataType.String, httpContext.Request), ("fooStr", DataType.String, cacheObj1), ("fooBytes", DataType.Binary, cacheObj2), }; var invocationContext = new ScriptInvocationContext() { ExecutionContext = new ExecutionContext() { InvocationId = Guid.NewGuid(), FunctionName = "Test", }, BindingData = bindingData, Inputs = inputs, ResultSource = new TaskCompletionSource <ScriptInvocationResult>(), Logger = logger, AsyncExecutionContext = System.Threading.ExecutionContext.Capture() }; var functionMetadata = new FunctionMetadata { Name = "Test" }; var httpTriggerBinding = new BindingMetadata { Name = "req", Type = "httpTrigger", Direction = BindingDirection.In, Raw = new JObject() }; var fooStrInputBinding = new BindingMetadata { Name = "fooStr", Type = "fooStr", Direction = BindingDirection.In }; var fooBytesInputBinding = new BindingMetadata { Name = "fooBytes", Type = "fooBytes", Direction = BindingDirection.In }; var httpOutputBinding = new BindingMetadata { Name = "res", Type = "http", Direction = BindingDirection.Out, Raw = new JObject(), DataType = DataType.String }; functionMetadata.Bindings.Add(httpTriggerBinding); functionMetadata.Bindings.Add(fooStrInputBinding); functionMetadata.Bindings.Add(fooBytesInputBinding); functionMetadata.Bindings.Add(httpOutputBinding); invocationContext.FunctionMetadata = functionMetadata; GrpcCapabilities capabilities = new GrpcCapabilities(logger); var result = await invocationContext.ToRpcInvocationRequest(logger, capabilities, isSharedMemoryDataTransferEnabled : true, _sharedMemoryManager); Assert.Equal(3, result.InputData.Count); Assert.Equal("fooStr", result.InputData[1].Name); Assert.Equal("fooBytes", result.InputData[2].Name); // The input data should be transferred over shared memory RpcSharedMemory sharedMem1 = result.InputData[1].RpcSharedMemory; // This is what the expected byte[] representation of the string should be // We use that to find expected length byte[] contentBytes = Encoding.UTF8.GetBytes(inputString); Assert.Equal(contentBytes.Length, sharedMem1.Count); // Check that the name of the shared memory map is a valid GUID Assert.True(Guid.TryParse(sharedMem1.Name, out _)); // Check the type being sent Assert.Equal(sharedMem1.Type, RpcDataType.String); // The input data should be transferred over shared memory RpcSharedMemory sharedMem2 = result.InputData[2].RpcSharedMemory; Assert.Equal(inputBytes.Length, sharedMem2.Count); // Check that the name of the shared memory map is a valid GUID Assert.True(Guid.TryParse(sharedMem2.Name, out _)); // Check the type being sent Assert.Equal(sharedMem2.Type, RpcDataType.Bytes); // Check that the inputs were inserted into shared memory object inputStringReadObj = await _sharedMemoryManager.GetObjectAsync(sharedMem1.Name, 0, (int)sharedMem1.Count, typeof(string)); Assert.NotNull(inputStringReadObj); string inputStringRead = inputStringReadObj as string; Assert.Equal(inputString, inputStringRead); object inputBytesReadObj = await _sharedMemoryManager.GetObjectAsync(sharedMem2.Name, 0, (int)sharedMem2.Count, typeof(byte[])); Assert.NotNull(inputBytesReadObj); byte[] inputBytesRead = inputBytesReadObj as byte[]; Assert.Equal(inputBytes, inputBytesRead); // Check that the inputs were not marked to be removed after the invocation Assert.Empty(_sharedMemoryManager.InvocationSharedMemoryMaps); // Check that the inputs were inserted into the cache Assert.True(_functionDataCache.TryGet(key1, isIncrementActiveReference: false, out _)); Assert.True(_functionDataCache.TryGet(key2, isIncrementActiveReference: false, out _)); }