From 3eb028f8e5460e5e88beaf6b1deb992a2b2ffdd2 Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Wed, 1 Nov 2023 14:27:37 +0100 Subject: [PATCH 01/28] app: quick'n'dirty displaying of bound port --- src/PostgREST/App.hs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/PostgREST/App.hs b/src/PostgREST/App.hs index b4a75b777d..7cef4d17c2 100644 --- a/src/PostgREST/App.hs +++ b/src/PostgREST/App.hs @@ -29,6 +29,7 @@ import System.Posix.Types (FileMode) import qualified Data.HashMap.Strict as HM import qualified Data.Text.Encoding as T +import qualified Data.Text as T (unpack) import qualified Hasql.Transaction.Sessions as SQL import qualified Network.Wai as Wai import qualified Network.Wai.Handler.Warp as Warp @@ -66,6 +67,8 @@ import qualified Data.Map as Map (fromList) import qualified Network.HTTP.Types as HTTP import Protolude hiding (Handler) import System.TimeIt (timeItT) +import qualified Network.Socket as Socket +import qualified Control.Exception as E type Handler = ExceptT Error @@ -96,8 +99,22 @@ run installHandlers maybeRunWithSocket appState = do panic "Cannot run with unix socket on non-unix platforms." Nothing -> do - AppState.logWithZTime appState $ "Listening on port " <> show configServerPort - Warp.runSettings (serverSettings conf) app + maddr <- resolveAddress configServerHost configServerPort + case maddr of + Nothing -> panic ("Could not resolve address " <> show configServerHost <> " port " <> show configServerPort) + Just addr -> do + sock <- E.bracketOnError (Socket.openSocket addr) Socket.close $ \sock -> do + Socket.bind sock $ Socket.addrAddress addr + pure sock + port <- Socket.socketPort sock + AppState.logWithZTime appState $ "Listening on port " <> show port + Warp.runSettingsSocket (serverSettings conf) sock app + + where + resolveAddress host port = do + let hints = Socket.defaultHints { Socket.addrSocketType = Socket.Stream } + head <$> Socket.getAddrInfo (Just hints) (Just $ T.unpack host) (Just $ show port) + serverSettings :: AppConfig -> Warp.Settings serverSettings AppConfig{..} = From eff8e1a7a988d3ba0886e14b622bb45dde5a3d4d Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Wed, 1 Nov 2023 15:30:03 +0100 Subject: [PATCH 02/28] postgrest style --- src/PostgREST/App.hs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/PostgREST/App.hs b/src/PostgREST/App.hs index 7cef4d17c2..84e77c63a1 100644 --- a/src/PostgREST/App.hs +++ b/src/PostgREST/App.hs @@ -28,8 +28,8 @@ import Network.Wai.Handler.Warp (defaultSettings, setHost, setPort, import System.Posix.Types (FileMode) import qualified Data.HashMap.Strict as HM +import qualified Data.Text as T (unpack) import qualified Data.Text.Encoding as T -import qualified Data.Text as T (unpack) import qualified Hasql.Transaction.Sessions as SQL import qualified Network.Wai as Wai import qualified Network.Wai.Handler.Warp as Warp @@ -61,14 +61,14 @@ import PostgREST.SchemaCache (SchemaCache (..)) import PostgREST.SchemaCache.Routine (Routine (..)) import PostgREST.Version (docsVersion, prettyVersion) +import qualified Control.Exception as E import qualified Data.ByteString.Char8 as BS import qualified Data.List as L import qualified Data.Map as Map (fromList) import qualified Network.HTTP.Types as HTTP +import qualified Network.Socket as Socket import Protolude hiding (Handler) import System.TimeIt (timeItT) -import qualified Network.Socket as Socket -import qualified Control.Exception as E type Handler = ExceptT Error @@ -103,18 +103,18 @@ run installHandlers maybeRunWithSocket appState = do case maddr of Nothing -> panic ("Could not resolve address " <> show configServerHost <> " port " <> show configServerPort) Just addr -> do - sock <- E.bracketOnError (Socket.openSocket addr) Socket.close $ \sock -> do + sock <- E.bracketOnError (Socket.openSocket addr) Socket.close $ \sock -> do Socket.bind sock $ Socket.addrAddress addr pure sock port <- Socket.socketPort sock AppState.logWithZTime appState $ "Listening on port " <> show port - Warp.runSettingsSocket (serverSettings conf) sock app - + Warp.runSettingsSocket (serverSettings conf) sock app + where resolveAddress host port = do let hints = Socket.defaultHints { Socket.addrSocketType = Socket.Stream } head <$> Socket.getAddrInfo (Just hints) (Just $ T.unpack host) (Just $ show port) - + serverSettings :: AppConfig -> Warp.Settings serverSettings AppConfig{..} = From f7c569702b7dacc63df75699c2a3c9f5e4f892c7 Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Fri, 3 Nov 2023 17:20:38 +0100 Subject: [PATCH 03/28] create app sockets ourselves also store them in AppState simplify Admin.reachMainApp --- src/PostgREST/Admin.hs | 42 +++++--------------------- src/PostgREST/App.hs | 34 +++++---------------- src/PostgREST/AppState.hs | 62 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 74 insertions(+), 64 deletions(-) diff --git a/src/PostgREST/Admin.hs b/src/PostgREST/Admin.hs index ffc61cbfba..0cf447f60a 100644 --- a/src/PostgREST/Admin.hs +++ b/src/PostgREST/Admin.hs @@ -22,19 +22,20 @@ import PostgREST.Config (AppConfig (..)) import qualified PostgREST.AppState as AppState import Protolude +import Protolude.Partial (fromJust) runAdmin :: AppConfig -> AppState -> Warp.Settings -> IO () runAdmin conf@AppConfig{configAdminServerPort} appState settings = - whenJust configAdminServerPort $ \adminPort -> do - AppState.logWithZTime appState $ "Admin server listening on port " <> show adminPort - void . forkIO $ Warp.runSettings (settings & Warp.setPort adminPort) adminApp + whenJust (AppState.getSocketAdmin appState) $ \adminSocket -> do + AppState.logWithZTime appState $ "Admin server listening on port " <> show (fromIntegral $ fromJust configAdminServerPort) + void . forkIO $ Warp.runSettingsSocket settings adminSocket adminApp where adminApp = admin appState conf -- | PostgREST admin application admin :: AppState.AppState -> AppConfig -> Wai.Application admin appState appConfig req respond = do - isMainAppReachable <- any isRight <$> reachMainApp appConfig + isMainAppReachable <- isRight <$> reachMainApp appConfig (AppState.getSocketREST appState) isSchemaCacheLoaded <- isJust <$> AppState.getSchemaCache appState isConnectionUp <- if configDbChannelEnabled appConfig @@ -53,35 +54,8 @@ admin appState appConfig req respond = do -- Note that it doesn't even send a valid HTTP request, we just want to check that the main app is accepting connections -- The code for resolving the "*4", "!4", "*6", "!6", "*" special values is taken from -- https://hackage.haskell.org/package/streaming-commons-0.2.2.4/docs/src/Data.Streaming.Network.html#bindPortGenEx -reachMainApp :: AppConfig -> IO [Either IOException ()] -reachMainApp AppConfig{..} = - case configServerUnixSocket of - Just path -> do - sock <- socket AF_UNIX Stream 0 - (:[]) <$> try (do - connect sock $ SockAddrUnix path - withSocketsDo $ bracket (pure sock) close sendEmpty) - Nothing -> do - let - host | configServerHost `elem` ["*4", "!4", "*6", "!6", "*"] = Nothing - | otherwise = Just configServerHost - filterAddrs xs = - case configServerHost of - "*4" -> ipv4Addrs xs ++ ipv6Addrs xs - "!4" -> ipv4Addrs xs - "*6" -> ipv6Addrs xs ++ ipv4Addrs xs - "!6" -> ipv6Addrs xs - _ -> xs - ipv4Addrs = filter ((/=) AF_INET6 . addrFamily) - ipv6Addrs = filter ((==) AF_INET6 . addrFamily) - - addrs <- getAddrInfo (Just $ defaultHints { addrSocketType = Stream }) (T.unpack <$> host) (Just . show $ configServerPort) - tryAddr `traverse` filterAddrs addrs +reachMainApp :: AppConfig -> Socket -> IO (Either IOException ()) +reachMainApp AppConfig{..} appSock = try $ do + withSocketsDo $ bracket (pure appSock) close sendEmpty where sendEmpty sock = void $ send sock mempty - tryAddr :: AddrInfo -> IO (Either IOException ()) - tryAddr addr = do - sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr) - try $ do - connect sock $ addrAddress addr - withSocketsDo $ bracket (pure sock) close sendEmpty diff --git a/src/PostgREST/App.hs b/src/PostgREST/App.hs index 84e77c63a1..44046fc892 100644 --- a/src/PostgREST/App.hs +++ b/src/PostgREST/App.hs @@ -66,6 +66,7 @@ import qualified Data.ByteString.Char8 as BS import qualified Data.List as L import qualified Data.Map as Map (fromList) import qualified Network.HTTP.Types as HTTP +import qualified Network.Socket as NS import qualified Network.Socket as Socket import Protolude hiding (Handler) import System.TimeIt (timeItT) @@ -88,33 +89,14 @@ run installHandlers maybeRunWithSocket appState = do let app = postgrest conf appState (AppState.connectionWorker appState) - case configServerUnixSocket of - Just socket -> - -- run the postgrest application with user defined socket. Only for UNIX systems - case maybeRunWithSocket of - Just runWithSocket -> do - AppState.logWithZTime appState $ "Listening on unix socket " <> show socket - runWithSocket (serverSettings conf) app configServerUnixSocketMode socket - Nothing -> - panic "Cannot run with unix socket on non-unix platforms." - Nothing -> - do - maddr <- resolveAddress configServerHost configServerPort - case maddr of - Nothing -> panic ("Could not resolve address " <> show configServerHost <> " port " <> show configServerPort) - Just addr -> do - sock <- E.bracketOnError (Socket.openSocket addr) Socket.close $ \sock -> do - Socket.bind sock $ Socket.addrAddress addr - pure sock - port <- Socket.socketPort sock - AppState.logWithZTime appState $ "Listening on port " <> show port - Warp.runSettingsSocket (serverSettings conf) sock app - - where - resolveAddress host port = do - let hints = Socket.defaultHints { Socket.addrSocketType = Socket.Stream } - head <$> Socket.getAddrInfo (Just hints) (Just $ T.unpack host) (Just $ show port) + what <- case configServerUnixSocket of + Just path -> pure $ "unix socket " <> show path + Nothing -> do + port <- NS.socketPort $ AppState.getSocketREST appState + pure $ "port " <> show port + AppState.logWithZTime appState $ "Listening on " <> what + Warp.runSettingsSocket (serverSettings conf) (AppState.getSocketREST appState) app serverSettings :: AppConfig -> Warp.Settings serverSettings AppConfig{..} = diff --git a/src/PostgREST/AppState.hs b/src/PostgREST/AppState.hs index 3183da80ed..2db3cd0902 100644 --- a/src/PostgREST/AppState.hs +++ b/src/PostgREST/AppState.hs @@ -14,6 +14,8 @@ module PostgREST.AppState , getRetryNextIn , getTime , getJwtCache + , getSocketREST + , getSocketAdmin , init , initWithPool , logWithZTime @@ -32,12 +34,14 @@ import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Lazy as LBS import qualified Data.Cache as C import Data.Either.Combinators (whenLeft) +import qualified Data.Text as T (unpack) import qualified Data.Text.Encoding as T import Hasql.Connection (acquire) import qualified Hasql.Notifications as SQL import qualified Hasql.Pool as SQL import qualified Hasql.Session as SQL import qualified Hasql.Transaction.Sessions as SQL +import qualified Network.Socket as NS import qualified PostgREST.Error as Error import PostgREST.Version (prettyVersion) @@ -64,8 +68,11 @@ import PostgREST.SchemaCache (SchemaCache, querySchemaCache) import PostgREST.SchemaCache.Identifiers (dumpQi) +import Data.Streaming.Network (bindPath, bindPortTCP, + bindRandomPortTCP) +import Data.String (IsString (..)) import Protolude - +import System.Posix.Files (setFileMode) data AuthResult = AuthResult { authClaims :: KM.KeyMap JSON.Value @@ -99,15 +106,23 @@ data AppState = AppState , debounceLogAcquisitionTimeout :: IO () -- | JWT Cache , jwtCache :: C.Cache ByteString AuthResult + -- | Network socket for REST API + , stateSocketREST :: NS.Socket + -- | Network socket for the admin UI + , stateSocketAdmin :: Maybe NS.Socket } +type AppSockets = (NS.Socket, Maybe NS.Socket) + init :: AppConfig -> IO AppState init conf = do pool <- initPool conf - initWithPool pool conf + (sock, adminSock) <- initSockets conf + state' <- initWithPool (sock, adminSock) pool conf + pure state' { stateSocketREST = sock, stateSocketAdmin = adminSock } -initWithPool :: SQL.Pool -> AppConfig -> IO AppState -initWithPool pool conf = do +initWithPool :: AppSockets -> SQL.Pool -> AppConfig -> IO AppState +initWithPool (sock, adminSock) pool conf = do appState <- AppState pool <$> newIORef minimumPgVersion -- assume we're in a supported version when starting, this will be corrected on a later step <*> newIORef Nothing @@ -121,6 +136,8 @@ initWithPool pool conf = do <*> newIORef 0 <*> pure (pure ()) <*> C.newCache Nothing + <*> pure sock + <*> pure adminSock debLogTimeout <- @@ -144,6 +161,37 @@ initWithPool pool conf = do destroy :: AppState -> IO () destroy = destroyPool +initSockets :: AppConfig -> IO AppSockets +initSockets AppConfig{..} = do + let + cfg'usp = configServerUnixSocket + cfg'uspm = configServerUnixSocketMode + cfg'host = configServerHost + cfg'port = configServerPort + cfg'adminport = configAdminServerPort + + (_port, sock) <- case cfg'usp of + Just usp -> do + sock <- bindPath usp + setFileMode (toS usp) cfg'uspm + pure (Nothing, sock) + Nothing -> do + (port, sock) <- if cfg'port /= 0 then do + sock <- bindPortTCP cfg'port (fromString $ T.unpack cfg'host) + pure (cfg'port, sock) + else do + (num, sock) <- bindRandomPortTCP (fromString $ T.unpack cfg'host) + pure (num, sock) + pure (Just port, sock) + + adminSock <- case cfg'adminport of + Just adminPort -> do + adminSock <- bindPortTCP adminPort (fromString $ T.unpack cfg'host) + pure $ Just adminSock + Nothing -> pure Nothing + + pure (sock, adminSock) + initPool :: AppConfig -> IO SQL.Pool initPool AppConfig{..} = SQL.acquire @@ -204,6 +252,12 @@ getTime = stateGetTime getJwtCache :: AppState -> C.Cache ByteString AuthResult getJwtCache = jwtCache +getSocketREST :: AppState -> NS.Socket +getSocketREST = stateSocketREST + +getSocketAdmin :: AppState -> Maybe NS.Socket +getSocketAdmin = stateSocketAdmin + -- | Log to stderr with local time logWithZTime :: AppState -> Text -> IO () logWithZTime appState txt = do From 41df536ef7af7c248eea088fbedc415f8eba33ae Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Fri, 3 Nov 2023 17:39:25 +0100 Subject: [PATCH 04/28] fix leftover errors --- main/Main.hs | 9 +-------- src/PostgREST/Admin.hs | 12 +++++------- src/PostgREST/App.hs | 7 ++----- src/PostgREST/CLI.hs | 6 +++--- src/PostgREST/Unix.hs | 34 ++-------------------------------- 5 files changed, 13 insertions(+), 55 deletions(-) diff --git a/main/Main.hs b/main/Main.hs index 5d5e18a710..82ed56d270 100644 --- a/main/Main.hs +++ b/main/Main.hs @@ -17,7 +17,7 @@ main :: IO () main = do setBuffering opts <- CLI.readCLIShowHelp - CLI.main installSignalHandlers runAppInSocket opts + CLI.main installSignalHandlers opts installSignalHandlers :: App.SignalHandlerInstaller #ifndef mingw32_HOST_OS @@ -26,13 +26,6 @@ installSignalHandlers = Unix.installSignalHandlers installSignalHandlers _ = pass #endif -runAppInSocket :: Maybe App.SocketRunner -#ifndef mingw32_HOST_OS -runAppInSocket = Just Unix.runAppWithSocket -#else -runAppInSocket = Nothing -#endif - setBuffering :: IO () setBuffering = do -- LineBuffering: the entire output buffer is flushed whenever a newline is diff --git a/src/PostgREST/Admin.hs b/src/PostgREST/Admin.hs index 0cf447f60a..4fed31626e 100644 --- a/src/PostgREST/Admin.hs +++ b/src/PostgREST/Admin.hs @@ -1,11 +1,9 @@ -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NamedFieldPuns #-} module PostgREST.Admin ( runAdmin ) where -import qualified Data.Text as T import qualified Hasql.Session as SQL import qualified Network.HTTP.Types.Status as HTTP import qualified Network.Wai as Wai @@ -27,7 +25,7 @@ import Protolude.Partial (fromJust) runAdmin :: AppConfig -> AppState -> Warp.Settings -> IO () runAdmin conf@AppConfig{configAdminServerPort} appState settings = whenJust (AppState.getSocketAdmin appState) $ \adminSocket -> do - AppState.logWithZTime appState $ "Admin server listening on port " <> show (fromIntegral $ fromJust configAdminServerPort) + AppState.logWithZTime appState $ "Admin server listening on port " <> show (fromIntegral (fromJust configAdminServerPort) :: Integer) void . forkIO $ Warp.runSettingsSocket settings adminSocket adminApp where adminApp = admin appState conf @@ -35,7 +33,7 @@ runAdmin conf@AppConfig{configAdminServerPort} appState settings = -- | PostgREST admin application admin :: AppState.AppState -> AppConfig -> Wai.Application admin appState appConfig req respond = do - isMainAppReachable <- isRight <$> reachMainApp appConfig (AppState.getSocketREST appState) + isMainAppReachable <- isRight <$> reachMainApp (AppState.getSocketREST appState) isSchemaCacheLoaded <- isJust <$> AppState.getSchemaCache appState isConnectionUp <- if configDbChannelEnabled appConfig @@ -54,8 +52,8 @@ admin appState appConfig req respond = do -- Note that it doesn't even send a valid HTTP request, we just want to check that the main app is accepting connections -- The code for resolving the "*4", "!4", "*6", "!6", "*" special values is taken from -- https://hackage.haskell.org/package/streaming-commons-0.2.2.4/docs/src/Data.Streaming.Network.html#bindPortGenEx -reachMainApp :: AppConfig -> Socket -> IO (Either IOException ()) -reachMainApp AppConfig{..} appSock = try $ do +reachMainApp :: Socket -> IO (Either IOException ()) +reachMainApp appSock = try $ do withSocketsDo $ bracket (pure appSock) close sendEmpty where sendEmpty sock = void $ send sock mempty diff --git a/src/PostgREST/App.hs b/src/PostgREST/App.hs index 44046fc892..26b34be67c 100644 --- a/src/PostgREST/App.hs +++ b/src/PostgREST/App.hs @@ -28,7 +28,6 @@ import Network.Wai.Handler.Warp (defaultSettings, setHost, setPort, import System.Posix.Types (FileMode) import qualified Data.HashMap.Strict as HM -import qualified Data.Text as T (unpack) import qualified Data.Text.Encoding as T import qualified Hasql.Transaction.Sessions as SQL import qualified Network.Wai as Wai @@ -61,13 +60,11 @@ import PostgREST.SchemaCache (SchemaCache (..)) import PostgREST.SchemaCache.Routine (Routine (..)) import PostgREST.Version (docsVersion, prettyVersion) -import qualified Control.Exception as E import qualified Data.ByteString.Char8 as BS import qualified Data.List as L import qualified Data.Map as Map (fromList) import qualified Network.HTTP.Types as HTTP import qualified Network.Socket as NS -import qualified Network.Socket as Socket import Protolude hiding (Handler) import System.TimeIt (timeItT) @@ -77,8 +74,8 @@ type SignalHandlerInstaller = AppState -> IO() type SocketRunner = Warp.Settings -> Wai.Application -> FileMode -> FilePath -> IO() -run :: SignalHandlerInstaller -> Maybe SocketRunner -> AppState -> IO () -run installHandlers maybeRunWithSocket appState = do +run :: SignalHandlerInstaller -> AppState -> IO () +run installHandlers appState = do conf@AppConfig{..} <- AppState.getConfig appState AppState.connectionWorker appState -- Loads the initial SchemaCache installHandlers appState diff --git a/src/PostgREST/CLI.hs b/src/PostgREST/CLI.hs index 9120d3e5ac..86148a36a8 100644 --- a/src/PostgREST/CLI.hs +++ b/src/PostgREST/CLI.hs @@ -29,8 +29,8 @@ import qualified PostgREST.Config as Config import Protolude hiding (hPutStrLn) -main :: App.SignalHandlerInstaller -> Maybe App.SocketRunner -> CLI -> IO () -main installSignalHandlers runAppWithSocket CLI{cliCommand, cliPath} = do +main :: App.SignalHandlerInstaller -> CLI -> IO () +main installSignalHandlers CLI{cliCommand, cliPath} = do conf@AppConfig{..} <- either panic identity <$> Config.readAppConfig mempty cliPath Nothing mempty mempty @@ -45,7 +45,7 @@ main installSignalHandlers runAppWithSocket CLI{cliCommand, cliPath} = do when configDbConfig $ AppState.reReadConfig True appState putStr . Config.toText =<< AppState.getConfig appState CmdDumpSchema -> putStrLn =<< dumpSchema appState - CmdRun -> App.run installSignalHandlers runAppWithSocket appState) + CmdRun -> App.run installSignalHandlers appState) -- | Dump SchemaCache schema to JSON dumpSchema :: AppState -> IO LBS.ByteString diff --git a/src/PostgREST/Unix.hs b/src/PostgREST/Unix.hs index 5cb09f7261..13becbefd3 100644 --- a/src/PostgREST/Unix.hs +++ b/src/PostgREST/Unix.hs @@ -1,44 +1,14 @@ module PostgREST.Unix - ( runAppWithSocket - , installSignalHandlers + ( installSignalHandlers ) where -import qualified Network.Socket as Socket -import qualified Network.Wai.Handler.Warp as Warp -import qualified System.Posix.Signals as Signals - -import Network.Wai (Application) -import System.Directory (removeFile) -import System.IO.Error (isDoesNotExistError) -import System.Posix.Files (setFileMode) -import System.Posix.Types (FileMode) +import qualified System.Posix.Signals as Signals import qualified PostgREST.AppState as AppState import Protolude --- | Run the PostgREST application with user defined socket. -runAppWithSocket :: Warp.Settings -> Application -> FileMode -> FilePath -> IO () -runAppWithSocket settings app socketFileMode socketFilePath = - bracket createAndBindSocket Socket.close $ \socket -> do - Socket.listen socket Socket.maxListenQueue - Warp.runSettingsSocket settings socket app - where - createAndBindSocket = do - deleteSocketFileIfExist socketFilePath - sock <- Socket.socket Socket.AF_UNIX Socket.Stream Socket.defaultProtocol - Socket.bind sock $ Socket.SockAddrUnix socketFilePath - setFileMode socketFilePath socketFileMode - return sock - - deleteSocketFileIfExist path = - removeFile path `catch` handleDoesNotExist - - handleDoesNotExist e - | isDoesNotExistError e = return () - | otherwise = throwIO e - -- | Set signal handlers, only for systems with signals installSignalHandlers :: AppState.AppState -> IO () installSignalHandlers appState = do From ab5bc73b01d456eb0d426012887e57c249bc8c33 Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Mon, 6 Nov 2023 15:10:14 +0100 Subject: [PATCH 05/28] cabal: add streaming-commons --- postgrest.cabal | 1 + 1 file changed, 1 insertion(+) diff --git a/postgrest.cabal b/postgrest.cabal index 80601ebf0b..736d155e5d 100644 --- a/postgrest.cabal +++ b/postgrest.cabal @@ -114,6 +114,7 @@ library , regex-tdfa >= 1.2.2 && < 1.4 , retry >= 0.7.4 && < 0.10 , scientific >= 0.3.4 && < 0.4 + , streaming-commons >= 0.1.1 && < 0.3 , swagger2 >= 2.4 && < 2.9 , text >= 1.2.2 && < 1.3 , time >= 1.6 && < 1.12 From b47a35883502f64c6efc90d76d206009827e3596 Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Mon, 6 Nov 2023 15:34:37 +0100 Subject: [PATCH 06/28] spec: fix tests --- src/PostgREST/AppState.hs | 1 + test/spec/Main.hs | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/PostgREST/AppState.hs b/src/PostgREST/AppState.hs index 2db3cd0902..4fdfc8ce91 100644 --- a/src/PostgREST/AppState.hs +++ b/src/PostgREST/AppState.hs @@ -17,6 +17,7 @@ module PostgREST.AppState , getSocketREST , getSocketAdmin , init + , initSockets , initWithPool , logWithZTime , putSchemaCache diff --git a/test/spec/Main.hs b/test/spec/Main.hs index 86ced44310..db3a883099 100644 --- a/test/spec/Main.hs +++ b/test/spec/Main.hs @@ -73,11 +73,12 @@ main = do -- cached schema cache so most tests run fast baseSchemaCache <- loadSchemaCache pool testCfg + sockets <- AppState.initSockets testCfg let -- For tests that run with the same refSchemaCache app config = do - appState <- AppState.initWithPool pool config + appState <- AppState.initWithPool sockets pool config AppState.putPgVersion appState actualPgVersion AppState.putSchemaCache appState (Just baseSchemaCache) return ((), postgrest config appState $ pure ()) @@ -85,7 +86,7 @@ main = do -- For tests that run with a different SchemaCache(depends on configSchemas) appDbs config = do customSchemaCache <- loadSchemaCache pool config - appState <- AppState.initWithPool pool config + appState <- AppState.initWithPool sockets pool config AppState.putPgVersion appState actualPgVersion AppState.putSchemaCache appState (Just customSchemaCache) return ((), postgrest config appState $ pure ()) From 4e381285844de545efd267976fd069eec3a39111 Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Thu, 9 Nov 2023 17:23:15 +0100 Subject: [PATCH 07/28] admin: fixed crash in reachMainApp --- src/PostgREST/Admin.hs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/PostgREST/Admin.hs b/src/PostgREST/Admin.hs index 4fed31626e..cff94b31fd 100644 --- a/src/PostgREST/Admin.hs +++ b/src/PostgREST/Admin.hs @@ -53,7 +53,15 @@ admin appState appConfig req respond = do -- The code for resolving the "*4", "!4", "*6", "!6", "*" special values is taken from -- https://hackage.haskell.org/package/streaming-commons-0.2.2.4/docs/src/Data.Streaming.Network.html#bindPortGenEx reachMainApp :: Socket -> IO (Either IOException ()) -reachMainApp appSock = try $ do - withSocketsDo $ bracket (pure appSock) close sendEmpty +reachMainApp appSock = do + sockAddr <- getSocketName appSock + sock <- socket (addrFamily sockAddr) Stream defaultProtocol + try $ do + connect sock sockAddr + withSocketsDo $ bracket (pure sock) close sendEmpty where sendEmpty sock = void $ send sock mempty + addrFamily (SockAddrInet _ _) = AF_INET + addrFamily (SockAddrInet6 _ _ _ _) = AF_INET6 + addrFamily (SockAddrUnix _) = AF_UNIX + From 6081cd62562caa8e73d7c814ea69100973809761 Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Thu, 9 Nov 2023 17:23:45 +0100 Subject: [PATCH 08/28] postgrest-style --- src/PostgREST/Admin.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PostgREST/Admin.hs b/src/PostgREST/Admin.hs index cff94b31fd..916522d173 100644 --- a/src/PostgREST/Admin.hs +++ b/src/PostgREST/Admin.hs @@ -61,7 +61,7 @@ reachMainApp appSock = do withSocketsDo $ bracket (pure sock) close sendEmpty where sendEmpty sock = void $ send sock mempty - addrFamily (SockAddrInet _ _) = AF_INET + addrFamily (SockAddrInet _ _) = AF_INET addrFamily (SockAddrInet6 _ _ _ _) = AF_INET6 - addrFamily (SockAddrUnix _) = AF_UNIX + addrFamily (SockAddrUnix _) = AF_UNIX From 394b8947facb82b7b30f992a4c46ee8d3bb45dd3 Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Thu, 9 Nov 2023 17:30:31 +0100 Subject: [PATCH 09/28] postgrest-style --- src/PostgREST/Admin.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PostgREST/Admin.hs b/src/PostgREST/Admin.hs index 916522d173..64ef9372af 100644 --- a/src/PostgREST/Admin.hs +++ b/src/PostgREST/Admin.hs @@ -61,7 +61,7 @@ reachMainApp appSock = do withSocketsDo $ bracket (pure sock) close sendEmpty where sendEmpty sock = void $ send sock mempty - addrFamily (SockAddrInet _ _) = AF_INET - addrFamily (SockAddrInet6 _ _ _ _) = AF_INET6 - addrFamily (SockAddrUnix _) = AF_UNIX + addrFamily (SockAddrInet _ _) = AF_INET + addrFamily (SockAddrInet6 {}) = AF_INET6 + addrFamily (SockAddrUnix _) = AF_UNIX From 17306e3bb3f162245c09de7de39891eb5be9adcc Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Fri, 10 Nov 2023 17:58:39 +0100 Subject: [PATCH 10/28] get unix socket runtime check back --- src/PostgREST/AppState.hs | 42 ++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/PostgREST/AppState.hs b/src/PostgREST/AppState.hs index 4fdfc8ce91..da75e469cc 100644 --- a/src/PostgREST/AppState.hs +++ b/src/PostgREST/AppState.hs @@ -74,6 +74,9 @@ import Data.Streaming.Network (bindPath, bindPortTCP, import Data.String (IsString (..)) import Protolude import System.Posix.Files (setFileMode) +import Network.Socket (isUnixDomainSocketAvailable) +import System.Directory (removeFile) +import System.IO.Error (isDoesNotExistError) data AuthResult = AuthResult { authClaims :: KM.KeyMap JSON.Value @@ -171,19 +174,21 @@ initSockets AppConfig{..} = do cfg'port = configServerPort cfg'adminport = configAdminServerPort - (_port, sock) <- case cfg'usp of - Just usp -> do - sock <- bindPath usp - setFileMode (toS usp) cfg'uspm - pure (Nothing, sock) + sock <- case cfg'usp of + Just path -> do + unless isUnixDomainSocketAvailable $ + panic "Cannot run with unix socket on non-unix platforms. Consider deleting the `server-unix-socket` config entry in order to continue." + createAndBindSocket path cfg'uspm Nothing -> do - (port, sock) <- if cfg'port /= 0 then do - sock <- bindPortTCP cfg'port (fromString $ T.unpack cfg'host) - pure (cfg'port, sock) - else do - (num, sock) <- bindRandomPortTCP (fromString $ T.unpack cfg'host) - pure (num, sock) - pure (Just port, sock) + (_, sock) <- + if cfg'port /= 0 + then do + sock <- bindPortTCP cfg'port (fromString $ T.unpack cfg'host) + pure (cfg'port, sock) + else do + (num, sock) <- bindRandomPortTCP (fromString $ T.unpack cfg'host) + pure (num, sock) + pure sock adminSock <- case cfg'adminport of Just adminPort -> do @@ -193,6 +198,19 @@ initSockets AppConfig{..} = do pure (sock, adminSock) + where + createAndBindSocket path mode = do + deleteSocketFileIfExist path + sock <- NS.socket NS.AF_UNIX NS.Stream NS.defaultProtocol + NS.bind sock $ NS.SockAddrUnix path + setFileMode path mode + return sock + deleteSocketFileIfExist path = + removeFile path `catch` handleDoesNotExist + handleDoesNotExist e + | isDoesNotExistError e = return () + | otherwise = throwIO e + initPool :: AppConfig -> IO SQL.Pool initPool AppConfig{..} = SQL.acquire From 44075d40082a4ed7c8b4be2df7d262cbdbde9764 Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Fri, 10 Nov 2023 18:01:59 +0100 Subject: [PATCH 11/28] postgrest-style --- src/PostgREST/AppState.hs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/PostgREST/AppState.hs b/src/PostgREST/AppState.hs index da75e469cc..a308387914 100644 --- a/src/PostgREST/AppState.hs +++ b/src/PostgREST/AppState.hs @@ -69,14 +69,13 @@ import PostgREST.SchemaCache (SchemaCache, querySchemaCache) import PostgREST.SchemaCache.Identifiers (dumpQi) -import Data.Streaming.Network (bindPath, bindPortTCP, - bindRandomPortTCP) +import Data.Streaming.Network (bindPortTCP, bindRandomPortTCP) import Data.String (IsString (..)) +import Network.Socket (isUnixDomainSocketAvailable) import Protolude +import System.Directory (removeFile) +import System.IO.Error (isDoesNotExistError) import System.Posix.Files (setFileMode) -import Network.Socket (isUnixDomainSocketAvailable) -import System.Directory (removeFile) -import System.IO.Error (isDoesNotExistError) data AuthResult = AuthResult { authClaims :: KM.KeyMap JSON.Value @@ -176,7 +175,7 @@ initSockets AppConfig{..} = do sock <- case cfg'usp of Just path -> do - unless isUnixDomainSocketAvailable $ + unless isUnixDomainSocketAvailable $ panic "Cannot run with unix socket on non-unix platforms. Consider deleting the `server-unix-socket` config entry in order to continue." createAndBindSocket path cfg'uspm Nothing -> do From 364f2d2cc96b732b3c1049472736563c1fe1fd5b Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Mon, 13 Nov 2023 18:30:11 +0100 Subject: [PATCH 12/28] bind then listen --- src/PostgREST/AppState.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PostgREST/AppState.hs b/src/PostgREST/AppState.hs index a308387914..9576fd818f 100644 --- a/src/PostgREST/AppState.hs +++ b/src/PostgREST/AppState.hs @@ -202,6 +202,7 @@ initSockets AppConfig{..} = do deleteSocketFileIfExist path sock <- NS.socket NS.AF_UNIX NS.Stream NS.defaultProtocol NS.bind sock $ NS.SockAddrUnix path + NS.listen sock (max 2048 NS.maxListenQueue) setFileMode path mode return sock deleteSocketFileIfExist path = From 759094a1c7e649e42251e18d54e94fab11d0fe99 Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Tue, 14 Nov 2023 17:16:33 +0100 Subject: [PATCH 13/28] use setPerms from unix-compat --- postgrest.cabal | 1 + src/PostgREST/App.hs | 4 ++-- src/PostgREST/AppState.hs | 18 +--------------- src/PostgREST/Unix.hs | 45 ++++++++++++++++++++++++++------------- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/postgrest.cabal b/postgrest.cabal index 736d155e5d..3aee90399f 100644 --- a/postgrest.cabal +++ b/postgrest.cabal @@ -120,6 +120,7 @@ library , time >= 1.6 && < 1.12 , timeit >= 2.0 && < 2.1 , unordered-containers >= 0.2.8 && < 0.3 + , unix-compat >= 0.7 && < 0.8 , vault >= 0.3.1.5 && < 0.4 , vector >= 0.11 && < 0.14 , wai >= 3.2.1 && < 3.3 diff --git a/src/PostgREST/App.hs b/src/PostgREST/App.hs index 26b34be67c..6fd52744c7 100644 --- a/src/PostgREST/App.hs +++ b/src/PostgREST/App.hs @@ -70,7 +70,7 @@ import System.TimeIt (timeItT) type Handler = ExceptT Error -type SignalHandlerInstaller = AppState -> IO() +type SignalHandlerInstaller = ThreadId -> IO () -> IO ()-> IO() type SocketRunner = Warp.Settings -> Wai.Application -> FileMode -> FilePath -> IO() @@ -78,7 +78,7 @@ run :: SignalHandlerInstaller -> AppState -> IO () run installHandlers appState = do conf@AppConfig{..} <- AppState.getConfig appState AppState.connectionWorker appState -- Loads the initial SchemaCache - installHandlers appState + installHandlers (AppState.getMainThreadId appState) (AppState.connectionWorker appState) (AppState.reReadConfig False appState) -- reload schema cache + config on NOTIFY AppState.runListener conf appState diff --git a/src/PostgREST/AppState.hs b/src/PostgREST/AppState.hs index 9576fd818f..ce8672ddab 100644 --- a/src/PostgREST/AppState.hs +++ b/src/PostgREST/AppState.hs @@ -68,14 +68,12 @@ import PostgREST.Config.PgVersion (PgVersion (..), import PostgREST.SchemaCache (SchemaCache, querySchemaCache) import PostgREST.SchemaCache.Identifiers (dumpQi) +import PostgREST.Unix (createAndBindSocket) import Data.Streaming.Network (bindPortTCP, bindRandomPortTCP) import Data.String (IsString (..)) import Network.Socket (isUnixDomainSocketAvailable) import Protolude -import System.Directory (removeFile) -import System.IO.Error (isDoesNotExistError) -import System.Posix.Files (setFileMode) data AuthResult = AuthResult { authClaims :: KM.KeyMap JSON.Value @@ -197,20 +195,6 @@ initSockets AppConfig{..} = do pure (sock, adminSock) - where - createAndBindSocket path mode = do - deleteSocketFileIfExist path - sock <- NS.socket NS.AF_UNIX NS.Stream NS.defaultProtocol - NS.bind sock $ NS.SockAddrUnix path - NS.listen sock (max 2048 NS.maxListenQueue) - setFileMode path mode - return sock - deleteSocketFileIfExist path = - removeFile path `catch` handleDoesNotExist - handleDoesNotExist e - | isDoesNotExistError e = return () - | otherwise = throwIO e - initPool :: AppConfig -> IO SQL.Pool initPool AppConfig{..} = SQL.acquire diff --git a/src/PostgREST/Unix.hs b/src/PostgREST/Unix.hs index 13becbefd3..c5d956a656 100644 --- a/src/PostgREST/Unix.hs +++ b/src/PostgREST/Unix.hs @@ -1,27 +1,42 @@ module PostgREST.Unix ( installSignalHandlers + , createAndBindSocket ) where -import qualified System.Posix.Signals as Signals - -import qualified PostgREST.AppState as AppState - -import Protolude +import qualified System.Posix.Signals as Signals +import System.Posix.Types (FileMode) +import System.PosixCompat.Files (setFileMode) +import Data.String (String) +import qualified Network.Socket as NS +import Protolude +import System.Directory (Permissions, removeFile, + setOwnerReadable, setPermissions) +import System.IO.Error (isDoesNotExistError) -- | Set signal handlers, only for systems with signals -installSignalHandlers :: AppState.AppState -> IO () -installSignalHandlers appState = do - let interrupt = throwTo (AppState.getMainThreadId appState) UserInterrupt +installSignalHandlers :: ThreadId -> IO () -> IO () -> IO () +installSignalHandlers tid usr1 usr2 = do + let interrupt = throwTo tid UserInterrupt install Signals.sigINT interrupt install Signals.sigTERM interrupt - - -- The SIGUSR1 signal updates the internal 'SchemaCache' by running - -- 'connectionWorker' exactly as before. - install Signals.sigUSR1 $ AppState.connectionWorker appState - - -- Re-read the config on SIGUSR2 - install Signals.sigUSR2 $ AppState.reReadConfig False appState + install Signals.sigUSR1 usr1 + install Signals.sigUSR2 usr2 where install signal handler = void $ Signals.installHandler signal (Signals.Catch handler) Nothing + +createAndBindSocket :: String -> FileMode -> IO NS.Socket +createAndBindSocket path mode = do + deleteSocketFileIfExist path + sock <- NS.socket NS.AF_UNIX NS.Stream NS.defaultProtocol + NS.bind sock $ NS.SockAddrUnix path + NS.listen sock (max 2048 NS.maxListenQueue) + setFileMode path mode + return sock + where + deleteSocketFileIfExist path = + removeFile path `catch` handleDoesNotExist + handleDoesNotExist e + | isDoesNotExistError e = return () + | otherwise = throwIO e From 95f665fe739f448a69b1e3b1d47d8992eced990e Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Tue, 14 Nov 2023 17:24:06 +0100 Subject: [PATCH 14/28] style --- src/PostgREST/Unix.hs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/PostgREST/Unix.hs b/src/PostgREST/Unix.hs index c5d956a656..cd9b0f1e7e 100644 --- a/src/PostgREST/Unix.hs +++ b/src/PostgREST/Unix.hs @@ -10,8 +10,7 @@ import System.PosixCompat.Files (setFileMode) import Data.String (String) import qualified Network.Socket as NS import Protolude -import System.Directory (Permissions, removeFile, - setOwnerReadable, setPermissions) +import System.Directory (removeFile) import System.IO.Error (isDoesNotExistError) -- | Set signal handlers, only for systems with signals @@ -35,8 +34,8 @@ createAndBindSocket path mode = do setFileMode path mode return sock where - deleteSocketFileIfExist path = - removeFile path `catch` handleDoesNotExist + deleteSocketFileIfExist path' = + removeFile path' `catch` handleDoesNotExist handleDoesNotExist e | isDoesNotExistError e = return () | otherwise = throwIO e From 50925df57129ce462fb828c7557b747177977d24 Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Tue, 14 Nov 2023 17:30:23 +0100 Subject: [PATCH 15/28] pin unix-compat for warp --- postgrest.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgrest.cabal b/postgrest.cabal index 3aee90399f..f8703cc74b 100644 --- a/postgrest.cabal +++ b/postgrest.cabal @@ -120,7 +120,7 @@ library , time >= 1.6 && < 1.12 , timeit >= 2.0 && < 2.1 , unordered-containers >= 0.2.8 && < 0.3 - , unix-compat >= 0.7 && < 0.8 + , unix-compat == 0.5.4 , vault >= 0.3.1.5 && < 0.4 , vector >= 0.11 && < 0.14 , wai >= 3.2.1 && < 3.3 From 7d21a4993707cfa4da01f532016c80e71605a1f1 Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Wed, 15 Nov 2023 16:04:50 +0100 Subject: [PATCH 16/28] put Unix specifics to Postgrest.Unix --- main/Main.hs | 16 +--------------- src/PostgREST/App.hs | 16 +++++----------- src/PostgREST/AppState.hs | 4 ++-- src/PostgREST/CLI.hs | 6 +++--- src/PostgREST/Unix.hs | 12 +++++++++--- 5 files changed, 20 insertions(+), 34 deletions(-) diff --git a/main/Main.hs b/main/Main.hs index 82ed56d270..4d23125184 100644 --- a/main/Main.hs +++ b/main/Main.hs @@ -1,30 +1,16 @@ -{-# LANGUAGE CPP #-} - module Main (main) where import System.IO (BufferMode (..), hSetBuffering) -import qualified PostgREST.App as App import qualified PostgREST.CLI as CLI import Protolude -#ifndef mingw32_HOST_OS -import qualified PostgREST.Unix as Unix -#endif - main :: IO () main = do setBuffering opts <- CLI.readCLIShowHelp - CLI.main installSignalHandlers opts - -installSignalHandlers :: App.SignalHandlerInstaller -#ifndef mingw32_HOST_OS -installSignalHandlers = Unix.installSignalHandlers -#else -installSignalHandlers _ = pass -#endif + CLI.main opts setBuffering :: IO () setBuffering = do diff --git a/src/PostgREST/App.hs b/src/PostgREST/App.hs index 6fd52744c7..93a055abad 100644 --- a/src/PostgREST/App.hs +++ b/src/PostgREST/App.hs @@ -12,9 +12,7 @@ Some of its functionality includes: {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RecordWildCards #-} module PostgREST.App - ( SignalHandlerInstaller - , SocketRunner - , postgrest + ( postgrest , run ) where @@ -25,7 +23,6 @@ import Data.Maybe (fromJust) import Data.String (IsString (..)) import Network.Wai.Handler.Warp (defaultSettings, setHost, setPort, setServerName) -import System.Posix.Types (FileMode) import qualified Data.HashMap.Strict as HM import qualified Data.Text.Encoding as T @@ -44,6 +41,7 @@ import qualified PostgREST.Logger as Logger import qualified PostgREST.Plan as Plan import qualified PostgREST.Query as Query import qualified PostgREST.Response as Response +import qualified PostgREST.Unix as Unix (installSignalHandlers) import PostgREST.ApiRequest (Action (..), ApiRequest (..), Mutation (..), Target (..)) @@ -70,15 +68,11 @@ import System.TimeIt (timeItT) type Handler = ExceptT Error -type SignalHandlerInstaller = ThreadId -> IO () -> IO ()-> IO() - -type SocketRunner = Warp.Settings -> Wai.Application -> FileMode -> FilePath -> IO() - -run :: SignalHandlerInstaller -> AppState -> IO () -run installHandlers appState = do +run :: AppState -> IO () +run appState = do conf@AppConfig{..} <- AppState.getConfig appState AppState.connectionWorker appState -- Loads the initial SchemaCache - installHandlers (AppState.getMainThreadId appState) (AppState.connectionWorker appState) (AppState.reReadConfig False appState) + Unix.installSignalHandlers (AppState.getMainThreadId appState) (AppState.connectionWorker appState) (AppState.reReadConfig False appState) -- reload schema cache + config on NOTIFY AppState.runListener conf appState diff --git a/src/PostgREST/AppState.hs b/src/PostgREST/AppState.hs index ce8672ddab..e6cd413003 100644 --- a/src/PostgREST/AppState.hs +++ b/src/PostgREST/AppState.hs @@ -68,7 +68,7 @@ import PostgREST.Config.PgVersion (PgVersion (..), import PostgREST.SchemaCache (SchemaCache, querySchemaCache) import PostgREST.SchemaCache.Identifiers (dumpQi) -import PostgREST.Unix (createAndBindSocket) +import PostgREST.Unix (createAndBindDomainSocket) import Data.Streaming.Network (bindPortTCP, bindRandomPortTCP) import Data.String (IsString (..)) @@ -175,7 +175,7 @@ initSockets AppConfig{..} = do Just path -> do unless isUnixDomainSocketAvailable $ panic "Cannot run with unix socket on non-unix platforms. Consider deleting the `server-unix-socket` config entry in order to continue." - createAndBindSocket path cfg'uspm + createAndBindDomainSocket path cfg'uspm Nothing -> do (_, sock) <- if cfg'port /= 0 diff --git a/src/PostgREST/CLI.hs b/src/PostgREST/CLI.hs index 86148a36a8..39d4f49dfd 100644 --- a/src/PostgREST/CLI.hs +++ b/src/PostgREST/CLI.hs @@ -29,8 +29,8 @@ import qualified PostgREST.Config as Config import Protolude hiding (hPutStrLn) -main :: App.SignalHandlerInstaller -> CLI -> IO () -main installSignalHandlers CLI{cliCommand, cliPath} = do +main :: CLI -> IO () +main CLI{cliCommand, cliPath} = do conf@AppConfig{..} <- either panic identity <$> Config.readAppConfig mempty cliPath Nothing mempty mempty @@ -45,7 +45,7 @@ main installSignalHandlers CLI{cliCommand, cliPath} = do when configDbConfig $ AppState.reReadConfig True appState putStr . Config.toText =<< AppState.getConfig appState CmdDumpSchema -> putStrLn =<< dumpSchema appState - CmdRun -> App.run installSignalHandlers appState) + CmdRun -> App.run appState) -- | Dump SchemaCache schema to JSON dumpSchema :: AppState -> IO LBS.ByteString diff --git a/src/PostgREST/Unix.hs b/src/PostgREST/Unix.hs index cd9b0f1e7e..ec1b5bab31 100644 --- a/src/PostgREST/Unix.hs +++ b/src/PostgREST/Unix.hs @@ -1,6 +1,8 @@ +{-# LANGUAGE CPP #-} + module PostgREST.Unix ( installSignalHandlers - , createAndBindSocket + , createAndBindDomainSocket ) where import qualified System.Posix.Signals as Signals @@ -15,6 +17,7 @@ import System.IO.Error (isDoesNotExistError) -- | Set signal handlers, only for systems with signals installSignalHandlers :: ThreadId -> IO () -> IO () -> IO () +#ifndef mingw32_HOST_OS installSignalHandlers tid usr1 usr2 = do let interrupt = throwTo tid UserInterrupt install Signals.sigINT interrupt @@ -24,9 +27,12 @@ installSignalHandlers tid usr1 usr2 = do where install signal handler = void $ Signals.installHandler signal (Signals.Catch handler) Nothing +#else +installSignalHandlers _ = pass +#endif -createAndBindSocket :: String -> FileMode -> IO NS.Socket -createAndBindSocket path mode = do +createAndBindDomainSocket :: String -> FileMode -> IO NS.Socket +createAndBindDomainSocket path mode = do deleteSocketFileIfExist path sock <- NS.socket NS.AF_UNIX NS.Stream NS.defaultProtocol NS.bind sock $ NS.SockAddrUnix path From 846bf5d5675ab87e432f4d26545d0d61a49d076d Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Wed, 15 Nov 2023 16:11:19 +0100 Subject: [PATCH 17/28] forgot .cabal --- postgrest.cabal | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/postgrest.cabal b/postgrest.cabal index f8703cc74b..35826a4ff0 100644 --- a/postgrest.cabal +++ b/postgrest.cabal @@ -64,6 +64,7 @@ library PostgREST.Plan.ReadPlan PostgREST.Plan.Types PostgREST.RangeQuery + PostgREST.Unix PostgREST.ApiRequest PostgREST.ApiRequest.Preferences PostgREST.ApiRequest.QueryParams @@ -120,6 +121,7 @@ library , time >= 1.6 && < 1.12 , timeit >= 2.0 && < 2.1 , unordered-containers >= 0.2.8 && < 0.3 + -- pinning unix-compat to match one's version in warp , unix-compat == 0.5.4 , vault >= 0.3.1.5 && < 0.4 , vector >= 0.11 && < 0.14 @@ -151,8 +153,6 @@ library build-depends: unix , directory >= 1.2.6 && < 1.4 - exposed-modules: - PostgREST.Unix executable postgrest default-language: Haskell2010 @@ -162,6 +162,7 @@ executable postgrest main-is: Main.hs build-depends: base >= 4.9 && < 4.17 , containers >= 0.5.7 && < 0.7 + , network >= 2.6 && < 3.2 , postgrest , protolude >= 0.3.1 && < 0.4 ghc-options: -threaded -rtsopts "-with-rtsopts=-N -I0 -qg" From 30495e6b64bd8bbdf7d471e42aa2af6d9ba68b42 Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Wed, 15 Nov 2023 16:21:15 +0100 Subject: [PATCH 18/28] fix windows build --- postgrest.cabal | 2 +- src/PostgREST/Unix.hs | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/postgrest.cabal b/postgrest.cabal index 35826a4ff0..3b160e7688 100644 --- a/postgrest.cabal +++ b/postgrest.cabal @@ -90,6 +90,7 @@ library , containers >= 0.5.7 && < 0.7 , contravariant-extras >= 0.3.3 && < 0.4 , cookie >= 0.4.2 && < 0.5 + , directory >= 1.2.6 && < 1.4 , either >= 4.4.1 && < 5.1 , extra >= 1.7.0 && < 2.0 , fuzzyset >= 0.2.3 @@ -152,7 +153,6 @@ library if !os(windows) build-depends: unix - , directory >= 1.2.6 && < 1.4 executable postgrest default-language: Haskell2010 diff --git a/src/PostgREST/Unix.hs b/src/PostgREST/Unix.hs index ec1b5bab31..a38da26369 100644 --- a/src/PostgREST/Unix.hs +++ b/src/PostgREST/Unix.hs @@ -5,9 +5,11 @@ module PostgREST.Unix , createAndBindDomainSocket ) where -import qualified System.Posix.Signals as Signals -import System.Posix.Types (FileMode) -import System.PosixCompat.Files (setFileMode) +#ifndef mingw32_HOST_OS +import qualified System.Posix.Signals as Signals +#endif +import System.Posix.Types (FileMode) +import System.PosixCompat.Files (setFileMode) import Data.String (String) import qualified Network.Socket as NS From 93c39536f1fb8ad6941cd2b478afa6ca86d8b4b8 Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Thu, 16 Nov 2023 12:35:05 +0100 Subject: [PATCH 19/28] fix installSignalHandlers stub --- src/PostgREST/Unix.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PostgREST/Unix.hs b/src/PostgREST/Unix.hs index a38da26369..46bfec5fe4 100644 --- a/src/PostgREST/Unix.hs +++ b/src/PostgREST/Unix.hs @@ -30,7 +30,7 @@ installSignalHandlers tid usr1 usr2 = do install signal handler = void $ Signals.installHandler signal (Signals.Catch handler) Nothing #else -installSignalHandlers _ = pass +installSignalHandlers _ _ _ = pass #endif createAndBindDomainSocket :: String -> FileMode -> IO NS.Socket From b09804edf9cae3ef96f4674a0bfc18c2ee138b02 Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Fri, 17 Nov 2023 14:46:36 +0100 Subject: [PATCH 20/28] move domain socket avail check to Unix --- src/PostgREST/AppState.hs | 9 ++++----- src/PostgREST/Unix.hs | 4 ++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/PostgREST/AppState.hs b/src/PostgREST/AppState.hs index e6cd413003..77c8633eff 100644 --- a/src/PostgREST/AppState.hs +++ b/src/PostgREST/AppState.hs @@ -72,7 +72,6 @@ import PostgREST.Unix (createAndBindDomainSocket) import Data.Streaming.Network (bindPortTCP, bindRandomPortTCP) import Data.String (IsString (..)) -import Network.Socket (isUnixDomainSocketAvailable) import Protolude data AuthResult = AuthResult @@ -172,10 +171,9 @@ initSockets AppConfig{..} = do cfg'adminport = configAdminServerPort sock <- case cfg'usp of - Just path -> do - unless isUnixDomainSocketAvailable $ - panic "Cannot run with unix socket on non-unix platforms. Consider deleting the `server-unix-socket` config entry in order to continue." - createAndBindDomainSocket path cfg'uspm + -- I'm not using `streaming-commons`' bindPath function here because it's not defined for Windows, + -- but we need to have runtime error if we try to use it in Windows, not compile time error + Just path -> createAndBindDomainSocket path cfg'uspm Nothing -> do (_, sock) <- if cfg'port /= 0 @@ -183,6 +181,7 @@ initSockets AppConfig{..} = do sock <- bindPortTCP cfg'port (fromString $ T.unpack cfg'host) pure (cfg'port, sock) else do + -- explicitly bind to a random port, returning bound port number (num, sock) <- bindRandomPortTCP (fromString $ T.unpack cfg'host) pure (num, sock) pure sock diff --git a/src/PostgREST/Unix.hs b/src/PostgREST/Unix.hs index 46bfec5fe4..2027cac023 100644 --- a/src/PostgREST/Unix.hs +++ b/src/PostgREST/Unix.hs @@ -33,8 +33,12 @@ installSignalHandlers tid usr1 usr2 = do installSignalHandlers _ _ _ = pass #endif +-- | Create a unix domain socket and bind it to the given path. +-- | The socket file will be deleted if it already exists. createAndBindDomainSocket :: String -> FileMode -> IO NS.Socket createAndBindDomainSocket path mode = do + unless NS.isUnixDomainSocketAvailable $ + panic "Cannot run with unix socket on non-unix platforms. Consider deleting the `server-unix-socket` config entry in order to continue." deleteSocketFileIfExist path sock <- NS.socket NS.AF_UNIX NS.Stream NS.defaultProtocol NS.bind sock $ NS.SockAddrUnix path From b9642a3a1132a18fe125402bed6111074e039c9d Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Fri, 17 Nov 2023 14:47:28 +0100 Subject: [PATCH 21/28] exe: drop network from deps --- postgrest.cabal | 1 - 1 file changed, 1 deletion(-) diff --git a/postgrest.cabal b/postgrest.cabal index 3b160e7688..e1b848c112 100644 --- a/postgrest.cabal +++ b/postgrest.cabal @@ -162,7 +162,6 @@ executable postgrest main-is: Main.hs build-depends: base >= 4.9 && < 4.17 , containers >= 0.5.7 && < 0.7 - , network >= 2.6 && < 3.2 , postgrest , protolude >= 0.3.1 && < 0.4 ghc-options: -threaded -rtsopts "-with-rtsopts=-N -I0 -qg" From 7fe26dcfce7abb32ffb166d4aed263f8d03e9ce8 Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Fri, 17 Nov 2023 15:04:26 +0100 Subject: [PATCH 22/28] style --- src/PostgREST/AppState.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PostgREST/AppState.hs b/src/PostgREST/AppState.hs index 77c8633eff..8fff8edc85 100644 --- a/src/PostgREST/AppState.hs +++ b/src/PostgREST/AppState.hs @@ -171,7 +171,7 @@ initSockets AppConfig{..} = do cfg'adminport = configAdminServerPort sock <- case cfg'usp of - -- I'm not using `streaming-commons`' bindPath function here because it's not defined for Windows, + -- I'm not using `streaming-commons`' bindPath function here because it's not defined for Windows, -- but we need to have runtime error if we try to use it in Windows, not compile time error Just path -> createAndBindDomainSocket path cfg'uspm Nothing -> do From e56e857b349d192e1a81fa5a6b234cf17722bbb9 Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Wed, 22 Nov 2023 17:34:44 +0100 Subject: [PATCH 23/28] unpin unix-compat --- postgrest.cabal | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/postgrest.cabal b/postgrest.cabal index e1b848c112..262fc956ba 100644 --- a/postgrest.cabal +++ b/postgrest.cabal @@ -122,8 +122,7 @@ library , time >= 1.6 && < 1.12 , timeit >= 2.0 && < 2.1 , unordered-containers >= 0.2.8 && < 0.3 - -- pinning unix-compat to match one's version in warp - , unix-compat == 0.5.4 + , unix-compat , vault >= 0.3.1.5 && < 0.4 , vector >= 0.11 && < 0.14 , wai >= 3.2.1 && < 3.3 From 56d524c4825fde31e05d6306a38890b9c74fb5fe Mon Sep 17 00:00:00 2001 From: Steve Chavez Date: Wed, 22 Nov 2023 12:30:44 -0500 Subject: [PATCH 24/28] Update postgrest.cabal --- postgrest.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgrest.cabal b/postgrest.cabal index 262fc956ba..b97d88a2ba 100644 --- a/postgrest.cabal +++ b/postgrest.cabal @@ -122,7 +122,7 @@ library , time >= 1.6 && < 1.12 , timeit >= 2.0 && < 2.1 , unordered-containers >= 0.2.8 && < 0.3 - , unix-compat + , unix-compat >= 0.6 && < 0.7 , vault >= 0.3.1.5 && < 0.4 , vector >= 0.11 && < 0.14 , wai >= 3.2.1 && < 3.3 From 9df15e10246d3fe2c517f0c9316d94f4d8112785 Mon Sep 17 00:00:00 2001 From: Steve Chavez Date: Wed, 22 Nov 2023 12:51:47 -0500 Subject: [PATCH 25/28] Update postgrest.cabal --- postgrest.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgrest.cabal b/postgrest.cabal index b97d88a2ba..c2d692f06e 100644 --- a/postgrest.cabal +++ b/postgrest.cabal @@ -122,7 +122,7 @@ library , time >= 1.6 && < 1.12 , timeit >= 2.0 && < 2.1 , unordered-containers >= 0.2.8 && < 0.3 - , unix-compat >= 0.6 && < 0.7 + , unix-compat >= 0.5.4 && < 0.6 , vault >= 0.3.1.5 && < 0.4 , vector >= 0.11 && < 0.14 , wai >= 3.2.1 && < 3.3 From 77a93450eec9374b7ad199ccd6f393efe3d058f0 Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Thu, 23 Nov 2023 11:06:04 +0100 Subject: [PATCH 26/28] test/io: test random port assignment --- test/io/test_io.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/io/test_io.py b/test/io/test_io.py index 9209463d13..efc76d49ca 100644 --- a/test/io/test_io.py +++ b/test/io/test_io.py @@ -196,6 +196,11 @@ def sleep(): t.join() +def test_random_port_bound(defaultenv): + "PostgREST should bind to a random port when PGRST_SERVER_PORT is 0." + + with run(env=defaultenv, port="0") as postgrest: + assert True # liveness check is done by run(), so we just need to check that it doesn't fail def test_app_settings_reload(tmp_path, defaultenv): "App settings should be reloaded from file when PostgREST is sent SIGUSR2." From 904f01d58d8134035c6b2c60a17979fa16da3e53 Mon Sep 17 00:00:00 2001 From: Andrei Dziahel Date: Thu, 23 Nov 2023 11:23:40 +0100 Subject: [PATCH 27/28] style --- test/io/test_io.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/io/test_io.py b/test/io/test_io.py index efc76d49ca..3993de751b 100644 --- a/test/io/test_io.py +++ b/test/io/test_io.py @@ -196,11 +196,13 @@ def sleep(): t.join() + def test_random_port_bound(defaultenv): "PostgREST should bind to a random port when PGRST_SERVER_PORT is 0." with run(env=defaultenv, port="0") as postgrest: - assert True # liveness check is done by run(), so we just need to check that it doesn't fail + assert True # liveness check is done by run(), so we just need to check that it doesn't fail + def test_app_settings_reload(tmp_path, defaultenv): "App settings should be reloaded from file when PostgREST is sent SIGUSR2." From bee8c8b0d55f65f81acb9e3913f40f6d7000e8a7 Mon Sep 17 00:00:00 2001 From: steve-chavez Date: Thu, 23 Nov 2023 17:53:52 -0500 Subject: [PATCH 28/28] delete outdated comment add CHANGELOG --- CHANGELOG.md | 1 + src/PostgREST/Admin.hs | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac956f5329..8c9321a80b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - #3019, Transaction-Scoped Settings are now shown clearly in the Postgres logs - @laurenceisla + Shows `set_config('pgrst.setting_name', $1)` instead of `setconfig($1, $2)` + Does not apply to role settings and `app.settings.*` + - #2420, Fix bogus message when listening on port 0 - @develop7 ### Changed diff --git a/src/PostgREST/Admin.hs b/src/PostgREST/Admin.hs index 64ef9372af..f7f8648e6c 100644 --- a/src/PostgREST/Admin.hs +++ b/src/PostgREST/Admin.hs @@ -50,8 +50,6 @@ admin appState appConfig req respond = do -- Try to connect to the main app socket -- Note that it doesn't even send a valid HTTP request, we just want to check that the main app is accepting connections --- The code for resolving the "*4", "!4", "*6", "!6", "*" special values is taken from --- https://hackage.haskell.org/package/streaming-commons-0.2.2.4/docs/src/Data.Streaming.Network.html#bindPortGenEx reachMainApp :: Socket -> IO (Either IOException ()) reachMainApp appSock = do sockAddr <- getSocketName appSock