14
import Network.Wai.Internal
14
15
import Network.Socket (getAddrInfo, addrAddress)
15
import System.Environment (getEnvironment)
16
16
import Data.Maybe (fromMaybe)
17
import Control.Exception (mask)
17
18
import qualified Data.ByteString.Char8 as B
18
19
import qualified Data.ByteString.Lazy as L
19
20
import Control.Arrow ((***))
32
33
import qualified Data.CaseInsensitive as CI
33
34
import Data.Monoid (mappend)
34
35
import Data.Conduit
35
import qualified Data.Conduit.List as CL
38
import System.Environment (getEnvironment)
40
import qualified System.Posix.Env.ByteString as Env
42
getEnvironment :: IO [(String, String)]
43
getEnvironment = map (B.unpack *** B.unpack) `fmap` Env.getEnvironment
37
46
safeRead :: Read a => a -> String -> a
67
76
-- stick with 'run' or 'runSendfile'.
69
78
:: [(String, String)] -- ^ all variables
70
-> (Int -> Source (ResourceT IO) B.ByteString) -- ^ responseBody of input
79
-> (Int -> Source IO B.ByteString) -- ^ responseBody of input
71
80
-> (B.ByteString -> IO ()) -- ^ destination for output
72
81
-> Maybe B.ByteString -- ^ does the server support the X-Sendfile header?
76
85
let rmethod = B.pack $ lookup' "REQUEST_METHOD" vars
77
86
pinfo = lookup' "PATH_INFO" vars
78
87
qstring = lookup' "QUERY_STRING" vars
79
servername = lookup' "SERVER_NAME" vars
80
serverport = safeRead 80 $ lookup' "SERVER_PORT" vars
81
88
contentLength = safeRead 0 $ lookup' "CONTENT_LENGTH" vars
83
90
case lookup "REMOTE_ADDR" vars of
96
103
a:_ -> addrAddress a
97
104
[] -> error $ "Invalid REMOTE_ADDR or REMOTE_HOST: " ++ remoteHost'
105
mask $ \restore -> do
106
let reqHeaders = map (cleanupVarName *** B.pack) vars
100
108
{ requestMethod = rmethod
101
109
, rawPathInfo = B.pack pinfo
102
110
, pathInfo = H.decodePathSegments $ B.pack pinfo
103
111
, rawQueryString = B.pack qstring
104
112
, queryString = H.parseQuery $ B.pack qstring
105
, serverName = B.pack servername
106
, serverPort = serverport
107
, requestHeaders = map (cleanupVarName *** B.pack) vars
113
, requestHeaders = reqHeaders
108
114
, isSecure = isSecure'
109
115
, remoteHost = addr
110
116
, httpVersion = H.http11 -- FIXME
111
117
, requestBody = inputH contentLength
113
#if MIN_VERSION_wai(1, 4, 0)
114
119
, requestBodyLength = KnownLength $ fromIntegral contentLength
120
, requestHeaderHost = lookup "host" reqHeaders
121
, requestHeaderRange = lookup "range" reqHeaders
117
123
-- FIXME worry about exception?
124
res <- restore $ app env
119
125
case (xsendfile, res) of
120
126
(Just sf, ResponseFile s hs fp Nothing) ->
121
liftIO $ mapM_ outputH $ L.toChunks $ toLazyByteString $ sfBuilder s hs sf fp
127
restore $ mapM_ outputH $ L.toChunks $ toLazyByteString $ sfBuilder s hs sf fp
123
let (s, hs, b) = responseSource res
124
src = CL.sourceList [Chunk $ headers s hs `mappend` fromChar '\n']
129
let (s, hs, wb) = responseToSource res
132
yield (Chunk $ headers s hs `mappend` fromChar '\n')
134
in src $$ builderSink
128
136
headers s hs = mconcat (map header $ status s : map header' (fixHeaders hs))
129
137
status (Status i m) = (fromByteString "Status", mconcat
170
178
helper' (x:rest) = toLower x : helper' rest
173
requestBodyHandle :: Handle -> Int -> Source (ResourceT IO) B.ByteString
181
requestBodyHandle :: Handle -> Int -> Source IO B.ByteString
174
182
requestBodyHandle h = requestBodyFunc $ \i -> do
176
184
return $ if B.null bs then Nothing else Just bs
178
requestBodyFunc :: (Int -> IO (Maybe B.ByteString)) -> Int -> Source (ResourceT IO) B.ByteString
186
requestBodyFunc :: (Int -> IO (Maybe B.ByteString)) -> Int -> Source IO B.ByteString
179
187
requestBodyFunc get =