// 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);
            }));
        }
Exemple #2
0
        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);
            }
        }