pqi-conformance 1.0.14.0 → 1.0.15.0
raw patch · 4 files changed
+86/−1 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGELOG.md +6/−0
- pqi-conformance.cabal +2/−1
- src/library/Pqi/Conformance/Operation/Connectdb.hs +2/−0
- src/library/Pqi/Conformance/Operation/Connectdb/Hostaddr.hs +76/−0
CHANGELOG.md view
@@ -1,3 +1,9 @@+# v1.0.15.0++## Non-breaking++- Added a `connectdb` spec covering a conninfo that gives an unresolvable `host` together with a `hostaddr` that is reachable (a loopback proxy in front of the container). `libpq` connects to the `hostaddr` and never resolves the `host`, and still reports the name from `host`. Both the `keyword=value` form and the URI form (`?hostaddr=`) are covered. Found `pqi-native` never reads `hostaddr`: it resolves `host` through DNS regardless, so the connection fails, and the key is passed on in the startup packet as if it were a server setting. Found via `pqi-native` issue #11.+ # v1.0.14.0 ## Non-breaking
pqi-conformance.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: pqi-conformance-version: 1.0.14.0+version: 1.0.15.0 category: Database, PostgreSQL, Testing synopsis: Differential conformance tests for pqi adapters description:@@ -96,6 +96,7 @@ Pqi.Conformance.Operation.CmdTuples Pqi.Conformance.Operation.Connectdb Pqi.Conformance.Operation.Connectdb.HandshakeReset+ Pqi.Conformance.Operation.Connectdb.Hostaddr Pqi.Conformance.Operation.Connectdb.MissingUnixSocketDirectory Pqi.Conformance.Operation.Connectdb.Rejection Pqi.Conformance.Operation.Connectdb.UnixSocketUri
src/library/Pqi/Conformance/Operation/Connectdb.hs view
@@ -15,6 +15,7 @@ import Pqi.Conformance.Harness import Pqi.Conformance.Observation import qualified Pqi.Conformance.Operation.Connectdb.HandshakeReset as HandshakeReset+import qualified Pqi.Conformance.Operation.Connectdb.Hostaddr as Hostaddr import qualified Pqi.Conformance.Operation.Connectdb.MissingUnixSocketDirectory as MissingUnixSocketDirectory import qualified Pqi.Conformance.Operation.Connectdb.Rejection as Rejection import qualified Pqi.Conformance.Operation.Connectdb.UnixSocketUri as UnixSocketUri@@ -31,6 +32,7 @@ MissingUnixSocketDirectory.spec adapter UnresolvableHost.spec adapter HandshakeReset.spec adapter+ Hostaddr.spec adapter describe "connectdb" do it "opens a usable connection" \conninfo ->
+ src/library/Pqi/Conformance/Operation/Connectdb/Hostaddr.hs view
@@ -0,0 +1,76 @@+-- | Coverage for a conninfo that gives both @host@ and @hostaddr@, in the+-- @keyword=value@ form and in the URI form (@?hostaddr=…@).+--+-- Per libpq's documentation+-- (<https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-HOSTADDR>),+-- when @hostaddr@ is present the client connects to that numeric address and+-- must not resolve @host@: the name is kept only for authentication, SSL+-- certificate verification and the like. A name that no resolver can+-- translate is therefore harmless as long as @hostaddr@ is usable.+--+-- Found in @pqi-native@ (<https://github.com/nikita-volkov/pqi-native/issues/11>):+-- @hostaddr@ is not one of the keys 'Pqi.Native.Connection.parseConnInfo'+-- recognises, so it was never consulted when dialling. The candidate resolved+-- @host@ through DNS regardless, and so reported a connection failure for a+-- conninfo that @libpq@ connects with. The key was also left in the+-- parameters forwarded to the server in the startup packet, where @libpq@+-- never sends it.+--+-- The scenario points @hostaddr@ at a loopback proxy in front of the shared+-- container ('withProxy'), which makes the numeric address valid wherever the+-- container's Docker daemon runs, and gives @host@ a name reserved by+-- RFC 2606 to never resolve. The observation is the connection status+-- together with the parsed host. The status carries the divergence. The host+-- pins that the name is still what 'Pqi.host' reports, as @libpq@ does, so a+-- fix that simply replaced @host@ with @hostaddr@ would be caught.+--+-- The URI form is its own example because @pqi-native@ parses a URI's query+-- through a separate code path, where a fix to the @keyword=value@ one does+-- not reach.+module Pqi.Conformance.Operation.Connectdb.Hostaddr+ ( spec,+ )+where++import qualified Data.ByteString.Char8 as ByteString.Char8+import qualified Pqi+import Pqi.Conformance.Fixture.ConnectionLoss (withProxy)+import Pqi.Conformance.Harness+import Pqi.Conformance.Prelude+import Test.Hspec++spec :: Pqi.Adapter -> SpecWith ByteString+spec adapter =+ describe "connectdb" do+ describe "a conninfo with an unresolvable host and a hostaddr" do+ it "connects to the hostaddr without resolving the host, like the reference" \conninfo ->+ differentialConnect adapter conninfo \adapter' conninfo' ->+ withProxy conninfo' \proxiedConninfo _ ->+ -- Later keywords win, so these override the proxy's @host@.+ observe adapter' (proxiedConninfo <> " host=nonexistent.invalid.host hostaddr=127.0.0.1")++ it "does the same when given as a URI query parameter" \conninfo ->+ differentialConnect adapter conninfo \adapter' conninfo' ->+ withProxy conninfo' \proxiedConninfo _ ->+ observe+ adapter'+ ( "postgresql://postgres@nonexistent.invalid.host:"+ <> proxyPort proxiedConninfo+ <> "/postgres?hostaddr=127.0.0.1"+ )++observe :: Pqi.Adapter -> ByteString -> IO (Pqi.ConnStatus, Maybe ByteString)+observe adapter conninfo = do+ connection <- Pqi.connectdb adapter conninfo+ observedStatus <- Pqi.status connection+ observedHost <- Pqi.host connection+ Pqi.finish connection+ pure (observedStatus, observedHost)++-- | The port the proxy listens on: 'withProxy' appends it as the last+-- keyword of the conninfo it hands out.+proxyPort :: ByteString -> ByteString+proxyPort conninfo =+ case [value | token <- reverse (ByteString.Char8.words conninfo), Just value <- [ByteString.Char8.stripPrefix "port=" token]] of+ value : _ -> value+ [] -> error "proxyPort: no port in the proxied conninfo"