http2-5.4.1: util/Client.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
module Client where
import Control.Concurrent
import Control.Concurrent.Async
import qualified Control.Exception as E
import Control.Monad
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as C8
import Data.UnixTime
import Foreign.C.Types
import Network.HTTP.Types
import System.IO
import Text.Printf
import Network.HTTP2.Client
import Monitor
data Options = Options
{ optPerformance :: Int
, optNumOfReqs :: Int
, optMonitor :: Bool
, optInteractive :: Bool
}
deriving (Show)
client :: Options -> [Path] -> Client ()
client Options{..} paths sendRequest _aux = do
labelMe "h2c client"
let cli
| optPerformance /= 0 = clientPF optPerformance sendRequest
| otherwise = clientNReqs optNumOfReqs sendRequest
ex <- E.try $ mapConcurrently_ cli paths
case ex of
Right () -> return ()
Left e -> print (e :: HTTP2Error)
clientNReqs :: Int -> SendRequest -> Path -> IO ()
clientNReqs n0 sendRequest path = do
labelMe "h2c clinet N requests"
loop n0
where
req = requestNoBody methodGet path []
loop 0 = return ()
loop n = do
sendRequest req $ \rsp -> do
print $ responseStatus rsp
getResponseBodyChunk rsp >>= C8.putStrLn
loop (n - 1)
-- Path is dummy
clientPF :: Int -> SendRequest -> Path -> IO ()
clientPF n sendRequest _ = do
labelMe "h2c clinet performance"
t1 <- getUnixTime
sendRequest req loop
t2 <- getUnixTime
printThroughput t1 t2 n
where
req = requestNoBody methodGet path []
path = "/perf/" <> C8.pack (show n)
loop rsp = do
bs <- getResponseBodyChunk rsp
when (bs /= "") $ loop rsp
printThroughput :: UnixTime -> UnixTime -> Int -> IO ()
printThroughput t1 t2 n =
printf
"Throughput %.2f Mbps (%d bytes in %d msecs)\n"
bytesPerSeconds
n
millisecs
where
UnixDiffTime (CTime s) u = t2 `diffUnixTime` t1
millisecs :: Int
millisecs = fromIntegral s * 1000 + fromIntegral u `div` 1000
bytesPerSeconds :: Double
bytesPerSeconds =
fromIntegral n
* (1000 :: Double)
* 8
/ fromIntegral millisecs
/ 1024
/ 1024
console
:: Options -> [ByteString] -> IO () -> Aux -> IO ()
console _opt paths cli aux = do
putStrLn "q -- quit"
putStrLn "g -- get"
putStrLn "p -- ping"
mvar <- newEmptyMVar
loop mvar `E.catch` \(E.SomeException _) -> return ()
where
loop mvar = do
hSetBuffering stdout NoBuffering
putStr "> "
hSetBuffering stdout LineBuffering
l <- getLine
case l of
"q" -> putStrLn "bye"
"g" -> do
mapM_ (\p -> putStrLn $ "GET " ++ C8.unpack p) paths
_ <- forkIO $ cli >> putMVar mvar ()
takeMVar mvar
loop mvar
"p" -> do
putStrLn "Ping"
auxSendPing aux
loop mvar
_ -> do
putStrLn "No such command"
loop mvar