internal GrpcWorkerChannel(
            string workerId,
            IScriptEventManager eventManager,
            RpcWorkerConfig workerConfig,
            IWorkerProcess rpcWorkerProcess,
            ILogger logger,
            IMetricsLogger metricsLogger,
            int attemptCount,
            IEnvironment environment,
            IOptionsMonitor <ScriptApplicationHostOptions> applicationHostOptions,
            ISharedMemoryManager sharedMemoryManager,
            IFunctionDataCache functionDataCache,
            IOptions <WorkerConcurrencyOptions> workerConcurrencyOptions)
        {
            _workerId                 = workerId;
            _eventManager             = eventManager;
            _workerConfig             = workerConfig;
            _runtime                  = workerConfig.Description.Language;
            _rpcWorkerProcess         = rpcWorkerProcess;
            _workerChannelLogger      = logger;
            _metricsLogger            = metricsLogger;
            _environment              = environment;
            _applicationHostOptions   = applicationHostOptions;
            _sharedMemoryManager      = sharedMemoryManager;
            _workerConcurrencyOptions = workerConcurrencyOptions;

            _workerCapabilities = new GrpcCapabilities(_workerChannelLogger);

            _inboundWorkerEvents = _eventManager.OfType <InboundGrpcEvent>()
                                   .Where(msg => msg.WorkerId == _workerId);

            _eventSubscriptions.Add(_inboundWorkerEvents
                                    .Where(msg => msg.IsMessageOfType(MsgType.RpcLog) && !msg.IsLogOfCategory(RpcLogCategory.System))
                                    .Subscribe(Log));

            _eventSubscriptions.Add(_inboundWorkerEvents
                                    .Where(msg => msg.IsMessageOfType(MsgType.RpcLog) && msg.IsLogOfCategory(RpcLogCategory.System))
                                    .Subscribe(SystemLog));

            _eventSubscriptions.Add(_eventManager.OfType <FileEvent>()
                                    .Where(msg => _workerConfig.Description.Extensions.Contains(Path.GetExtension(msg.FileChangeArguments.FullPath)))
                                    .Throttle(TimeSpan.FromMilliseconds(300)) // debounce
                                    .Subscribe(msg => _eventManager.Publish(new HostRestartEvent())));

            _eventSubscriptions.Add(_inboundWorkerEvents.Where(msg => msg.MessageType == MsgType.InvocationResponse)
                                    .Subscribe(async(msg) => await InvokeResponse(msg.Message.InvocationResponse)));

            _inboundWorkerEvents.Where(msg => msg.MessageType == MsgType.WorkerStatusResponse)
            .Subscribe((msg) => ReceiveWorkerStatusResponse(msg.Message.RequestId, msg.Message.WorkerStatusResponse));

            _startLatencyMetric = metricsLogger?.LatencyEvent(string.Format(MetricEventNames.WorkerInitializeLatency, workerConfig.Description.Language, attemptCount));

            _state = RpcWorkerChannelState.Default;
        }
        private static async Task PopulateBody(HttpRequest request, RpcHttp http, GrpcCapabilities capabilities, ILogger logger)
        {
            object body = null;

            if (request.IsMediaTypeOctetOrMultipart() || IsRawBodyBytesRequested(capabilities))
            {
                body = await request.GetRequestBodyAsBytesAsync();
            }
            else
            {
                body = await request.ReadAsStringAsync();
            }
            http.Body = await body.ToRpc(logger, capabilities);
        }
        public static async Task <TypedData> ToRpc(this object value, ILogger logger, GrpcCapabilities capabilities)
        {
            TypedData typedData = new TypedData();

            if (value == null)
            {
                return(typedData);
            }

            if (value is byte[] arr)
            {
                typedData.Bytes = ByteString.CopyFrom(arr);
            }
            else if (value is JObject jobj)
            {
                typedData.Json = jobj.ToString(Formatting.None);
            }
            else if (value is string str)
            {
                typedData.String = str;
            }
            else if (value.GetType().IsArray&& IsTypedDataCollectionSupported(capabilities))
            {
                typedData = value.ToRpcCollection();
            }
            else if (value is HttpRequest request)
            {
                typedData = await request.ToRpcHttp(logger, capabilities);
            }
            else
            {
                typedData = value.ToRpcDefault();
            }

            return(typedData);
        }
 private static bool ShouldUseNullableValueDictionary(GrpcCapabilities capabilities)
 {
     return(!string.IsNullOrEmpty(capabilities.GetCapabilityState(RpcWorkerConstants.UseNullableValueDictionaryForHttp)));
 }
 private static bool ShouldIgnoreEmptyHeaderValues(GrpcCapabilities capabilities)
 {
     return(!string.IsNullOrEmpty(capabilities.GetCapabilityState(RpcWorkerConstants.IgnoreEmptyValuedRpcHttpHeaders)));
 }
 private static bool IsTypedDataCollectionSupported(GrpcCapabilities capabilities)
 {
     return(!string.IsNullOrEmpty(capabilities.GetCapabilityState(RpcWorkerConstants.TypedDataCollection)));
 }
 private static bool IsBodyOnlySupported(GrpcCapabilities capabilities)
 {
     return(!string.IsNullOrEmpty(capabilities.GetCapabilityState(RpcWorkerConstants.RpcHttpBodyOnly)));
 }
 private static bool IsRawBodyBytesRequested(GrpcCapabilities capabilities)
 {
     return(!string.IsNullOrEmpty(capabilities.GetCapabilityState(RpcWorkerConstants.RawHttpBodyBytes)));
 }
        private static async Task PopulateBodyAndRawBody(HttpRequest request, RpcHttp http, GrpcCapabilities capabilities, ILogger logger)
        {
            object body          = null;
            string rawBodyString = null;

            if (MediaTypeHeaderValue.TryParse(request.ContentType, out MediaTypeHeaderValue mediaType))
            {
                if (string.Equals(mediaType.MediaType, "application/json", StringComparison.OrdinalIgnoreCase))
                {
                    rawBodyString = await request.ReadAsStringAsync();

                    try
                    {
                        // REVIEW: We are json deserializing this to a JObject only to serialze
                        // it back to string below. Why?
                        body = JsonConvert.DeserializeObject(rawBodyString);
                    }
                    catch (JsonException)
                    {
                        body = rawBodyString;
                    }
                }
                else if (request.IsMediaTypeOctetOrMultipart())
                {
                    byte[] bytes = await request.GetRequestBodyAsBytesAsync();

                    body = bytes;
                    if (!IsRawBodyBytesRequested(capabilities))
                    {
                        rawBodyString = Encoding.UTF8.GetString(bytes);
                    }
                }
            }

            // default if content-tye not found or recognized
            if (body == null && rawBodyString == null)
            {
                body = rawBodyString = await request.ReadAsStringAsync();
            }

            http.Body = await body.ToRpc(logger, capabilities);

            if (IsRawBodyBytesRequested(capabilities))
            {
                byte[] bytes = await request.GetRequestBodyAsBytesAsync();

                http.RawBody = await bytes.ToRpc(logger, capabilities);
            }
            else
            {
                http.RawBody = await rawBodyString.ToRpc(logger, capabilities);
            }
        }
        internal static async Task <TypedData> ToRpcHttp(this HttpRequest request, ILogger logger, GrpcCapabilities capabilities)
        {
            var http = new RpcHttp()
            {
                Url     = $"{(request.IsHttps ? "https" : "http")}://{request.Host}{request.Path}{request.QueryString}",
                Method  = request.Method.ToString(),
                RawBody = null
            };
            var typedData = new TypedData
            {
                Http = http
            };

            foreach (var pair in request.Query)
            {
                var value = pair.Value.ToString();
                if (ShouldUseNullableValueDictionary(capabilities))
                {
                    http.NullableQuery.Add(pair.Key, new NullableString {
                        Value = value
                    });
                }
                else
                {
                    if (!string.IsNullOrEmpty(value))
                    {
                        http.Query.Add(pair.Key, value);
                    }
                }
            }

            foreach (var pair in request.Headers)
            {
                if (ShouldUseNullableValueDictionary(capabilities))
                {
                    http.NullableHeaders.Add(pair.Key.ToLowerInvariant(), new NullableString {
                        Value = pair.Value.ToString()
                    });
                }
                else
                {
                    if (ShouldIgnoreEmptyHeaderValues(capabilities) && string.IsNullOrEmpty(pair.Value.ToString()))
                    {
                        continue;
                    }

                    http.Headers.Add(pair.Key.ToLowerInvariant(), pair.Value.ToString());
                }
            }

            if (request.HttpContext.Items.TryGetValue(HttpExtensionConstants.AzureWebJobsHttpRouteDataKey, out object routeData))
            {
                Dictionary <string, object> parameters = (Dictionary <string, object>)routeData;
                foreach (var pair in parameters)
                {
                    if (pair.Value != null)
                    {
                        if (ShouldUseNullableValueDictionary(capabilities))
                        {
                            http.NullableParams.Add(pair.Key, new NullableString {
                                Value = pair.Value.ToString()
                            });
                        }
                        else
                        {
                            http.Params.Add(pair.Key, pair.Value.ToString());
                        }
                    }
                }
            }

            // parse ClaimsPrincipal if exists
            if (request.HttpContext?.User?.Identities != null)
            {
                logger.LogTrace("HttpContext has ClaimsPrincipal; parsing to gRPC.");
                foreach (var id in request.HttpContext.User.Identities)
                {
                    var rpcClaimsIdentity = new RpcClaimsIdentity();
                    if (id.AuthenticationType != null)
                    {
                        rpcClaimsIdentity.AuthenticationType = new NullableString {
                            Value = id.AuthenticationType
                        };
                    }

                    if (id.NameClaimType != null)
                    {
                        rpcClaimsIdentity.NameClaimType = new NullableString {
                            Value = id.NameClaimType
                        };
                    }

                    if (id.RoleClaimType != null)
                    {
                        rpcClaimsIdentity.RoleClaimType = new NullableString {
                            Value = id.RoleClaimType
                        };
                    }

                    foreach (var claim in id.Claims)
                    {
                        if (claim.Type != null && claim.Value != null)
                        {
                            rpcClaimsIdentity.Claims.Add(new RpcClaim {
                                Value = claim.Value, Type = claim.Type
                            });
                        }
                    }

                    http.Identities.Add(rpcClaimsIdentity);
                }
            }

            // parse request body as content-type
            if (request.Body != null && request.ContentLength > 0)
            {
                if (IsBodyOnlySupported(capabilities))
                {
                    await PopulateBody(request, http, capabilities, logger);
                }
                else
                {
                    await PopulateBodyAndRawBody(request, http, capabilities, logger);
                }
            }

            return(typedData);
        }
