1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
|
{-# LANGUAGE OverloadedStrings #-}
module Server where
import Control.Monad
import Crypto.Hash (Context, SHA1) -- cryptonite
import qualified Crypto.Hash as CH
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Data.ByteString.Builder (byteString)
import qualified Data.ByteString.Builder as BB
import qualified Data.ByteString.Char8 as C8
import Network.HTTP.Types
import Network.HTTP2.Server
server :: Server
server req _aux sendResponse = case requestMethod req of
Just "GET" -> case requestPath req of
Nothing -> sendResponse response404 []
Just path
| path == "/" -> sendResponse responseHello []
| "/perf/" `B.isPrefixOf` path -> do
case C8.readInt (B.drop 6 path) of
Nothing -> sendResponse responseHello []
Just (n, _) -> sendResponse (responsePerf n) []
| otherwise -> sendResponse response404 []
Just "POST" -> sendResponse (responseEcho req) []
_ -> sendResponse response404 []
responseHello :: Response
responseHello = responseBuilder ok200 header body
where
header = [("Content-Type", "text/plain")]
body = byteString "Hello, world!\n"
responsePerf :: Int -> Response
responsePerf n0 = responseStreaming ok200 header streaming
where
header = [("Content-Type", "text/plain")]
bs1024 = BB.byteString $ B.replicate 1024 65
streaming write _flush = loop n0
where
loop 0 = return ()
loop n
| n < 1024 = write $ BB.byteString $ B.replicate (fromIntegral n) 65
| otherwise = do
write bs1024
loop (n - 1024)
response404 :: Response
response404 = responseBuilder notFound404 header body
where
header = [("Content-Type", "text/plain")]
body = byteString "Not found\n"
responseEcho :: Request -> Response
responseEcho req = setResponseTrailersMaker h2rsp maker
where
h2rsp = responseStreaming ok200 header streamingBody
header = [("Content-Type", "text/plain")]
streamingBody write _flush = loop
where
loop = do
bs <- getRequestBodyChunk req
unless (B.null bs) $ do
void $ write $ byteString bs
loop
maker = trailersMaker (CH.hashInit :: Context SHA1)
-- Strictness is important for Context.
trailersMaker :: Context SHA1 -> Maybe ByteString -> IO NextTrailersMaker
trailersMaker ctx Nothing = return $ Trailers [("X-SHA1", sha1)]
where
sha1 = C8.pack $ show $ CH.hashFinalize ctx
trailersMaker ctx (Just bs) = return $ NextTrailersMaker $ trailersMaker ctx'
where
ctx' = CH.hashUpdate ctx bs
|