// Specify the "builtin binding types". These are types that are directly accesible without needing an explicit load gesture. // This is the set of bindings we shipped prior to binding extensibility. // Map from BindingType to the Assembly Qualified Type name for its IExtensionConfigProvider object. public ScriptHost(IOptions <JobHostOptions> options, IOptions <LanguageWorkerOptions> languageWorkerOptions, IOptions <HttpWorkerOptions> httpWorkerOptions, IEnvironment environment, IJobHostContextFactory jobHostContextFactory, IConfiguration configuration, IDistributedLockManager distributedLockManager, IScriptEventManager eventManager, ILoggerFactory loggerFactory, IFunctionInvocationDispatcherFactory functionDispatcherFactory, IFunctionMetadataManager functionMetadataManager, IFileLoggingStatusManager fileLoggingStatusManager, IProxyMetadataManager proxyMetadataManager, IMetricsLogger metricsLogger, IOptions <ScriptJobHostOptions> scriptHostOptions, ITypeLocator typeLocator, IScriptHostManager scriptHostManager, IDebugStateProvider debugManager, IEnumerable <IScriptBindingProvider> bindingProviders, IPrimaryHostStateProvider primaryHostStateProvider, IJobHostMetadataProvider metadataProvider, IHostIdProvider hostIdProvider, IHttpRoutesManager httpRoutesManager, IApplicationLifetime applicationLifetime, ScriptSettingsManager settingsManager = null) : base(options, jobHostContextFactory) { _environment = environment; _typeLocator = typeLocator as ScriptTypeLocator ?? throw new ArgumentException(nameof(typeLocator), $"A {nameof(ScriptTypeLocator)} instance is required."); _instanceId = Guid.NewGuid().ToString(); _hostOptions = options; _configuration = configuration; _storageConnectionString = configuration.GetWebJobsConnectionString(ConnectionStringNames.Storage); _distributedLockManager = distributedLockManager; _functionMetadataManager = functionMetadataManager; _fileLoggingStatusManager = fileLoggingStatusManager; _applicationLifetime = applicationLifetime; _hostIdProvider = hostIdProvider; _httpRoutesManager = httpRoutesManager; _proxyMetadataManager = proxyMetadataManager; _workerConfigs = languageWorkerOptions.Value.WorkerConfigs; _isHttpWorker = httpWorkerOptions.Value.Description != null; ScriptOptions = scriptHostOptions.Value; _scriptHostManager = scriptHostManager; FunctionErrors = new Dictionary <string, ICollection <string> >(StringComparer.OrdinalIgnoreCase); EventManager = eventManager; _functionDispatcher = functionDispatcherFactory.GetFunctionDispatcher(); _settingsManager = settingsManager ?? ScriptSettingsManager.Instance; _metricsLogger = metricsLogger; _hostLogPath = Path.Combine(ScriptOptions.RootLogPath, "Host"); _workerRuntime = _environment.GetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName); _loggerFactory = loggerFactory; _logger = loggerFactory.CreateLogger(LogCategories.Startup); Logger = _logger; _debugManager = debugManager; _primaryHostStateProvider = primaryHostStateProvider; _bindingProviders = new List <IScriptBindingProvider>(bindingProviders); _metadataProvider = metadataProvider; _eventSubscriptions.Add(EventManager.OfType <FunctionIndexingEvent>() .Subscribe(evt => { HandleHostError(evt.Exception); })); }
public override async Task EventStream(IAsyncStreamReader <StreamingMessage> requestStream, IServerStreamWriter <StreamingMessage> responseStream, ServerCallContext context) { var cancelSource = new TaskCompletionSource <bool>(); IDictionary <string, IDisposable> outboundEventSubscriptions = new Dictionary <string, IDisposable>(); try { context.CancellationToken.Register(() => cancelSource.TrySetResult(false)); Func <Task <bool> > messageAvailable = async() => { // GRPC does not accept cancellation tokens for individual reads, hence wrapper var requestTask = requestStream.MoveNext(CancellationToken.None); var completed = await Task.WhenAny(cancelSource.Task, requestTask); return(completed.Result); }; if (await messageAvailable()) { string workerId = requestStream.Current.StartStream.WorkerId; _logger.LogDebug("Established RPC channel. WorkerId: {workerId}", workerId); outboundEventSubscriptions.Add(workerId, _eventManager.OfType <OutboundEvent>() .Where(evt => evt.WorkerId == workerId) .ObserveOn(NewThreadScheduler.Default) .Subscribe(async evt => { try { if (evt.MessageType == MsgType.InvocationRequest) { _logger.LogTrace("Writing invocation request invocationId: {invocationId} to workerId: {workerId}", evt.Message.InvocationRequest.InvocationId, workerId); } try { // WriteAsync only allows one pending write at a time, so we // serialize access to the stream for each subscription await _writeLock.WaitAsync(); await responseStream.WriteAsync(evt.Message); } finally { _writeLock.Release(); } } catch (Exception subscribeEventEx) { _logger.LogError(subscribeEventEx, "Error writing message type {messageType} to workerId: {workerId}", evt.MessageType, workerId); } })); do { var currentMessage = requestStream.Current; if (currentMessage.InvocationResponse != null && !string.IsNullOrEmpty(currentMessage.InvocationResponse.InvocationId)) { _logger.LogTrace("Received invocation response for invocationId: {invocationId} from workerId: {workerId}", currentMessage.InvocationResponse.InvocationId, workerId); } _eventManager.Publish(new InboundEvent(workerId, currentMessage)); }while (await messageAvailable()); } } catch (Exception rpcException) { // We catch the exception, just to report it, then re-throw it _logger.LogError(rpcException, "Exception encountered while listening to EventStream"); throw; } finally { foreach (var sub in outboundEventSubscriptions) { sub.Value?.Dispose(); } // ensure cancellationSource task completes cancelSource.TrySetResult(false); } }