@@ -19,6 +19,8 @@ import { alloc } from '../channel'
1919import { newError } from 'neo4j-driver-core'
2020
2121const BOLT_MAGIC_PREAMBLE = 0x6060b017
22+ const AVAILABLE_BOLT_PROTOCOLS = [ 5.8 , 5.7 , 5.6 , 5.4 , 5.3 , 5.2 , 5.1 , 5.0 , 4.4 , 4.3 , 4.2 , 3.0 ] // bolt protocols the client will accept, ordered by preference
23+ const DESIRED_CAPABILITES = 0
2224
2325function version ( major , minor ) {
2426 return {
@@ -70,15 +72,69 @@ function parseNegotiatedResponse (buffer, log) {
7072 return Number ( h [ 3 ] + '.' + h [ 2 ] )
7173}
7274
75+ function handshakeNegotiationV2 ( channel , buffer , log ) {
76+ const numVersions = buffer . readVarInt ( )
77+ let versions = [ ]
78+ for ( let i = 0 ; i < numVersions ; i ++ ) {
79+ const versionRange = [
80+ buffer . readUInt8 ( ) ,
81+ buffer . readUInt8 ( ) ,
82+ buffer . readUInt8 ( ) ,
83+ buffer . readUInt8 ( )
84+ ]
85+ versions = versions . concat ( getVersions ( versionRange ) )
86+ }
87+ const capabilityBitMask = buffer . readVarInt ( )
88+ const capabilites = selectCapabilites ( capabilityBitMask )
89+
90+ let major = 0
91+ let minor = 0
92+ versions . sort ( ( a , b ) => {
93+ if ( Number ( a . major ) !== Number ( b . major ) ) {
94+ return Number ( b . major ) - Number ( a . major )
95+ } else {
96+ return Number ( b . minor ) - Number ( a . minor )
97+ }
98+ } )
99+ for ( let i = 0 ; i < versions . length ; i ++ ) {
100+ const version = versions [ i ]
101+ if ( AVAILABLE_BOLT_PROTOCOLS . includes ( Number ( version . major + '.' + version . minor ) ) ) {
102+ major = version . major
103+ minor = version . minor
104+ break
105+ }
106+ }
107+
108+ return new Promise ( ( resolve , reject ) => {
109+ try {
110+ const selectionBuffer = alloc ( 5 )
111+ selectionBuffer . writeInt32 ( ( minor << 8 ) | major )
112+ selectionBuffer . writeVarInt ( capabilites )
113+ channel . write ( selectionBuffer )
114+ resolve ( {
115+ protocolVersion : Number ( major + '.' + minor ) ,
116+ capabilites,
117+ consumeRemainingBuffer : consumer => {
118+ if ( buffer . hasRemaining ( ) ) {
119+ consumer ( buffer . readSlice ( buffer . remaining ( ) ) )
120+ }
121+ }
122+ } )
123+ } catch ( e ) {
124+ reject ( e )
125+ }
126+ } )
127+ }
128+
73129/**
74130 * @return {BaseBuffer }
75131 * @private
76132 */
77133function newHandshakeBuffer ( ) {
78134 return createHandshakeMessage ( [
135+ version ( 255 , 1 ) ,
79136 [ version ( 5 , 8 ) , version ( 5 , 0 ) ] ,
80137 [ version ( 4 , 4 ) , version ( 4 , 2 ) ] ,
81- version ( 4 , 1 ) ,
82138 version ( 3 , 0 )
83139 ] )
84140}
@@ -91,8 +147,10 @@ function newHandshakeBuffer () {
91147/**
92148 * @typedef HandshakeResult
93149 * @property {number } protocolVersion The protocol version negotiated in the handshake
150+ * @property {number } capabilites A bitmask representing the capabilities negotiated in the handshake
94151 * @property {function(BufferConsumerCallback) } consumeRemainingBuffer A function to consume the remaining buffer if it exists
95152 */
153+
96154/**
97155 * Shake hands using the channel and return the protocol version
98156 *
@@ -101,6 +159,23 @@ function newHandshakeBuffer () {
101159 * @returns {Promise<HandshakeResult> } Promise of protocol version and consumeRemainingBuffer
102160 */
103161export default function handshake ( channel , log ) {
162+ return initialHandshake ( channel , log ) . then ( ( result ) => {
163+ if ( result . protocolVersion === 255.1 ) {
164+ return handshakeNegotiationV2 ( channel , result . buffer , log )
165+ } else {
166+ return result
167+ }
168+ } )
169+ }
170+
171+ /**
172+ * Shake hands using the channel and return the protocol version, or the improved handshake protocol if communicating with a newer server.
173+ *
174+ * @param {Channel } channel the channel use to shake hands
175+ * @param {Logger } log the log object
176+ * @returns {Promise<HandshakeResult> } Promise of protocol version and consumeRemainingBuffer
177+ */
178+ function initialHandshake ( channel , log ) {
104179 return new Promise ( ( resolve , reject ) => {
105180 const handshakeErrorHandler = error => {
106181 reject ( error )
@@ -115,9 +190,10 @@ export default function handshake (channel, log) {
115190 try {
116191 // read the response buffer and initialize the protocol
117192 const protocolVersion = parseNegotiatedResponse ( buffer , log )
118-
119193 resolve ( {
120194 protocolVersion,
195+ capabilites : 0 ,
196+ buffer,
121197 consumeRemainingBuffer : consumer => {
122198 if ( buffer . hasRemaining ( ) ) {
123199 consumer ( buffer . readSlice ( buffer . remaining ( ) ) )
@@ -132,3 +208,17 @@ export default function handshake (channel, log) {
132208 channel . write ( newHandshakeBuffer ( ) )
133209 } )
134210}
211+
212+ function getVersions ( versionArray ) {
213+ const resultArr = [ ]
214+ const major = versionArray [ 3 ]
215+ const minor = versionArray [ 2 ]
216+ for ( let i = 0 ; i <= versionArray [ 1 ] ; i ++ ) {
217+ resultArr . push ( { major, minor : minor - i } )
218+ }
219+ return resultArr
220+ }
221+
222+ function selectCapabilites ( capabilityBitMask ) {
223+ return DESIRED_CAPABILITES // capabilites are currently unused and will always be 0.
224+ }
0 commit comments