diff --git a/rendezvous/client.go b/rendezvous/client.go index 15dbf48b..1ff3f3d1 100644 --- a/rendezvous/client.go +++ b/rendezvous/client.go @@ -13,6 +13,7 @@ import ( "github.com/gorilla/websocket" "github.com/psanford/wormhole-william/internal/crypto" "github.com/psanford/wormhole-william/rendezvous/internal/msgs" + "github.com/psanford/wormhole-william/version" ) // NewClient returns a Rendezvous client. URL is the websocket @@ -21,8 +22,8 @@ import ( // AppID is the application identity string of the client. // // Two clients can only communicate if they have the same AppID. -func NewClient(url, sideID, appID string) *Client { - return &Client{ +func NewClient(url, sideID, appID string, opts ...ClientOption) *Client { + c := &Client{ url: url, sideID: sideID, appID: appID, @@ -33,6 +34,12 @@ func NewClient(url, sideID, appID string) *Client { pendingMsgWaiters: make(map[uint32]chan uint32), } + + for _, opt := range opts { + opt.setValue(c) + } + + return c } type pendingMsg struct { @@ -52,6 +59,9 @@ type Client struct { nameplate string + agentString string + agentVersion string + wsClient *websocket.Conn mailboxMsgs []MailboxEvent @@ -511,10 +521,26 @@ func (c *Client) prepareMsg(msg interface{}) (string, error) { return id, nil } +func (c *Client) agentID() (string, string) { + agent := c.agentString + if agent == "" { + agent = version.AgentString + } + v := c.agentVersion + if v == "" { + v = version.AgentVersion + } + + return agent, v +} + func (c *Client) bind(ctx context.Context, side, appID string) error { + agent, version := c.agentID() + bind := msgs.Bind{ - Side: side, - AppID: appID, + Side: side, + AppID: appID, + ClientVersion: []string{agent, version}, } _, err := c.sendAndWait(ctx, &bind) diff --git a/rendezvous/client_test.go b/rendezvous/client_test.go index 4a7dcf79..2c990f22 100644 --- a/rendezvous/client_test.go +++ b/rendezvous/client_test.go @@ -7,6 +7,7 @@ import ( "github.com/psanford/wormhole-william/internal/crypto" "github.com/psanford/wormhole-william/rendezvous/rendezvousservertest" + "github.com/psanford/wormhole-william/version" ) func TestBasicClient(t *testing.T) { @@ -30,6 +31,15 @@ func TestBasicClient(t *testing.T) { t.Fatalf("MOTD got=%s expected=%s", info.MOTD, rendezvousservertest.TestMotd) } + gotAgents := ts.Agents() + expectAgents := [][]string{ + []string{version.AgentString, version.AgentVersion}, + } + + if !reflect.DeepEqual(gotAgents, expectAgents) { + t.Fatalf("got agents=%v expected=%v", gotAgents, expectAgents) + } + nameplate, err := c0.CreateMailbox(ctx) if err != nil { t.Fatal(err) @@ -100,3 +110,33 @@ func TestBasicClient(t *testing.T) { default: } } + +func TestCustomUserAgent(t *testing.T) { + ts := rendezvousservertest.NewServer() + defer ts.Close() + + side0 := crypto.RandSideID() + appID := "nightclubs-reasonableness" + + agentString := "deafening-buttermilk" + agentVersion := "v1.2.3" + c0 := NewClient(ts.WebSocketURL(), side0, appID, WithVersion(agentString, agentVersion)) + + ctx := context.Background() + + _, err := c0.Connect(ctx) + if err != nil { + t.Fatal(err) + } + + gotAgents := ts.Agents() + expectAgents := [][]string{ + []string{agentString, agentVersion}, + } + + if !reflect.DeepEqual(gotAgents, expectAgents) { + t.Fatalf("got agents=%v expected=%v", gotAgents, expectAgents) + } + + c0.Close(ctx, "") +} diff --git a/rendezvous/internal/msgs/msgs.go b/rendezvous/internal/msgs/msgs.go index 9da619a7..0e3765db 100644 --- a/rendezvous/internal/msgs/msgs.go +++ b/rendezvous/internal/msgs/msgs.go @@ -19,6 +19,9 @@ type Bind struct { ID string `json:"id"` Side string `json:"side"` AppID string `json:"appid"` + // ClientVersion is by convention a two value array + // of [client_id, version] + ClientVersion []string `json:"client_version"` } // Client sent aollocate message diff --git a/rendezvous/options.go b/rendezvous/options.go new file mode 100644 index 00000000..75774466 --- /dev/null +++ b/rendezvous/options.go @@ -0,0 +1,24 @@ +package rendezvous + +type ClientOption interface { + setValue(*Client) +} + +type versionOption struct { + agentString string + agentVersion string +} + +func (o *versionOption) setValue(c *Client) { + c.agentString = o.agentString + c.agentVersion = o.agentVersion +} + +// WithVersion returns a ClientOption to override the default client +// identifier and version reported to the rendezvous server. +func WithVersion(agentID string, version string) ClientOption { + return &versionOption{ + agentString: agentID, + agentVersion: version, + } +} diff --git a/rendezvous/rendezvousservertest/rendezvousservertest.go b/rendezvous/rendezvousservertest/rendezvousservertest.go index cfb7e733..ffdbae50 100644 --- a/rendezvous/rendezvousservertest/rendezvousservertest.go +++ b/rendezvous/rendezvousservertest/rendezvousservertest.go @@ -25,6 +25,7 @@ type TestServer struct { mu sync.Mutex mailboxes map[string]*mailbox nameplates map[int16]string + agents [][]string } func NewServer() *TestServer { @@ -40,6 +41,10 @@ func NewServer() *TestServer { return ts } +func (ts *TestServer) Agents() [][]string { + return ts.agents +} + func (ts *TestServer) WebSocketURL() string { u, err := url.Parse(ts.URL) if err != nil { @@ -236,6 +241,10 @@ func (ts *TestServer) handleWS(w http.ResponseWriter, r *http.Request) { continue } + ts.mu.Lock() + ts.agents = append(ts.agents, m.ClientVersion) + ts.mu.Unlock() + sideID = m.Side case *msgs.Allocate: ackMsg(m.ID) diff --git a/version/version.go b/version/version.go new file mode 100644 index 00000000..5d1b7d71 --- /dev/null +++ b/version/version.go @@ -0,0 +1,8 @@ +package version + +var ( + // Default Agent identifier sent to rendezvous server + AgentString = "go-william" + // Default Agent version sent to rendezvous server + AgentVersion = "v1.0.0" +)