on master help sign in
Better protection in getLen function
commit36fbd3f9c7d601efe1f8af93e93ef100f0e8ed06 authorMichael Snoyman <michael@snoyman.com> dateSun 13 May 2012, 17:26 (14 years ago) parentfafc1df
2 files changed+46−13
warp/Network/Wai/Handler/Warp.hs+27−13 warp/test/main.hs+19
warp/Network/Wai/Handler/Warp.hs+27−13
@@ -406,12 +406,15 @@ chunkedSource ipair = do
406406 (src, mlen) <- liftIO $ I.readIORef ipair
407407 go src mlen
408408 where
409− go src NeedLen = do
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
409+ go' src front = do
410+ (src', (len, bs)) <- lift $ src C.$$+ front getLen
411+ let src''
412+ | S.null bs = src'
413+ | otherwise = C.yield bs >> src'
414+ go src'' $ HaveLen len
415+
416+ go src NeedLen = go' src id
417+ go src NeedLenNewline = go' src (CB.take 2 >>)
415418 go src (HaveLen 0) = liftIO $ I.writeIORef ipair (src, HaveLen 0)
416419 go src (HaveLen len) = do
417420 (src', mbs) <- lift $ src C.$$+ CL.head
@@ -433,14 +436,25 @@ chunkedSource ipair = do
433436 C.yield bs
434437 go src mlen
435438
436− getLen :: MonadIO m => C.Sink ByteString m Word
439+ getLen :: Monad m => C.Sink ByteString m (Word, ByteString)
437440 getLen = do
438− bss <- CB.takeWhile (/= 10) C.=$ CL.consume -- FIXME avoid attack
439− _ <- CB.take 1
440− return $
441− S.foldl' (\i c -> i * 16 + fromIntegral (hexToWord c)) 0
442− $ B.takeWhile isHexDigit
443− $ B.concat bss
441+ mbs <- CL.head
442+ case mbs of
443+ Nothing -> return (0, S.empty)
444+ Just bs -> do
445+ (x, y) <-
446+ case S.breakByte 10 bs of
447+ (x, y)
448+ | S.null y -> do
449+ mbs2 <- CL.head
450+ case mbs2 of
451+ Nothing -> return (x, y)
452+ Just bs2 -> return $ S.breakByte 10 $ bs `S.append` bs2
453+ | otherwise -> return (x, y)
454+ let w =
455+ S.foldl' (\i c -> i * 16 + fromIntegral (hexToWord c)) 0
456+ $ B.takeWhile isHexDigit x
457+ return (w, S.drop 1 y)
444458
445459 hexToWord w
446460 | w < 58 = w - 48
warp/test/main.hs+19
@@ -173,6 +173,25 @@ main = hspecX $ do
173173 [ "Hello World\nBye"
174174 , "Hello World"
175175 ]
176+ it "lots of chunks" $ do
177+ ifront <- I.newIORef id
178+ port <- getPort
179+ tid <- forkIO $ run port $ \req -> do
180+ bss <- requestBody req $$ Data.Conduit.List.consume
181+ liftIO $ I.atomicModifyIORef ifront $ \front -> (front . (S.concat bss:), ())
182+ return $ responseLBS status200 [] ""
183+ threadDelay 1000
184+ handle <- connectTo "127.0.0.1" $ PortNumber $ fromIntegral port
185+ let input = concat $ replicate 2 $
186+ ["POST / HTTP/1.1\r\nTransfer-Encoding: Chunked\r\n\r\n"] ++
187+ (replicate 500 "5\r\n12345\r\n") ++
188+ ["0\r\n"]
189+ mapM_ (\bs -> hPutStr handle bs >> hFlush handle) input
190+ hClose handle
191+ threadDelay 1000
192+ killThread tid
193+ front <- I.readIORef ifront
194+ front [] @?= replicate 2 (S.concat $ replicate 500 "12345")
176195 it "in chunks" $ do
177196 ifront <- I.newIORef id
178197 port <- getPort