{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
module Test.HttpReply
( HttpReply(..)
, HttpReplyMismatch(..)
, compareHttpReplies
, assertHttpRepliesAreEq
, assertHttpRepliesDiffer
)
where
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C8
import Data.List (intercalate)
import Data.Maybe (catMaybes, isJust, isNothing)
import Network.HTTP.Types (Header, HeaderName)
import Data.CaseInsensitive (original)
data HttpReply =
HttpReply
{ hrSecure :: !Bool
, hrStatus :: !Int
, hrHeaders :: ![Header]
, hrBytes :: !BS.ByteString
}
instance Show HttpReply where
show r = intercalate "\n" $
[ "Status: " ++ (show . hrStatus) r
, "Body:"
, C8.unpack $ hrBytes r
] <> (map (show . concatHeader) $ hrHeaders r)
where
concatHeader (f, v) = BS.concat [ " ", original f , ": " , v]
data HttpReplyMismatch
= StatusMismatch Int Int
| HeaderMismatch HeaderName (Maybe C8.ByteString) (Maybe C8.ByteString)
| BodyMismatch BS.ByteString BS.ByteString
| MissingViaHeader
| UnexpectedViaHeader
deriving (Eq)
instance Show HttpReplyMismatch where
show (StatusMismatch x y)= "HTTP status codes don't match : " ++ show x ++ " /= " ++ show y
show (HeaderMismatch name x y) = "Header field '" ++ show name ++ "' doesn't match : '" ++ show x ++ "' /= '" ++ show y
show (BodyMismatch x y) = "HTTP response bodies are different :\n" ++ C8.unpack x ++ "\n-----------\n" ++ C8.unpack y
show MissingViaHeader = "Error: Proxy connection should contain 'X-Via-Proxy' header."
show UnexpectedViaHeader = "Error: Direct connection should not contain 'X-Via-Proxy' header."
assertHttpRepliesAreEq :: HttpReply -> HttpReply -> IO ()
assertHttpRepliesAreEq direct proxied = do
let assertNoMismatches [] = return ()
assertNoMismatches xs = error $ intercalate "\n" $ map show xs
assertNoMismatches $ compareHttpReplies direct proxied
assertHttpRepliesDiffer :: HttpReply -> HttpReply -> IO ()
assertHttpRepliesDiffer direct proxied = do
let assertHasMismatches [] = error "Responses should be different!"
assertHasMismatches _ = return ()
assertHasMismatches $ compareHttpReplies direct proxied
compareHttpReplies :: HttpReply -> HttpReply -> [HttpReplyMismatch]
compareHttpReplies direct proxied = catMaybes mbMismatches
where
mbMismatches =
[ compare' hrStatus StatusMismatch
, compare' hrBytes BodyMismatch
, missingViaHeader
, unexpectedViaHeader
, mismatchedHeader "server"
, mismatchedHeader "content-type"
, mismatchedHeader "content-length"
]
wantSecure = hrSecure direct
maybeHeader n r = lookup n $ hrHeaders r
compare' f g
| f direct == f proxied = Nothing
| otherwise = Just $ g (f direct) (f proxied)
missingViaHeader
| not wantSecure && (isJust $ maybeHeader "X-Via-Proxy" direct) = Just UnexpectedViaHeader
| otherwise = Nothing
unexpectedViaHeader
| not wantSecure && (isNothing $ maybeHeader "X-Via-Proxy" proxied) = Just MissingViaHeader
| otherwise = Nothing
mismatchedHeader name
| x' /= y' = Just $ HeaderMismatch name x' y'
| otherwise = Nothing
where x' = maybeHeader name direct
y' = maybeHeader name proxied