@@ -20,6 +20,11 @@ module Network.Wai.Handler.Warp
21 , sendResponse
21 , sendResponse
22 , parseRequest
22 , parseRequest
26+ , InvalidRequest (..)
25import Prelude hiding (catch)
30import Prelude hiding (catch)
@@ -47,6 +52,7 @@ import Data.Typeable (Typeable)
47import Data.Enumerator (($$), (>>==))
52import Data.Enumerator (($$), (>>==))
48import qualified Data.Enumerator as E
53import qualified Data.Enumerator as E
49import qualified Data.Enumerator.List as EL
54import qualified Data.Enumerator.List as EL
55+import qualified Data.Enumerator.Binary as EB
50import Blaze.ByteString.Builder.Enumerator (builderToByteString)
56import Blaze.ByteString.Builder.Enumerator (builderToByteString)
51import Blaze.ByteString.Builder.HTTP
57import Blaze.ByteString.Builder.HTTP
52 (chunkedTransferEncoding, chunkedTransferTerminator)
58 (chunkedTransferEncoding, chunkedTransferTerminator)
@@ -55,6 +61,7 @@ import Blaze.ByteString.Builder
55import Blaze.ByteString.Builder.Char8 (fromChar, fromShow)
61import Blaze.ByteString.Builder.Char8 (fromChar, fromShow)
56import Data.Monoid (mconcat, mappend)
62import Data.Monoid (mconcat, mappend)
57import Network.Socket.SendFile (sendFile)
63import Network.Socket.SendFile (sendFile)
64+import Network.Socket.Enumerator (iterSocket)
59import Control.Monad.IO.Class (liftIO)
66import Control.Monad.IO.Class (liftIO)
60import System.Timeout (timeout)
67import System.Timeout (timeout)
@@ -103,38 +110,6 @@ maxHeaderLength = 1024
103bytesPerRead = 4096
110bytesPerRead = 4096
104readTimeout = 3000000
111readTimeout = 3000000
106−takeUntilBlank :: Int
107− -> ([ByteString] -> [ByteString])
108− -> E.Iteratee S.ByteString IO [ByteString]
109−takeUntilBlank count _
110− | count > maxHeaders = E.throwError TooManyHeaders
111−takeUntilBlank count front = do
114− then return $ front []
115− else takeUntilBlank (count + 1) $ front . (:) l
118− -> ([ByteString] -> [ByteString])
119− -> E.Iteratee ByteString IO ByteString
120−takeLine len front = do
123− Nothing -> E.throwError IncompleteHeaders
125− let (x, y) = S.breakByte 10 bs
126− x' = if S.length x > 0 && S.last x == 13
129− let len' = len + B.length x
132− | len' > maxHeaderLength -> E.throwError OverLargeHeader
133− | B.null y -> takeLine len' $ front . (:) x
135− E.yield () $ E.Chunks [B.drop 1 y]
136− return $ B.concat $ front [x']
138data InvalidRequest =
113data InvalidRequest =
139 NotEnoughLines [String]
114 NotEnoughLines [String]
140 | BadFirstLine String
115 | BadFirstLine String
@@ -143,7 +118,7 @@ data InvalidRequest =
143 | IncompleteHeaders
118 | IncompleteHeaders
144 | OverLargeHeader
119 | OverLargeHeader
145 | SocketTimeout
120 | SocketTimeout
146− deriving (Show, Typeable)
121+ deriving (Show, Typeable, Eq)
147instance Exception InvalidRequest
122instance Exception InvalidRequest
149-- | Parse a set of header lines and body into a 'Request'.
124-- | Parse a set of header lines and body into a 'Request'.
@@ -170,7 +145,9 @@ parseRequest' port (firstLine:otherLines) remoteHost' = do
170 (x, _):_ -> x
145 (x, _):_ -> x
172 let serverName' = takeUntil 58 host -- ':'
147 let serverName' = takeUntil 58 host -- ':'
148+ -- FIXME isolate takes an Integer instead of Int or Int64. If this is a
149+ -- performance penalty, we may need our own version.
173− return (requestBodyHandle len, Request
150+ return (EB.isolate len, Request
174 { requestMethod = method
151 { requestMethod = method
175 , httpVersion = httpversion
152 , httpVersion = httpversion
176 , pathInfo = rpath
153 , pathInfo = rpath
@@ -297,45 +274,8 @@ parseHeaderNoAttr s =
297 else rest
274 else rest
298 in (mkCIByteString k, rest')
275 in (mkCIByteString k, rest')
300−requestBodyHandle :: Int
277+-- FIXME when we switch to Jeremy's timeout code, we can probably start using
301− -> E.Enumeratee ByteString ByteString IO a
278+-- network-enumerator's enumSocket
302−requestBodyHandle initLen =
305− go 0 step = return step
306− go len (E.Continue k) = do
309− Nothing -> return $ E.Continue k
311− (bs', newlen) <- yieldExtra len bs
312− k (E.Chunks [bs']) >>== go newlen
320− Nothing -> return ()
322− (_, newlen) <- yieldExtra len bs
325− | B.length bs == len = return (bs, 0)
326− | B.length bs < len = return (bs, len - B.length bs)
328− let (x, y) = B.splitAt len bs
329− E.yield () $ E.Chunks [y]
332−iterSocket :: Socket -> E.Iteratee ByteString IO ()
336− go E.EOF = E.yield () E.EOF
337− go (E.Chunks cs) = liftIO (Sock.sendMany socket cs) >> E.continue go
339enumSocket :: Int -> Socket -> E.Enumerator ByteString IO a
279enumSocket :: Int -> Socket -> E.Enumerator ByteString IO a
340enumSocket len socket (E.Continue k) = do
280enumSocket len socket (E.Continue k) = do
341#if NO_TIMEOUT_PROTECTION
281#if NO_TIMEOUT_PROTECTION
@@ -352,3 +292,38 @@ enumSocket len socket (E.Continue k) = do
352 | S.length bs == 0 = E.continue k
292 | S.length bs == 0 = E.continue k
353 | otherwise = k (E.Chunks [bs]) >>== enumSocket len socket
293 | otherwise = k (E.Chunks [bs]) >>== enumSocket len socket
354enumSocket _ _ step = E.returnI step
294enumSocket _ _ step = E.returnI step
296+------ The functions below are not warp-specific and could be split out into a
299+takeUntilBlank :: Int
300+ -> ([ByteString] -> [ByteString])
301+ -> E.Iteratee S.ByteString IO [ByteString]
302+takeUntilBlank count _
303+ | count > maxHeaders = E.throwError TooManyHeaders
304+takeUntilBlank count front = do
305+ l <- takeLineMax 0 id
307+ then return $ front []
308+ else takeUntilBlank (count + 1) $ front . (:) l
311+ -> ([ByteString] -> [ByteString])
312+ -> E.Iteratee ByteString IO ByteString
313+takeLineMax len front = do
316+ Nothing -> E.throwError IncompleteHeaders
318+ let (x, y) = S.breakByte 10 bs
319+ x' = if S.length x > 0 && S.last x == 13
322+ let len' = len + B.length x
325+ | len' > maxHeaderLength -> E.throwError OverLargeHeader
326+ | B.null y -> takeLineMax len' $ front . (:) x
328+ E.yield () $ E.Chunks [B.drop 1 y]
329+ return $ B.concat $ front [x']