Kittyfisto/SharpRemote

Detect incompatible wire types

Opened this issue · 7 comments

Currently SharpRemote simply assumes that if both endpoints can load a Type using its full typename, then the types must be compatible and can roundtrip.

This is not always the case as one can easily re/mis -place an assembly with a modified type, introducing/removing a new property, changing a property's type or changing the type's hierarchy.

All of these changes are breaking and cause the deserialization to fail (at best) or to succeed but contain garbage values (at worst).

A mechanism must be introduced that catches all of these incompatibilities and reports them to the user by throwing an appropriate exception.

The solution will be to allow SharpRemote endpoints to exchange type models upon establishing a connection. Either of the two endpoints will then compare the remote type model to their internal one and abort the connection if the two type models are incompatible. A very detailed error message should be logged, however, so the user can find out exactly where the mismatch occurred. Such a message must contain:
The types which do not match (Fully qualified type name) and the reason why there's a mismatch:

  • Class hierarchy changed
  • Type changed (singleton <==> by reference <==> data contract)
  • Property introduced, removed or type changed
  • Field introduced, removed or type changed
  • Method parameter introduced, removed or type changed
  • Method return type changed
  • Method removed
  • Method introduced

This type model, when created from .NET types, shall contain most, if not all validation code and be used by the current binary serializer as well as the planned XmlSerializer. It is sensible that, for this purpose, .NET types and MemberInfo are also exposed either via the same type model, or one implementation with an extended interface.

On top of the initial exchange of types, and endpoint shall keep track of which types have not yet been exchanged (On a per client basis) and send type models of those on demand.

How are partial type models created/how are deltas computed?

If an endpoint needs to keep track of which type models have been exchanged and which ones have not, then how should this be done? I case an endpoint connects to multiple others in its lifetime, then said endpoint should not throw away its own type model upon disconnecting, nor would a flat be sufficient. The best option might be to have one type model for serializing types and one that contains all types which have been sent to the other client.

If the above approach is to be taken, then its possible that concurrent IPCs will require the exchange of partially overlapping type models. This means that once confirmation is received that a client actually supports a type, its possible that confirmation of other types happened even earlier and thus the code which updates type models needs to deal with this (and only insert unknown types).

How is the type model actually computed (which component is responsible for doing that?). The serializer could be a natural choice since it inspects the entire graph. But how is the type model serialized?

Contrary to what I wrote earlier, serializers should be responsible for defining which rules are used for comparison:

Example: The current binary serializer requires that fields/properties don't change order whereas the xml serializer requires that fields don't change names.

Maybe there should be a shared implementation which can be configured by either serializer (only if the shared code is complex enough).