diff --git a/src/server/PGFService.hs b/src/server/PGFService.hs index c19f7961c..4d8cd2f51 100644 --- a/src/server/PGFService.hs +++ b/src/server/PGFService.hs @@ -5,6 +5,8 @@ import qualified PGF import Cache import FastCGIUtils import URLEncoding +import RunHTTP +import ServeStaticFile import Network.FastCGI import Text.JSON @@ -25,6 +27,7 @@ import System.FilePath import System.Process import System.Exit import System.IO +import System.Environment(getArgs) logFile :: FilePath logFile = "pgf-error.log" @@ -33,33 +36,64 @@ logFile = "pgf-error.log" main :: IO () main = do stderrToFile logFile cache <- newCache PGF.readPGF + args <- getArgs + case args of + [] -> fcgiMain cache + ["http"] -> httpMain cache 41296 + ["http",port] -> httpMain cache =<< readIO port + +httpMain cache port = runHTTP port (do log ; serve =<< getPath) + where + log = do method <- requestMethod + uri <- getVarWithDefault "REQUEST_URI" "-" + logCGI $ method++" "++uri + + serve path = + if takeExtension path==".pgf" + then cgiMain' cache path + else if takeFileName path=="grammars.cgi" + then grammarList (takeDirectory path) + else serveStaticFile path + + grammarList dir = + do paths <- liftIO $ getDirectoryContents dir + let pgfs = [path|path<-paths, takeExtension path==".pgf"] + outputJSONP pgfs + +fcgiMain :: Cache PGF -> IO () +fcgiMain cache = #ifndef mingw32_HOST_OS - runFastCGIConcurrent' forkIO 100 (handleErrors (handleCGIErrors (cgiMain cache))) + runFastCGIConcurrent' forkIO 100 (cgiMain cache) #else - runFastCGI (handleErrors (handleCGIErrors (cgiMain cache))) + runFastCGI (cgiMain cache) #endif +getPath = getVarWithDefault "SCRIPT_FILENAME" "" + cgiMain :: Cache PGF -> CGI CGIResult -cgiMain cache = - do path <- getVarWithDefault "SCRIPT_FILENAME" "" - pgf <- liftIO $ readCache cache path +cgiMain cache = cgiMain' cache =<< getPath + +cgiMain' :: Cache PGF -> FilePath -> CGI CGIResult +cgiMain' cache path = + handleErrors . handleCGIErrors $ + do pgf <- liftIO $ readCache cache path command <- liftM (maybe "grammar" (urlDecodeUnicode . UTF8.decodeString)) (getInput "command") pgfMain pgf command pgfMain :: PGF -> String -> CGI CGIResult pgfMain pgf command = case command of - "parse" -> return (doParse pgf) `ap` getText `ap` getCat `ap` getFrom >>= outputJSONP - "complete" -> return (doComplete pgf) `ap` getText `ap` getCat `ap` getFrom `ap` getLimit >>= outputJSONP - "linearize" -> return (doLinearize pgf) `ap` getTree `ap` getTo >>= outputJSONP + "parse" -> outputJSONP =<< doParse pgf `fmap` getText `ap` getCat `ap` getFrom + "complete" -> outputJSONP =<< doComplete pgf `fmap` getText `ap` getCat `ap` getFrom `ap` getLimit + "linearize" -> outputJSONP =<< doLinearize pgf `fmap` getTree `ap` getTo "random" -> getCat >>= \c -> getLimit >>= liftIO . doRandom pgf c >>= outputJSONP - "translate" -> return (doTranslate pgf) `ap` getText `ap` getCat `ap` getFrom `ap` getTo >>= outputJSONP - "translategroup" -> return (doTranslateGroup pgf) `ap` getText `ap` getCat `ap` getFrom `ap` getTo >>= outputJSONP - "grammar" -> return (doGrammar pgf) `ap` requestAcceptLanguage >>= outputJSONP - "abstrtree" -> getTree >>= liftIO . doGraphvizAbstrTree pgf >>= outputPNG + "translate" -> outputJSONP =<< doTranslate pgf `fmap` getText `ap` getCat `ap` getFrom `ap` getTo + "translategroup" -> outputJSONP =<< doTranslateGroup pgf `fmap` getText `ap` getCat `ap` getFrom `ap` getTo + "grammar" -> outputJSONP =<< doGrammar pgf `fmap` requestAcceptLanguage + "abstrtree" -> outputPNG =<< liftIO . doGraphvizAbstrTree pgf =<< getTree "parsetree" -> getTree >>= \t -> getFrom >>= \(Just l) -> liftIO (doGraphvizParseTree pgf l t) >>= outputPNG "alignment" -> getTree >>= liftIO . doGraphvizAlignment pgf >>= outputPNG - "browse" -> return (doBrowse pgf) `ap` getId `ap` getCSSClass `ap` getHRef >>= outputHTML + "browse" -> outputHTML =<< doBrowse pgf `fmap` getId `ap` getCSSClass `ap` getHRef _ -> throwCGIError 400 "Unknown command" ["Unknown command: " ++ show command] where getText :: CGI String @@ -447,5 +481,5 @@ langCodeLanguage pgf code = listToMaybe [l | l <- PGF.languages pgf, PGF.languag -- * General utilities -cleanFilePath :: FilePath -> FilePath -cleanFilePath = takeFileName +--cleanFilePath :: FilePath -> FilePath +--cleanFilePath = takeFileName diff --git a/src/server/RunHTTP.hs b/src/server/RunHTTP.hs new file mode 100644 index 000000000..cf536d054 --- /dev/null +++ b/src/server/RunHTTP.hs @@ -0,0 +1,41 @@ +module RunHTTP(runHTTP) where +import Network.URI(uriPath,uriQuery) +import Network.CGI(ContentType(..)) +import Network.CGI.Protocol(CGIResult(..),CGIRequest(..),Input(..), + Headers,HeaderName(..)) +import Network.CGI.Monad(runCGIT) +import Network.Shed.Httpd(initServer,Request(..),Response(..),queryToArguments) +import qualified Data.ByteString.Lazy.Char8 as BS(pack,unpack) +import qualified Data.Map as M(fromList) + +documentRoot = "www" + +runHTTP port = initServer port . cgiHandler + +cgiHandler h = fmap httpResp . runCGIT h . cgiReq + +httpResp :: (Headers,CGIResult) -> Response +httpResp (hdrs,r) = Response code (map name hdrs) (body r) + where + code = maybe 200 (read.head.words) (lookup (HeaderName "Status") hdrs) + body CGINothing = "" + body (CGIOutput s) = BS.unpack s + + name (HeaderName n,v) = (n,v) + +cgiReq :: Request -> CGIRequest +cgiReq (Request method uri hdrs body) = CGIRequest vars inputs body' + where + vars = M.fromList [("REQUEST_METHOD",method), + ("REQUEST_URI",show uri), + ("SCRIPT_FILENAME",documentRoot++uriPath uri), + ("QUERY_STRING",qs)] + qs = case uriQuery uri of + '?':s -> s + s -> s + inputs = map input $ queryToArguments qs -- assumes method=="GET" + body' = BS.pack body + + input (name,val) = (name,Input (BS.pack val) Nothing plaintext) + plaintext = ContentType "plain" "text" [] + \ No newline at end of file diff --git a/src/server/ServeStaticFile.hs b/src/server/ServeStaticFile.hs new file mode 100644 index 000000000..f2bbc3e81 --- /dev/null +++ b/src/server/ServeStaticFile.hs @@ -0,0 +1,20 @@ +module ServeStaticFile where +import System.FilePath +import Network.CGI(setHeader,outputFPS,liftIO) +import qualified Data.ByteString.Lazy.Char8 as BS + +serveStaticFile path = + do setHeader "Content-Type" (contentTypeFromExt (takeExtension path)) + outputFPS =<< liftIO (BS.readFile path) + +contentTypeFromExt ext = + case ext of + ".html" -> "text/html; charset=\"iso8859-1\"" + ".htm" -> "text/html; charset=\"iso8859-1\"" + ".xml" -> "text/xml; charset=\"iso8859-1\"" + ".txt" -> "text/plain; charset=\"iso8859-1\"" + ".css" -> "text/css; charset=\"iso8859-1\"" + ".js" -> "text/javascript; charset=\"iso8859-1\"" + ".png" -> "image/png" + ".jpg" -> "image/jpg" + _ -> "application/octet-stream" \ No newline at end of file diff --git a/src/server/gf-server.cabal b/src/server/gf-server.cabal index fa576db4e..619efdde1 100644 --- a/src/server/gf-server.cabal +++ b/src/server/gf-server.cabal @@ -16,6 +16,8 @@ executable pgf-server gf >= 3.1, cgi >= 3001.1.8.0, fastcgi >= 3001.0.2.2, + httpd-shed, + network, json >= 0.3.3, utf8-string >= 0.3.1.1, bytestring, @@ -27,6 +29,8 @@ executable pgf-server FastCGIUtils Cache URLEncoding + RunHTTP + ServeStaticFile ghc-options: -threaded if os(windows) ghc-options: -optl-mwindows