mirror of
https://github.com/GrammaticalFramework/gf-core.git
synced 2026-05-09 03:02:50 -06:00
gf -server mode improvements
+ Avoid looping if it is not possible to create a new server directory. + Work on FastCGI support using the direct-fastcgi package (commented out for now because of buggy behavior).
This commit is contained in:
@@ -4,8 +4,8 @@ import Data.List(partition)
|
|||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import Control.Monad(when)
|
import Control.Monad(when)
|
||||||
import System.Random(randomRIO)
|
import System.Random(randomRIO)
|
||||||
import System.IO(stdout,stderr)
|
import System.IO(stdout,stderr,hPutStrLn)
|
||||||
import System.IO.Error(try,ioError)
|
import System.IO.Error(try,ioError,isAlreadyExistsError)
|
||||||
import System.Directory(doesDirectoryExist,doesFileExist,createDirectory,
|
import System.Directory(doesDirectoryExist,doesFileExist,createDirectory,
|
||||||
setCurrentDirectory,getCurrentDirectory,
|
setCurrentDirectory,getCurrentDirectory,
|
||||||
getDirectoryContents,removeFile,removeDirectory)
|
getDirectoryContents,removeFile,removeDirectory)
|
||||||
@@ -14,12 +14,14 @@ import System.FilePath(takeExtension,takeFileName,takeDirectory,(</>))
|
|||||||
import System.Posix.Files(getSymbolicLinkStatus,isSymbolicLink,removeLink,
|
import System.Posix.Files(getSymbolicLinkStatus,isSymbolicLink,removeLink,
|
||||||
createSymbolicLink)
|
createSymbolicLink)
|
||||||
#endif
|
#endif
|
||||||
import Control.Concurrent.MVar(newMVar,modifyMVar)
|
import Control.Concurrent(newMVar,modifyMVar,forkIO)
|
||||||
import Network.URI(URI(..))
|
import Network.URI(URI(..),parseURI)
|
||||||
import Network.Shed.Httpd(initServer,Request(..),Response(..),queryToArguments,
|
import Network.Shed.Httpd(initServer,Request(..),Response(..),queryToArguments,
|
||||||
noCache)
|
noCache)
|
||||||
|
--import qualified Network.FastCGI as FCGI -- from hackage direct-fastcgi
|
||||||
|
--import qualified Data.ByteString.Char8 as BS(pack,unpack,length)
|
||||||
import Network.CGI(handleErrors,liftIO)
|
import Network.CGI(handleErrors,liftIO)
|
||||||
import FastCGIUtils(outputJSONP,handleCGIErrors)
|
import FastCGIUtils(outputJSONP,handleCGIErrors,stderrToFile)
|
||||||
import System.IO.Silently(hCapture)
|
import System.IO.Silently(hCapture)
|
||||||
import System.Process(readProcessWithExitCode)
|
import System.Process(readProcessWithExitCode)
|
||||||
import System.Exit(ExitCode(..))
|
import System.Exit(ExitCode(..))
|
||||||
@@ -32,22 +34,61 @@ import Paths_gf(getDataDir,version)
|
|||||||
import GF.Infra.BuildInfo (buildInfo)
|
import GF.Infra.BuildInfo (buildInfo)
|
||||||
import RunHTTP(cgiHandler)
|
import RunHTTP(cgiHandler)
|
||||||
|
|
||||||
-- * HTTP server
|
--logFile :: FilePath
|
||||||
|
--logFile = "pgf-error.log"
|
||||||
|
|
||||||
|
debug s = liftIO (logPutStrLn s)
|
||||||
|
|
||||||
|
-- | Combined FastCGI and HTTP server
|
||||||
server execute1 state0 =
|
server execute1 state0 =
|
||||||
do state <- newMVar M.empty
|
do --stderrToFile logFile
|
||||||
|
state <- newMVar M.empty
|
||||||
cache <- PS.newPGFCache
|
cache <- PS.newPGFCache
|
||||||
datadir <- getDataDir
|
datadir <- getDataDir
|
||||||
let root = datadir</>"www"
|
let root = datadir</>"www"
|
||||||
port = 41296
|
debug $ "document root="++root
|
||||||
putStrLn $ "This is GF version "++showVersion version++"."
|
|
||||||
putStrLn buildInfo
|
|
||||||
putStrLn $ "Document root = "++root
|
|
||||||
putStrLn $ "Starting HTTP server, open http://localhost:"
|
|
||||||
++show port++"/ in your web browser."
|
|
||||||
setCurrentDirectory root
|
setCurrentDirectory root
|
||||||
initServer port (modifyMVar state . handle state0 cache execute1)
|
-- FCGI.acceptLoop forkIO (handle_fcgi execute1 state0 state cache)
|
||||||
|
-- if acceptLoop returns, then GF was not invoked as a FastCGI script
|
||||||
|
http_server execute1 state0 state cache root
|
||||||
|
where
|
||||||
|
-- | HTTP server
|
||||||
|
http_server execute1 state0 state cache root =
|
||||||
|
do putStrLn $ "This is GF version "++showVersion version++"."
|
||||||
|
putStrLn buildInfo
|
||||||
|
putStrLn $ "Document root = "++root
|
||||||
|
putStrLn $ "Starting HTTP server, open http://localhost:"
|
||||||
|
++show port++"/ in your web browser."
|
||||||
|
initServer port (modifyMVar state . handle state0 cache execute1)
|
||||||
|
|
||||||
-- * HTTP request handler
|
port = 41296
|
||||||
|
{-
|
||||||
|
-- | FastCGI request handler
|
||||||
|
handle_fcgi execute1 state0 stateM cache =
|
||||||
|
do Just method <- FCGI.getRequestMethod
|
||||||
|
debug $ "request method="++method
|
||||||
|
Just path <- FCGI.getPathInfo
|
||||||
|
-- debug $ "path info="++path
|
||||||
|
query <- maybe (return "") return =<< FCGI.getQueryString
|
||||||
|
-- debug $ "query string="++query
|
||||||
|
let uri = URI "" Nothing path query ""
|
||||||
|
headers <- fmap (mapFst show) FCGI.getAllRequestHeaders
|
||||||
|
body <- fmap BS.unpack FCGI.fGetContents
|
||||||
|
let req = Request method uri headers body
|
||||||
|
-- debug (show req)
|
||||||
|
(output,resp) <- liftIO $ hCapture [stdout] $ modifyMVar stateM $ handle state0 cache execute1 req
|
||||||
|
let Response code headers body = resp
|
||||||
|
-- debug output
|
||||||
|
debug $ " "++show code++" "++show headers
|
||||||
|
FCGI.setResponseStatus code
|
||||||
|
mapM_ (uncurry (FCGI.setResponseHeader . toHeader)) headers
|
||||||
|
let pbody = BS.pack body
|
||||||
|
n = BS.length pbody
|
||||||
|
FCGI.fPut pbody
|
||||||
|
debug $ "done "++show n
|
||||||
|
-}
|
||||||
|
|
||||||
|
-- | HTTP request handler
|
||||||
handle state0 cache execute1
|
handle state0 cache execute1
|
||||||
rq@(Request method URI{uriPath=upath,uriQuery=q} hdrs body) state =
|
rq@(Request method URI{uriPath=upath,uriQuery=q} hdrs body) state =
|
||||||
do let qs = decodeQ $
|
do let qs = decodeQ $
|
||||||
@@ -227,7 +268,7 @@ serveStaticFile' path =
|
|||||||
else return (resp404 path)
|
else return (resp404 path)
|
||||||
|
|
||||||
-- * Logging
|
-- * Logging
|
||||||
logPutStrLn = putStrLn
|
logPutStrLn = hPutStrLn stderr
|
||||||
|
|
||||||
-- * Standard HTTP responses
|
-- * Standard HTTP responses
|
||||||
ok200 = Response 200 [plainUTF8,noCache] . encodeString
|
ok200 = Response 200 [plainUTF8,noCache] . encodeString
|
||||||
@@ -265,24 +306,42 @@ updateFile path new =
|
|||||||
seq (either (const 0) length old) $
|
seq (either (const 0) length old) $
|
||||||
writeBinaryFile path new
|
writeBinaryFile path new
|
||||||
|
|
||||||
|
|
||||||
newDirectory =
|
newDirectory =
|
||||||
do k <- randomRIO (1,maxBound::Int)
|
do debug "newDirectory"
|
||||||
let path = "tmp/gfse."++show k
|
loop 10
|
||||||
b <- try $ createDirectory path
|
where
|
||||||
case b of
|
loop 0 = fail "Failed to create a new directory"
|
||||||
Left _ -> newDirectory
|
loop n = maybe (loop (n-1)) return =<< once
|
||||||
Right _ -> return ('/':path)
|
|
||||||
|
once =
|
||||||
|
do k <- randomRIO (1,maxBound::Int)
|
||||||
|
let path = "tmp/gfse."++show k
|
||||||
|
b <- try $ createDirectory path
|
||||||
|
case b of
|
||||||
|
Left err -> do debug (show err) ;
|
||||||
|
if isAlreadyExistsError err
|
||||||
|
then return Nothing
|
||||||
|
else ioError err
|
||||||
|
Right _ -> return (Just ('/':path))
|
||||||
|
|
||||||
-- | Remove a directory and the files in it, but not recursively
|
-- | Remove a directory and the files in it, but not recursively
|
||||||
removeDir dir =
|
removeDir dir =
|
||||||
do files <- filter (`notElem` [".",".."]) `fmap` getDirectoryContents dir
|
do files <- filter (`notElem` [".",".."]) `fmap` getDirectoryContents dir
|
||||||
mapM (removeFile . (dir</>)) files
|
mapM (removeFile . (dir</>)) files
|
||||||
removeDirectory dir
|
removeDirectory dir
|
||||||
|
{-
|
||||||
|
-- * direct-fastcgi deficiency workaround
|
||||||
|
|
||||||
|
--toHeader = FCGI.toHeader -- not exported, unfortuntately
|
||||||
|
|
||||||
|
toHeader "Content-Type" = FCGI.HttpContentType -- to avoid duplicate headers
|
||||||
|
toHeader s = FCGI.HttpExtensionHeader s -- cheating a bit
|
||||||
|
-}
|
||||||
-- * misc utils
|
-- * misc utils
|
||||||
|
|
||||||
decodeQ qs = [(decode n,decode v)|(n,v)<-qs]
|
decodeQ qs = [(decode n,decode v)|(n,v)<-qs]
|
||||||
decode = map decode1
|
decode = map decode1
|
||||||
decode1 '+' = ' ' -- httpd-shed bug workaround
|
decode1 '+' = ' ' -- httpd-shed bug workaround
|
||||||
decode1 c = c
|
decode1 c = c
|
||||||
|
|
||||||
|
mapFst f xys = [(f x,y)|(x,y)<-xys]
|
||||||
Reference in New Issue
Block a user