module Network.WebSockets.Decode
( request
, frame
) where
import Control.Applicative (pure, (<$>), (<*>), (*>), (<*))
import Data.Bits ((.&.))
import Data.Attoparsec (Parser, anyWord8, string, takeWhile1, word8)
import Data.Attoparsec.Combinator (manyTill)
import Data.Binary.Get (runGet, getWord16be, getWord64be)
import Data.ByteString (ByteString)
import Data.ByteString.Char8 ()
import Data.ByteString.Internal (c2w)
import Data.Int (Int64)
import qualified Data.Attoparsec as A
import qualified Data.ByteString.Lazy as BL
import qualified Data.CaseInsensitive as CI
import Network.WebSockets.Mask
import Network.WebSockets.Types
request :: Parser Request
request = Request
<$> requestLine
<*> manyTill header newline
where
space = word8 (c2w ' ')
newline = string "\r\n"
requestLine = string "GET" *> space *> takeWhile1 (/= c2w ' ')
<* space
<* string "HTTP/1.1" <* newline
header = (,)
<$> (CI.mk <$> takeWhile1 (/= c2w ':'))
<* string ": "
<*> takeWhile1 (/= c2w '\r')
<* newline
frame :: Parser Frame
frame = do
byte0 <- anyWord8
let fin = byte0 .&. 0x80 == 0x80
opcode = byte0 .&. 0x0f
let ft = case opcode of
0x00 -> ContinuationFrame
0x01 -> TextFrame
0x02 -> BinaryFrame
0x08 -> CloseFrame
0x09 -> PingFrame
0x0a -> PongFrame
_ -> error "Unknown opcode"
byte1 <- anyWord8
let mask = byte1 .&. 0x80 == 0x80
lenflag = fromIntegral (byte1 .&. 0x7f)
len <- case lenflag of
126 -> fromIntegral . runGet' getWord16be <$> A.take 2
127 -> fromIntegral . runGet' getWord64be <$> A.take 8
_ -> return lenflag
masker <- maskPayload <$> if mask then Just <$> A.take 4 else pure Nothing
chunks <- take64 len
return $ Frame fin ft (masker $ BL.fromChunks chunks)
where
runGet' g = runGet g . BL.fromChunks . return
take64 :: Int64 -> Parser [ByteString]
take64 n
| n <= 0 = return []
| otherwise = do
let n' = min intMax n
chunk <- A.take (fromIntegral n')
(chunk :) <$> take64 (n n')
where
intMax :: Int64
intMax = fromIntegral (maxBound :: Int)