refactor: deduplicate host resolution logic and observations

Replaces the "API server listening on unix socket" with simpler
"API server listening on " observation. This allows refactoring
redundant code.

Signed-off-by: Taimoor Zaeem <taimoorzaeem@gmail.com>
This commit is contained in:
Taimoor Zaeem
2025-09-01 15:00:20 -05:00
committed by Steve Chavez
parent ef9ac61d9e
commit 41b6ebea58
6 changed files with 46 additions and 44 deletions
+4 -9
View File
@@ -1,5 +1,3 @@
{-# LANGUAGE NamedFieldPuns #-}
module PostgREST.Admin module PostgREST.Admin
( runAdmin ( runAdmin
) where ) where
@@ -10,15 +8,13 @@ import qualified Network.Wai as Wai
import qualified Network.Wai.Handler.Warp as Warp import qualified Network.Wai.Handler.Warp as Warp
import Control.Monad.Extra (whenJust) import Control.Monad.Extra (whenJust)
import Network.Socket hiding (addrFamily)
import Network.Socket
import Network.Socket.ByteString import Network.Socket.ByteString
import PostgREST.AppState (AppState) import PostgREST.AppState (AppState)
import PostgREST.Config (AppConfig (..))
import PostgREST.MediaType (MediaType (..), toContentType) import PostgREST.MediaType (MediaType (..), toContentType)
import PostgREST.Metrics (metricsToText) import PostgREST.Metrics (metricsToText)
import PostgREST.Network (resolveHost) import PostgREST.Network (resolveSocketToAddress)
import PostgREST.Observation (Observation (..)) import PostgREST.Observation (Observation (..))
import qualified PostgREST.AppState as AppState import qualified PostgREST.AppState as AppState
@@ -27,10 +23,9 @@ import Protolude
runAdmin :: AppState -> Warp.Settings -> IO () runAdmin :: AppState -> Warp.Settings -> IO ()
runAdmin appState settings = do runAdmin appState settings = do
AppConfig{configAdminServerPort} <- AppState.getConfig appState
whenJust (AppState.getSocketAdmin appState) $ \adminSocket -> do whenJust (AppState.getSocketAdmin appState) $ \adminSocket -> do
host <- resolveHost adminSocket address <- resolveSocketToAddress adminSocket
observer $ AdminStartObs host configAdminServerPort observer $ AdminStartObs address
void . forkIO $ Warp.runSettingsSocket settings adminSocket adminApp void . forkIO $ Warp.runSettingsSocket settings adminSocket adminApp
where where
adminApp = admin appState adminApp = admin appState
+4 -9
View File
@@ -46,7 +46,7 @@ import PostgREST.Auth.Types (AuthResult (..))
import PostgREST.Config (AppConfig (..), LogLevel (..), import PostgREST.Config (AppConfig (..), LogLevel (..),
LogQuery (..)) LogQuery (..))
import PostgREST.Error (Error) import PostgREST.Error (Error)
import PostgREST.Network (resolveHost) import PostgREST.Network (resolveSocketToAddress)
import PostgREST.Observation (Observation (..)) import PostgREST.Observation (Observation (..))
import PostgREST.Response.Performance (ServerTiming (..), import PostgREST.Response.Performance (ServerTiming (..),
serverTimingHeader) serverTimingHeader)
@@ -56,7 +56,6 @@ import PostgREST.Version (docsVersion, prettyVersion)
import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Char8 as BS
import qualified Data.List as L import qualified Data.List as L
import qualified Network.HTTP.Types as HTTP import qualified Network.HTTP.Types as HTTP
import qualified Network.Socket as NS
import Protolude hiding (Handler) import Protolude hiding (Handler)
import System.TimeIt (timeItT) import System.TimeIt (timeItT)
@@ -76,13 +75,9 @@ run appState = do
let app = postgrest configLogLevel appState (AppState.schemaCacheLoader appState) let app = postgrest configLogLevel appState (AppState.schemaCacheLoader appState)
case configServerUnixSocket of do
Just path -> do address <- resolveSocketToAddress (AppState.getSocketREST appState)
observer $ AppServerUnixObs path observer $ AppServerAddressObs address
Nothing -> do
port <- NS.socketPort $ AppState.getSocketREST appState
host <- resolveHost $ AppState.getSocketREST appState
observer $ AppServerPortObs (fromJust host) port
Warp.runSettingsSocket (serverSettings conf) (AppState.getSocketREST appState) app Warp.runSettingsSocket (serverSettings conf) (AppState.getSocketREST appState) app
+28 -12
View File
@@ -1,21 +1,37 @@
module PostgREST.Network module PostgREST.Network
( resolveHost ( resolveSocketToAddress
) where ) where
import Data.IP (fromHostAddress, fromHostAddress6)
import Data.String (IsString (..)) import Data.String (IsString (..))
import qualified Network.Socket as NS import qualified Network.Socket as NS
import Protolude import Protolude
resolveHost :: NS.Socket -> IO (Maybe Text) -- | Resolves the socket to an address depending on the socket type. The Show
resolveHost sock = do -- instance of the socket types automatically resolves it to the correct
-- address. Example resolution:
-- -----------------------------------------------------
-- | IPv4 | IPv6 | Unix |
-- -----------------------------------------------------
-- | 127.0.0.1:80 | [2001:db8::1]:80 | /tmp/pgrst.sock |
-- -----------------------------------------------------
resolveSocketToAddress :: NS.Socket -> IO Text
resolveSocketToAddress sock = do
sn <- NS.getSocketName sock sn <- NS.getSocketName sock
case sn of return $ showSocketAddr sn
NS.SockAddrInet _ hostAddr -> pure $ Just $ fromString $ show $ fromHostAddress hostAddr
-- The IPv6 addresses are wrapped in [] brackets. This is done in accordance -- |
-- to RFC 3986 (https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2). -- >>> let addr_ipv4 = NS.SockAddrInet 80 (NS.tupleToHostAddress (127,0,0,1))
-- In short, we did this to have a clear separation between the port and host -- >>> let addr_ipv6 = NS.SockAddrInet6 80 0 (0,0,0,1) 0
-- because the components of an IPv6 are separated with the ':' character. -- >>> let addr_unix = NS.SockAddrUnix "/tmp/pgrst.sock"
NS.SockAddrInet6 _ _ hostAddr6 _ -> pure $ Just $ fromString $ "[" ++ show (fromHostAddress6 hostAddr6) ++ "]" --
_ -> pure Nothing -- >>> showSocketAddr addr_ipv4
-- "127.0.0.1:80"
-- >>> showSocketAddr addr_ipv6
-- "[::1]:80"
--
-- >>> showSocketAddr addr_unix
-- "/tmp/pgrst.sock"
showSocketAddr :: NS.SockAddr -> Text
showSocketAddr = fromString . show
+6 -11
View File
@@ -21,19 +21,16 @@ import qualified Hasql.Connection as SQL
import qualified Hasql.Pool as SQL import qualified Hasql.Pool as SQL
import qualified Hasql.Pool.Observation as SQL import qualified Hasql.Pool.Observation as SQL
import Network.HTTP.Types.Status (Status) import Network.HTTP.Types.Status (Status)
import qualified Network.Socket as NS
import Numeric (showFFloat) import Numeric (showFFloat)
import PostgREST.Config.PgVersion import PostgREST.Config.PgVersion
import qualified PostgREST.Error as Error import qualified PostgREST.Error as Error
import Protolude hiding (toList) import Protolude hiding (toList)
import Protolude.Partial (fromJust)
data Observation data Observation
= AdminStartObs (Maybe Text) (Maybe Int) = AdminStartObs Text
| AppStartObs ByteString | AppStartObs ByteString
| AppServerPortObs Text NS.PortNumber | AppServerAddressObs Text
| AppServerUnixObs FilePath
| ExitUnsupportedPgVersion PgVersion PgVersion | ExitUnsupportedPgVersion PgVersion PgVersion
| ExitDBNoRecoveryObs | ExitDBNoRecoveryObs
| ExitDBFatalError ObsFatalError SQL.UsageError | ExitDBFatalError ObsFatalError SQL.UsageError
@@ -69,14 +66,12 @@ type ObservationHandler = Observation -> IO ()
observationMessage :: Observation -> Text observationMessage :: Observation -> Text
observationMessage = \case observationMessage = \case
AdminStartObs host port -> AdminStartObs address ->
"Admin server listening on " <> fromJust host <> ":" <> show (fromIntegral (fromJust port) :: Integer) "Admin server listening on " <> address
AppStartObs ver -> AppStartObs ver ->
"Starting PostgREST " <> T.decodeUtf8 ver <> "..." "Starting PostgREST " <> T.decodeUtf8 ver <> "..."
AppServerPortObs host port -> AppServerAddressObs address ->
"API server listening on " <> host <> ":" <> show port "API server listening on " <> address
AppServerUnixObs sock ->
"API server listening on unix socket " <> show sock
DBConnectedObs ver -> DBConnectedObs ver ->
"Successfully connected to " <> ver "Successfully connected to " <> ver
ExitUnsupportedPgVersion pgVer minPgVer -> ExitUnsupportedPgVersion pgVer minPgVer ->
+1
View File
@@ -18,6 +18,7 @@ main =
, "src/PostgREST/Config.hs" , "src/PostgREST/Config.hs"
, "src/PostgREST/Error.hs" , "src/PostgREST/Error.hs"
, "src/PostgREST/MediaType.hs" , "src/PostgREST/MediaType.hs"
, "src/PostgREST/Network.hs"
, "src/PostgREST/Plan.hs" , "src/PostgREST/Plan.hs"
, "src/PostgREST/Query/SqlFragment.hs" , "src/PostgREST/Query/SqlFragment.hs"
, "src/PostgREST/Response.hs" , "src/PostgREST/Response.hs"
+1 -1
View File
@@ -1378,7 +1378,7 @@ def test_log_postgrest_host_and_port(host, defaultenv):
output = postgrest.read_stdout(nlines=10) output = postgrest.read_stdout(nlines=10)
if is_unix: if is_unix:
re.match(r'API server listening on unix socket "/tmp/.*\.sock"', output[2]) re.match(r'API server listening on "/tmp/.*\.sock"', output[2])
elif is_ipv6(host): elif is_ipv6(host):
assert f"API server listening on [{host}]:{port}" in output[2] assert f"API server listening on [{host}]:{port}" in output[2]
else: # IPv4 else: # IPv4