{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE BangPatterns #-} module Main (main) where import qualified Control.Exception as E 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.Char8 as C8 import Network.HPACK import Network.HPACK.Token import Network.HTTP.Types import Network.HTTP2.Server import Network.Run.TCP -- network-run import System.Environment import System.Exit main :: IO () main = do args <- getArgs when (length args /= 2) $ do putStrLn "server " exitFailure let [host,port] = args runTCPServer (Just host) port runHTTP2Server where runHTTP2Server s = E.bracket (allocSimpleConfig s 4096) freeSimpleConfig (`run` server) server req _aux sendResponse = case getHeaderValue tokenMethod vt of Just "GET" -> sendResponse responseHello [] Just "POST" -> sendResponse (responseEcho req) [] _ -> sendResponse response404 [] where (_, vt) = requestHeaders req responseHello :: Response responseHello = responseBuilder ok200 header body where header = [("Content-Type", "text/plain")] body = byteString "Hello, world!\n" response404 :: Response response404 = responseNoBody notFound404 [] 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