Skip to content

Commit 165af68

Browse files
feliixxdomodwyer
authored andcommitted
send metadata during handshake (#28)
fix [#484](https://github.com/go-mgo/mgo/issues/484) Annotate connections with metadata provided by the connecting client. informations send: { "aplication": { // optional "name": "myAppName" } "driver": { "name": "mgo", "version": "v2" }, "os": { "type": runtime.GOOS, "architecture": runtime.GOARCH } } to set "application.name", add `appname` param in options of string connection URI, for example : "mongodb://localhost:27017?appname=myAppName"
1 parent 93aaa6e commit 165af68

File tree

3 files changed

+74
-3
lines changed

3 files changed

+74
-3
lines changed

cluster.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"errors"
3131
"fmt"
3232
"net"
33+
"runtime"
3334
"strconv"
3435
"strings"
3536
"sync"
@@ -61,16 +62,18 @@ type mongoCluster struct {
6162
cachedIndex map[string]bool
6263
sync chan bool
6364
dial dialer
65+
appName string
6466
}
6567

66-
func newCluster(userSeeds []string, direct, failFast bool, dial dialer, setName string) *mongoCluster {
68+
func newCluster(userSeeds []string, direct, failFast bool, dial dialer, setName string, appName string) *mongoCluster {
6769
cluster := &mongoCluster{
6870
userSeeds: userSeeds,
6971
references: 1,
7072
direct: direct,
7173
failFast: failFast,
7274
dial: dial,
7375
setName: setName,
76+
appName: appName,
7477
}
7578
cluster.serverSynced.L = cluster.RWMutex.RLocker()
7679
cluster.sync = make(chan bool, 1)
@@ -144,7 +147,17 @@ func (cluster *mongoCluster) isMaster(socket *mongoSocket, result *isMasterResul
144147
// Monotonic let's it talk to a slave and still hold the socket.
145148
session := newSession(Monotonic, cluster, 10*time.Second)
146149
session.setSocket(socket)
147-
err := session.Run("ismaster", result)
150+
151+
// provide some meta infos on the client,
152+
// see https://github.com/mongodb/specifications/blob/master/source/mongodb-handshake/handshake.rst#connection-handshake
153+
// for details
154+
metaInfo := bson.M{"driver": bson.M{"name": "mgo", "version": "globalsign"},
155+
"os": bson.M{"type": runtime.GOOS, "architecture": runtime.GOARCH}}
156+
157+
if cluster.appName != "" {
158+
metaInfo["application"] = bson.M{"name": cluster.appName}
159+
}
160+
err := session.Run(bson.D{{"isMaster", 1}, {"client", metaInfo}}, result)
148161
session.Close()
149162
return err
150163
}

session.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,10 @@ const (
235235
// Defines the per-server socket pool limit. Defaults to 4096.
236236
// See Session.SetPoolLimit for details.
237237
//
238+
// appName=<appName>
239+
//
240+
// The identifier of the client application which ran the operation. This
241+
// param can't exceed 128 bytes
238242
//
239243
// Relevant documentation:
240244
//
@@ -279,6 +283,7 @@ func ParseURL(url string) (*DialInfo, error) {
279283
source := ""
280284
setName := ""
281285
poolLimit := 0
286+
appName := ""
282287
readPreferenceMode := Primary
283288
var readPreferenceTagSets []bson.D
284289
for _, opt := range uinfo.options {
@@ -296,6 +301,11 @@ func ParseURL(url string) (*DialInfo, error) {
296301
if err != nil {
297302
return nil, errors.New("bad value for maxPoolSize: " + opt.value)
298303
}
304+
case "appName":
305+
if len(opt.value) > 128 {
306+
return nil, errors.New("appName too long, must be < 128 bytes: " + opt.value)
307+
}
308+
appName = opt.value
299309
case "readPreference":
300310
switch opt.value {
301311
case "nearest":
@@ -350,6 +360,7 @@ func ParseURL(url string) (*DialInfo, error) {
350360
Service: service,
351361
Source: source,
352362
PoolLimit: poolLimit,
363+
AppName: appName,
353364
ReadPreference: &ReadPreference{
354365
Mode: readPreferenceMode,
355366
TagSets: readPreferenceTagSets,
@@ -409,6 +420,9 @@ type DialInfo struct {
409420
// See Session.SetPoolLimit for details.
410421
PoolLimit int
411422

423+
// The identifier of the client application which ran the operation.
424+
AppName string
425+
412426
// ReadPreference defines the manner in which servers are chosen. See
413427
// Session.SetMode and Session.SelectServers.
414428
ReadPreference *ReadPreference
@@ -472,7 +486,7 @@ func DialWithInfo(info *DialInfo) (*Session, error) {
472486
}
473487
addrs[i] = addr
474488
}
475-
cluster := newCluster(addrs, info.Direct, info.FailFast, dialer{info.Dial, info.DialServer}, info.ReplicaSetName)
489+
cluster := newCluster(addrs, info.Direct, info.FailFast, dialer{info.Dial, info.DialServer}, info.ReplicaSetName, info.AppName)
476490
session := newSession(Eventual, cluster, info.Timeout)
477491
session.defaultdb = info.Database
478492
if session.defaultdb == "" {

session_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,50 @@ func (s *S) TestURLInvalidReadPreferenceTags(c *C) {
200200
}
201201
}
202202

203+
func (s *S) TestURLWithAppName(c *C) {
204+
if !s.versionAtLeast(3, 4) {
205+
c.Skip("appName depends on MongoDB 3.4+")
206+
}
207+
appName := "myAppName"
208+
session, err := mgo.Dial("localhost:40001?appName=" + appName)
209+
c.Assert(err, IsNil)
210+
defer session.Close()
211+
212+
db := session.DB("mydb")
213+
214+
err = db.Run(bson.D{{"profile", 2}}, nil)
215+
c.Assert(err, IsNil)
216+
217+
coll := db.C("mycoll")
218+
err = coll.Insert(M{"a": 1, "b": 2})
219+
c.Assert(err, IsNil)
220+
221+
result := struct{ A, B int }{}
222+
err = coll.Find(M{"a": 1}).One(&result)
223+
c.Assert(err, IsNil)
224+
225+
profileResult := struct {
226+
AppName string `bson:"appName"`
227+
}{}
228+
229+
err = db.C("system.profile").Find(nil).Sort("-ts").One(&profileResult)
230+
c.Assert(err, IsNil)
231+
c.Assert(appName, Equals, profileResult.AppName)
232+
// reset profiling to 0 as it add unecessary overhead to all other test
233+
err = db.Run(bson.D{{"profile", 0}}, nil)
234+
c.Assert(err, IsNil)
235+
}
236+
237+
func (s *S) TestURLWithAppNameTooLong(c *C) {
238+
if !s.versionAtLeast(3, 4) {
239+
c.Skip("appName depends on MongoDB 3.4+")
240+
}
241+
appName := "myAppNameWayTooLongmyAppNameWayTooLongmyAppNameWayTooLongmyAppNameWayTooLong"
242+
appName += appName
243+
_, err := mgo.Dial("localhost:40001?appName=" + appName)
244+
c.Assert(err, ErrorMatches, "appName too long, must be < 128 bytes: "+appName)
245+
}
246+
203247
func (s *S) TestInsertFindOne(c *C) {
204248
session, err := mgo.Dial("localhost:40001")
205249
c.Assert(err, IsNil)

0 commit comments

Comments
 (0)