-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Open
Labels
area-signalrIncludes: SignalR clients and serversIncludes: SignalR clients and servers
Description
Describe the bug
In writing a Nerdbank.MessagePack implementation of IHubProtocol
, I've been studying SignalR's existing implementation based on MessagePack-CSharp. I've noticed several potential issues that I will collect here for your consideration:
Real bugs
- Serialization requires knowing the declared types of arguments and return values. It is not enough to simply call
object.GetType()
and serialize with that. We need to know that the parameter is typed asBaseType
and that the argument is an instance ofDerivedType
so that[Union]
attributes onBaseType
can be serialized. As you have it here where the serializer relies onGetType
on the value to be serialized, a parameter or return type declared asBaseType
but has an instance ofDerivedType
will serialize as a 'naked' DerivedType. But upon deserialization where we do haveBaseType
as a known type, the deserializer will look for a type disambiguating header to precede theDerivedType
data. But it won't be there becauseBaseType
wasn't a known type at serialization time. As a result, your SignalR implementation will fail when users try to send types with[Union]
attributes on them. It also means my own implementation for Nerdbank.MessagePack will have the same bug, because yourIHubProtocol
interface simply doesn't make this information available. - The spec mandates a fixed protocol version of 1. But your code currently sets it to be 2. And while your code allegedly accepts a version 1 as compatible, if the version is exchanged both directions, surely your version 1 implementation on the other side will reject version 2 anyway. Am I missing something?
Other issues for your consideration
- The protocol version being part of the protocol really ought to be communicated with the
IHubProtocol
implementation. At the moment, all the protocol version evidently does is to serve as an accept/reject decision. But it would be much more useful if theIHubProtocol
also knew which version the remote party was using so that any necessary adjustments could be made in the protocol. - The message deserializing functions do not adequately guard against reading too much. While it calls
ReadArrayHeader
, most paths disregard its value and read as many elements as they want. In fact the only paths that use the return value are those that expect an optional value in the array. This issue is a lower priority than it would otherwise be because theMessagePackReader
works off of a bounded buffer so that it cannot read more than one message. So this is ok for now. - The message deserializing functions do not guard against reading too little. Typically when reading an array, you should make sure you've read or skipped every single element in the array. Again, the fact that the top-level read function has guaranteed to have consumed the entire envelope makes this less of a manifesting bug. But it does prevent you from noticing when there's garbage at the end of a message.
- The schema is fundamentally incompatible with using standard serialize/deserialize functions from msgpack libraries because it mixes both the disambiguating 'type' integer into the same array that stores the object's fields. This makes it impossible to have each message serialize/deserialize itself, and have a higher-level method write/read the message type identifier first. The best practice here would be something like this:
[1, [message, fields, here]]
instead of[1, message, fields, here]
. Fixing this would be a protocol breaking change.
Expected Behavior
Ideally a messagepack protocol that follows best practices in the messagepack community.
Metadata
Metadata
Assignees
Labels
area-signalrIncludes: SignalR clients and serversIncludes: SignalR clients and servers