summaryrefslogtreecommitdiff
path: root/src/server/FastCGIUtils.hs
diff options
context:
space:
mode:
authorbjorn <bjorn@bringert.net>2008-08-14 09:06:26 +0000
committerbjorn <bjorn@bringert.net>2008-08-14 09:06:26 +0000
commita7aa8fb9812a204f0a1a984cb1d4c727761490ff (patch)
tree4fc793d703114d2ce84920b28ae2430cc6ed341b /src/server/FastCGIUtils.hs
parent77270a010a0b453e9a84c3e62db7cfd22e49d55d (diff)
Added first version of the GF FastCGI server.
Diffstat (limited to 'src/server/FastCGIUtils.hs')
-rw-r--r--src/server/FastCGIUtils.hs122
1 files changed, 122 insertions, 0 deletions
diff --git a/src/server/FastCGIUtils.hs b/src/server/FastCGIUtils.hs
new file mode 100644
index 000000000..762127f7c
--- /dev/null
+++ b/src/server/FastCGIUtils.hs
@@ -0,0 +1,122 @@
+module FastCGIUtils (initFastCGI, loopFastCGI,
+ DataRef, newDataRef, getData) where
+
+import Control.Concurrent
+import Control.Exception
+import Control.Monad
+import Data.IORef
+import Prelude hiding (catch)
+import System.Directory
+import System.Environment
+import System.Exit
+import System.IO
+import System.IO.Unsafe
+import System.Posix
+import System.Time
+
+import Network.FastCGI
+
+initFastCGI :: IO ()
+initFastCGI = installSignalHandlers
+
+loopFastCGI :: CGI CGIResult -> IO ()
+loopFastCGI f =
+ do (do runOneFastCGI f
+ exitIfToldTo
+ restartIfModified)
+ `catchAborted` logError "Request aborted"
+ loopFastCGI f
+
+
+-- Signal handling for FastCGI programs.
+
+
+installSignalHandlers :: IO ()
+installSignalHandlers =
+ do t <- myThreadId
+ installHandler sigUSR1 (Catch gracefulExit) Nothing
+ installHandler sigTERM (Catch gracelessExit) Nothing
+ installHandler sigPIPE (Catch (requestAborted t)) Nothing
+ return ()
+
+{-# NOINLINE shouldExit #-}
+shouldExit :: IORef Bool
+shouldExit = unsafePerformIO $ newIORef False
+
+catchAborted :: IO a -> IO a -> IO a
+catchAborted x y = x `catch` \e -> case e of
+ ErrorCall "**aborted**" -> y
+ _ -> throw e
+
+requestAborted :: ThreadId -> IO ()
+requestAborted t = throwTo t (ErrorCall "**aborted**")
+
+gracelessExit :: IO ()
+gracelessExit = do logError "Graceless exit"
+ exitWith ExitSuccess
+
+gracefulExit :: IO ()
+gracefulExit =
+ do logError "Graceful exit"
+ writeIORef shouldExit True
+
+exitIfToldTo :: IO ()
+exitIfToldTo =
+ do b <- readIORef shouldExit
+ when b $ do logError "Exiting..."
+ exitWith ExitSuccess
+
+
+-- Restart handling for FastCGI programs.
+
+{-# NOINLINE myModTimeRef #-}
+myModTimeRef :: IORef EpochTime
+myModTimeRef = unsafePerformIO (getProgModTime >>= newIORef)
+
+-- FIXME: doesn't get directory
+myProgPath :: IO FilePath
+myProgPath = getProgName
+
+getProgModTime :: IO EpochTime
+getProgModTime = liftM modificationTime (myProgPath >>= getFileStatus)
+
+needsRestart :: IO Bool
+needsRestart = liftM2 (/=) (readIORef myModTimeRef) getProgModTime
+
+exitIfModified :: IO ()
+exitIfModified =
+ do restart <- needsRestart
+ when restart $ exitWith ExitSuccess
+
+restartIfModified :: IO ()
+restartIfModified =
+ do restart <- needsRestart
+ when restart $ do prog <- myProgPath
+ args <- getArgs
+ hPutStrLn stderr $ prog ++ " has been modified, restarting ..."
+ -- FIXME: setCurrentDirectory?
+ executeFile prog False args Nothing
+
+-- Utilities for getting and caching read-only data from disk.
+-- The data is reloaded when the file on disk has been modified.
+
+type DataRef a = IORef (Maybe (ClockTime, a))
+
+newDataRef :: MonadIO m => m (DataRef a)
+newDataRef = liftIO $ newIORef Nothing
+
+getData :: MonadIO m => (FilePath -> IO a) -> DataRef a -> FilePath -> m a
+getData loadData ref file = liftIO $
+ do t' <- getModificationTime file
+ m <- readIORef ref
+ case m of
+ Just (t,x) | t' == t -> return x
+ _ -> do logCGI $ "Loading " ++ show file ++ "..."
+ x <- loadData file
+ writeIORef ref (Just (t',x))
+ return x
+
+-- Logging
+
+logError :: String -> IO ()
+logError s = hPutStrLn stderr s \ No newline at end of file