@@ -146,6 +146,8 @@ type Connection struct {
146
146
lenbuf [PacketLengthBytes ]byte
147
147
148
148
lastStreamId uint64
149
+
150
+ serverProtocolInfo ProtocolInfo
149
151
}
150
152
151
153
var _ = Connector (& Connection {}) // Check compatibility with connector interface.
@@ -269,6 +271,14 @@ type Opts struct {
269
271
Transport string
270
272
// SslOpts is used only if the Transport == 'ssl' is set.
271
273
Ssl SslOpts
274
+ // Minimal protocol version that should be supported by
275
+ // Go connection client and Tarantool server. By default
276
+ // it is equal to 0 (no restrictions)
277
+ RequiredProtocolVersion ProtocolVersion
278
+ // List of protocol features that should be supported by
279
+ // Go connection client and Tarantool server. By default
280
+ // it is a nil array (no restrictions)
281
+ RequiredProtocolFeatures []ProtocolFeature
272
282
}
273
283
274
284
// SslOpts is a way to configure ssl transport.
@@ -296,6 +306,9 @@ type SslOpts struct {
296
306
func (opts Opts ) Copy () (optsCopy Opts ) {
297
307
optsCopy = opts
298
308
309
+ optsCopy .RequiredProtocolFeatures = make ([]ProtocolFeature , len (opts .RequiredProtocolFeatures ))
310
+ copy (optsCopy .RequiredProtocolFeatures , opts .RequiredProtocolFeatures )
311
+
299
312
return optsCopy
300
313
}
301
314
@@ -508,6 +521,23 @@ func (conn *Connection) dial() (err error) {
508
521
conn .Greeting .Version = bytes .NewBuffer (greeting [:64 ]).String ()
509
522
conn .Greeting .auth = bytes .NewBuffer (greeting [64 :108 ]).String ()
510
523
524
+ // IPROTO_ID requests can be processed without authentication.
525
+ // https://www.tarantool.io/en/doc/latest/dev_guide/internals/iproto/requests/#iproto-id
526
+ if err = conn .identify (w , r ); err != nil {
527
+ connection .Close ()
528
+ return err
529
+ }
530
+
531
+ if err = checkProtocolVersion (opts .RequiredProtocolVersion , conn .ServerProtocolVersion ()); err != nil {
532
+ connection .Close ()
533
+ return fmt .Errorf ("identify: %w" , err )
534
+ }
535
+
536
+ if err = checkProtocolFeatures (opts .RequiredProtocolFeatures , conn .ServerProtocolFeatures ()); err != nil {
537
+ connection .Close ()
538
+ return fmt .Errorf ("identify: %w" , err )
539
+ }
540
+
511
541
// Auth
512
542
if opts .User != "" {
513
543
scr , err := scramble (conn .Greeting .auth , opts .Pass )
@@ -592,13 +622,13 @@ func (conn *Connection) writeRequest(w *bufio.Writer, req Request) (err error) {
592
622
err = pack (& packet , newEncoder (& packet ), 0 , req , ignoreStreamId , conn .Schema )
593
623
594
624
if err != nil {
595
- return fmt .Errorf ("pack error %w" , err )
625
+ return fmt .Errorf ("pack error: %w" , err )
596
626
}
597
627
if err := write (w , packet .b ); err != nil {
598
- return fmt .Errorf ("write error %w" , err )
628
+ return fmt .Errorf ("write error: %w" , err )
599
629
}
600
630
if err = w .Flush (); err != nil {
601
- return fmt .Errorf ("flush error %w" , err )
631
+ return fmt .Errorf ("flush error: %w" , err )
602
632
}
603
633
return
604
634
}
@@ -614,23 +644,35 @@ func (conn *Connection) writeAuthRequest(w *bufio.Writer, scramble []byte) (err
614
644
return nil
615
645
}
616
646
647
+ func (conn * Connection ) writeIdRequest (w * bufio.Writer , version ProtocolVersion ,
648
+ features []ProtocolFeature ) (err error ) {
649
+ req := NewIdRequest (version , features )
650
+
651
+ err = conn .writeRequest (w , req )
652
+ if err != nil {
653
+ return fmt .Errorf ("identify: %w" , err )
654
+ }
655
+
656
+ return nil
657
+ }
658
+
617
659
func (conn * Connection ) readResponse (r io.Reader ) (resp Response , err error ) {
618
660
respBytes , err := conn .read (r )
619
661
if err != nil {
620
- return resp , fmt .Errorf ("read error %w" , err )
662
+ return resp , fmt .Errorf ("read error: %w" , err )
621
663
}
622
664
resp = Response {buf : smallBuf {b : respBytes }}
623
665
err = resp .decodeHeader (conn .dec )
624
666
if err != nil {
625
- return resp , fmt .Errorf ("decode response header error %w" , err )
667
+ return resp , fmt .Errorf ("decode response header error: %w" , err )
626
668
}
627
669
err = resp .decodeBody ()
628
670
if err != nil {
629
671
switch err .(type ) {
630
672
case Error :
631
673
return resp , err
632
674
default :
633
- return resp , fmt .Errorf ("decode response body error %w" , err )
675
+ return resp , fmt .Errorf ("decode response body error: %w" , err )
634
676
}
635
677
}
636
678
return resp , nil
@@ -645,6 +687,15 @@ func (conn *Connection) readAuthResponse(r io.Reader) (err error) {
645
687
return nil
646
688
}
647
689
690
+ func (conn * Connection ) readIdResponse (r io.Reader ) (resp Response , err error ) {
691
+ resp , err = conn .readResponse (r )
692
+ if err != nil {
693
+ return resp , fmt .Errorf ("identify: %w" , err )
694
+ }
695
+
696
+ return resp , nil
697
+ }
698
+
648
699
func (conn * Connection ) createConnection (reconnect bool ) (err error ) {
649
700
var reconnects uint
650
701
for conn .c == nil && conn .state == connDisconnected {
@@ -1188,3 +1239,97 @@ func (conn *Connection) NewStream() (*Stream, error) {
1188
1239
Conn : conn ,
1189
1240
}, nil
1190
1241
}
1242
+
1243
+ // checkProtocolVersion checks that expected protocol version is supported.
1244
+ func checkProtocolVersion (expected ProtocolVersion , actual ProtocolVersion ) error {
1245
+ if expected > actual {
1246
+ return fmt .Errorf ("protocol version %d is not supported" , expected )
1247
+ }
1248
+
1249
+ return nil
1250
+ }
1251
+
1252
+ // checkProtocolFeatures checks that expected protocol features are supported.
1253
+ func checkProtocolFeatures (expectedList []ProtocolFeature , actualList []ProtocolFeature ) error {
1254
+ var found bool
1255
+ // It seems that iterating over a small list is way faster
1256
+ // than building a map: https://stackoverflow.com/a/52710077/11646599
1257
+ for _ , expected := range expectedList {
1258
+ found = false
1259
+ for _ , actual := range actualList {
1260
+ if expected == actual {
1261
+ found = true
1262
+ }
1263
+ }
1264
+ if ! found {
1265
+ // "Feature" already is a part of the String().
1266
+ return fmt .Errorf ("%s is not supported" , expected )
1267
+ }
1268
+ }
1269
+
1270
+ return nil
1271
+ }
1272
+
1273
+ // identify sends info about client protocol, receives info
1274
+ // about server protocol in response and store it in the connection.
1275
+ func (conn * Connection ) identify (w * bufio.Writer , r * bufio.Reader ) error {
1276
+ var ok bool
1277
+
1278
+ werr := conn .writeIdRequest (w , conn .ClientProtocolVersion (), conn .ClientProtocolFeatures ())
1279
+ if werr != nil {
1280
+ return werr
1281
+ }
1282
+
1283
+ resp , rerr := conn .readIdResponse (r )
1284
+ if rerr != nil {
1285
+ if resp .Code == ErrUnknownRequestType {
1286
+ // IPROTO_ID requests are not supported by server.
1287
+ return nil
1288
+ }
1289
+
1290
+ return rerr
1291
+ }
1292
+
1293
+ if len (resp .Data ) == 0 {
1294
+ return fmt .Errorf ("identify: unexpected response: no data" )
1295
+ }
1296
+
1297
+ conn .serverProtocolInfo , ok = resp .Data [0 ].(ProtocolInfo )
1298
+ if ! ok {
1299
+ return fmt .Errorf ("identify: unexpected response: wrong data" )
1300
+ }
1301
+
1302
+ return nil
1303
+ }
1304
+
1305
+ // ServerProtocolVersion returns protocol version supported by
1306
+ // connected Tarantool server. Beware that values might be outdated
1307
+ // if connection is in a disconnected state.
1308
+ // Since 1.10.0
1309
+ func (conn * Connection ) ServerProtocolVersion () ProtocolVersion {
1310
+ return conn .serverProtocolInfo .Version
1311
+ }
1312
+
1313
+ // ClientProtocolVersion returns protocol version supported by
1314
+ // Go connection client.
1315
+ // Since 1.10.0
1316
+ func (conn * Connection ) ClientProtocolVersion () ProtocolVersion {
1317
+ return сlientProtocolVersion
1318
+ }
1319
+
1320
+ // ServerFeatures returns protocol features supported by connected Tarantool server.
1321
+ // Beware that values might be outdated if connection is in a disconnected state.
1322
+ // Since 1.10.0
1323
+ func (conn * Connection ) ServerProtocolFeatures () []ProtocolFeature {
1324
+ res := make ([]ProtocolFeature , len (conn .serverProtocolInfo .Features ))
1325
+ copy (res , conn .serverProtocolInfo .Features )
1326
+ return res
1327
+ }
1328
+
1329
+ // ClientFeatures returns protocol features supported by Go connection client.
1330
+ // Since 1.10.0
1331
+ func (conn * Connection ) ClientProtocolFeatures () []ProtocolFeature {
1332
+ res := make ([]ProtocolFeature , len (сlientProtocolFeatures ))
1333
+ copy (res , сlientProtocolFeatures )
1334
+ return res
1335
+ }
0 commit comments