{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} module Main where import Control.Monad (when) import Data.Foldable (for_) import Data.Maybe (maybe) import System.Environment (lookupEnv) import Data.Default (Default (..)) import Network.HTTP.Client.TLS (newTlsManager) import Network.HTTP.Types (status500) import Network.Wai (Application, rawPathInfo, responseLBS) import Network.Wai.Handler.Warp (Port, testWithApplication) import Network.Wai.Middleware.Delegate (ProxySettings (..), delegateToProxy) import Test.Fetch (fetch) import Test.Hspec import Test.HttpReply import Test.TestRequests (RequestBuilder (..), buildRequest, testNotProxiedRequests, testRequests) import Test.WithExtras (defaultTlsSettings, testWithTLSApplication) defaultTestSettings :: ProxySettings defaultTestSettings = def { proxyHost = "httpbin.org", proxyTimeout = 2 } main :: IO () main = do dumpDebug' <- lookupEnv "DEBUG" let dumpDebug = maybe False (const True) dumpDebug' hspec $ do insecureProxyTest dumpDebug secureProxyTest dumpDebug defaultTestDelegate :: ProxySettings -> IO Application defaultTestDelegate s = do -- delegate everything but /status/418 let handleFunnyStatus = \req -> rawPathInfo req /= "/status/418" dummyApp _ respond = respond $ responseLBS status500 [] "I should have been proxied" manager <- newTlsManager return $ delegateToProxy s manager (handleFunnyStatus) dummyApp testWithInsecureProxy :: (Port -> IO ()) -> IO () testWithInsecureProxy = testWithApplication (defaultTestDelegate defaultTestSettings) testWithSecureProxy :: (Port -> IO ()) -> IO () testWithSecureProxy = testWithTLSApplication defaultTlsSettings (defaultTestDelegate defaultTestSettings) onDirectAndProxy :: (HttpReply -> HttpReply -> IO ()) -> Bool -> Int -> RequestBuilder -> IO () onDirectAndProxy f debug testProxyPort builder = do let proxiedBuilder = builder { rbHost = "localhost", rbPort = Just testProxyPort } directReq <- buildRequest builder proxiedReq <- buildRequest proxiedBuilder when debug $ do putStrLn "---------------" putStrLn "Direct Request:" putStrLn "---------------" print directReq putStrLn "----------------" putStrLn "Proxied Request:" putStrLn "----------------" print proxiedReq proxied <- fetch proxiedReq direct <- fetch directReq when debug $ do putStrLn "Direct:" putStrLn "-------" print direct putStrLn "Proxied:" putStrLn "--------" print proxied f direct proxied insecureProxyTest :: Bool -> Spec insecureProxyTest debug = let scheme = "HTTP" desc = "Simple " ++ scheme ++ " proxying:" assertEq = onDirectAndProxy assertHttpRepliesAreEq debug assertNeq = onDirectAndProxy assertHttpRepliesDiffer debug in around testWithInsecureProxy $ describe desc $ do for_ testRequests $ \(title, modifier) -> it (scheme ++ " " ++ title) $ \port -> assertEq port $ modifier def for_ testNotProxiedRequests $ \(title, modifier) -> it (scheme ++ " " ++ title) $ \port -> assertNeq port $ modifier def secureProxyTest :: Bool -> Spec secureProxyTest debug = let scheme = "HTTPS" desc = "Simple " ++ scheme ++ " proxying:" assertEq = onDirectAndProxy assertHttpRepliesAreEq debug assertNeq = onDirectAndProxy assertHttpRepliesDiffer debug def' = def { rbSecure = True } in around testWithSecureProxy $ describe desc $ do for_ testRequests $ \(title, modifier) -> it (scheme ++ " " ++ title) $ \port -> assertEq port $ modifier def' for_ testNotProxiedRequests $ \(title, modifier) -> it (scheme ++ " " ++ title) $ \port -> assertNeq port $ modifier def'