@@ -90,10 +90,55 @@ class Bolt:
9090 the handshake was carried out.
9191 """
9292
93- MAGIC_PREAMBLE = 0x6060B017
93+ MAGIC_PREAMBLE = b" \x60 \x60 \xB0 \x17 "
9494
9595 PROTOCOL_VERSION = None
9696
97+ @classmethod
98+ def get_handshake (cls ):
99+ """ Return the supported Bolt versions as bytes.
100+ The length is 16 bytes as specified in the Bolt version negotiation.
101+ :return: bytes
102+ """
103+ offered_versions = sorted (cls .protocol_handlers ().keys (), reverse = True )[:4 ]
104+ return b"" .join (version .to_bytes () for version in offered_versions ).ljust (16 , b"\x00 " )
105+
106+ @classmethod
107+ def protocol_handlers (cls , protocol_version = None ):
108+ """ Return a dictionary of available Bolt protocol handlers,
109+ keyed by version tuple. If an explicit protocol version is
110+ provided, the dictionary will contain either zero or one items,
111+ depending on whether that version is supported. If no protocol
112+ version is provided, all available versions will be returned.
113+
114+ :param protocol_version: tuple identifying a specific protocol
115+ version (e.g. (3, 5)) or None
116+ :return: dictionary of version tuple to handler class for all
117+ relevant and supported protocol versions
118+ :raise TypeError: if protocol version is not passed in a tuple
119+ """
120+
121+ # Carry out subclass imports locally to avoid circular
122+ # dependency issues.
123+ from neo4j .io ._bolt3 import Bolt3
124+ from neo4j .io ._bolt4x0 import Bolt4x0
125+
126+ handlers = {
127+ Bolt3 .PROTOCOL_VERSION : Bolt3 ,
128+ Bolt4x0 .PROTOCOL_VERSION : Bolt4x0
129+ }
130+
131+ if protocol_version is None :
132+ return handlers
133+
134+ if not isinstance (protocol_version , tuple ):
135+ raise TypeError ("Protocol version must be specified as a tuple" )
136+
137+ if protocol_version in handlers :
138+ return {protocol_version : handlers [protocol_version ]}
139+
140+ return {}
141+
97142 @classmethod
98143 def ping (cls , address , * , timeout = None , ** config ):
99144 """ Attempt to establish a Bolt connection, returning the
@@ -757,12 +802,18 @@ def _handshake(s, resolved_address):
757802 """
758803 local_port = s .getsockname ()[1 ]
759804
760- # Send details of the protocol versions supported
761- supported_versions = [3 , 0 , 0 , 0 ]
762- handshake = [Bolt .MAGIC_PREAMBLE ] + supported_versions
763- log .debug ("[#%04X] C: <MAGIC> 0x%08X" , local_port , Bolt .MAGIC_PREAMBLE )
764- log .debug ("[#%04X] C: <HANDSHAKE> 0x%08X 0x%08X 0x%08X 0x%08X" , local_port , * supported_versions )
765- data = b"" .join (struct_pack (">I" , num ) for num in handshake )
805+ # TODO: Optimize logging code
806+ handshake = Bolt .get_handshake ()
807+ import struct
808+ handshake = struct .unpack (">16B" , handshake )
809+ handshake = [handshake [i :i + 4 ] for i in range (0 , len (handshake ), 4 )]
810+
811+ supported_versions = [("0x%02X%02X%02X%02X" % (vx [0 ], vx [1 ], vx [2 ], vx [3 ])) for vx in handshake ]
812+
813+ log .debug ("[#%04X] C: <MAGIC> 0x%08X" , local_port , int .from_bytes (Bolt .MAGIC_PREAMBLE , byteorder = "big" ))
814+ log .debug ("[#%04X] C: <HANDSHAKE> %s %s %s %s" , local_port , * supported_versions )
815+
816+ data = Bolt .MAGIC_PREAMBLE + Bolt .get_handshake ()
766817 s .sendall (data )
767818
768819 # Handle the handshake response
@@ -796,17 +847,7 @@ def _handshake(s, resolved_address):
796847 "(looks like HTTP)" .format (resolved_address ))
797848 agreed_version = data [- 1 ], data [- 2 ]
798849 log .debug ("[#%04X] S: <HANDSHAKE> 0x%06X%02X" , local_port , agreed_version [1 ], agreed_version [0 ])
799- if agreed_version == (0 , 0 ):
800- log .debug ("[#%04X] C: <CLOSE>" , local_port )
801- s .shutdown (SHUT_RDWR )
802- s .close ()
803- elif agreed_version in ((3 , 0 ),):
804- return s , agreed_version
805- else :
806- log .debug ("[#%04X] S: <CLOSE>" , local_port )
807- s .close ()
808- raise ProtocolError ("Unknown Bolt protocol version: "
809- "{}" .format (agreed_version ))
850+ return s , agreed_version
810851
811852
812853def connect (address , * , timeout = None , config ):
0 commit comments