From 7f2da7d1943f25b44df43a0438fb9f6ec078bfef Mon Sep 17 00:00:00 2001 From: Mariusz Gronczewski Date: Sun, 19 Nov 2023 20:05:12 +0100 Subject: [PATCH] pass `*url.URL` to ConnectPacketBuilder As per discussion in #192 I've added URL as a parameter, and also an example on how to use it to extract password from URL --- autopaho/auto.go | 8 +++---- autopaho/auto_test.go | 42 ++++++++++++++++++++++++++++++++---- autopaho/net.go | 2 +- autopaho/queue/file/queue.go | 1 - 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/autopaho/auto.go b/autopaho/auto.go index d3626dd..385a7cf 100644 --- a/autopaho/auto.go +++ b/autopaho/auto.go @@ -79,7 +79,7 @@ type ClientConfig struct { WillMessage *paho.WillMessage WillProperties *paho.WillProperties - ConnectPacketBuilder func(*paho.Connect) *paho.Connect // called prior to connection allowing customisation of the CONNECT packet + ConnectPacketBuilder func(*paho.Connect, *url.URL) *paho.Connect // called prior to connection allowing customisation of the CONNECT packet // DisconnectPacketBuilder - called prior to disconnection allowing customisation of the DISCONNECT // packet. If the function returns nil, then no DISCONNECT packet will be passed; if nil a default packet is sent. @@ -157,7 +157,7 @@ func (cfg *ClientConfig) SetWillMessage(topic string, payload []byte, qos byte, // // Deprecated: Set ConnectPacketBuilder directly instead. This function exists for // backwards compatibility only (and may be removed in the future). -func (cfg *ClientConfig) SetConnectPacketConfigurator(fn func(*paho.Connect) *paho.Connect) bool { +func (cfg *ClientConfig) SetConnectPacketConfigurator(fn func(*paho.Connect, *url.URL) *paho.Connect) bool { cfg.ConnectPacketBuilder = fn return fn != nil } @@ -174,7 +174,7 @@ func (cfg *ClientConfig) SetDisConnectPacketConfigurator(fn func() *paho.Disconn // buildConnectPacket constructs a Connect packet for the paho client, based on staged configuration. // If the program uses SetConnectPacketConfigurator, the provided callback will be executed with the preliminary Connect packet representation. -func (cfg *ClientConfig) buildConnectPacket(firstConnection bool) *paho.Connect { +func (cfg *ClientConfig) buildConnectPacket(firstConnection bool, serverURL *url.URL) *paho.Connect { cp := &paho.Connect{ KeepAlive: cfg.KeepAlive, @@ -206,7 +206,7 @@ func (cfg *ClientConfig) buildConnectPacket(firstConnection bool) *paho.Connect } if cfg.ConnectPacketBuilder != nil { - cp = cfg.ConnectPacketBuilder(cp) + cp = cfg.ConnectPacketBuilder(cp, serverURL) } return cp diff --git a/autopaho/auto_test.go b/autopaho/auto_test.go index 638da1d..080085d 100644 --- a/autopaho/auto_test.go +++ b/autopaho/auto_test.go @@ -413,7 +413,7 @@ func TestClientConfig_buildConnectPacket(t *testing.T) { } // Validate initial state - cp := config.buildConnectPacket(true) + cp := config.buildConnectPacket(true, nil) if !cp.CleanStart { t.Errorf("Expected Clean Start to be true") @@ -434,7 +434,7 @@ func TestClientConfig_buildConnectPacket(t *testing.T) { config.SetUsernamePassword("testuser", []byte("testpassword")) config.SetWillMessage(fmt.Sprintf("client/%s/state", config.ClientID), []byte("disconnected"), 1, true) - cp = config.buildConnectPacket(false) + cp = config.buildConnectPacket(false, nil) if cp.CleanStart { t.Errorf("Expected Clean Start to be false") } @@ -470,16 +470,50 @@ func TestClientConfig_buildConnectPacket(t *testing.T) { } // Set an override method for the CONNECT packet - config.SetConnectPacketConfigurator(func(c *paho.Connect) *paho.Connect { + config.SetConnectPacketConfigurator(func(c *paho.Connect, u *url.URL) *paho.Connect { delay := uint32(200) c.WillProperties.WillDelayInterval = &delay + c.Username = u.User.Username() + p, _ := u.User.Password() + c.Password = []byte(p) return c }) - cp = config.buildConnectPacket(false) + testUrl, _ := url.Parse("mqtt://mqtt_user:mqtt_pass@127.0.0.1:1883") + cp = config.buildConnectPacket(false, testUrl) if *(cp.WillProperties.WillDelayInterval) != 200 { // verifies the override t.Errorf("Will message Delay Interval did not match expected [200]: found [%v]", *(cp.Properties.WillDelayInterval)) } + if cp.Username != "mqtt_user" { // verifies the override + t.Errorf("username did not match expected [mqtt_user]: found [%v]", cp.Username) + } + if string(cp.Password) != "mqtt_pass" { // verifies the override + t.Errorf("password did not match expected [mqtt_pass]: found [%v]", string(cp.Password)) + } + +} +// Example of using ConnectPacketBuilder to extract server password from URL +func ExampleClientConfig_ConnectPacketBuilder() { + serverURL, _ := url.Parse("mqtt://mqtt_user:mqtt_pass@127.0.0.1:1883") + config := ClientConfig{ + ServerUrls: []*url.URL{serverURL}, + ConnectRetryDelay: 5 * time.Second, + ConnectTimeout: 5 * time.Second, + ClientConfig: paho.ClientConfig{ + ClientID: "test", + }, + } + config.ConnectPacketBuilder = func(c *paho.Connect, u *url.URL) *paho.Connect { + // Extracting password from URL + c.Username = u.User.Username() + // up to user to catch empty password passed via URL + p, _ := u.User.Password() + c.Password = []byte(p) + return c + } + cp := config.buildConnectPacket(false, serverURL) + fmt.Printf("user: %s, pass: %s", cp.Username, string(cp.Password)) + // Output: user: mqtt_user, pass: mqtt_pass } diff --git a/autopaho/net.go b/autopaho/net.go index 267bcc2..446c0e8 100644 --- a/autopaho/net.go +++ b/autopaho/net.go @@ -65,7 +65,7 @@ func establishServerConnection(ctx context.Context, cfg ClientConfig, firstConne cli.SetErrorLogger(cfg.PahoErrors) } - cp := cfg.buildConnectPacket(firstConnection) + cp := cfg.buildConnectPacket(firstConnection, u) connack, err = cli.Connect(connectionCtx, cp) // will return an error if the connection is unsuccessful (checks the reason code) if err == nil { // Successfully connected cancelConnCtx() diff --git a/autopaho/queue/file/queue.go b/autopaho/queue/file/queue.go index bee833e..04e6651 100644 --- a/autopaho/queue/file/queue.go +++ b/autopaho/queue/file/queue.go @@ -156,7 +156,6 @@ func (q *Queue) put(p io.Reader) error { if err != nil { return err } - if _, err = io.Copy(f, p); err != nil { f.Close() _ = os.Remove(f.Name()) // Attempt to remove the partial file (not much we can do if this fails)