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:
committed by
Steve Chavez
parent
ef9ac61d9e
commit
41b6ebea58
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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 ->
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user