/// <summary>
        /// Writes the serialized representation of a <see cref="HandshakeResponseMessage"/> to the specified writer.
        /// </summary>
        /// <param name="responseMessage">The message to write.</param>
        /// <param name="output">The output writer.</param>
        public static void WriteResponseMessage(HandshakeResponseMessage responseMessage, IBufferWriter <byte> output)
        {
            var reusableWriter = ReusableUtf8JsonWriter.Get(output);

            try
            {
                var writer = reusableWriter.GetJsonWriter();

                writer.WriteStartObject();
                if (!string.IsNullOrEmpty(responseMessage.Error))
                {
                    writer.WriteString(ErrorPropertyNameBytes, responseMessage.Error);
                }

                writer.WriteEndObject();
                writer.Flush();
                Debug.Assert(writer.CurrentDepth == 0);
            }
            finally
            {
                ReusableUtf8JsonWriter.Return(reusableWriter);
            }

            TextMessageFormatter.WriteRecordSeparator(output);
        }
Example #2
0
        /// <summary>
        /// Writes the serialized representation of a <see cref="HandshakeResponseMessage"/> to the specified writer.
        /// </summary>
        /// <param name="responseMessage">The message to write.</param>
        /// <param name="output">The output writer.</param>
        public static void WriteResponseMessage(HandshakeResponseMessage responseMessage, IBufferWriter <byte> output)
        {
            var textWriter = Utf8BufferTextWriter.Get(output);

            try
            {
                using (var writer = JsonUtils.CreateJsonTextWriter(textWriter))
                {
                    writer.WriteStartObject();
                    if (!string.IsNullOrEmpty(responseMessage.Error))
                    {
                        writer.WritePropertyName(ErrorPropertyName);
                        writer.WriteValue(responseMessage.Error);
                    }

                    writer.WritePropertyName(MinorVersionPropertyName);
                    writer.WriteValue(responseMessage.MinorVersion);

                    writer.WriteEndObject();
                    writer.Flush();
                }
            }
            finally
            {
                Utf8BufferTextWriter.Return(textWriter);
            }

            TextMessageFormatter.WriteRecordSeparator(output);
        }
