public static async Task <bool> ReceiveData(RemoteJSRuntime runtime, long streamId, long chunkId, byte[] chunk, string error)
    {
        if (!runtime.RemoteJSDataStreamInstances.TryGetValue(streamId, out var instance))
        {
            // There is no data stream with the given identifier. It may have already been disposed.
            // We notify JS that the stream has been cancelled/disposed.
            return(false);
        }

        return(await instance.ReceiveData(chunkId, chunk, error));
    }
Example #2
0
    /// <summary>
    /// Creates a new <see cref="RemoteRenderer"/>.
    /// </summary>
    public RemoteRenderer(
        IServiceProvider serviceProvider,
        ILoggerFactory loggerFactory,
        CircuitOptions options,
        CircuitClientProxy client,
        ILogger logger,
        RemoteJSRuntime jsRuntime,
        CircuitJSComponentInterop jsComponentInterop)
        : base(serviceProvider, loggerFactory, jsRuntime.ReadJsonSerializerOptions(), jsComponentInterop)
    {
        _client  = client;
        _options = options;
        _logger  = logger;

        ElementReferenceContext = jsRuntime.ElementReferenceContext;
    }
Example #3
0
    public CircuitHost(
        CircuitId circuitId,
        AsyncServiceScope scope,
        CircuitOptions options,
        CircuitClientProxy client,
        RemoteRenderer renderer,
        IReadOnlyList <ComponentDescriptor> descriptors,
        RemoteJSRuntime jsRuntime,
        RemoteNavigationManager navigationManager,
        CircuitHandler[] circuitHandlers,
        ILogger logger)
    {
        CircuitId = circuitId;
        if (CircuitId.Secret is null)
        {
            // Prevent the use of a 'default' secret.
            throw new ArgumentException($"Property '{nameof(CircuitId.Secret)}' cannot be null.", nameof(circuitId));
        }

        _scope             = scope;
        _options           = options ?? throw new ArgumentNullException(nameof(options));
        Client             = client ?? throw new ArgumentNullException(nameof(client));
        Renderer           = renderer ?? throw new ArgumentNullException(nameof(renderer));
        Descriptors        = descriptors ?? throw new ArgumentNullException(nameof(descriptors));
        JSRuntime          = jsRuntime ?? throw new ArgumentNullException(nameof(jsRuntime));
        _navigationManager = navigationManager ?? throw new ArgumentNullException(nameof(navigationManager));
        _circuitHandlers   = circuitHandlers ?? throw new ArgumentNullException(nameof(circuitHandlers));
        _logger            = logger ?? throw new ArgumentNullException(nameof(logger));

        Services = scope.ServiceProvider;

        Circuit = new Circuit(this);
        Handle  = new CircuitHandle()
        {
            CircuitHost = this,
        };

        // An unhandled exception from the renderer is always fatal because it came from user code.
        Renderer.UnhandledException += ReportAndInvoke_UnhandledException;
        Renderer.UnhandledSynchronizationException += SynchronizationContext_UnhandledException;

        JSRuntime.UnhandledException += ReportAndInvoke_UnhandledException;

        _navigationManager.UnhandledException += ReportAndInvoke_UnhandledException;
    }