示例#11
0
        public static async Task <InvocationRequest> ToRpcInvocationRequest(this ScriptInvocationContext context, ILogger logger, GrpcCapabilities capabilities, bool isSharedMemoryDataTransferEnabled, ISharedMemoryManager sharedMemoryManager)
        {
            bool excludeHttpTriggerMetadata = !string.IsNullOrEmpty(capabilities.GetCapabilityState(RpcWorkerConstants.RpcHttpTriggerMetadataRemoved));

            var invocationRequest = new InvocationRequest
            {
                FunctionId   = context.FunctionMetadata.GetFunctionId(),
                InvocationId = context.ExecutionContext.InvocationId.ToString(),
                TraceContext = GetRpcTraceContext(context.Traceparent, context.Tracestate, context.Attributes, logger),
            };

            SetRetryContext(context, invocationRequest);

            var rpcValueCache = new Dictionary <object, TypedData>();
            Dictionary <object, RpcSharedMemory> sharedMemValueCache = null;
            StringBuilder logBuilder       = null;
            bool          usedSharedMemory = false;

            if (isSharedMemoryDataTransferEnabled)
            {
                sharedMemValueCache = new Dictionary <object, RpcSharedMemory>();
                logBuilder          = new StringBuilder();
            }

            foreach (var input in context.Inputs)
            {
                RpcSharedMemory  sharedMemValue   = null;
                ParameterBinding parameterBinding = null;
                if (isSharedMemoryDataTransferEnabled)
                {
                    // Try to transfer this data over shared memory instead of RPC
                    if (input.val == null || !sharedMemValueCache.TryGetValue(input.val, out sharedMemValue))
                    {
                        sharedMemValue = await input.val.ToRpcSharedMemoryAsync(logger, invocationRequest.InvocationId, sharedMemoryManager);

                        if (input.val != null)
                        {
                            sharedMemValueCache.Add(input.val, sharedMemValue);
                        }
                    }
                }

                if (sharedMemValue != null)
                {
                    // Data was successfully transferred over shared memory; create a ParameterBinding accordingly
                    parameterBinding = new ParameterBinding
                    {
                        Name            = input.name,
                        RpcSharedMemory = sharedMemValue
                    };

                    usedSharedMemory = true;
                    logBuilder.AppendFormat("{0}:{1},", input.name, sharedMemValue.Count);
                }
                else
                {
                    // Data was not transferred over shared memory (either disabled, type not supported or some error); resort to RPC
                    TypedData rpcValue = null;
                    if (input.val == null || !rpcValueCache.TryGetValue(input.val, out rpcValue))
                    {
                        rpcValue = await input.val.ToRpc(logger, capabilities);

                        if (input.val != null)
                        {
                            rpcValueCache.Add(input.val, rpcValue);
                        }
                    }

                    parameterBinding = new ParameterBinding
                    {
                        Name = input.name,
                        Data = rpcValue
                    };
                }

                invocationRequest.InputData.Add(parameterBinding);
            }

            foreach (var pair in context.BindingData)
            {
                if (ShouldSkipBindingData(pair, context, excludeHttpTriggerMetadata))
                {
                    continue;
                }

                if (!rpcValueCache.TryGetValue(pair.Value, out TypedData rpcValue))
                {
                    rpcValue = await pair.Value.ToRpc(logger, capabilities);

                    rpcValueCache.Add(pair.Value, rpcValue);
                }

                invocationRequest.TriggerMetadata.Add(pair.Key, rpcValue);
            }

            if (usedSharedMemory)
            {
                logger.LogDebug("Shared memory usage for request of invocation Id: {Id} is {SharedMemoryUsage}", invocationRequest.InvocationId, logBuilder.ToString());
            }

            return(invocationRequest);
        }
