@@ -77,12 +77,15 @@ import Control.Exception
78import Control.Concurrent (forkIO)
78import Control.Concurrent (forkIO)
79import Data.Maybe (fromMaybe, isJust)
79import Data.Maybe (fromMaybe, isJust)
80+import Data.Char (toLower, isHexDigit)
81+import Data.Word (Word)
81import Data.Typeable (Typeable)
83import Data.Typeable (Typeable)
83import Data.Conduit (ResourceT, runResourceT)
85import Data.Conduit (ResourceT, runResourceT)
84import qualified Data.Conduit as C
86import qualified Data.Conduit as C
85import qualified Data.Conduit.List as CL
87import qualified Data.Conduit.List as CL
88+import qualified Data.Conduit.Binary as CB
86import Data.Conduit.Blaze (builderToByteString)
89import Data.Conduit.Blaze (builderToByteString)
87import Control.Exception.Lifted (throwIO)
90import Control.Exception.Lifted (throwIO)
88import Blaze.ByteString.Builder.HTTP
91import Blaze.ByteString.Builder.HTTP
@@ -96,6 +99,7 @@ import Network.Sendfile
96import qualified System.PosixCompat.Files as P
99import qualified System.PosixCompat.Files as P
98import Control.Monad.IO.Class (MonadIO, liftIO)
101import Control.Monad.IO.Class (MonadIO, liftIO)
102+import Control.Monad.Trans.Class (lift)
99import qualified Timeout as T
103import qualified Timeout as T
100import Timeout (Manager, registerKillThread, pause, resume)
104import Timeout (Manager, registerKillThread, pause, resume)
101import Data.Word (Word8)
105import Data.Word (Word8)
@@ -107,7 +111,6 @@ import System.IO (hPrint, stderr)
107import ReadInt (readInt64)
111import ReadInt (readInt64)
108import qualified Data.IORef as I
112import qualified Data.IORef as I
109import Data.Conduit.Network (bindPort, HostPreference (HostIPv4))
113import Data.Conduit.Network (bindPort, HostPreference (HostIPv4))
110−import Network.HTTP.Conduit.Chunk (chunkedConduit)
112#if WINDOWS
115#if WINDOWS
113import Control.Concurrent (threadDelay)
116import Control.Concurrent (threadDelay)
@@ -270,17 +273,6 @@ ibsIsolate ibs@(IsolatedBSSource ref) =
270ibsDone :: IsolatedBSSource -> IO (C.Source (ResourceT IO) ByteString)
273ibsDone :: IsolatedBSSource -> IO (C.Source (ResourceT IO) ByteString)
271ibsDone (IsolatedBSSource ref) = fmap snd $ I.readIORef ref
274ibsDone (IsolatedBSSource ref) = fmap snd $ I.readIORef ref
273−refSource :: MonadIO m => I.IORef (C.Source m a) -> C.Source m a
274−refSource ref = C.PipeM pull (return ())
277− src <- liftIO $ I.readIORef ref
278− (src', ma) <- src C.$$+ CL.head
279− liftIO $ I.writeIORef ref src'
281− Nothing -> return $ C.Done Nothing ()
282− Just a -> return $ C.HaveOutput (refSource ref) (return ()) a
284serveConnection :: Settings
276serveConnection :: Settings
285 -> T.Handle
277 -> T.Handle
286 -> (SomeException -> IO ())
278 -> (SomeException -> IO ())
@@ -376,13 +368,13 @@ parseRequest' conn port (firstLine:otherLines) remoteHost' src = do
376 Nothing -> 0
368 Nothing -> 0
377 Just bs -> readInt bs
369 Just bs -> readInt bs
378 let serverName' = takeUntil 58 host -- ':'
370 let serverName' = takeUntil 58 host -- ':'
379− let chunked = maybe False (=="chunked")
371+ let chunked = maybe False ((== "chunked") . B.map toLower)
380 $ lookup "transfer-encoding" heads
372 $ lookup "transfer-encoding" heads
381 (rbody, getSource) <- liftIO $
373 (rbody, getSource) <- liftIO $
382 if chunked
374 if chunked
384− ref <- I.newIORef (src C.$= chunkedConduit False)
376+ ref <- I.newIORef (src, NeedLen)
385− return (refSource ref, I.readIORef ref)
377+ return (chunkedSource ref, fmap fst $ I.readIORef ref)
387 ibs <- fmap IsolatedBSSource $ I.newIORef (len0, src)
379 ibs <- fmap IsolatedBSSource $ I.newIORef (len0, src)
388 return (ibsIsolate ibs, ibsDone ibs)
380 return (ibsIsolate ibs, ibsDone ibs)
@@ -403,6 +395,57 @@ parseRequest' conn port (firstLine:otherLines) remoteHost' src = do
403 , vault = mempty
395 , vault = mempty
404 }, getSource)
396 }, getSource)
398+data ChunkState = NeedLen
402+chunkedSource :: MonadIO m
403+ => I.IORef (C.Source m ByteString, ChunkState)
404+ -> C.Source m ByteString
405+chunkedSource ipair = do
406+ (src, mlen) <- liftIO $ I.readIORef ipair
410+ (src', len) <- lift $ src C.$$+ getLen
411+ go src' $ HaveLen len
412+ go src NeedLenNewline = do
413+ (src', len) <- lift $ src C.$$+ (CB.take 2 >> getLen)
414+ go src' $ HaveLen len
415+ go src (HaveLen 0) = liftIO $ I.writeIORef ipair (src, HaveLen 0)
416+ go src (HaveLen len) = do
417+ (src', mbs) <- lift $ src C.$$+ CL.head
419+ Nothing -> liftIO $ I.writeIORef ipair (src', HaveLen 0)
421+ case S.length bs `compare` fromIntegral len of
422+ EQ -> yield' src' NeedLenNewline bs
424+ let mlen = HaveLen $ len - fromIntegral (S.length bs)
427+ let (x, y) = S.splitAt (fromIntegral len) bs
428+ let src'' = C.yield y >> src'
429+ yield' src'' NeedLenNewline x
431+ yield' src mlen bs = do
432+ liftIO $ I.writeIORef ipair (src, mlen)
436+ getLen :: MonadIO m => C.Sink ByteString m Word
438+ bss <- CB.takeWhile (/= 10) C.=$ CL.consume -- FIXME avoid attack
441+ S.foldl' (\i c -> i * 16 + fromIntegral (hexToWord c)) 0
442+ $ B.takeWhile isHexDigit
448+ | otherwise = w - 87
407takeUntil :: Word8 -> ByteString -> ByteString
450takeUntil :: Word8 -> ByteString -> ByteString
408takeUntil c bs =
451takeUntil c bs =