Example #4
0
    public static CircuitHost Create(
        CircuitId?circuitId            = null,
        AsyncServiceScope?serviceScope = null,
        RemoteRenderer remoteRenderer  = null,
        IReadOnlyList <ComponentDescriptor> descriptors = null,
        CircuitHandler[] handlers      = null,
        CircuitClientProxy clientProxy = null)
    {
        serviceScope = serviceScope ?? new AsyncServiceScope(Mock.Of <IServiceScope>());
        clientProxy  = clientProxy ?? new CircuitClientProxy(Mock.Of <IClientProxy>(), Guid.NewGuid().ToString());
        var jsRuntime         = new RemoteJSRuntime(Options.Create(new CircuitOptions()), Options.Create(new HubOptions <ComponentHub>()), Mock.Of <ILogger <RemoteJSRuntime> >());
        var navigationManager = new RemoteNavigationManager(Mock.Of <ILogger <RemoteNavigationManager> >());
        var serviceProvider   = new Mock <IServiceProvider>();

        serviceProvider
        .Setup(services => services.GetService(typeof(IJSRuntime)))
        .Returns(jsRuntime);

        if (remoteRenderer == null)
        {
            remoteRenderer = new RemoteRenderer(
                serviceProvider.Object,
                NullLoggerFactory.Instance,
                new CircuitOptions(),
                clientProxy,
                NullLogger.Instance,
                jsRuntime,
                new CircuitJSComponentInterop(new CircuitOptions()));
        }

        handlers ??= Array.Empty <CircuitHandler>();
        return(new TestCircuitHost(
                   circuitId is null ? new CircuitId(Guid.NewGuid().ToString(), Guid.NewGuid().ToString()) : circuitId.Value,
                   serviceScope.Value,
                   new CircuitOptions(),
                   clientProxy,
                   remoteRenderer,
                   descriptors ?? new List <ComponentDescriptor>(),
                   jsRuntime,
                   navigationManager,
                   handlers,
                   NullLogger <CircuitHost> .Instance));
    }
    public static async ValueTask <RemoteJSDataStream> CreateRemoteJSDataStreamAsync(
        RemoteJSRuntime runtime,
        IJSStreamReference jsStreamReference,
        long totalLength,
        long signalRMaximumIncomingBytes,
        TimeSpan jsInteropDefaultCallTimeout,
        CancellationToken cancellationToken = default)
    {
        // Enforce minimum 1 kb, maximum 50 kb, SignalR message size.
        // We budget 512 bytes overhead for the transfer, thus leaving at least 512 bytes for data
        // transfer per chunk with a 1 kb message size.
        // Additionally, to maintain interactivity, we put an upper limit of 50 kb on the message size.
        var chunkSize = signalRMaximumIncomingBytes > 1024 ?
                        (int)Math.Min(signalRMaximumIncomingBytes, 50 * 1024) - 512 :
                        throw new ArgumentException($"SignalR MaximumIncomingBytes must be at least 1 kb.");

        var streamId           = runtime.RemoteJSDataStreamNextInstanceId++;
        var remoteJSDataStream = new RemoteJSDataStream(runtime, streamId, totalLength, chunkSize, jsInteropDefaultCallTimeout, cancellationToken);
        await runtime.InvokeVoidAsync("Blazor._internal.sendJSDataStream", jsStreamReference, streamId, chunkSize);

        return(remoteJSDataStream);
    }
    private RemoteJSDataStream(
        RemoteJSRuntime runtime,
        long streamId,
        long totalLength,
        int chunkSize,
        TimeSpan jsInteropDefaultCallTimeout,
        CancellationToken cancellationToken)
    {
        _runtime     = runtime;
        _streamId    = streamId;
        _totalLength = totalLength;
        _chunkSize   = chunkSize;
        _jsInteropDefaultCallTimeout = jsInteropDefaultCallTimeout;
        _streamCancellationToken     = cancellationToken;

        _lastDataReceivedTime = DateTimeOffset.UtcNow;
        _ = ThrowOnTimeout();

        _runtime.RemoteJSDataStreamInstances.Add(_streamId, this);

        _pipe             = new Pipe();
        _pipeReaderStream = _pipe.Reader.AsStream();
        PipeReader        = _pipe.Reader;
    }
Example #7
0
 private TestCircuitHost(CircuitId circuitId, AsyncServiceScope scope, CircuitOptions options, CircuitClientProxy client, RemoteRenderer renderer, IReadOnlyList <ComponentDescriptor> descriptors, RemoteJSRuntime jsRuntime, RemoteNavigationManager navigationManager, CircuitHandler[] circuitHandlers, ILogger logger)
     : base(circuitId, scope, options, client, renderer, descriptors, jsRuntime, navigationManager, circuitHandlers, logger)
 {
 }