Commit ef858c5d authored by Tim Peters's avatar Tim Peters

Rewrite ZEO protocol negotiation.

3.3 should have bumped the ZEO protocol number (new methods were
added for MVCC support), but didn't.  Untangling this is a mess.
parent 11bf2882
...@@ -87,7 +87,9 @@ class MTDelay(Delay): ...@@ -87,7 +87,9 @@ class MTDelay(Delay):
# own protocol version and the server protocol version, allowing it to # own protocol version and the server protocol version, allowing it to
# talk to servers using later protocol versions (2.0.2 and higher) as # talk to servers using later protocol versions (2.0.2 and higher) as
# well: the effective protocol used will be the lower of the client # well: the effective protocol used will be the lower of the client
# and server protocol. # and server protocol. However, this changed in ZODB 3.3.1 (and
# should have changed in ZODB 3.3) because an older server doesn't
# support MVCC methods required by 3.3 clients.
# #
# [Ugly details: In order to treat the first received message (protocol # [Ugly details: In order to treat the first received message (protocol
# handshake) differently than all later messages, both client and server # handshake) differently than all later messages, both client and server
...@@ -174,14 +176,6 @@ class Connection(smac.SizedMessageAsyncConnection, object): ...@@ -174,14 +176,6 @@ class Connection(smac.SizedMessageAsyncConnection, object):
__super_close = smac.SizedMessageAsyncConnection.close __super_close = smac.SizedMessageAsyncConnection.close
__super_setSessionKey = smac.SizedMessageAsyncConnection.setSessionKey __super_setSessionKey = smac.SizedMessageAsyncConnection.setSessionKey
# Protocol variables:
#
# oldest_protocol_version -- the oldest protocol version we support
# protocol_version -- the newest protocol version we support; preferred
oldest_protocol_version = "Z200"
protocol_version = "Z201"
# Protocol history: # Protocol history:
# #
# Z200 -- Original ZEO 2.0 protocol # Z200 -- Original ZEO 2.0 protocol
...@@ -193,9 +187,54 @@ class Connection(smac.SizedMessageAsyncConnection, object): ...@@ -193,9 +187,54 @@ class Connection(smac.SizedMessageAsyncConnection, object):
# getAuthProtocol() and scheme-specific authentication methods # getAuthProtocol() and scheme-specific authentication methods
# getExtensionMethods(). # getExtensionMethods().
# getInvalidations(). # getInvalidations().
#
# Z303 -- named after the ZODB release 3.3
# Added methods for MVCC:
# loadBefore()
# loadEx()
# A Z303 client cannot talk to a Z201 server, because the latter
# doesn't support MVCC. A Z201 client can talk to a Z303 server,
# but because (at least) the type of the root object changed
# from ZODB.PersistentMapping to persistent.mapping, the older
# client can't actually make progress if a Z303 client created,
# or ever modified, the root.
# Protocol variables:
# Our preferred protocol.
current_protocol = "Z303"
# If we're a client, an exhaustive list of the server protocols we
# can accept.
servers_we_can_talk_to = [current_protocol]
# If we're a server, an exhaustive list of the client protocols we
# can accept.
clients_we_can_talk_to = ["Z200", "Z201", current_protocol]
# This is pretty excruciating. Details:
#
# 3.3 server 3.2 client
# server sends Z303 to client
# client computes min(Z303, Z201) == Z201 as the protocol to use
# client sends Z201 to server
# OK, because Z201 is in the server's clients_we_can_talk_to
#
# 3.2 server 3.3 client
# server sends Z201 to client
# client computes min(Z303, Z201) == Z201 as the protocol to use
# Z201 isn't in the client's servers_we_can_talk_to, so client
# raises exception
#
# 3.3 server 3.3 client
# server sends Z303 to client
# client computes min(Z303, Z303) == Z303 as the protocol to use
# Z303 is in the client's servers_we_can_talk_to, so client
# sends Z303 to server
# OK, because Z303 is in the server's clients_we_can_talk_to
# Client constructor passes 'C' for tag, server constructor 'S'. This # Client constructor passes 'C' for tag, server constructor 'S'. This
# is used in log messages. # is used in log messages, and to determine whether we can speak with
# our peer.
def __init__(self, sock, addr, obj, tag): def __init__(self, sock, addr, obj, tag):
self.obj = None self.obj = None
self.marshal = Marshaller() self.marshal = Marshaller()
...@@ -203,6 +242,7 @@ class Connection(smac.SizedMessageAsyncConnection, object): ...@@ -203,6 +242,7 @@ class Connection(smac.SizedMessageAsyncConnection, object):
self.peer_protocol_version = None # set in recv_handshake() self.peer_protocol_version = None # set in recv_handshake()
assert tag in "CS" assert tag in "CS"
self.tag = tag
self.logger = logging.getLogger('ZEO.zrpc.Connection(%c)' % tag) self.logger = logging.getLogger('ZEO.zrpc.Connection(%c)' % tag)
if isinstance(addr, types.TupleType): if isinstance(addr, types.TupleType):
self.log_label = "(%s:%d) " % addr self.log_label = "(%s:%d) " % addr
...@@ -324,7 +364,14 @@ class Connection(smac.SizedMessageAsyncConnection, object): ...@@ -324,7 +364,14 @@ class Connection(smac.SizedMessageAsyncConnection, object):
# Extended by ManagedClientConnection. # Extended by ManagedClientConnection.
del self.message_input # uncover normal-case message_input() del self.message_input # uncover normal-case message_input()
self.peer_protocol_version = proto self.peer_protocol_version = proto
if self.oldest_protocol_version <= proto <= self.protocol_version:
if self.tag == 'C':
good_protos = self.servers_we_can_talk_to
else:
assert self.tag == 'S'
good_protos = self.clients_we_can_talk_to
if proto in good_protos:
self.log("received handshake %r" % proto, level=logging.INFO) self.log("received handshake %r" % proto, level=logging.INFO)
else: else:
self.log("bad handshake %s" % short_repr(proto), self.log("bad handshake %s" % short_repr(proto),
...@@ -673,7 +720,7 @@ class ManagedServerConnection(Connection): ...@@ -673,7 +720,7 @@ class ManagedServerConnection(Connection):
def handshake(self): def handshake(self):
# Send the server's preferred protocol to the client. # Send the server's preferred protocol to the client.
self.message_output(self.protocol_version) self.message_output(self.current_protocol)
def close(self): def close(self):
self.obj.notifyDisconnected() self.obj.notifyDisconnected()
...@@ -729,7 +776,7 @@ class ManagedClientConnection(Connection): ...@@ -729,7 +776,7 @@ class ManagedClientConnection(Connection):
def recv_handshake(self, proto): def recv_handshake(self, proto):
# The protocol to use is the older of our and the server's preferred # The protocol to use is the older of our and the server's preferred
# protocols. # protocols.
proto = min(proto, self.protocol_version) proto = min(proto, self.current_protocol)
# Restore the normal message_input method, and raise an exception # Restore the normal message_input method, and raise an exception
# if the protocol version is too old. # if the protocol version is too old.
...@@ -752,6 +799,7 @@ class ManagedClientConnection(Connection): ...@@ -752,6 +799,7 @@ class ManagedClientConnection(Connection):
def close_trigger(self): def close_trigger(self):
# the manager should actually close the trigger # the manager should actually close the trigger
# TODO: what is that comment trying to say? What 'manager'?
del self.trigger del self.trigger
def set_async(self, map): def set_async(self, map):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment