diff --git a/Network/FastCGI.hsc b/Network/FastCGI.hsc
new file mode 100644
--- /dev/null
+++ b/Network/FastCGI.hsc
@@ -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)
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,4 @@
+#!/usr/bin/env runghc
+
+import Distribution.Simple
+main = defaultMain
diff --git a/fastcgi.cabal b/fastcgi.cabal
new file mode 100644
--- /dev/null
+++ b/fastcgi.cabal
@@ -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
