@@ -9,6 +9,7 @@ module Network.Wai.Handler.Warp.Request (
9 , headerLines
9 , headerLines
10 , pauseTimeoutKey
10 , pauseTimeoutKey
11 , getFileInfoKey
11 , getFileInfoKey
12+ , getClientCertificateKey
12 , NoKeepAliveRequest (..)
13 , NoKeepAliveRequest (..)
@@ -21,6 +22,7 @@ import qualified Data.CaseInsensitive as CI
21import qualified Data.IORef as I
22import qualified Data.IORef as I
22import Data.Typeable (Typeable)
23import Data.Typeable (Typeable)
23import qualified Data.Vault.Lazy as Vault
24import qualified Data.Vault.Lazy as Vault
24import qualified Network.HTTP.Types as H
26import qualified Network.HTTP.Types as H
25import Network.Socket (SockAddr)
27import Network.Socket (SockAddr)
26import Network.Wai
28import Network.Wai
@@ -36,13 +38,7 @@ import Network.Wai.Handler.Warp.Header
36import Network.Wai.Handler.Warp.Imports hiding (readInt, lines)
38import Network.Wai.Handler.Warp.Imports hiding (readInt, lines)
37import Network.Wai.Handler.Warp.ReadInt
39import Network.Wai.Handler.Warp.ReadInt
38import Network.Wai.Handler.Warp.RequestHeader
40import Network.Wai.Handler.Warp.RequestHeader
39−import Network.Wai.Handler.Warp.Settings (Settings, settingsNoParsePath)
41+import Network.Wai.Handler.Warp.Settings (Settings, settingsNoParsePath, settingsMaxTotalHeaderLength)
41−----------------------------------------------------------------
43−-- FIXME come up with good values here
44−maxTotalHeaderLength :: Int
45−maxTotalHeaderLength = 50 * 1024
47----------------------------------------------------------------
43----------------------------------------------------------------
@@ -66,7 +62,7 @@ recvRequest :: Bool -- ^ first request on this connection?
66 -- Body producing action used for flushing the request body
62 -- Body producing action used for flushing the request body
68recvRequest firstRequest settings conn ii th addr src transport = do
64recvRequest firstRequest settings conn ii th addr src transport = do
69− hdrlines <- headerLines firstRequest src
65+ hdrlines <- headerLines (settingsMaxTotalHeaderLength settings) firstRequest src
70 (method, unparsedPath, path, query, httpversion, hdr) <- parseHeaderLines hdrlines
66 (method, unparsedPath, path, query, httpversion, hdr) <- parseHeaderLines hdrlines
71 let idxhdr = indexRequestHeader hdr
67 let idxhdr = indexRequestHeader hdr
72 expect = idxhdr ! fromEnum ReqExpect
68 expect = idxhdr ! fromEnum ReqExpect
@@ -76,6 +72,7 @@ recvRequest firstRequest settings conn ii th addr src transport = do
76 rawPath = if settingsNoParsePath settings then unparsedPath else path
72 rawPath = if settingsNoParsePath settings then unparsedPath else path
77 vaultValue = Vault.insert pauseTimeoutKey (Timeout.pause th)
73 vaultValue = Vault.insert pauseTimeoutKey (Timeout.pause th)
78 $ Vault.insert getFileInfoKey (getFileInfo ii)
74 $ Vault.insert getFileInfoKey (getFileInfo ii)
75+ $ Vault.insert getClientCertificateKey (tlsClientCertificate transport)
79 Vault.empty
76 Vault.empty
80 (rbody, remainingRef, bodyLength) <- bodyAndSource src cl te
77 (rbody, remainingRef, bodyLength) <- bodyAndSource src cl te
81 -- body producing function which will produce '100-continue', if needed
78 -- body producing function which will produce '100-continue', if needed
@@ -104,8 +101,8 @@ recvRequest firstRequest settings conn ii th addr src transport = do
105----------------------------------------------------------------
102----------------------------------------------------------------
107−headerLines :: Bool -> Source -> IO [ByteString]
104+headerLines :: Int -> Bool -> Source -> IO [ByteString]
108−headerLines firstRequest src = do
105+headerLines maxTotalHeaderLength firstRequest src = do
109 bs <- readSource src
106 bs <- readSource src
110 if S.null bs
107 if S.null bs
111 -- When we're working on a keep-alive connection and trying to
108 -- When we're working on a keep-alive connection and trying to
@@ -113,7 +110,7 @@ headerLines firstRequest src = do
113 -- lack of data as a real exception. See the http1 function in
110 -- lack of data as a real exception. See the http1 function in
114 -- the Run module for more details.
111 -- the Run module for more details.
115 then if firstRequest then throwIO ConnectionClosedByPeer else throwIO NoKeepAliveRequest
112 then if firstRequest then throwIO ConnectionClosedByPeer else throwIO NoKeepAliveRequest
116− else push src (THStatus 0 id id) bs
113+ else push maxTotalHeaderLength src (THStatus 0 id id) bs
118data NoKeepAliveRequest = NoKeepAliveRequest
115data NoKeepAliveRequest = NoKeepAliveRequest
119 deriving (Show, Typeable)
116 deriving (Show, Typeable)
@@ -223,8 +220,8 @@ close :: Sink ByteString IO a
223close = throwIO IncompleteHeaders
220close = throwIO IncompleteHeaders
226−push :: Source -> THStatus -> ByteString -> IO [ByteString]
223+push :: Int -> Source -> THStatus -> ByteString -> IO [ByteString]
227−push src (THStatus len lines prepend) bs'
224+push maxTotalHeaderLength src (THStatus len lines prepend) bs'
228 -- Too many bytes
225 -- Too many bytes
229 | len > maxTotalHeaderLength = throwIO OverLargeHeader
226 | len > maxTotalHeaderLength = throwIO OverLargeHeader
230 | otherwise = push' mnl
227 | otherwise = push' mnl
@@ -252,13 +249,13 @@ push src (THStatus len lines prepend) bs'
252 push' Nothing = do
249 push' Nothing = do
253 bst <- readSource' src
250 bst <- readSource' src
254 when (S.null bst) $ throwIO IncompleteHeaders
251 when (S.null bst) $ throwIO IncompleteHeaders
255− push src status bst
252+ push maxTotalHeaderLength src status bst
257 len' = len + bsLen
254 len' = len + bsLen
258 prepend' = S.append bs
255 prepend' = S.append bs
259 status = THStatus len' lines prepend'
256 status = THStatus len' lines prepend'
260 -- Found a newline, but next line continues as a multiline header
257 -- Found a newline, but next line continues as a multiline header
261− push' (Just (end, True)) = push src status rest
258+ push' (Just (end, True)) = push maxTotalHeaderLength src status rest
263 rest = S.drop (end + 1) bs
260 rest = S.drop (end + 1) bs
264 prepend' = S.append (SU.unsafeTake (checkCR bs end) bs)
261 prepend' = S.append (SU.unsafeTake (checkCR bs end) bs)
@@ -277,12 +274,12 @@ push src (THStatus len lines prepend) bs'
277 in if start < bsLen then
274 in if start < bsLen then
278 -- more bytes in this chunk, push again
275 -- more bytes in this chunk, push again
279 let bs'' = SU.unsafeDrop start bs
276 let bs'' = SU.unsafeDrop start bs
280− in push src status bs''
277+ in push maxTotalHeaderLength src status bs''
282 -- no more bytes in this chunk, ask for more
279 -- no more bytes in this chunk, ask for more
283 bst <- readSource' src
280 bst <- readSource' src
284 when (S.null bs) $ throwIO IncompleteHeaders
281 when (S.null bs) $ throwIO IncompleteHeaders
285− push src status bst
282+ push maxTotalHeaderLength src status bst
287 start = end + 1 -- start of next chunk
284 start = end + 1 -- start of next chunk
288 line = SU.unsafeTake (checkCR bs end) bs
285 line = SU.unsafeTake (checkCR bs end) bs
@@ -300,3 +297,7 @@ pauseTimeoutKey = unsafePerformIO Vault.newKey
300getFileInfoKey :: Vault.Key (FilePath -> IO FileInfo)
297getFileInfoKey :: Vault.Key (FilePath -> IO FileInfo)
301getFileInfoKey = unsafePerformIO Vault.newKey
298getFileInfoKey = unsafePerformIO Vault.newKey
302{-# NOINLINE getFileInfoKey #-}
299{-# NOINLINE getFileInfoKey #-}
301+getClientCertificateKey :: Vault.Key (Maybe CertificateChain)
302+getClientCertificateKey = unsafePerformIO Vault.newKey
303+{-# NOINLINE getClientCertificateKey #-}