@@ -1,12 +1,11 @@
1{-# LANGUAGE ForeignFunctionInterface #-}
1{-# LANGUAGE ForeignFunctionInterface #-}
2{-# LANGUAGE EmptyDataDecls #-}
2{-# LANGUAGE EmptyDataDecls #-}
3−{-# LANGUAGE OverloadedStrings #-}
4-----------------------------------------------------------------------------
3-----------------------------------------------------------------------------
6-- Module : Network.Wai.Handler.FastCGI
5-- Module : Network.Wai.Handler.FastCGI
7−-- Copyright : (c) Bjorn Bringert 2004-2005, (c) Lemmih 2006
6+-- Copyright : (c) Bjorn Bringert 2004-2005, (c) Lemmih 2006, (c) Michael Snoyman 2010
8-- License : BSD-style (see the file libraries/network/LICENSE)
7-- License : BSD-style (see the file libraries/network/LICENSE)
10-- Maintainer : michael@snoyman.com
9-- Maintainer : michael@snoyman.com
11-- Stability : experimental
10-- Stability : experimental
12-- Portability : non-portable (uses FFI)
11-- Portability : non-portable (uses FFI)
@@ -18,15 +17,16 @@
18module Network.Wai.Handler.FastCGI
17module Network.Wai.Handler.FastCGI
20 , runSendfile
19 , runSendfile
24−import Control.Monad ( liftM )
23+import Control.Monad ( liftM, forever )
25import Data.Word (Word8)
24import Data.Word (Word8)
26import Foreign ( Ptr, castPtr, nullPtr, peekArray0
25import Foreign ( Ptr, castPtr, nullPtr, peekArray0
27− , alloca, throwIfNeg_)
26+ , throwIfNeg_, mallocBytes, free )
28import Foreign.C ( CInt, CString, CStringLen
27import Foreign.C ( CInt, CString, CStringLen
29 , peekCString )
28 , peekCString )
29+import Control.Exception (finally)
30import Foreign.Storable ( Storable (..) )
30import Foreign.Storable ( Storable (..) )
32import qualified Network.Wai as W
32import qualified Network.Wai as W
@@ -34,15 +34,8 @@ import qualified Network.Wai.Handler.CGI as CGI
34import qualified Network.Wai.Handler.Helper as CGI
34import qualified Network.Wai.Handler.Helper as CGI
36import qualified Data.ByteString as BS
36import qualified Data.ByteString as BS
37−#if __GLASGOW_HASKELL__ >= 608
38import qualified Data.ByteString.Internal as BSB
37import qualified Data.ByteString.Internal as BSB
39import qualified Data.ByteString.Unsafe as BSB
38import qualified Data.ByteString.Unsafe as BSB
41−import qualified Data.ByteString.Base as BSB
45−import Prelude hiding ( log, catch )
47#include <fcgiapp.h>
40#include <fcgiapp.h>
@@ -60,59 +53,80 @@ foreign import ccall unsafe "fcgiapp.h FCGX_GetStr" fcgx_getStr
60foreign import ccall unsafe "fcgiapp.h FCGX_PutStr" fcgx_putStr
53foreign import ccall unsafe "fcgiapp.h FCGX_PutStr" fcgx_putStr
61 :: CString -> CInt -> StreamPtr -> IO CInt
54 :: CString -> CInt -> StreamPtr -> IO CInt
63−foreign import ccall safe "fcgiapp.h FCGX_Accept" fcgx_accept
64− :: Ptr StreamPtr -> Ptr StreamPtr -> Ptr StreamPtr -> Ptr Environ -> IO CInt
65−foreign import ccall unsafe "fcgiapp.h FCGX_Finish" fcgx_finish
68------------------------------------------------------------------------
57------------------------------------------------------------------------
59+foreign import ccall unsafe "fcgiapp.h FCGX_Init" fcgx_init
62+runFork :: Maybe String -> (IO () -> IO a) -> Int -> W.Application -> IO ()
63+runFork sf fork threads app = do
64+ testReturn "FCGX_Init" $ fcgx_init
65+ let oneThread = forever $ oneRequest app sf
66+ mapM_ fork $ replicate (threads - 1) oneThread
70-- | Handle FastCGI requests in an infinite loop.
69-- | Handle FastCGI requests in an infinite loop.
71run :: W.Application -> IO ()
70run :: W.Application -> IO ()
72−run f = runOneFastCGI Nothing f >> run f
71+run = runFork Nothing id 1
74-- | Handle FastCGI requests in an infinite loop. For a server which supports
73-- | Handle FastCGI requests in an infinite loop. For a server which supports
75-- the X-Sendfile header.
74-- the X-Sendfile header.
76runSendfile :: String -> W.Application -> IO ()
75runSendfile :: String -> W.Application -> IO ()
77−runSendfile sf f = runOneFastCGI (Just sf) f >> runSendfile sf f
76+runSendfile sf = runFork (Just sf) id 1
79−-- | Handle a single FastCGI request.
80−runOneFastCGI :: Maybe String -- X-Sendfile
81− -> W.Application -> IO ()
82−runOneFastCGI xsendfile f = do
87− oneRequest f inp outp errp envp
90oneRequest :: W.Application
78oneRequest :: W.Application
95 -> Maybe String -- X-Sendfile
79 -> Maybe String -- X-Sendfile
97−oneRequest f inp outp errp envp xsendfile =
81+oneRequest app xsendfile = withRequest $ \r -> do
98− do
82+ putStrLn "Received 1 request"
99− testReturn "FCGX_Accept" $ fcgx_accept inp outp errp envp
83+ env <- peekEnvp r
100− ins <- peek inp
84+ ins <- peekIn r
101− outs <- peek outp
85+ outs <- peekOut r
104− handleRequest f ins outs errs env xsendfile
86+ handleRequest app ins outs env xsendfile
88+peekIn, peekOut :: Ptr FCGX_Request -> IO (Ptr FCGX_Stream)
89+peekIn = (#peek FCGX_Request, in)
90+peekOut = (#peek FCGX_Request, out)
92+peekEnvp :: Ptr FCGX_Request -> IO Environ
93+peekEnvp = (#peek FCGX_Request, envp)
95+foreign import ccall unsafe "fcgiapp.h FCGX_InitRequest" fcgx_initrequest
96+ :: Ptr FCGX_Request -> CInt -> CInt -> IO CInt
98+foreign import ccall safe "fcgiapp.h FCGX_Accept_r" fcgx_accept_r
99+ :: Ptr FCGX_Request -> IO CInt
101+acceptRequest :: IO (Ptr FCGX_Request)
103+ reqp <- mallocBytes (#size FCGX_Request)
106+ where initAndAccept reqp = do
107+ testReturn "FCGX_InitRequest" $ fcgx_initrequest reqp 0 0
108+ testReturn "FCGX_Accept_r" $ fcgx_accept_r reqp
110+withRequest :: (Ptr FCGX_Request -> IO ()) -> IO ()
112+ req <- acceptRequest
113+ f req `finally` finishRequest req
115+foreign import ccall unsafe "fcgiapp.h FCGX_Finish_r" fcgx_finish_r
116+ :: Ptr FCGX_Request -> IO ()
118+finishRequest :: Ptr FCGX_Request -> IO ()
119+finishRequest reqp = do
107handleRequest :: W.Application
123handleRequest :: W.Application
109 -> StreamPtr
124 -> StreamPtr
110 -> StreamPtr
125 -> StreamPtr
111 -> Environ
126 -> Environ
112 -> Maybe String -- sendfile
127 -> Maybe String -- sendfile
114−handleRequest f ins outs _errs env xsendfile =
129+handleRequest f ins outs env xsendfile = do
116 vars <- environToTable env
130 vars <- environToTable env
117 let input = const $ sRead ins
131 let input = const $ sRead ins
118 let hPut = sPutStr' outs
132 let hPut = sPutStr' outs
@@ -120,9 +134,6 @@ handleRequest f ins outs _errs env xsendfile =
121data FCGX_Request
135data FCGX_Request
123−_peekErr :: Ptr FCGX_Request -> IO (Ptr FCGX_Stream)
124−_peekErr = (#peek FCGX_Request, err)
127-- * Stream IO
138-- * Stream IO