1
{-# LANGUAGE OverloadedStrings #-}
2
module Network.Wai.Handler.CGI
10
import Network.Wai.Enumerator (fromResponseBody)
11
import Network.Wai.Handler.Helper
12
import System.Environment (getEnvironment)
13
import Data.Maybe (fromMaybe)
14
import qualified Data.ByteString.Char8 as B
15
import Control.Arrow ((***))
16
import Data.Char (toLower)
17
import qualified System.IO
18
import Data.String (fromString)
20
safeRead :: Read a => a -> String -> a
26
lookup' :: String -> [(String, String)] -> String
27
lookup' key pairs = fromMaybe "" $ lookup key pairs
29
run :: Application -> IO ()
31
vars <- getEnvironment
32
let input = requestBodyHandle System.IO.stdin
33
output = B.hPut System.IO.stdout
34
run'' vars input output Nothing app
36
runSendfile :: String -- ^ sendfile header
37
-> Application -> IO ()
38
runSendfile sf app = do
39
vars <- getEnvironment
40
let input = requestBodyHandle System.IO.stdin
41
output = B.hPut System.IO.stdout
42
run'' vars input output (Just sf) app
44
run' :: [(String, String)] -- ^ all variables
45
-> System.IO.Handle -- ^ responseBody of input
46
-> System.IO.Handle -- ^ destination for output
49
run' vars inputH outputH app = do
50
let input = requestBodyHandle inputH
51
output = B.hPut outputH
52
run'' vars input output Nothing app
54
run'' :: [(String, String)] -- ^ all variables
55
-> (Int -> Source) -- ^ responseBody of input
56
-> (B.ByteString -> IO ()) -- ^ destination for output
57
-> Maybe String -- ^ does the server support the X-Sendfile header?
60
run'' vars inputH outputH xsendfile app = do
61
let rmethod = B.pack $ lookup' "REQUEST_METHOD" vars
62
pinfo = lookup' "PATH_INFO" vars
63
qstring = lookup' "QUERY_STRING" vars
64
servername = lookup' "SERVER_NAME" vars
65
serverport = safeRead 80 $ lookup' "SERVER_PORT" vars
66
contentLength = safeRead 0 $ lookup' "CONTENT_LENGTH" vars
68
case lookup "REMOTE_HOST" vars of
71
case lookup "REMOTE_ADDR" vars of
75
case map toLower $ lookup' "SERVER_PROTOCOL" vars of
79
{ requestMethod = rmethod
80
, pathInfo = B.pack pinfo
81
, queryString = B.pack qstring
82
, serverName = B.pack servername
83
, serverPort = serverport
84
, requestHeaders = map (cleanupVarName *** B.pack) vars
85
, isSecure = isSecure'
86
, requestBody = inputH contentLength
87
, errorHandler = System.IO.hPutStr System.IO.stderr
88
, remoteHost = B.pack remoteHost'
89
, httpVersion = "1.1" -- FIXME
92
let h = responseHeaders res
93
let h' = case lookup "Content-Type" h of
94
Nothing -> ("Content-Type", "text/html; charset=utf-8")
98
hPut $ B.pack $ "Status: " ++ (show $ statusCode $ status res) ++ " "
99
hPut $ statusMessage $ status res
100
hPut $ B.singleton '\n'
101
mapM_ (printHeader hPut) h'
102
case (xsendfile, responseBody res) of
103
(Just sf, ResponseFile fp) ->
104
hPut $ B.pack $ concat
113
hPut $ B.singleton '\n'
114
_ <- runEnumerator (fromResponseBody (responseBody res))
118
myPut :: (B.ByteString -> IO ()) -> () -> B.ByteString -> IO (Either () ())
119
myPut output _ bs = output bs >> return (Right ())
121
printHeader :: (B.ByteString -> IO ())
122
-> (ResponseHeader, B.ByteString)
124
printHeader f (x, y) = do
130
cleanupVarName :: String -> RequestHeader
131
cleanupVarName ('H':'T':'T':'P':'_':a:as) =
132
fromString $ a : helper' as
134
helper' ('_':x:rest) = '-' : x : helper' rest
135
helper' (x:rest) = toLower x : helper' rest
137
cleanupVarName "CONTENT_TYPE" = "Content-Type"
138
cleanupVarName "CONTENT_LENGTH" = "Content-Length"
139
cleanupVarName "SCRIPT_NAME" = "CGI-Script-Name"
140
cleanupVarName x = fromString x -- FIXME remove?