Example #3
0
        public async Task TestCloseConnectionMessageWithMigrateOut()
        {
            var clientConnectionFactory = new TestClientConnectionFactory();

            var connection = CreateServiceConnection(clientConnectionFactory: clientConnectionFactory, handler: new TestConnectionHandler(3000, "foobar"));

            _ = connection.StartAsync();
            await connection.ConnectionInitializedTask.OrTimeout(1000);

            var openConnectionMessage = new OpenConnectionMessage("foo", Array.Empty <Claim>());

            _ = connection.WriteFromServiceAsync(openConnectionMessage);
            await connection.ClientConnectedTask;

            Assert.Equal(1, clientConnectionFactory.Connections.Count);
            var clientConnection = clientConnectionFactory.Connections[0];

            Assert.False(clientConnection.IsMigrated);

            // write a signalr handshake response
            var message = new SignalRProtocol.HandshakeResponseMessage("");

            SignalRProtocol.HandshakeProtocol.WriteResponseMessage(message, clientConnection.Transport.Output);
            await clientConnection.Transport.Output.FlushAsync();

            // write a close connection message with migration header
            var closeMessage = new CloseConnectionMessage(clientConnection.ConnectionId);

            closeMessage.Headers.Add(Constants.AsrsMigrateTo, "another-server");
            await connection.WriteFromServiceAsync(closeMessage);

            // wait until app task completed.
            await Assert.ThrowsAsync <TimeoutException>(async() => await clientConnection.LifetimeTask.OrTimeout(1000));

            await clientConnection.LifetimeTask.OrTimeout(3000);

            // expect a handshake response message.
            await connection.ExpectSignalRMessage(SignalRProtocol.HandshakeResponseMessage.Empty).OrTimeout(3000);

            // signalr close message should be skipped.
            await Assert.ThrowsAsync <TimeoutException>(async() => await connection.ExpectSignalRMessage(SignalRProtocol.CloseMessage.Empty).OrTimeout(1000));

            var feature = clientConnection.Features.Get <IConnectionMigrationFeature>();

            Assert.NotNull(feature);
            Assert.Equal("another-server", feature.MigrateTo);

            await connection.StopAsync();
        }
        /// <summary>
        /// Creates a new <see cref="HandshakeResponseMessage"/> from the specified serialized representation.
        /// </summary>
        /// <param name="buffer">The serialized representation of the message.</param>
        /// <param name="responseMessage">When this method returns, contains the parsed message.</param>
        /// <returns>A value that is <c>true</c> if the <see cref="HandshakeResponseMessage"/> was successfully parsed; otherwise, <c>false</c>.</returns>
        public static bool TryParseResponseMessage(ref ReadOnlySequence <byte> buffer, out HandshakeResponseMessage responseMessage)
        {
            if (!TextMessageParser.TryParseMessage(ref buffer, out var payload))
            {
                responseMessage = null;
                return(false);
            }

            var reader = new Utf8JsonReader(payload, isFinalBlock: true, state: default);

            reader.CheckRead();
            reader.EnsureObjectStart();

            string error = null;

            while (reader.CheckRead())
            {
                if (reader.TokenType == JsonTokenType.PropertyName)
                {
                    if (reader.ValueTextEquals(TypePropertyNameBytes.EncodedUtf8Bytes))
                    {
                        // a handshake response does not have a type
                        // check the incoming message was not any other type of message
                        throw new InvalidDataException("Expected a handshake response from the server.");
                    }
                    else if (reader.ValueTextEquals(ErrorPropertyNameBytes.EncodedUtf8Bytes))
                    {
                        error = reader.ReadAsString(ErrorPropertyName);
                    }
                    else
                    {
                        reader.Skip();
                    }
                }
                else if (reader.TokenType == JsonTokenType.EndObject)
                {
                    break;
                }
                else
                {
                    throw new InvalidDataException($"Unexpected token '{reader.TokenType}' when reading handshake response JSON.");
                }
            }
            ;

            responseMessage = new HandshakeResponseMessage(error);
            return(true);
        }
        /// <summary>
        /// Writes the serialized representation of a <see cref="HandshakeResponseMessage"/> to the specified writer.
        /// </summary>
        /// <param name="responseMessage">The message to write.</param>
        /// <param name="output">The output writer.</param>
        public static void WriteResponseMessage(HandshakeResponseMessage responseMessage, IBufferWriter <byte> output)
        {
            var writer = new Utf8JsonWriter(output, new JsonWriterState(new JsonWriterOptions()
            {
                SkipValidation = true
            }));

            writer.WriteStartObject();
            if (!string.IsNullOrEmpty(responseMessage.Error))
            {
                writer.WriteString(ErrorPropertyNameBytes, responseMessage.Error);
            }

            writer.WriteNumber(MinorVersionPropertyNameBytes, responseMessage.MinorVersion, escape: false);

            writer.WriteEndObject();
            writer.Flush(isFinalBlock: true);

            TextMessageFormatter.WriteRecordSeparator(output);
        }
        /// <summary>
        /// Writes the serialized representation of a <see cref="HandshakeResponseMessage"/> to the specified writer.
        /// </summary>
        /// <param name="responseMessage">The message to write.</param>
        /// <param name="output">The output writer.</param>
        public static void WriteResponseMessage(HandshakeResponseMessage responseMessage, IBufferWriter <byte> output)
        {
            using var writer = new Utf8JsonWriter(output, new JsonWriterOptions()
            {
                SkipValidation = true
            });

            writer.WriteStartObject();
            if (!string.IsNullOrEmpty(responseMessage.Error))
            {
                writer.WriteString(ErrorPropertyNameBytes, responseMessage.Error);
            }

            writer.WriteNumber(MinorVersionPropertyNameBytes, responseMessage.MinorVersion);

            writer.WriteEndObject();
            writer.Flush();
            Debug.Assert(writer.CurrentDepth == 0);

            TextMessageFormatter.WriteRecordSeparator(output);
        }
Example #7
0
        public async Task TestCloseConnectionMessage()
        {
            var clientConnectionFactory = new TestClientConnectionFactory();

            var connection = CreateServiceConnection(clientConnectionFactory: clientConnectionFactory, handler: new TestConnectionHandler(3000, "foobar"));

            _ = connection.StartAsync();
            await connection.ConnectionInitializedTask.OrTimeout(1000);

            var openConnectionMessage = new OpenConnectionMessage("foo", Array.Empty <Claim>());

            _ = connection.WriteFromServiceAsync(openConnectionMessage);
            await connection.ClientConnectedTask;

            Assert.Equal(1, clientConnectionFactory.Connections.Count);
            var clientConnection = clientConnectionFactory.Connections[0];

            // write a signalr handshake response
            var message = new SignalRProtocol.HandshakeResponseMessage("");

            SignalRProtocol.HandshakeProtocol.WriteResponseMessage(message, clientConnection.Transport.Output);

            // write close connection message
            await connection.WriteFromServiceAsync(new CloseConnectionMessage(clientConnection.ConnectionId));

            // wait until app task completed.
            await Assert.ThrowsAsync <TimeoutException>(async() => await clientConnection.LifetimeTask.OrTimeout(1000));

            await clientConnection.LifetimeTask;

            await connection.ExpectSignalRMessage(SignalRProtocol.HandshakeResponseMessage.Empty).OrTimeout(1000);

            await connection.ExpectStringMessage("foobar").OrTimeout(1000);

            await connection.ExpectSignalRMessage(SignalRProtocol.CloseMessage.Empty).OrTimeout(1000);

            await connection.StopAsync();
        }
