public static void SendingMessage(ILogger logger, HubMessage message) { if (logger.IsEnabled(LogLevel.Debug)) { if (message is HubInvocationMessage invocationMessage) { SendingMessage(logger, message.GetType().Name, invocationMessage.InvocationId); } else { SendingMessageGeneric(logger, message.GetType().Name); } } }
public static void MessageSent(ILogger logger, HubMessage message) { if (logger.IsEnabled(LogLevel.Debug)) { if (message is HubInvocationMessage invocationMessage) { _messageSent(logger, message.GetType().Name, invocationMessage.InvocationId, null); } else { _messageSentGeneric(logger, message.GetType().Name, null); } } }
private void WriteMessageCore(HubMessage message, Stream stream) { using (var writer = new JsonTextWriter(new StreamWriter(stream))) { switch (message) { case InvocationMessage m: WriteInvocationMessage(m, writer); break; case StreamInvocationMessage m: WriteStreamInvocationMessage(m, writer); break; case StreamItemMessage m: WriteStreamItemMessage(m, writer); break; case CompletionMessage m: WriteCompletionMessage(m, writer); break; case CancelInvocationMessage m: WriteCancelInvocationMessage(m, writer); break; case PingMessage m: WritePingMessage(m, writer); break; default: throw new InvalidOperationException($"Unsupported message type: {message.GetType().FullName}"); } } }
public static void AssertHubMessage(HubMessage expected, HubMessage actual) { // We aren't testing InvocationIds here switch (expected) { case CompletionMessage expectedCompletion: var actualCompletion = Assert.IsType <CompletionMessage>(actual); Assert.Equal(expectedCompletion.Error, actualCompletion.Error); Assert.Equal(expectedCompletion.HasResult, actualCompletion.HasResult); Assert.Equal(expectedCompletion.Result, actualCompletion.Result); break; case StreamItemMessage expectedStreamItem: var actualStreamItem = Assert.IsType <StreamItemMessage>(actual); Assert.Equal(expectedStreamItem.Item, actualStreamItem.Item); break; case InvocationMessage expectedInvocation: var actualInvocation = Assert.IsType <InvocationMessage>(actual); // Either both must have non-null invocationIds or both must have null invocation IDs. Checking the exact value is NOT desired here though as it could be randomly generated Assert.True((expectedInvocation.InvocationId == null && actualInvocation.InvocationId == null) || (expectedInvocation.InvocationId != null && actualInvocation.InvocationId != null)); Assert.Equal(expectedInvocation.Target, actualInvocation.Target); Assert.Equal(expectedInvocation.Arguments, actualInvocation.Arguments); break; default: throw new InvalidOperationException($"Unsupported Hub Message type {expected.GetType()}"); } }
private async Task <(bool close, Exception exception)> ProcessMessagesAsync(HubMessage message, ConnectionState connectionState) { InvocationRequest irq; switch (message) { case InvocationMessage invocation: Log.ReceivedInvocation(_logger, invocation.InvocationId, invocation.Target, invocation.ArgumentBindingException != null ? null : invocation.Arguments); await DispatchInvocationAsync(invocation); break; case CompletionMessage completion: if (!connectionState.TryRemoveInvocation(completion.InvocationId, out irq)) { Log.DroppedCompletionMessage(_logger, completion.InvocationId); } else { DispatchInvocationCompletion(completion, irq); irq.Dispose(); } break; case StreamItemMessage streamItem: // Complete the invocation with an error, we don't support streaming (yet) if (!connectionState.TryGetInvocation(streamItem.InvocationId, out irq)) { Log.DroppedStreamMessage(_logger, streamItem.InvocationId); return(close : false, exception : null); } await DispatchInvocationStreamItemAsync(streamItem, irq); break; case CloseMessage close: if (string.IsNullOrEmpty(close.Error)) { Log.ReceivedClose(_logger); return(close : true, exception : null); } else { Log.ReceivedCloseWithError(_logger, close.Error); return(close : true, exception : new HubException($"The server closed the connection with the following error: {close.Error}")); } case PingMessage _: Log.ReceivedPing(_logger); // Nothing to do on receipt of a ping. break; default: throw new InvalidOperationException($"Unexpected message type: {message.GetType().FullName}"); } return(close : false, exception : null); }
public override Task DispatchMessageAsync(HubConnectionContext connection, HubMessage hubMessage) { // Messages are dispatched sequentially and will stop other messages from being processed until they complete. // Streaming methods will run sequentially until they start streaming, then they will fire-and-forget allowing other messages to run. switch (hubMessage) { case InvocationBindingFailureMessage bindingFailureMessage: return(ProcessInvocationBindingFailure(connection, bindingFailureMessage)); case StreamBindingFailureMessage bindingFailureMessage: return(ProcessStreamBindingFailure(connection, bindingFailureMessage)); case InvocationMessage invocationMessage: Log.ReceivedHubInvocation(_logger, invocationMessage); return(ProcessInvocation(connection, invocationMessage, isStreamResponse: false)); case StreamInvocationMessage streamInvocationMessage: Log.ReceivedStreamHubInvocation(_logger, streamInvocationMessage); return(ProcessInvocation(connection, streamInvocationMessage, isStreamResponse: true)); case CancelInvocationMessage cancelInvocationMessage: // Check if there is an associated active stream and cancel it if it exists. // The cts will be removed when the streaming method completes executing if (connection.ActiveRequestCancellationSources.TryGetValue(cancelInvocationMessage.InvocationId, out var cts)) { Log.CancelStream(_logger, cancelInvocationMessage.InvocationId); cts.Cancel(); } else { // Stream can be canceled on the server while client is canceling stream. Log.UnexpectedCancel(_logger); } break; case PingMessage _: connection.StartClientTimeout(); break; case StreamDataMessage streamItem: Log.ReceivedStreamItem(_logger, streamItem); return(ProcessStreamItem(connection, streamItem)); case StreamCompleteMessage streamCompleteMessage: // closes channels, removes from Lookup dict // user's method can see the channel is complete and begin wrapping up Log.CompletingStream(_logger, streamCompleteMessage); connection.StreamTracker.Complete(streamCompleteMessage); break; // Other kind of message we weren't expecting default: Log.UnsupportedMessageReceived(_logger, hubMessage.GetType().FullName); throw new NotSupportedException($"Received unsupported message: {hubMessage}"); } return(Task.CompletedTask); }
private void ProcessHubMessage(HubConnectionContext connection, HubMessage hubMessage) { switch (hubMessage) { case InvocationMessage invocationMessage: _logger.ReceivedHubInvocation(invocationMessage); // Don't wait on the result of execution, continue processing other // incoming messages on this connection. _ = _hubInvoker.OnInvocationAsync(connection, invocationMessage, isStreamedInvocation: false); break; case StreamInvocationMessage streamInvocationMessage: _logger.ReceivedStreamHubInvocation(streamInvocationMessage); // Don't wait on the result of execution, continue processing other // incoming messages on this connection. _ = _hubInvoker.OnInvocationAsync(connection, streamInvocationMessage, isStreamedInvocation: true); break; case CancelInvocationMessage cancelInvocationMessage: // Check if there is an associated active stream and cancel it if it exists. // The cts will be removed when the streaming method completes executing if (connection.ActiveRequestCancellationSources.TryGetValue(cancelInvocationMessage.InvocationId, out var cts)) { _logger.CancelStream(cancelInvocationMessage.InvocationId); cts.Cancel(); } else { // Stream can be canceled on the server while client is canceling stream. _logger.UnexpectedCancel(); } break; case CompletionMessage completionMessage: _logger.ReceivedCompletion(completionMessage); _ = _hubInvoker.OnCompletionAsync(connection, completionMessage); break; case PingMessage _: // We don't care about pings break; // Other kind of message we weren't expecting default: _logger.UnsupportedMessageReceived(hubMessage.GetType().FullName); throw new NotSupportedException($"Received unsupported message: {hubMessage}"); } }
public override async Task DispatchMessageAsync(HubConnectionContext connection, HubMessage hubMessage) { if (!(connection is CloudHubConnectionContext cloudConnection)) { _logger.LogError("Invalid type. CloudHubConnectionContext expected."); return; } switch (hubMessage) { case InvocationMessage invocationMessage: _logger.LogDebug($"Received invocation: {invocationMessage}"); await ProcessInvocation(cloudConnection, invocationMessage, isStreamedInvocation : false); break; case StreamInvocationMessage streamInvocationMessage: _logger.LogDebug($"Received stream invocation: {streamInvocationMessage}"); await ProcessInvocation(cloudConnection, streamInvocationMessage, isStreamedInvocation : true); break; case CompletionMessage completionMessage: break; case CancelInvocationMessage cancelInvocationMessage: // Check if there is an associated active stream and cancel it if it exists. // The cts will be removed when the streaming method completes executing if (cloudConnection.ActiveRequestCancellationSources.TryGetValue(cancelInvocationMessage.InvocationId, out var cts)) { _logger.LogDebug($"Cancel stream invocation: {cancelInvocationMessage.InvocationId}"); cts.Cancel(); } else { _logger.LogWarning("Unexpected stream invocation cancel."); } break; case PingMessage _: // We don't care about pings break; // Other kind of message we weren't expecting default: _logger.LogError($"Received unsupported message type: {hubMessage.GetType().FullName}"); throw new NotSupportedException($"Received unsupported message: {hubMessage}"); } }
public override async Task DispatchMessageAsync(HubConnectionContext connection, HubMessage hubMessage) { switch (hubMessage) { case InvocationBindingFailureMessage bindingFailureMessage: await ProcessBindingFailure(connection, bindingFailureMessage); break; case InvocationMessage invocationMessage: Log.ReceivedHubInvocation(_logger, invocationMessage); await ProcessInvocation(connection, invocationMessage, isStreamedInvocation : false); break; case StreamInvocationMessage streamInvocationMessage: Log.ReceivedStreamHubInvocation(_logger, streamInvocationMessage); await ProcessInvocation(connection, streamInvocationMessage, isStreamedInvocation : true); break; case CancelInvocationMessage cancelInvocationMessage: // Check if there is an associated active stream and cancel it if it exists. // The cts will be removed when the streaming method completes executing if (connection.ActiveRequestCancellationSources.TryGetValue(cancelInvocationMessage.InvocationId, out var cts)) { Log.CancelStream(_logger, cancelInvocationMessage.InvocationId); cts.Cancel(); } else { // Stream can be canceled on the server while client is canceling stream. Log.UnexpectedCancel(_logger); } break; case PingMessage _: // We don't care about pings break; // Other kind of message we weren't expecting default: Log.UnsupportedMessageReceived(_logger, hubMessage.GetType().FullName); throw new NotSupportedException($"Received unsupported message: {hubMessage}"); } }
private void WriteMessageCore(HubMessage message, IBufferWriter <byte> bufferWriter) { var writer = new MessagePackWriter(bufferWriter); switch (message) { case InvocationMessage invocationMessage: WriteInvocationMessage(invocationMessage, ref writer); break; case StreamInvocationMessage streamInvocationMessage: WriteStreamInvocationMessage(streamInvocationMessage, ref writer); break; case StreamItemMessage streamItemMessage: WriteStreamingItemMessage(streamItemMessage, ref writer); break; case CompletionMessage completionMessage: WriteCompletionMessage(completionMessage, ref writer); break; case CancelInvocationMessage cancelInvocationMessage: WriteCancelInvocationMessage(cancelInvocationMessage, ref writer); break; case PingMessage pingMessage: WritePingMessage(pingMessage, ref writer); break; case CloseMessage closeMessage: WriteCloseMessage(closeMessage, ref writer); break; default: throw new InvalidDataException($"Unexpected message type: {message.GetType().Name}"); } writer.Flush(); }
private void WriteMessageCore(HubMessage message, IBufferWriter <byte> output) { switch (message) { case InvocationMessage invocationMessage: WriteInvocationMessage(invocationMessage, output); break; case StreamInvocationMessage streamInvocationMessage: WriteStreamInvocationMessage(streamInvocationMessage, output); break; case StreamItemMessage streamItemMessage: WriteItemMessage(streamItemMessage, output); break; case CompletionMessage completionMessage: WriteCompletionMessage(completionMessage, output); break; case CancelInvocationMessage cancelInvocationMessage: WriteCancelInvocationMessage(cancelInvocationMessage, output); break; case PingMessage pingMessage: WritePingMessage(pingMessage, output); break; case CloseMessage closeMessage: WriteCloseMessage(closeMessage, output); break; default: _logger.LogCritical($"Unexpected message type: {message.GetType().Name}"); break; } }
private void WriteMessageCore(HubMessage message, Stream output) { // PackerCompatibilityOptions.None prevents from serializing byte[] as strings // and allows extended objects var packer = Packer.Create(output, PackerCompatibilityOptions.None); switch (message) { case InvocationMessage invocationMessage: WriteInvocationMessage(invocationMessage, packer); break; case StreamInvocationMessage streamInvocationMessage: WriteStreamInvocationMessage(streamInvocationMessage, packer); break; case StreamItemMessage streamItemMessage: WriteStreamingItemMessage(streamItemMessage, packer); break; case CompletionMessage completionMessage: WriteCompletionMessage(completionMessage, packer); break; case CancelInvocationMessage cancelInvocationMessage: WriteCancelInvocationMessage(cancelInvocationMessage, packer); break; case PingMessage pingMessage: WritePingMessage(pingMessage, packer); break; default: throw new FormatException($"Unexpected message type: {message.GetType().Name}"); } }
private void WriteMessageCore(HubMessage message, IBufferWriter <byte> stream) { var textWriter = Utf8BufferTextWriter.Get(stream); try { using (var writer = JsonUtils.CreateJsonTextWriter(textWriter)) { writer.WriteStartObject(); switch (message) { case InvocationMessage m: WriteMessageType(writer, HubProtocolConstants.InvocationMessageType); WriteHeaders(writer, m); WriteInvocationMessage(m, writer); break; case StreamInvocationMessage m: WriteMessageType(writer, HubProtocolConstants.StreamInvocationMessageType); WriteHeaders(writer, m); WriteStreamInvocationMessage(m, writer); break; case StreamItemMessage m: WriteMessageType(writer, HubProtocolConstants.StreamItemMessageType); WriteHeaders(writer, m); WriteStreamItemMessage(m, writer); break; case CompletionMessage m: WriteMessageType(writer, HubProtocolConstants.CompletionMessageType); WriteHeaders(writer, m); WriteCompletionMessage(m, writer); break; case CancelInvocationMessage m: WriteMessageType(writer, HubProtocolConstants.CancelInvocationMessageType); WriteHeaders(writer, m); WriteCancelInvocationMessage(m, writer); break; case PingMessage _: WriteMessageType(writer, HubProtocolConstants.PingMessageType); break; case CloseMessage m: WriteMessageType(writer, HubProtocolConstants.CloseMessageType); WriteCloseMessage(m, writer); break; default: throw new InvalidOperationException($"Unsupported message type: {message.GetType().FullName}"); } writer.WriteEndObject(); writer.Flush(); } } finally { Utf8BufferTextWriter.Return(textWriter); } }
public override void Complete(HubMessage message) { Debug.Assert(message != null, "message is null"); if (!(message is StreamCompletionMessage streamCompletionMessage)) { Logger.ReceivedUnexpectedMessageTypeForStreamCompletion(InvocationId, message.GetType().Name); // This is not 100% accurate but it is the only case that can be encountered today when running end-to-end // and this is the most useful message to show to the user. Fail(new InvalidOperationException($"Streaming hub methods must be invoked with the '{nameof(HubConnection)}.{nameof(HubConnection.StreamAsync)}' method.")); return; } if (!string.IsNullOrEmpty(streamCompletionMessage.Error)) { Fail(new HubException(streamCompletionMessage.Error)); return; } Logger.InvocationCompleted(InvocationId); _channel.Out.TryComplete(); }
/// <summary> /// 处理消息 /// </summary> /// <param name="hubMessage">转换后的消息对象</param> private void ProcessMessages(HubMessage hubMessage) { if (hubMessage == null) { Log.Debug("为获取到可处理的消息"); throw new ArgumentNullException($"为获取到可处理的消息.{nameof(hubMessage)}"); } switch (hubMessage) { case InvocationMessage invocation: { var handler = new InvocationMessageHandler(_handlers, (r) => { if (!string.IsNullOrEmpty(invocation.InvocationId)) { var completionMessage = new CompletionMessage(invocation.InvocationId, null, r, r != null); SendHubMessage(completionMessage); } }); try { handler.Handler(invocation); } catch (Exception e) { Log.Error(null, e); if (invocation != null && !string.IsNullOrEmpty(invocation.InvocationId)) { SendHubMessage(new CompletionMessage(invocation.InvocationId, e.Message, null, false)); } } } break; case StreamItemMessage streamItem: { Log.Info($"客户端暂不支持 {nameof(StreamItemMessage)}"); if (streamItem != null && !string.IsNullOrEmpty(streamItem.InvocationId)) { SendHubMessage(new CompletionMessage(streamItem.InvocationId, $"客户端暂不支持 {nameof(StreamItemMessage)}", null, false)); } } break; case StreamInvocationMessage streamInvocation: { Log.Info($"客户端暂不支持 {nameof(StreamInvocationMessage)}"); if (streamInvocation != null && !string.IsNullOrEmpty(streamInvocation.InvocationId)) { SendHubMessage(new CompletionMessage(streamInvocation.InvocationId, $"客户端暂不支持 {nameof(StreamInvocationMessage)}", null, false)); } } break; case CancelInvocationMessage cancelInvocation: { Log.Info($"客户端暂不支持 {nameof(CancelInvocationMessage)}"); } break; case CompletionMessage completion: { var handler = new CompletionMessageHandler(_sendedMessageCallBacks); handler.Handler(completion); // 清除当前 InvocationId 对应的回调 if (_sendedMessageCallBacks.ContainsKey(completion.InvocationId)) { lock (_sendedMessageCallBacks) { _sendedMessageCallBacks.Remove(completion.InvocationId); } } } break; case CloseMessage close: { if (string.IsNullOrEmpty(close.Error)) { Log.Debug("服务器将关闭连接,客户端主动断开连接"); } else { Log.Error("服务器将关闭连接,客户端主动断开连接", new Exception(close.Error)); } StopAsync(); } break; case PingMessage _: Log.Debug("接收到服务器端 Ping"); break; default: throw new InvalidOperationException($"未知的消息类型: {hubMessage.GetType().FullName}"); } }
private void WriteMessageCore(HubMessage message, IBufferWriter <byte> stream) { var reusableWriter = ReusableUtf8JsonWriter.Get(stream); try { var writer = reusableWriter.GetJsonWriter(); writer.WriteStartObject(); switch (message) { case InvocationMessage m: WriteMessageType(writer, HubProtocolConstants.InvocationMessageType); WriteHeaders(writer, m); WriteInvocationMessage(m, writer); break; case StreamInvocationMessage m: WriteMessageType(writer, HubProtocolConstants.StreamInvocationMessageType); WriteHeaders(writer, m); WriteStreamInvocationMessage(m, writer); break; case StreamItemMessage m: WriteMessageType(writer, HubProtocolConstants.StreamItemMessageType); WriteHeaders(writer, m); WriteStreamItemMessage(m, writer); break; case CompletionMessage m: WriteMessageType(writer, HubProtocolConstants.CompletionMessageType); WriteHeaders(writer, m); WriteCompletionMessage(m, writer); break; case CancelInvocationMessage m: WriteMessageType(writer, HubProtocolConstants.CancelInvocationMessageType); WriteHeaders(writer, m); WriteCancelInvocationMessage(m, writer); break; case PingMessage _: WriteMessageType(writer, HubProtocolConstants.PingMessageType); break; case CloseMessage m: WriteMessageType(writer, HubProtocolConstants.CloseMessageType); WriteCloseMessage(m, writer); break; default: throw new InvalidOperationException($"Unsupported message type: {message.GetType().FullName}"); } writer.WriteEndObject(); writer.Flush(); Debug.Assert(writer.CurrentDepth == 0); } finally { ReusableUtf8JsonWriter.Return(reusableWriter); } }
public void EnqueueMessage(HubMessage m) => _hubMessagesFromSDK.GetOrAdd(m.GetType(), _ => CreateChannel <HubMessage>()).Writer.TryWrite(m);