示例#12
0
        public static async Task <InvocationRequest> ToRpcInvocationRequest(this ScriptInvocationContext context, ILogger logger, GrpcCapabilities capabilities)
        {
            bool excludeHttpTriggerMetadata = !string.IsNullOrEmpty(capabilities.GetCapabilityState(RpcWorkerConstants.RpcHttpTriggerMetadataRemoved));

            var invocationRequest = new InvocationRequest
            {
                FunctionId   = context.FunctionMetadata.GetFunctionId(),
                InvocationId = context.ExecutionContext.InvocationId.ToString(),
                TraceContext = GetRpcTraceContext(context.Traceparent, context.Tracestate, context.Attributes, logger),
            };

            var rpcValueCache = new Dictionary <object, TypedData>();

            foreach (var input in context.Inputs)
            {
                TypedData rpcValue = null;
                if (input.val == null || !rpcValueCache.TryGetValue(input.val, out rpcValue))
                {
                    rpcValue = await input.val.ToRpc(logger, capabilities);

                    if (input.val != null)
                    {
                        rpcValueCache.Add(input.val, rpcValue);
                    }
                }

                var parameterBinding = new ParameterBinding
                {
                    Name = input.name,
                    Data = rpcValue
                };
                invocationRequest.InputData.Add(parameterBinding);
            }

            foreach (var pair in context.BindingData)
            {
                if (ShouldSkipBindingData(pair, context, excludeHttpTriggerMetadata))
                {
                    continue;
                }

                if (!rpcValueCache.TryGetValue(pair.Value, out TypedData rpcValue))
                {
                    rpcValue = await pair.Value.ToRpc(logger, capabilities);

                    rpcValueCache.Add(pair.Value, rpcValue);
                }

                invocationRequest.TriggerMetadata.Add(pair.Key, rpcValue);
            }

            return(invocationRequest);
        }