Example #8
0
        public async Task TestOpenConnectionMessageWithMigrateIn()
        {
            var clientConnectionFactory = new TestClientConnectionFactory();
            var connection = CreateServiceConnection(clientConnectionFactory: clientConnectionFactory);

            _ = connection.StartAsync();
            await connection.ConnectionInitializedTask.OrTimeout(1000);

            var openConnectionMessage = new OpenConnectionMessage("foo", Array.Empty <Claim>());

            openConnectionMessage.Headers.Add(Constants.AsrsMigrateFrom, "another-server");
            _ = connection.WriteFromServiceAsync(openConnectionMessage);
            await connection.ClientConnectedTask;

            Assert.Equal(1, clientConnectionFactory.Connections.Count);
            var clientConnection = clientConnectionFactory.Connections[0];

            Assert.True(clientConnection.IsMigrated);

            // write a handshake response
            var message = new SignalRProtocol.HandshakeResponseMessage("");

            SignalRProtocol.HandshakeProtocol.WriteResponseMessage(message, clientConnection.Transport.Output);
            await clientConnection.Transport.Output.FlushAsync();

            // signalr handshake response should be skipped.
            await Assert.ThrowsAsync <TimeoutException>(async() => await connection.ExpectSignalRMessage(SignalRProtocol.HandshakeResponseMessage.Empty).OrTimeout(1000));

            Assert.True(clientConnection.IsMigrated);

            var feature = clientConnection.Features.Get <IConnectionMigrationFeature>();

            Assert.NotNull(feature);
            Assert.Equal("another-server", feature.MigrateFrom);

            await connection.StopAsync();
        }
        /// <summary>
        /// Creates a new <see cref="HandshakeResponseMessage"/> from the specified serialized representation.
        /// </summary>
        /// <param name="buffer">The serialized representation of the message.</param>
        /// <param name="responseMessage">When this method returns, contains the parsed message.</param>
        /// <returns>A value that is <c>true</c> if the <see cref="HandshakeResponseMessage"/> was successfully parsed; otherwise, <c>false</c>.</returns>
        public static bool TryParseResponseMessage(ref ReadOnlySequence <byte> buffer, out HandshakeResponseMessage responseMessage)
        {
            if (!TextMessageParser.TryParseMessage(ref buffer, out var payload))
            {
                responseMessage = null;
                return(false);
            }

            var reader = new Utf8JsonReader(in payload, isFinalBlock: true, state: default);
Example #10
0
        /// <summary>
        /// Creates a new <see cref="HandshakeResponseMessage"/> from the specified serialized representation.
        /// </summary>
        /// <param name="buffer">The serialized representation of the message.</param>
        /// <param name="responseMessage">When this method returns, contains the parsed message.</param>
        /// <returns>A value that is <c>true</c> if the <see cref="HandshakeResponseMessage"/> was successfully parsed; otherwise, <c>false</c>.</returns>
        public static bool TryParseResponseMessage(ref ReadOnlySequence <byte> buffer, out HandshakeResponseMessage responseMessage)
        {
            if (!TextMessageParser.TryParseMessage(ref buffer, out var payload))
            {
                responseMessage = null;
                return(false);
            }

            var textReader = Utf8BufferTextReader.Get(payload);

            try
            {
                using (var reader = JsonUtils.CreateJsonTextReader(textReader))
                {
                    JsonUtils.CheckRead(reader);
                    JsonUtils.EnsureObjectStart(reader);

                    int?   minorVersion = null;
                    string error        = null;

                    var completed = false;
                    while (!completed && JsonUtils.CheckRead(reader))
                    {
                        switch (reader.TokenType)
                        {
                        case JsonToken.PropertyName:
                            var memberName = reader.Value.ToString();

                            switch (memberName)
                            {
                            case TypePropertyName:
                                // a handshake response does not have a type
                                // check the incoming message was not any other type of message
                                throw new InvalidDataException("Expected a handshake response from the server.");

                            case ErrorPropertyName:
                                error = JsonUtils.ReadAsString(reader, ErrorPropertyName);
                                break;

                            case MinorVersionPropertyName:
                                minorVersion = JsonUtils.ReadAsInt32(reader, MinorVersionPropertyName);
                                break;

                            default:
                                reader.Skip();
                                break;
                            }
                            break;

                        case JsonToken.EndObject:
                            completed = true;
                            break;

                        default:
                            throw new InvalidDataException($"Unexpected token '{reader.TokenType}' when reading handshake response JSON.");
                        }
                    }
                    ;

                    responseMessage = new HandshakeResponseMessage(minorVersion, error);
                    return(true);
                }
            }
            finally
            {
                Utf8BufferTextReader.Return(textReader);
            }
        }