Skip to content

support client connection attributes on the client side #675

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions client/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,20 @@ func (c *Conn) genAuthResponse(authData []byte) ([]byte, bool, error) {
}
}

// generate connection attributes data
func (c *Conn) genAttributes() []byte {
if len(c.attributes) == 0 {
return nil
}

attrData := make([]byte, 0)
for k, v := range c.attributes {
attrData = append(attrData, PutLengthEncodedString([]byte(k))...)
attrData = append(attrData, PutLengthEncodedString([]byte(v))...)
}
return append(PutLengthEncodedInt(uint64(len(attrData))), attrData...)
}

// See: http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse
func (c *Conn) writeAuthHandshake() error {
if !authPluginAllowed(c.authPluginName) {
Expand Down Expand Up @@ -195,6 +209,12 @@ func (c *Conn) writeAuthHandshake() error {
capability |= CLIENT_CONNECT_WITH_DB
length += len(c.db) + 1
}
// connection attributes
attrData := c.genAttributes()
if len(attrData) > 0 {
capability |= CLIENT_CONNECT_ATTRS
length += len(attrData)
}

data := make([]byte, length+4)

Expand Down Expand Up @@ -264,6 +284,12 @@ func (c *Conn) writeAuthHandshake() error {
// Assume native client during response
pos += copy(data[pos:], c.authPluginName)
data[pos] = 0x00
pos++

// connection attributes
if len(attrData) > 0 {
copy(data[pos:], attrData)
}

return c.WritePacket(data)
}
42 changes: 42 additions & 0 deletions client/auth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package client

import (
"bytes"
"testing"

"github.com/go-mysql-org/go-mysql/mysql"
)

func TestConnGenAttributes(t *testing.T) {
c := &Conn{
// example data from
// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse41
attributes: map[string]string{
"_os": "debian6.0",
"_client_name": "libmysql",
"_pid": "22344",
"_client_version": "5.6.6-m9",
"_platform": "x86_64",
"foo": "bar",
},
}

data := c.genAttributes()

// the order of the attributes map cannot be guaranteed so to test the content
// of the attribute data we need to check its partial contents

if len(data) != 98 {
t.Fatalf("unexpected data length, got %d", len(data))
}
if data[0] != 0x61 {
t.Fatalf("unexpected length-encoded int, got %#x", data[0])
}

for k, v := range c.attributes {
fixt := append(mysql.PutLengthEncodedString([]byte(k)), mysql.PutLengthEncodedString([]byte(v))...)
if !bytes.Contains(data, fixt) {
t.Fatalf("%s attribute not found", k)
}
}
}
6 changes: 6 additions & 0 deletions client/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ type Conn struct {
// client-set capabilities only
ccaps uint32

attributes map[string]string

status uint16

charset string
Expand Down Expand Up @@ -302,6 +304,10 @@ func (c *Conn) Rollback() error {
return errors.Trace(err)
}

func (c *Conn) SetAttributes(attributes map[string]string) {
c.attributes = attributes
}

func (c *Conn) SetCharset(charset string) error {
if c.charset == charset {
return nil
Expand Down