diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1563be83c078..38c6e5da529a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -183,7 +183,8 @@ you can run those with `task test-unit` after starting the environment as descri We also have a set of "integration" tests in the `integration` directory. They use the Go MongoDB driver like a regular user application. -They could test any MongoDB-compatible database (such as FerretDB or MongoDB itself) via a regular TCP or TLS port or Unix socket. +They could test any MongoDB-compatible database (such as FerretDB or MongoDB itself) via a regular TCP or TLS port +or Unix domain socket. They also could test in-process FerretDB instances (meaning that integration tests start and stop them themselves) with a given backend. Finally, some integration tests (so-called compatibility or "compat" tests) connect to two systems diff --git a/ferretdb/ferretdb.go b/ferretdb/ferretdb.go index 9e8421604f12..08b1274b8f54 100644 --- a/ferretdb/ferretdb.go +++ b/ferretdb/ferretdb.go @@ -214,7 +214,7 @@ func (f *FerretDB) MongoDBURI() string { Path: "/", } case f.config.Listener.Unix != "": - // MongoDB really wants Unix socket path in the host part of the URI + // MongoDB really wants Unix domain socket path in the host part of the URI u = &url.URL{ Scheme: "mongodb", Host: f.l.UnixAddr().String(), diff --git a/integration/commands_diagnostic_test.go b/integration/commands_diagnostic_test.go index 3c46e8bfe242..5c78689a8d06 100644 --- a/integration/commands_diagnostic_test.go +++ b/integration/commands_diagnostic_test.go @@ -403,7 +403,7 @@ func TestCommandsDiagnosticWhatsMyURI(t *testing.T) { databaseName := s.Collection.Database().Name() collectionName := s.Collection.Name() - // only check port number on TCP connection, no need to check on Unix socket + // only check port number on TCP connection, no need to check on Unix domain socket isUnix := s.IsUnixSocket(t) // setup second client connection to check that `whatsmyuri` returns different ports diff --git a/integration/setup/setup.go b/integration/setup/setup.go index 8cbd81b06098..b375f9a3b22b 100644 --- a/integration/setup/setup.go +++ b/integration/setup/setup.go @@ -45,7 +45,7 @@ var ( targetProxyAddrF = flag.String("target-proxy-addr", "", "in-process FerretDB: use given proxy") targetTLSF = flag.Bool("target-tls", false, "in-process FerretDB: use TLS") - targetUnixSocketF = flag.Bool("target-unix-socket", false, "in-process FerretDB: use Unix socket") + targetUnixSocketF = flag.Bool("target-unix-socket", false, "in-process FerretDB: use Unix domain socket") postgreSQLURLF = flag.String("postgresql-url", "", "in-process FerretDB: PostgreSQL URL for 'postgresql' handler.") sqliteURLF = flag.String("sqlite-url", "", "in-process FerretDB: SQLite URI for 'sqlite' handler.") @@ -97,12 +97,12 @@ type SetupResult struct { MongoDBURI string } -// IsUnixSocket returns true if MongoDB URI is a Unix socket. +// IsUnixSocket returns true if MongoDB URI is a Unix domain socket. func (s *SetupResult) IsUnixSocket(tb testtb.TB) bool { tb.Helper() // we can't use a regular url.Parse because - // MongoDB really wants Unix socket path in the host part of the URI + // MongoDB really wants Unix domain socket path in the host part of the URI opts := options.Client().ApplyURI(s.MongoDBURI) res := slices.ContainsFunc(opts.Hosts, func(host string) bool { return strings.Contains(host, "/") diff --git a/internal/clientconn/conn.go b/internal/clientconn/conn.go index c4977652caa5..7385debfdf46 100644 --- a/internal/clientconn/conn.go +++ b/internal/clientconn/conn.go @@ -24,6 +24,7 @@ import ( "fmt" "io" "net" + "net/netip" "os" "path/filepath" "runtime/pprof" @@ -140,7 +141,10 @@ func (c *conn) run(ctx context.Context) (err error) { connInfo := conninfo.New() if c.netConn.RemoteAddr().Network() != "unix" { - connInfo.PeerAddr = c.netConn.RemoteAddr().String() + connInfo.Peer, err = netip.ParseAddrPort(c.netConn.RemoteAddr().String()) + if err != nil { + return + } } ctx = conninfo.Ctx(ctx, connInfo) diff --git a/internal/clientconn/conninfo/conn_info.go b/internal/clientconn/conninfo/conn_info.go index 88fe95b49eff..20921bae724d 100644 --- a/internal/clientconn/conninfo/conn_info.go +++ b/internal/clientconn/conninfo/conn_info.go @@ -17,6 +17,7 @@ package conninfo import ( "context" + "net/netip" "sync" "github.com/xdg-go/scram" @@ -32,21 +33,23 @@ var connInfoKey = contextKey{} type ConnInfo struct { // the order of fields is weird to make the struct smaller due to alignment - PeerAddr string - username string // protected by rw - password string // protected by rw - metadataRecv bool // protected by rw - sc *scram.ServerConversation // protected by rw + Peer netip.AddrPort // invalid for Unix domain sockets + + username string // protected by rw + password string // protected by rw + + rw sync.RWMutex + + metadataRecv bool // protected by rw + // If true, backend implementations should not perform authentication // by adding username and password to the connection string. // It is set to true for background connections (such us capped collections cleanup) // and by the new authentication mechanism. // See where it is used for more details. bypassBackendAuth bool // protected by rw - - rw sync.RWMutex } // New returns a new ConnInfo. @@ -54,6 +57,11 @@ func New() *ConnInfo { return new(ConnInfo) } +// LocalPeer returns whether the peer is considered local (using Unix domain socket or loopback IP). +func (connInfo *ConnInfo) LocalPeer() bool { + return !connInfo.Peer.IsValid() || connInfo.Peer.Addr().IsLoopback() +} + // Username returns stored username. func (connInfo *ConnInfo) Username() string { connInfo.rw.RLock() @@ -89,8 +97,8 @@ func (connInfo *ConnInfo) Conv() *scram.ServerConversation { // SetConv stores the SCRAM server conversation. func (connInfo *ConnInfo) SetConv(sc *scram.ServerConversation) { - connInfo.rw.RLock() - defer connInfo.rw.RUnlock() + connInfo.rw.Lock() + defer connInfo.rw.Unlock() connInfo.username = sc.Username() connInfo.sc = sc diff --git a/internal/clientconn/conninfo/conn_info_test.go b/internal/clientconn/conninfo/conn_info_test.go index fdb24436ab8f..f944e6746f0a 100644 --- a/internal/clientconn/conninfo/conn_info_test.go +++ b/internal/clientconn/conninfo/conn_info_test.go @@ -16,6 +16,7 @@ package conninfo import ( "context" + "net/netip" "testing" "github.com/stretchr/testify/assert" @@ -25,26 +26,30 @@ func TestGet(t *testing.T) { t.Parallel() for name, tc := range map[string]struct { - peerAddr string + peer netip.AddrPort + local bool }{ - "EmptyPeerAddr": { - peerAddr: "", + "Unix": { + local: true, }, - "NonEmptyPeerAddr": { - peerAddr: "127.0.0.8:1234", + "Local": { + peer: netip.MustParseAddrPort("127.42.7.1:1234"), + local: true, + }, + "NonLocal": { + peer: netip.MustParseAddrPort("192.168.0.1:1234"), + local: false, }, } { - tc := tc t.Run(name, func(t *testing.T) { - t.Parallel() - ctx := context.Background() connInfo := &ConnInfo{ - PeerAddr: tc.peerAddr, + Peer: tc.peer, } ctx = Ctx(ctx, connInfo) actual := Get(ctx) assert.Equal(t, connInfo, actual) + assert.Equal(t, tc.local, actual.LocalPeer()) }) } diff --git a/internal/handler/msg_whatsmyuri.go b/internal/handler/msg_whatsmyuri.go index 20d7ac680deb..025e96d7e982 100644 --- a/internal/handler/msg_whatsmyuri.go +++ b/internal/handler/msg_whatsmyuri.go @@ -28,7 +28,7 @@ func (h *Handler) MsgWhatsMyURI(ctx context.Context, msg *wire.OpMsg) (*wire.OpM var reply wire.OpMsg must.NoError(reply.SetSections(wire.MakeOpMsgSection( must.NotFail(types.NewDocument( - "you", conninfo.Get(ctx).PeerAddr, + "you", conninfo.Get(ctx).Peer.String(), "ok", float64(1), )), ))) diff --git a/website/blog/2022-11-21-ferretdb-0-6-2-version-release.md b/website/blog/2022-11-21-ferretdb-0-6-2-version-release.md index c2c29734c190..6f888a68352e 100644 --- a/website/blog/2022-11-21-ferretdb-0-6-2-version-release.md +++ b/website/blog/2022-11-21-ferretdb-0-6-2-version-release.md @@ -33,7 +33,7 @@ In the latest release, we have published our commands parity guide with MongoDB, ## Bug Fixes -We've fixed issues with Unix socket listeners, where you get internal errors or panic when running FerretDB with a Unix socket listener. +We've fixed issues with Unix domain socket listeners, where you get internal errors or panic when running FerretDB with a Unix domain socket listener. ## Other changes and enhancements