@@ -19,6 +19,8 @@ import { alloc } from '../channel'
19
19
import { newError } from 'neo4j-driver-core'
20
20
21
21
const 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
22
24
23
25
function version ( major , minor ) {
24
26
return {
@@ -70,15 +72,69 @@ function parseNegotiatedResponse (buffer, log) {
70
72
return Number ( h [ 3 ] + '.' + h [ 2 ] )
71
73
}
72
74
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
+
73
129
/**
74
130
* @return {BaseBuffer }
75
131
* @private
76
132
*/
77
133
function newHandshakeBuffer ( ) {
78
134
return createHandshakeMessage ( [
135
+ version ( 255 , 1 ) ,
79
136
[ version ( 5 , 8 ) , version ( 5 , 0 ) ] ,
80
137
[ version ( 4 , 4 ) , version ( 4 , 2 ) ] ,
81
- version ( 4 , 1 ) ,
82
138
version ( 3 , 0 )
83
139
] )
84
140
}
@@ -91,8 +147,10 @@ function newHandshakeBuffer () {
91
147
/**
92
148
* @typedef HandshakeResult
93
149
* @property {number } protocolVersion The protocol version negotiated in the handshake
150
+ * @property {number } capabilites A bitmask representing the capabilities negotiated in the handshake
94
151
* @property {function(BufferConsumerCallback) } consumeRemainingBuffer A function to consume the remaining buffer if it exists
95
152
*/
153
+
96
154
/**
97
155
* Shake hands using the channel and return the protocol version
98
156
*
@@ -101,6 +159,23 @@ function newHandshakeBuffer () {
101
159
* @returns {Promise<HandshakeResult> } Promise of protocol version and consumeRemainingBuffer
102
160
*/
103
161
export 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 ) {
104
179
return new Promise ( ( resolve , reject ) => {
105
180
const handshakeErrorHandler = error => {
106
181
reject ( error )
@@ -115,9 +190,10 @@ export default function handshake (channel, log) {
115
190
try {
116
191
// read the response buffer and initialize the protocol
117
192
const protocolVersion = parseNegotiatedResponse ( buffer , log )
118
-
119
193
resolve ( {
120
194
protocolVersion,
195
+ capabilites : 0 ,
196
+ buffer,
121
197
consumeRemainingBuffer : consumer => {
122
198
if ( buffer . hasRemaining ( ) ) {
123
199
consumer ( buffer . readSlice ( buffer . remaining ( ) ) )
@@ -132,3 +208,17 @@ export default function handshake (channel, log) {
132
208
channel . write ( newHandshakeBuffer ( ) )
133
209
} )
134
210
}
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