@@ -63,7 +63,8 @@ import PostgREST.Version (docsVersion, prettyVersion)
6363
6464import qualified Data.ByteString.Char8 as BS
6565import qualified Data.List as L
66- import Data.Streaming.Network (bindPortTCP ,
66+ import Data.Streaming.Network (HostPreference ,
67+ bindPortGenEx , bindPortTCP ,
6768 bindRandomPortTCP )
6869import qualified Data.Text as T
6970import qualified Network.HTTP.Types as HTTP
@@ -260,7 +261,7 @@ initSockets AppConfig{..} = do
260261 (_, sock) <-
261262 if cfg'port /= 0
262263 then do
263- sock <- bindPortTCP cfg'port (fromString $ T. unpack cfg'host)
264+ sock <- bindPortTCPWithReusePort cfg'port (fromString $ T. unpack cfg'host)
264265 pure (cfg'port, sock)
265266 else do
266267 -- explicitly bind to a random port, returning bound port number
@@ -270,9 +271,21 @@ initSockets AppConfig{..} = do
270271
271272 adminSock <- case cfg'adminPort of
272273 Just adminPort -> do
273- adminSock <- bindPortTCP adminPort (fromString $ T. unpack cfg'adminHost)
274+ adminSock <- bindPortTCPWithReusePort adminPort (fromString $ T. unpack cfg'adminHost)
274275 pure $ Just adminSock
275276 Nothing -> pure Nothing
276277
277278 pure (sock, adminSock)
278279
280+ bindPortTCPWithReusePort :: Int -> HostPreference -> IO NS. Socket
281+ bindPortTCPWithReusePort port hostPreference
282+ = do
283+ -- Some unix variants can expose ReusePort but reject it at runtime.
284+ -- Fall back to the default behavior when that happens.
285+ socketWithReusePort <- try (bindPortGenEx reusePortOpts NS. Stream port hostPreference) :: IO (Either SomeException NS. Socket )
286+ either (const $ bindPortTCP port hostPreference) listenSocket socketWithReusePort
287+ where
288+ reusePortOpts = [(NS. ReusePort , 1 )]
289+ listenSocket sock = do
290+ NS. listen sock (max 2048 NS. maxListenQueue)
291+ pure sock
0 commit comments