예제 #1
0
        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);
            }
        }
예제 #3
0
        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);
        }
예제 #4
0
        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);
        }
예제 #5
0
        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"));
        }
예제 #6
0
        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 _));
        }