mirror of
https://github.com/GrammaticalFramework/gf-core.git
synced 2026-05-20 08:32:50 -06:00
gf -server: allow multiple PGF service requests to be handled in parallel
PGF service requests are stateless and can run in parallel, but some other requests handled by the server are not and might even change the current working directory temporarily, and this affects all threads, so it is important that the PGF service requests access PGF files by absolute paths.
This commit is contained in:
@@ -9,6 +9,7 @@ import Control.Monad.Error(ErrorT(..),Error(..))
|
|||||||
import System.Random(randomRIO)
|
import System.Random(randomRIO)
|
||||||
--import System.IO(stderr,hPutStrLn)
|
--import System.IO(stderr,hPutStrLn)
|
||||||
import GF.System.Catch(try)
|
import GF.System.Catch(try)
|
||||||
|
import Control.Exception(bracket_)
|
||||||
import System.IO.Error(isAlreadyExistsError)
|
import System.IO.Error(isAlreadyExistsError)
|
||||||
import GF.System.Directory(doesDirectoryExist,doesFileExist,createDirectory,
|
import GF.System.Directory(doesDirectoryExist,doesFileExist,createDirectory,
|
||||||
setCurrentDirectory,getCurrentDirectory,
|
setCurrentDirectory,getCurrentDirectory,
|
||||||
@@ -57,7 +58,7 @@ server port optroot execute1 state0 =
|
|||||||
datadir <- getDataDir
|
datadir <- getDataDir
|
||||||
let root = maybe (datadir</>"www") id optroot
|
let root = maybe (datadir</>"www") id optroot
|
||||||
-- debug $ "document root="++root
|
-- debug $ "document root="++root
|
||||||
setCurrentDirectory root
|
setDir root
|
||||||
-- FCGI.acceptLoop forkIO (handle_fcgi execute1 state0 state cache)
|
-- FCGI.acceptLoop forkIO (handle_fcgi execute1 state0 state cache)
|
||||||
-- if acceptLoop returns, then GF was not invoked as a FastCGI script
|
-- if acceptLoop returns, then GF was not invoked as a FastCGI script
|
||||||
http_server execute1 state0 state cache root
|
http_server execute1 state0 state cache root
|
||||||
@@ -68,7 +69,7 @@ server port optroot execute1 state0 =
|
|||||||
logPutStrLn $ "Document root = "++root
|
logPutStrLn $ "Document root = "++root
|
||||||
logPutStrLn $ "Starting HTTP server, open http://localhost:"
|
logPutStrLn $ "Starting HTTP server, open http://localhost:"
|
||||||
++show port++"/ in your web browser."
|
++show port++"/ in your web browser."
|
||||||
initServer port (modifyMVar state . handle root state0 cache execute1)
|
initServer port (handle root state0 cache execute1 state)
|
||||||
|
|
||||||
gf_version = "This is GF version "++showVersion version++".\n"++buildInfo
|
gf_version = "This is GF version "++showVersion version++".\n"++buildInfo
|
||||||
|
|
||||||
@@ -117,50 +118,51 @@ put_state state = do qs <- get_qs; put (qs,state)
|
|||||||
err :: Response -> HM s a
|
err :: Response -> HM s a
|
||||||
err e = StateT $ \ s -> ErrorT $ return $ Left e
|
err e = StateT $ \ s -> ErrorT $ return $ Left e
|
||||||
|
|
||||||
hmtry :: HM s a -> HM s (Either (Either IOError Response) a)
|
hmbracket_ :: IO () -> IO () -> HM s a -> HM s a
|
||||||
hmtry m = do s <- get
|
hmbracket_ pre post m =
|
||||||
e <- liftIO $ try $ runErrorT $ runStateT m s
|
do s <- get
|
||||||
|
e <- liftIO $ bracket_ pre post $ runErrorT $ runStateT m s
|
||||||
case e of
|
case e of
|
||||||
Left ioerror -> return (Left (Left ioerror))
|
Left resp -> err resp
|
||||||
Right (Left resp) -> return (Left (Right resp))
|
Right (a,s) -> do put s;return a
|
||||||
Right (Right (a,s)) -> do put s;return (Right a)
|
|
||||||
|
|
||||||
-- | HTTP request handler
|
-- | HTTP request handler
|
||||||
handle documentroot state0 cache execute1
|
handle documentroot state0 cache execute1 stateVar
|
||||||
rq@(Request method URI{uriPath=upath,uriQuery=q} hdrs body) state =
|
rq@(Request method URI{uriPath=upath,uriQuery=q} hdrs body) =
|
||||||
case method of
|
case method of
|
||||||
"POST" -> run normal_request (utf8inputs body,state)
|
"POST" -> normal_request (utf8inputs body)
|
||||||
"GET" -> run normal_request (utf8inputs q,state)
|
"GET" -> normal_request (utf8inputs q)
|
||||||
_ -> return (state,resp501 $ "method "++method)
|
_ -> return (resp501 $ "method "++method)
|
||||||
where
|
where
|
||||||
normal_request =
|
normal_request qs =
|
||||||
do -- Defend against unhandled errors under inDir:
|
do logPutStrLn $ method++" "++upath++" "++show (mapSnd (take 100.fst) qs)
|
||||||
liftIO $ setDir documentroot
|
let stateful m = modifyMVar stateVar $ \ s -> run m (qs,s)
|
||||||
qs <- get_qs
|
-- stateful ensures mutual exclusion, so you can use/change the cwd
|
||||||
logPutStrLn $ method++" "++upath++" "++show (mapSnd (take 100.fst) qs)
|
|
||||||
case upath of
|
case upath of
|
||||||
"/new" -> new
|
"/new" -> stateful $ new
|
||||||
|
"/gfshell" -> stateful $ inDir command
|
||||||
|
"/cloud" -> stateful $ inDir cloud
|
||||||
-- "/stop" ->
|
-- "/stop" ->
|
||||||
-- "/start" ->
|
-- "/start" ->
|
||||||
"/gfshell" -> inDir command
|
|
||||||
"/parse" -> parse (decoded qs)
|
"/parse" -> parse (decoded qs)
|
||||||
"/cloud" -> inDir cloud
|
|
||||||
"/version" -> return (ok200 gf_version)
|
"/version" -> return (ok200 gf_version)
|
||||||
'/':rpath ->
|
'/':rpath ->
|
||||||
|
-- This code runs without mutual exclusion, so it must *not*
|
||||||
|
-- use/change the cwd. Access files by absolute paths only.
|
||||||
case (takeDirectory path,takeFileName path,takeExtension path) of
|
case (takeDirectory path,takeFileName path,takeExtension path) of
|
||||||
(_ ,_ ,".pgf") -> wrapCGI $ PS.cgiMain' cache path
|
(_ ,_ ,".pgf") -> do debug $ "PGF service: "++path
|
||||||
|
wrapCGI $ PS.cgiMain' cache path
|
||||||
(dir,"grammars.cgi",_ ) -> grammarList dir (decoded qs)
|
(dir,"grammars.cgi",_ ) -> grammarList dir (decoded qs)
|
||||||
(dir ,"exb.fcgi" ,_ ) -> wrapCGI $ ES.cgiMain' root dir (fst cache)
|
(dir ,"exb.fcgi" ,_ ) -> wrapCGI $ ES.cgiMain' root dir (fst cache)
|
||||||
_ -> liftIO $ serveStaticFile path
|
_ -> serveStaticFile path
|
||||||
where path = translatePath rpath
|
where path = translatePath rpath
|
||||||
_ -> err $ resp400 upath
|
_ -> return $ resp400 upath
|
||||||
|
|
||||||
root = "."
|
root = documentroot
|
||||||
|
|
||||||
translatePath rpath = root</>rpath -- hmm, check for ".."
|
translatePath rpath = root</>rpath -- hmm, check for ".."
|
||||||
|
|
||||||
wrapCGI cgi =
|
wrapCGI cgi = cgiHandler root (handleErrors . handleCGIErrors $ cgi) rq
|
||||||
liftIO $ cgiHandler root (handleErrors . handleCGIErrors $ cgi) rq
|
|
||||||
|
|
||||||
look field =
|
look field =
|
||||||
do qs <- get_qs
|
do qs <- get_qs
|
||||||
@@ -173,18 +175,19 @@ handle documentroot state0 cache execute1
|
|||||||
where
|
where
|
||||||
cd ('/':dir@('t':'m':'p':_)) =
|
cd ('/':dir@('t':'m':'p':_)) =
|
||||||
do cwd <- liftIO $ getCurrentDirectory
|
do cwd <- liftIO $ getCurrentDirectory
|
||||||
b <- liftIO $ try $ setDir dir
|
b <- liftIO $ doesDirectoryExist dir
|
||||||
case b of
|
case b of
|
||||||
Left _ -> do b <- liftIO $ try $ readFile dir -- poor man's symbolic links
|
False -> do b <- liftIO $ try $ readFile dir -- poor man's symbolic links
|
||||||
case b of
|
case b of
|
||||||
Left _ -> err $ resp404 dir
|
Left _ -> err $ resp404 dir
|
||||||
Right dir' -> cd dir'
|
Right dir' -> cd dir'
|
||||||
Right _ -> do --logPutStrLn $ "cd "++dir
|
True -> do --logPutStrLn $ "cd "++dir
|
||||||
r <- hmtry (ok dir)
|
hmInDir dir (ok dir)
|
||||||
liftIO $ setDir cwd
|
|
||||||
either (either (liftIO . ioError) err) return r
|
|
||||||
cd dir = err $ resp400 $ "unacceptable directory "++dir
|
cd dir = err $ resp400 $ "unacceptable directory "++dir
|
||||||
|
|
||||||
|
-- First ensure that only one thread that depends on the cwd is running!
|
||||||
|
hmInDir dir = hmbracket_ (setDir dir) (setDir documentroot)
|
||||||
|
|
||||||
new = fmap ok200 $ liftIO $ newDirectory
|
new = fmap ok200 $ liftIO $ newDirectory
|
||||||
|
|
||||||
command dir =
|
command dir =
|
||||||
@@ -262,9 +265,8 @@ handle documentroot state0 cache execute1
|
|||||||
download path = liftIO $ serveStaticFile path
|
download path = liftIO $ serveStaticFile path
|
||||||
|
|
||||||
link_directories olddir newdir@('/':'t':'m':'p':'/':_) | old/=new =
|
link_directories olddir newdir@('/':'t':'m':'p':'/':_) | old/=new =
|
||||||
liftIO $
|
hmInDir ".." $ liftIO $
|
||||||
do setDir ".."
|
do logPutStrLn =<< getCurrentDirectory
|
||||||
logPutStrLn =<< getCurrentDirectory
|
|
||||||
logPutStrLn $ "link_dirs new="++new++", old="++old
|
logPutStrLn $ "link_dirs new="++new++", old="++old
|
||||||
#ifdef mingw32_HOST_OS
|
#ifdef mingw32_HOST_OS
|
||||||
isDir <- doesDirectoryExist old
|
isDir <- doesDirectoryExist old
|
||||||
|
|||||||
Reference in New Issue
Block a user