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); }
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); }
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); }