module Network.WebSockets.Protocol.Hybi00.Internal
( Hybi00_ (..)
) where
import Control.Applicative ((<|>))
import Data.Char (isDigit)
import Data.Binary (encode)
import Data.ByteString.Lazy.Char8 ()
import Data.Digest.Pure.MD5 (md5)
import Data.Int (Int32)
import qualified Blaze.ByteString.Builder as BB
import qualified Data.Attoparsec as A
import qualified Data.Attoparsec.Enumerator as A
import qualified Data.ByteString as B
import qualified Data.Enumerator as E
import qualified Data.Enumerator.List as EL
import qualified Data.ByteString.Char8 as BC
import qualified Data.ByteString.Lazy.Char8 ()
import qualified Data.ByteString.Lazy as BL
import Network.WebSockets.Handshake.Http
import Network.WebSockets.Protocol
import Network.WebSockets.Types
data Hybi00_ = Hybi00_
instance Protocol Hybi00_ where
version Hybi00_ = "hybi00"
headerVersions Hybi00_ = []
supported Hybi00_ h = getSecWebSocketVersion h == Nothing
encodeMessages Hybi00_ _ _ = EL.map encodeMessage
decodeMessages Hybi00_ = E.sequence (A.iterParser parseMessage)
createRequest Hybi00_ = error "createRequest Hybi00_"
finishRequest Hybi00_ = handshakeHybi00
finishResponse Hybi00_ = error "finishResponse Hybi00_"
implementations = [Hybi00_]
instance TextProtocol Hybi00_
encodeMessage :: Message p -> BB.Builder
encodeMessage (DataMessage (Text pl)) =
BB.fromLazyByteString $ "\0" `BL.append` pl `BL.append` "\255"
encodeMessage (ControlMessage (Close _)) =
BB.fromLazyByteString "\255\0"
encodeMessage msg = error $
"Network.WebSockets.Protocol.Hybi00.encodeFrame: unsupported message: " ++
show msg
parseMessage :: A.Parser (Message p)
parseMessage = parseText <|> parseClose
where
parseText = do
_ <- A.word8 0x00
utf8string <- A.manyTill A.anyWord8 (A.try $ A.word8 0xff)
return $ DataMessage $ Text $ BL.pack utf8string
parseClose = do
_ <- A.word8 0xff
_ <- A.word8 0x00
return $ ControlMessage $ Close ""
divBySpaces :: String -> Maybe Int32
divBySpaces str
| spaces == 0 = Nothing
| otherwise = Just . fromIntegral $ number `div` spaces
where
number = read $ filter isDigit str :: Integer
spaces = fromIntegral . length $ filter (== ' ') str
handshakeHybi00 :: Monad m
=> RequestHttpPart
-> E.Iteratee B.ByteString m Request
handshakeHybi00 reqHttp@(RequestHttpPart path h isSecure) = do
keyPart3 <- A.iterParser $ A.take 8
keyPart1 <- numberFromToken =<< getHeader "Sec-WebSocket-Key1"
keyPart2 <- numberFromToken =<< getHeader "Sec-WebSocket-Key2"
let key = B.concat . BL.toChunks . encode . md5 $ BL.concat
[keyPart1, keyPart2, BL.fromChunks [keyPart3]]
host <- getHeader "Host"
origin <- getHeader "Origin"
let schema = if isSecure then "wss://" else "ws://"
let response = response101
[ ("Sec-WebSocket-Location", B.concat [schema, host, path])
, ("Sec-WebSocket-Origin", origin)
]
key
return $ Request path h response
where
getHeader = getRequestHeader reqHttp
numberFromToken token = case divBySpaces (BC.unpack token) of
Just n -> return $ encode n
Nothing -> E.throwError $ MalformedRequest reqHttp
"Security token does not contain enough spaces"