@@ -10,9 +10,9 @@ use crate::{
10
10
bson:: { doc, Bson , Document } ,
11
11
client:: auth:: { ClientFirst , FirstRound } ,
12
12
cmap:: { options:: ConnectionPoolOptions , Command , Connection , StreamDescription } ,
13
- error:: Result ,
13
+ error:: { ErrorKind , Result } ,
14
14
is_master:: { IsMasterCommandResponse , IsMasterReply } ,
15
- options:: { AuthMechanism , Credential } ,
15
+ options:: { AuthMechanism , ClientOptions , Credential , DriverInfo , ServerApi } ,
16
16
} ;
17
17
18
18
#[ cfg( feature = "tokio-runtime" ) ]
@@ -136,95 +136,167 @@ lazy_static! {
136
136
137
137
/// Contains the logic needed to handshake a connection.
138
138
#[ derive( Debug , Clone ) ]
139
- pub ( super ) struct Handshaker {
139
+ pub ( crate ) struct Handshaker {
140
140
/// The `isMaster` command to send when handshaking. This will always be identical
141
141
/// given the same pool options, so it can be created at the time the Handshaker is created.
142
142
command : Command ,
143
+ credential : Option < Credential > ,
143
144
}
144
145
145
146
impl Handshaker {
146
147
/// Creates a new Handshaker.
147
- pub ( super ) fn new ( options : Option < & ConnectionPoolOptions > ) -> Self {
148
+ pub ( crate ) fn new ( options : Option < HandshakerOptions > ) -> Self {
148
149
let mut metadata = BASE_CLIENT_METADATA . clone ( ) ;
150
+ let mut credential = None ;
151
+ let mut db = None ;
152
+ let mut server_api = None ;
149
153
150
- if let Some ( app_name) = options. as_ref ( ) . and_then ( |opts| opts. app_name . as_ref ( ) ) {
151
- metadata. application = Some ( AppMetadata {
152
- name : app_name. to_string ( ) ,
153
- } ) ;
154
- }
155
-
156
- if let Some ( driver_info) = options. as_ref ( ) . and_then ( |opts| opts. driver_info . as_ref ( ) ) {
157
- metadata. driver . name . push ( '|' ) ;
158
- metadata. driver . name . push_str ( & driver_info. name ) ;
154
+ let mut body = doc ! {
155
+ "isMaster" : 1 ,
156
+ } ;
159
157
160
- if let Some ( ref version ) = driver_info . version {
161
- metadata . driver . version . push ( '|' ) ;
162
- metadata. driver . version . push_str ( version ) ;
158
+ if let Some ( options ) = options {
159
+ if let Some ( app_name ) = options . app_name {
160
+ metadata. application = Some ( AppMetadata { name : app_name } ) ;
163
161
}
164
162
165
- if let Some ( ref mut platform) = metadata. platform {
166
- if let Some ( ref driver_info_platform) = driver_info. platform {
167
- platform. push ( '|' ) ;
168
- platform. push_str ( driver_info_platform) ;
163
+ if let Some ( driver_info) = options. driver_info {
164
+ metadata. driver . name . push ( '|' ) ;
165
+ metadata. driver . name . push_str ( & driver_info. name ) ;
166
+
167
+ if let Some ( ref version) = driver_info. version {
168
+ metadata. driver . version . push ( '|' ) ;
169
+ metadata. driver . version . push_str ( version) ;
170
+ }
171
+
172
+ if let Some ( ref mut platform) = metadata. platform {
173
+ if let Some ( ref driver_info_platform) = driver_info. platform {
174
+ platform. push ( '|' ) ;
175
+ platform. push_str ( driver_info_platform) ;
176
+ }
169
177
}
170
178
}
179
+
180
+ if let Some ( cred) = options. credential {
181
+ cred. append_needed_mechanism_negotiation ( & mut body) ;
182
+ db = Some ( cred. resolved_source ( ) . to_string ( ) ) ;
183
+ credential = Some ( cred) ;
184
+ }
185
+
186
+ server_api = options. server_api ;
171
187
}
172
188
173
- let mut db = "admin" ;
189
+ body . insert ( "client" , metadata ) ;
174
190
175
- let mut body = doc ! {
176
- "isMaster" : 1 ,
177
- "client" : metadata,
178
- } ;
191
+ let mut command = Command :: new_read (
192
+ "isMaster" . to_string ( ) ,
193
+ db. unwrap_or_else ( || "admin" . to_string ( ) ) ,
194
+ None ,
195
+ body,
196
+ ) ;
179
197
180
- if let Some ( credential) = options. as_ref ( ) . and_then ( |opts| opts. credential . as_ref ( ) ) {
181
- credential. append_needed_mechanism_negotiation ( & mut body) ;
182
- db = credential. resolved_source ( ) ;
198
+ if let Some ( server_api) = server_api {
199
+ command. set_server_api ( & server_api)
183
200
}
184
201
185
- let mut command = Command :: new_read ( "isMaster" . to_string ( ) , db . to_string ( ) , None , body ) ;
186
- if let Some ( server_api ) = options . as_ref ( ) . and_then ( |opts| opts . server_api . as_ref ( ) ) {
187
- command . set_server_api ( server_api )
202
+ Self {
203
+ command ,
204
+ credential ,
188
205
}
189
-
190
- Self { command }
191
206
}
192
207
193
208
/// Handshakes a connection.
194
- pub ( super ) async fn handshake (
195
- & self ,
196
- conn : & mut Connection ,
197
- credential : Option < & Credential > ,
198
- ) -> Result < Option < FirstRound > > {
209
+ pub ( crate ) async fn handshake ( & self , conn : & mut Connection ) -> Result < HandshakeResult > {
199
210
let mut command = self . command . clone ( ) ;
200
211
201
- let client_first = set_speculative_auth_info ( & mut command. body , credential) ?;
202
-
203
- let start_time = PreciseTime :: now ( ) ;
204
- let response = conn. send_command ( command, None ) . await ?;
205
- let end_time = PreciseTime :: now ( ) ;
212
+ let client_first = set_speculative_auth_info ( & mut command. body , self . credential . as_ref ( ) ) ?;
206
213
207
- response . validate ( ) ?;
208
- let mut command_response : IsMasterCommandResponse = response . body ( ) ? ;
214
+ let mut is_master_reply = is_master ( command , conn ) . await ?;
215
+ conn . stream_description = Some ( StreamDescription :: from_is_master ( is_master_reply . clone ( ) ) ) ;
209
216
210
217
// Record the client's message and the server's response from speculative authentication if
211
218
// the server did send a response.
212
219
let first_round = client_first. and_then ( |client_first| {
213
- command_response
220
+ is_master_reply
221
+ . command_response
214
222
. speculative_authenticate
215
223
. take ( )
216
224
. map ( |server_first| client_first. into_first_round ( server_first) )
217
225
} ) ;
218
226
219
- let is_master_reply = IsMasterReply {
220
- command_response,
221
- round_trip_time : Some ( start_time. to ( end_time) . to_std ( ) . unwrap ( ) ) ,
222
- cluster_time : None ,
223
- } ;
227
+ Ok ( HandshakeResult {
228
+ first_round,
229
+ is_master_reply,
230
+ } )
231
+ }
232
+ }
233
+
234
+ /// The information returned from the server as part of the handshake.
235
+ ///
236
+ /// Also optionally includes the first round of speculative authentication
237
+ /// if applicable.
238
+ #[ derive( Debug ) ]
239
+ pub ( crate ) struct HandshakeResult {
240
+ /// The response from the server.
241
+ pub ( crate ) is_master_reply : IsMasterReply ,
242
+
243
+ /// The first round of speculative authentication, if applicable.
244
+ pub ( crate ) first_round : Option < FirstRound > ,
245
+ }
246
+
247
+ #[ derive( Debug ) ]
248
+ pub ( crate ) struct HandshakerOptions {
249
+ app_name : Option < String > ,
250
+ credential : Option < Credential > ,
251
+ driver_info : Option < DriverInfo > ,
252
+ server_api : Option < ServerApi > ,
253
+ }
254
+
255
+ impl From < ConnectionPoolOptions > for HandshakerOptions {
256
+ fn from ( options : ConnectionPoolOptions ) -> Self {
257
+ Self {
258
+ app_name : options. app_name ,
259
+ credential : options. credential ,
260
+ driver_info : options. driver_info ,
261
+ server_api : options. server_api ,
262
+ }
263
+ }
264
+ }
265
+
266
+ impl From < ClientOptions > for HandshakerOptions {
267
+ fn from ( options : ClientOptions ) -> Self {
268
+ Self {
269
+ app_name : options. app_name ,
270
+ credential : options. credential ,
271
+ driver_info : options. driver_info ,
272
+ server_api : options. server_api ,
273
+ }
274
+ }
275
+ }
224
276
225
- conn. stream_description = Some ( StreamDescription :: from_is_master ( is_master_reply) ) ;
226
- Ok ( first_round)
277
+ /// Run the given isMaster command.
278
+ ///
279
+ /// If the given command is not an isMaster, this function will return an error.
280
+ pub ( crate ) async fn is_master ( command : Command , conn : & mut Connection ) -> Result < IsMasterReply > {
281
+ if !command. name . eq_ignore_ascii_case ( "ismaster" ) {
282
+ return Err ( ErrorKind :: OperationError {
283
+ message : format ! ( "invalid ismaster command: {}" , command. name) ,
284
+ }
285
+ . into ( ) ) ;
227
286
}
287
+ let start_time = PreciseTime :: now ( ) ;
288
+ let response = conn. send_command ( command, None ) . await ?;
289
+ let end_time = PreciseTime :: now ( ) ;
290
+
291
+ response. validate ( ) ?;
292
+ let cluster_time = response. cluster_time ( ) . cloned ( ) ;
293
+ let command_response: IsMasterCommandResponse = response. body ( ) ?;
294
+
295
+ Ok ( IsMasterReply {
296
+ command_response,
297
+ round_trip_time : Some ( start_time. to ( end_time) . to_std ( ) . unwrap ( ) ) ,
298
+ cluster_time,
299
+ } )
228
300
}
229
301
230
302
/// Updates the handshake command document with the speculative authenitication info.
0 commit comments