packages feed

wai-middleware-delegate-0.1.0.0: test/IntegrationTest.hs

{-# 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'