215 lines
5.9 KiB
Go
215 lines
5.9 KiB
Go
package apm
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type urlInfo struct {
|
|
addrs []string
|
|
user string
|
|
pass string
|
|
db string
|
|
options map[string]string
|
|
}
|
|
|
|
func isOptSep(c rune) bool {
|
|
return c == ';' || c == '&'
|
|
}
|
|
|
|
func extractURL(s string) (*urlInfo, error) {
|
|
if strings.HasPrefix(s, "mongodb://") {
|
|
s = s[10:]
|
|
}
|
|
info := &urlInfo{options: make(map[string]string)}
|
|
if c := strings.Index(s, "?"); c != -1 {
|
|
for _, pair := range strings.FieldsFunc(s[c+1:], isOptSep) {
|
|
l := strings.SplitN(pair, "=", 2)
|
|
if len(l) != 2 || l[0] == "" || l[1] == "" {
|
|
return nil, errors.New("connection option must be key=value: " + pair)
|
|
}
|
|
info.options[l[0]] = l[1]
|
|
}
|
|
s = s[:c]
|
|
}
|
|
if c := strings.Index(s, "@"); c != -1 {
|
|
pair := strings.SplitN(s[:c], ":", 2)
|
|
if len(pair) > 2 || pair[0] == "" {
|
|
return nil, errors.New("credentials must be provided as user:pass@host")
|
|
}
|
|
var err error
|
|
info.user, err = url.QueryUnescape(pair[0])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot unescape username in URL: %q", pair[0])
|
|
}
|
|
if len(pair) > 1 {
|
|
info.pass, err = url.QueryUnescape(pair[1])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot unescape password in URL")
|
|
}
|
|
}
|
|
s = s[c+1:]
|
|
}
|
|
if c := strings.Index(s, "/"); c != -1 {
|
|
info.db = s[c+1:]
|
|
s = s[:c]
|
|
}
|
|
info.addrs = strings.Split(s, ",")
|
|
return info, nil
|
|
}
|
|
|
|
func ParseMongoURL(url string) (scheme string, host string, path string) {
|
|
dialInfo, err := doParseMongoURL(url)
|
|
if err != nil {
|
|
return "", "", ""
|
|
}
|
|
scheme = "mongodb"
|
|
if dialInfo.Addrs == nil {
|
|
host = ""
|
|
} else {
|
|
host = strings.Join(dialInfo.Addrs, ",")
|
|
}
|
|
path = dialInfo.Database
|
|
return scheme, host, path
|
|
}
|
|
|
|
// ParseURL parses a MongoDB URL as accepted by the Dial function and returns
|
|
// a value suitable for providing into DialWithInfo.
|
|
//
|
|
// See Dial for more details on the format of url.
|
|
func doParseMongoURL(url string) (*DialInfo, error) {
|
|
uinfo, err := extractURL(url)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
direct := false
|
|
mechanism := ""
|
|
service := ""
|
|
source := ""
|
|
setName := ""
|
|
poolLimit := 0
|
|
for k, v := range uinfo.options {
|
|
switch k {
|
|
case "authSource":
|
|
source = v
|
|
case "authMechanism":
|
|
mechanism = v
|
|
case "gssapiServiceName":
|
|
service = v
|
|
case "replicaSet":
|
|
setName = v
|
|
case "maxPoolSize":
|
|
poolLimit, err = strconv.Atoi(v)
|
|
if err != nil {
|
|
return nil, errors.New("bad value for maxPoolSize: " + v)
|
|
}
|
|
case "connect":
|
|
if v == "direct" {
|
|
direct = true
|
|
break
|
|
}
|
|
if v == "replicaSet" {
|
|
break
|
|
}
|
|
fallthrough
|
|
default:
|
|
return nil, errors.New("unsupported connection URL option: " + k + "=" + v)
|
|
}
|
|
}
|
|
info := DialInfo{
|
|
Addrs: uinfo.addrs,
|
|
Direct: direct,
|
|
Database: uinfo.db,
|
|
Username: uinfo.user,
|
|
Password: uinfo.pass,
|
|
Mechanism: mechanism,
|
|
Service: service,
|
|
Source: source,
|
|
PoolLimit: poolLimit,
|
|
ReplicaSetName: setName,
|
|
}
|
|
return &info, nil
|
|
}
|
|
|
|
// DialInfo holds options for establishing a session with a MongoDB cluster.
|
|
// To use a URL, see the Dial function.
|
|
type DialInfo struct {
|
|
// Addrs holds the addresses for the seed servers.
|
|
Addrs []string
|
|
|
|
// Direct informs whether to establish connections only with the
|
|
// specified seed servers, or to obtain information for the whole
|
|
// cluster and establish connections with further servers too.
|
|
Direct bool
|
|
|
|
// Timeout is the amount of time to wait for a server to respond when
|
|
// first connecting and on follow up operations in the session. If
|
|
// timeout is zero, the call may block forever waiting for a connection
|
|
// to be established. Timeout does not affect logic in DialServer.
|
|
Timeout time.Duration
|
|
|
|
// FailFast will cause connection and query attempts to fail faster when
|
|
// the server is unavailable, instead of retrying until the configured
|
|
// timeout period. Note that an unavailable server may silently drop
|
|
// packets instead of rejecting them, in which case it's impossible to
|
|
// distinguish it from a slow server, so the timeout stays relevant.
|
|
FailFast bool
|
|
|
|
// Database is the default database name used when the Session.DB method
|
|
// is called with an empty name, and is also used during the initial
|
|
// authentication if Source is unset.
|
|
Database string
|
|
|
|
// ReplicaSetName, if specified, will prevent the obtained session from
|
|
// communicating with any server which is not part of a replica set
|
|
// with the given name. The default is to communicate with any server
|
|
// specified or discovered via the servers contacted.
|
|
ReplicaSetName string
|
|
|
|
// Source is the database used to establish credentials and privileges
|
|
// with a MongoDB server. Defaults to the value of Database, if that is
|
|
// set, or "admin" otherwise.
|
|
Source string
|
|
|
|
// Service defines the service name to use when authenticating with the GSSAPI
|
|
// mechanism. Defaults to "mongodb".
|
|
Service string
|
|
|
|
// ServiceHost defines which hostname to use when authenticating
|
|
// with the GSSAPI mechanism. If not specified, defaults to the MongoDB
|
|
// server's address.
|
|
ServiceHost string
|
|
|
|
// Mechanism defines the protocol for credential negotiation.
|
|
// Defaults to "MONGODB-CR".
|
|
Mechanism string
|
|
|
|
// Username and Password inform the credentials for the initial authentication
|
|
// done on the database defined by the Source field. See Session.Login.
|
|
Username string
|
|
Password string
|
|
|
|
// PoolLimit defines the per-server socket pool limit. Defaults to 4096.
|
|
// See Session.SetPoolLimit for details.
|
|
PoolLimit int
|
|
|
|
// DialServer optionally specifies the dial function for establishing
|
|
// connections with the MongoDB servers.
|
|
DialServer func(addr *ServerAddr) (net.Conn, error)
|
|
|
|
// WARNING: This field is obsolete. See DialServer above.
|
|
Dial func(addr net.Addr) (net.Conn, error)
|
|
}
|
|
|
|
// ServerAddr represents the address for establishing a connection to an
|
|
// individual MongoDB server.
|
|
type ServerAddr struct {
|
|
str string
|
|
tcp *net.TCPAddr
|
|
}
|