{-# LANGUAGE CPP #-}
module Network.Wai.Handler.Warp.Conduit where
import UnliftIO (assert, throwIO)
import qualified Data.ByteString as S
import qualified Data.IORef as I
import Network.Wai.Handler.Warp.Imports
import Network.Wai.Handler.Warp.Types
data ISource = ISource !Source !(I.IORef Int)
mkISource :: Source -> Int -> IO ISource
mkISource :: Source -> Int -> IO ISource
mkISource Source
src Int
cnt = do
IORef Int
ref <- forall a. a -> IO (IORef a)
I.newIORef Int
cnt
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$! Source -> IORef Int -> ISource
ISource Source
src IORef Int
ref
readISource :: ISource -> IO ByteString
readISource :: ISource -> IO ByteString
readISource (ISource Source
src IORef Int
ref) = do
Int
count <- forall a. IORef a -> IO a
I.readIORef IORef Int
ref
if Int
count forall a. Eq a => a -> a -> Bool
== Int
0
then forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
S.empty
else do
ByteString
bs <- Source -> IO ByteString
readSource Source
src
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (ByteString -> Bool
S.null ByteString
bs) forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) e a. (MonadIO m, Exception e) => e -> m a
throwIO InvalidRequest
ConnectionClosedByPeer
let
toSend :: Int
toSend = forall a. Ord a => a -> a -> a
min Int
count (ByteString -> Int
S.length ByteString
bs)
count' :: Int
count' = Int
count forall a. Num a => a -> a -> a
- Int
toSend
case () of
()
| Int
count' forall a. Ord a => a -> a -> Bool
> Int
0 -> do
forall a. IORef a -> a -> IO ()
I.writeIORef IORef Int
ref Int
count'
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
bs
| Bool
otherwise -> do
let (ByteString
x, ByteString
y) = Int -> ByteString -> (ByteString, ByteString)
S.splitAt Int
toSend ByteString
bs
Source -> ByteString -> IO ()
leftoverSource Source
src ByteString
y
forall a. (?callStack::CallStack) => Bool -> a -> a
assert (Int
count' forall a. Eq a => a -> a -> Bool
== Int
0) forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> a -> IO ()
I.writeIORef IORef Int
ref Int
count'
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
x
data CSource = CSource !Source !(I.IORef ChunkState)
data ChunkState = NeedLen
| NeedLenNewline
| HaveLen Word
| DoneChunking
deriving Int -> ChunkState -> ShowS
[ChunkState] -> ShowS
ChunkState -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ChunkState] -> ShowS
$cshowList :: [ChunkState] -> ShowS
show :: ChunkState -> String
$cshow :: ChunkState -> String
showsPrec :: Int -> ChunkState -> ShowS
$cshowsPrec :: Int -> ChunkState -> ShowS
Show
mkCSource :: Source -> IO CSource
mkCSource :: Source -> IO CSource
mkCSource Source
src = do
IORef ChunkState
ref <- forall a. a -> IO (IORef a)
I.newIORef ChunkState
NeedLen
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$! Source -> IORef ChunkState -> CSource
CSource Source
src IORef ChunkState
ref
readCSource :: CSource -> IO ByteString
readCSource :: CSource -> IO ByteString
readCSource (CSource Source
src IORef ChunkState
ref) = do
ChunkState
mlen <- forall a. IORef a -> IO a
I.readIORef IORef ChunkState
ref
ChunkState -> IO ByteString
go ChunkState
mlen
where
withLen :: Word -> ByteString -> IO ByteString
withLen Word
0 ByteString
bs = do
Source -> ByteString -> IO ()
leftoverSource Source
src ByteString
bs
IO ()
dropCRLF
forall {b}. b -> ChunkState -> IO b
yield' ByteString
S.empty ChunkState
DoneChunking
withLen Word
len ByteString
bs
| ByteString -> Bool
S.null ByteString
bs = do
forall a. IORef a -> a -> IO ()
I.writeIORef IORef ChunkState
ref ChunkState
DoneChunking
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
S.empty
| Bool
otherwise =
case ByteString -> Int
S.length ByteString
bs forall a. Ord a => a -> a -> Ordering
`compare` forall a b. (Integral a, Num b) => a -> b
fromIntegral Word
len of
Ordering
EQ -> forall {b}. b -> ChunkState -> IO b
yield' ByteString
bs ChunkState
NeedLenNewline
Ordering
LT -> forall {b}. b -> ChunkState -> IO b
yield' ByteString
bs forall a b. (a -> b) -> a -> b
$ Word -> ChunkState
HaveLen forall a b. (a -> b) -> a -> b
$ Word
len forall a. Num a => a -> a -> a
- forall a b. (Integral a, Num b) => a -> b
fromIntegral (ByteString -> Int
S.length ByteString
bs)
Ordering
GT -> do
let (ByteString
x, ByteString
y) = Int -> ByteString -> (ByteString, ByteString)
S.splitAt (forall a b. (Integral a, Num b) => a -> b
fromIntegral Word
len) ByteString
bs
Source -> ByteString -> IO ()
leftoverSource Source
src ByteString
y
forall {b}. b -> ChunkState -> IO b
yield' ByteString
x ChunkState
NeedLenNewline
yield' :: b -> ChunkState -> IO b
yield' b
bs ChunkState
mlen = do
forall a. IORef a -> a -> IO ()
I.writeIORef IORef ChunkState
ref ChunkState
mlen
forall (m :: * -> *) a. Monad m => a -> m a
return b
bs
dropCRLF :: IO ()
dropCRLF = do
ByteString
bs <- Source -> IO ByteString
readSource Source
src
case ByteString -> Maybe (Word8, ByteString)
S.uncons ByteString
bs of
Maybe (Word8, ByteString)
Nothing -> forall (m :: * -> *) a. Monad m => a -> m a
return ()
Just (Word8
13, ByteString
bs') -> ByteString -> IO ()
dropLF ByteString
bs'
Just (Word8
10, ByteString
bs') -> Source -> ByteString -> IO ()
leftoverSource Source
src ByteString
bs'
Just (Word8, ByteString)
_ -> Source -> ByteString -> IO ()
leftoverSource Source
src ByteString
bs
dropLF :: ByteString -> IO ()
dropLF ByteString
bs =
case ByteString -> Maybe (Word8, ByteString)
S.uncons ByteString
bs of
Maybe (Word8, ByteString)
Nothing -> do
ByteString
bs2 <- Source -> IO ByteString
readSource' Source
src
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (ByteString -> Bool
S.null ByteString
bs2) forall a b. (a -> b) -> a -> b
$ ByteString -> IO ()
dropLF ByteString
bs2
Just (Word8
10, ByteString
bs') -> Source -> ByteString -> IO ()
leftoverSource Source
src ByteString
bs'
Just (Word8, ByteString)
_ -> Source -> ByteString -> IO ()
leftoverSource Source
src ByteString
bs
go :: ChunkState -> IO ByteString
go ChunkState
NeedLen = IO ByteString
getLen
go ChunkState
NeedLenNewline = IO ()
dropCRLF forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> IO ByteString
getLen
go (HaveLen Word
0) = do
IO ()
dropCRLF
forall a. IORef a -> a -> IO ()
I.writeIORef IORef ChunkState
ref ChunkState
DoneChunking
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
S.empty
go (HaveLen Word
len) = do
ByteString
bs <- Source -> IO ByteString
readSource Source
src
Word -> ByteString -> IO ByteString
withLen Word
len ByteString
bs
go ChunkState
DoneChunking = forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
S.empty
getLen :: IO ByteString
getLen = do
ByteString
bs <- Source -> IO ByteString
readSource Source
src
if ByteString -> Bool
S.null ByteString
bs
then do
forall a. IORef a -> a -> IO ()
I.writeIORef IORef ChunkState
ref forall a b. (a -> b) -> a -> b
$ forall a. (?callStack::CallStack) => Bool -> a -> a
assert Bool
False forall a b. (a -> b) -> a -> b
$ Word -> ChunkState
HaveLen Word
0
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
S.empty
else do
(ByteString
x, ByteString
y) <-
case (Word8 -> Bool) -> ByteString -> (ByteString, ByteString)
S.break (forall a. Eq a => a -> a -> Bool
== Word8
10) ByteString
bs of
(ByteString
x, ByteString
y)
| ByteString -> Bool
S.null ByteString
y -> do
ByteString
bs2 <- Source -> IO ByteString
readSource' Source
src
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ if ByteString -> Bool
S.null ByteString
bs2
then (ByteString
x, ByteString
y)
else (Word8 -> Bool) -> ByteString -> (ByteString, ByteString)
S.break (forall a. Eq a => a -> a -> Bool
== Word8
10) forall a b. (a -> b) -> a -> b
$ ByteString
bs ByteString -> ByteString -> ByteString
`S.append` ByteString
bs2
| Bool
otherwise -> forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString
x, ByteString
y)
let w :: Word
w =
forall a. (a -> Word8 -> a) -> a -> ByteString -> a
S.foldl' (\Word
i Word8
c -> Word
i forall a. Num a => a -> a -> a
* Word
16 forall a. Num a => a -> a -> a
+ forall a b. (Integral a, Num b) => a -> b
fromIntegral (forall {a}. (Ord a, Num a) => a -> a
hexToWord Word8
c)) Word
0
forall a b. (a -> b) -> a -> b
$ (Word8 -> Bool) -> ByteString -> ByteString
S.takeWhile Word8 -> Bool
isHexDigit ByteString
x
let y' :: ByteString
y' = Int -> ByteString -> ByteString
S.drop Int
1 ByteString
y
ByteString
y'' <-
if ByteString -> Bool
S.null ByteString
y'
then Source -> IO ByteString
readSource Source
src
else forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
y'
Word -> ByteString -> IO ByteString
withLen Word
w ByteString
y''
hexToWord :: a -> a
hexToWord a
w
| a
w forall a. Ord a => a -> a -> Bool
< a
58 = a
w forall a. Num a => a -> a -> a
- a
48
| a
w forall a. Ord a => a -> a -> Bool
< a
71 = a
w forall a. Num a => a -> a -> a
- a
55
| Bool
otherwise = a
w forall a. Num a => a -> a -> a
- a
87
isHexDigit :: Word8 -> Bool
isHexDigit :: Word8 -> Bool
isHexDigit Word8
w = Word8
w forall a. Ord a => a -> a -> Bool
>= Word8
48 Bool -> Bool -> Bool
&& Word8
w forall a. Ord a => a -> a -> Bool
<= Word8
57
Bool -> Bool -> Bool
|| Word8
w forall a. Ord a => a -> a -> Bool
>= Word8
65 Bool -> Bool -> Bool
&& Word8
w forall a. Ord a => a -> a -> Bool
<= Word8
70
Bool -> Bool -> Bool
|| Word8
w forall a. Ord a => a -> a -> Bool
>= Word8
97 Bool -> Bool -> Bool
&& Word8
w forall a. Ord a => a -> a -> Bool
<= Word8
102