packages feed

fastcgi (empty) → 3000.0.0

raw patch · 3 files changed

+338/−0 lines, 3 filesdep +basedep +cgibuild-type:Customsetup-changed

Dependencies added: base, cgi

Files

+ Network/FastCGI.hsc view
@@ -0,0 +1,317 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Network.FastCGI+-- Copyright   :  (c) Bjorn Bringert 2004-2005+-- License     :  BSD-style (see the file libraries/network/LICENSE)+-- +-- Maintainer  :  bjorn@bringert.net+-- Stability   :  experimental+-- Portability :  non-portable (uses FFI)+--+-- Interface for FastCGI <http://fastcgi.com/>, using the fcgiapp API.+--+-----------------------------------------------------------------------------+module Network.FastCGI ({-runFastCGIConcurrent,-}+                        runFastCGIorCGI, runOneFastCGIorCGI, +                        runFastCGI, runOneFastCGI,+                        module Network.CGI) where++import Control.Concurrent (forkOS)+import Control.Exception as Exception (catch, finally, throwIO)+import Control.Monad (when, liftM)+import Data.Word (Word8)+import Foreign          ( Ptr, castPtr, nullPtr, plusPtr, peekArray0 +                        , alloca+		        , mallocBytes, free, throwIfNeg_, allocaBytes+                        , mallocArray, reallocArray, withForeignPtr)+import Foreign.C        ( CInt, CString, CStringLen, CChar, withCStringLen+                        , peekCString, castCCharToChar )+import Foreign.Storable ( Storable (..) )+import System.IO.Unsafe (unsafeInterleaveIO)++import Network.CGI+import Network.CGI.Monad (runCGIT)+import Network.CGI.Protocol (runCGIEnvFPS)++import qualified Data.ByteString as BS+import qualified Data.ByteString.Base as BSB+import qualified Data.ByteString.Base as Lazy (LazyByteString(LPS))+import qualified Data.ByteString.Lazy.Char8 as Lazy++-- For debugging+import Control.Concurrent (myThreadId)+import Prelude hiding (log)+import System.Mem (performGC)+import System.IO (hPutStrLn, stderr)++#include <fcgiapp.h>++data FCGX_Stream = FCGX_Stream+type StreamPtr = Ptr FCGX_Stream+type Environ = Ptr CString++foreign import ccall threadsafe "fcgiapp.h FCGX_IsCGI" fcgx_isCGI+    :: IO CInt++foreign import ccall threadsafe "fcgiapp.h FCGX_GetStr" fcgx_getStr+    :: CString -> CInt -> StreamPtr -> IO CInt++foreign import ccall threadsafe "fcgiapp.h FCGX_PutStr" fcgx_putStr+    :: CString -> CInt -> StreamPtr -> IO CInt++--+-- This code uses the not MT safe functions of the fcgiapp API,+-- to avoid getting intermittent SIGPIPEs. Dunno why that happens.+--++foreign import ccall "fcgiapp.h FCGX_Accept" fcgx_accept+    :: Ptr StreamPtr -> Ptr StreamPtr -> Ptr StreamPtr -> Ptr Environ -> IO CInt+foreign import ccall "fcgiapp.h FCGX_Finish" fcgx_finish+    :: IO ()++-- | Handle a single CGI request, or FastCGI requests in an infinite loop.+--   This function only returns normally if it was a CGI request.+--   This lets you use the same program+--   as either a FastCGI or CGI program, depending on what the server +--   treats it as.+runFastCGIorCGI :: CGI CGIResult -> IO ()+runFastCGIorCGI f = do fcgi <- runOneFastCGIorCGI f+                       if fcgi then performGC >> runFastCGIorCGI f+                               else return ()++-- | Handle a single FastCGI or CGI request. This lets you use the same program+--   as either a FastCGI or CGI program, depending on what the server +--   treats it as.+runOneFastCGIorCGI :: CGI CGIResult +                   -> IO Bool -- ^ True if it was a FastCGI request, +                              --   False if CGI.+runOneFastCGIorCGI f =+    do x <- fcgx_isCGI+       if x /= 0 then runCGI f >> return False+                 else runOneFastCGI f >> return True++-- | Handle FastCGI requests in an infinite loop.+runFastCGI :: CGI CGIResult -> IO ()+runFastCGI f = runOneFastCGI f >> performGC >> runFastCGI f++-- | Handle a single FastCGI request.+runOneFastCGI :: CGI CGIResult -> IO ()+runOneFastCGI f = do+    alloca (\inp ->+            alloca (\outp ->+                    alloca (\errp ->+                            alloca (\envp ->+                                    oneRequest f inp outp errp envp))))++oneRequest :: CGI CGIResult+           -> Ptr StreamPtr+           -> Ptr StreamPtr+           -> Ptr StreamPtr+           -> Ptr Environ+           -> IO ()+oneRequest f inp outp errp envp =+    do+    testReturn "FCGX_Accept" $ fcgx_accept inp outp errp envp+    ins  <- peek inp+    outs <- peek outp+    errs <- peek errp+    env  <- peek envp+    handleRequest f ins outs errs env+    fcgx_finish++handleRequest :: CGI CGIResult+              -> StreamPtr+              -> StreamPtr+              -> StreamPtr+              -> Environ+              -> IO ()+handleRequest f ins outs errs env =+    do+    vars <- environToTable env+    input <- sRead ins+    output <- runCGIEnvFPS vars input (runCGIT f)+    sPutStr outs output++++--+-- The following code uses the MT safe fcgiapp API,+-- but causes mod_fastcgi to get SIGPIPE once in a while.+--++{-++data FCGX_Request = FCGX_Request++foreign import ccall threadsafe "fcgiapp.h FCGX_Init" fcgx_init+    :: IO CInt++foreign import ccall threadsafe "fcgiapp.h FCGX_InitRequest" fcgx_initrequest+    :: Ptr FCGX_Request -> CInt -> CInt -> IO CInt++foreign import ccall threadsafe "fcgiapp.h FCGX_Accept_r" fcgx_accept_r+    :: Ptr FCGX_Request -> IO CInt++foreign import ccall threadsafe "fcgiapp.h FCGX_Finish_r" fcgx_finish_r+    :: Ptr FCGX_Request -> IO ()++-- | Like 'Network.NewCGI.runCGI', but uses the FastCGI interface.+runFastCGI :: CGI CGIResult -> IO ()+runFastCGI = runFastCGI' id++-- | Like 'Network.NewCGI.runCGI', but uses the FastCGI interface+--   and forks off a new thread (using 'forkOS') for every request.+runFastCGIConcurrent :: CGI CGIResult -> IO ()+runFastCGIConcurrent = runFastCGI' fork+    where fork m = do+		   t <- forkOS m+		   log $ "Created child: " ++ show t++runFastCGI' :: (IO () -> IO a) -> CGI CGIResult -> IO ()+runFastCGI' fork f = do+		     log "Calling FCGX_Init"+		     testReturn "FCGX_Init" $ fcgx_init+		     loop+    where +    loop = do+	   reqp <- acceptRequest+	   log "Forking"+	   fork (oneRequest f reqp `finally` finishRequest reqp)+	   loop++oneRequest :: CGI CGIResult -> Ptr FCGX_Request -> IO ()+oneRequest f r = do+		 env <- peekEnvp r+		 vars <- environToTable env+		 ins <- peekIn r+		 input <- sStrictRead ins+		 log "Running CGI action"+		 output <- runCGIEnv vars input f+		 outs <- peekOut r+		 log "Returning output"+		 sPutStr outs output++--+-- * FCGX_Reqest struct+--++acceptRequest :: IO (Ptr FCGX_Request)+acceptRequest = do+		log "Allocating FCGX_Request"+ 		reqp <- mallocBytes (#size FCGX_Request)+		initAndAccept reqp `onError` free reqp+		return reqp+    where initAndAccept reqp = +	      do +	      log "Calling FCGX_InitRequest"+	      testReturn "FCGX_InitRequest" $ fcgx_initrequest reqp 0 0+	      log "Calling FCGX_Accept_r"+	      testReturn "FCGX_Accept_r" $ fcgx_accept_r reqp++finishRequest :: Ptr FCGX_Request -> IO ()+finishRequest reqp = do+		     log "Calling FCGX_Finish_r"+		     fcgx_finish_r reqp+		     log "Freeing FCGX_Request"+		     free reqp++peekIn, peekOut, peekErr :: Ptr FCGX_Request -> IO (Ptr FCGX_Stream)+peekIn  = (#peek FCGX_Request, in)+peekOut = (#peek FCGX_Request, out)+peekErr = (#peek FCGX_Request, err)++peekEnvp :: Ptr FCGX_Request -> IO Environ+peekEnvp = (#peek FCGX_Request, envp)++-}++--+-- * Stream IO+--++sPutStr :: StreamPtr -> Lazy.ByteString -> IO ()+sPutStr h (Lazy.LPS strs) = +    mapM_ (flip BSB.unsafeUseAsCStringLen (fcgxPutCStringLen h)) strs++fcgxPutCStringLen :: StreamPtr -> CStringLen -> IO ()+fcgxPutCStringLen h (cs,len) = +    testReturn "FCGX_PutStr" $ fcgx_putStr cs (fromIntegral len) h++{-+-- Based on Data.FastPackedString.hGetContents+sRead :: StreamPtr -> IO BS.ByteString+sRead h = do +    let start_size = 1024+    p <- mallocArray start_size+    i <- fcgx_getStr p start_size h+    if i < start_size+        then liftM BS.packMallocCString $ reallocArray p i+        else f p start_size+    where +        f p s = do +        let s' = 2 * s+        p' <- reallocArray p s'+        i  <- fcgx_getStr (p' `plusPtr` s) s h+        if i < s +            then liftM BS.packMallocCString $ reallocArray p' (s + i)+            else f p' s'+-}++sRead :: StreamPtr -> IO Lazy.ByteString+sRead h = buildByteString (fcgxGetBuf h) 4096++fcgxGetBuf :: StreamPtr -> Ptr a -> Int -> IO Int+fcgxGetBuf h p c = +    liftM fromIntegral $ fcgx_getStr (castPtr p) (fromIntegral c) h++--+-- * ByteString utilities+--++-- | Data.ByteString.Lazy.hGetContentsN generalized to arbitrary +--   reading functions.+buildByteString :: (Ptr Word8 -> Int -> IO Int) -> Int -> IO Lazy.ByteString+buildByteString f k = lazyRead >>= return . Lazy.LPS+  where+    lazyRead = unsafeInterleaveIO $ do+        ps <- BSB.createAndTrim k $ \p -> f p k+        case BS.length ps of+            0         -> return []+            n | n < k -> return [ps]+            _         -> do pss <- lazyRead+                            return (ps : pss)++--+-- * Utilities+--++-- | Run some action if the first action throws an exception.+--   The exception is re-thrown.+onError :: IO a -> IO () -> IO a+onError f h = f `Exception.catch` (\e -> h >> throwIO e)++testReturn :: String -> IO CInt -> IO ()+testReturn e = throwIfNeg_ (const $ e ++ " failed")++environToTable :: Environ -> IO [(String,String)]+environToTable arr = +    do css <- peekArray0 nullPtr arr+       ss <- mapM peekCString css+       return $ map (splitBy '=') ss++-- | Split a list at the first occurence of a marker.+--   Do not include the marker in any of the resulting lists.+--   If the marker does not occur in the list, the entire+--   input with be in the first list.+splitBy :: Eq a => a -> [a] -> ([a],[a])+splitBy x xs = (y, drop 1 z)+    where (y,z) = break (==x) xs++--+-- * Debugging+--++log :: String -> IO ()+log msg = do+	  t <- myThreadId+	  hPutStrLn stderr (show t ++ ": " ++ msg)
+ Setup.hs view
@@ -0,0 +1,4 @@+#!/usr/bin/env runghc++import Distribution.Simple+main = defaultMain
+ fastcgi.cabal view
@@ -0,0 +1,17 @@+Name: fastcgi+Version: 3000.0.0+Copyright: Bjorn Bringert+Maintainer: bjorn@bringert.net+Author: Bjorn Bringert+Homepage: http://www.cs.chalmers.se/~bringert/darcs/haskell-fastcgi/doc/+License: BSD3+build-depends: base>=2.0, cgi >= 3000.0.0+Extensions: ForeignFunctionInterface+Synopsis: A Haskell library for writing FastCGI programs+Description:+ This library lets you write FastCGI programs. This package reuses the+ cgi package API, making it very easy to port CGI programs to FastCGI.+Exposed-Modules: +   Network.FastCGI+ghc-options: -O2+extra-libraries: fcgi