module Happstack.Server.Proxy where
import Control.Monad (MonadPlus(mzero), liftM)
import Control.Monad.Trans (MonadIO(liftIO))
import qualified Data.ByteString.Char8 as B
import Data.List (isPrefixOf)
import Data.Maybe (fromJust, fromMaybe)
import Happstack.Server.Monads (ServerMonad(askRq), FilterMonad, WebMonad, escape')
import Happstack.Server.Response (badGateway, toResponse)
import Happstack.Server.Client (getResponse)
import Happstack.Server.Types (Request(rqPaths, rqHeaders, rqPeer), Response, setHeader, getHeader)
proxyServe :: (MonadIO m, WebMonad Response m, ServerMonad m, MonadPlus m, FilterMonad Response m) => [String] -> m Response
proxyServe allowed = do
rq <- askRq
if cond rq then proxyServe' rq else mzero
where
cond rq
| "*" `elem` allowed = True
| domain `elem` allowed = True
| superdomain `elem` wildcards =True
| otherwise = False
where
domain = head (rqPaths rq)
superdomain = tail $ snd $ break (=='.') domain
wildcards = (map (drop 2) $ filter ("*." `isPrefixOf`) allowed)
proxyServe' :: (MonadIO m, FilterMonad Response m, WebMonad Response m) => Request-> m Response
proxyServe' rq = liftIO (getResponse (unproxify rq)) >>=
either (badGateway . toResponse . show) escape'
rproxyServe :: (ServerMonad m, WebMonad Response m, FilterMonad Response m, MonadIO m) =>
String
-> [(String, String)]
-> m Response
rproxyServe defaultHost list =
do rq <- askRq
r <- liftIO (getResponse (unrproxify defaultHost list rq))
either (badGateway . toResponse . show) (escape') r
unproxify :: Request -> Request
unproxify rq = rq {rqPaths = tail $ rqPaths rq,
rqHeaders =
forwardedFor $ forwardedHost $
setHeader "host" (head $ rqPaths rq) $
rqHeaders rq}
where
appendInfo hdr val = setHeader hdr (csv val $
maybe "" B.unpack $
getHeader hdr rq)
forwardedFor = appendInfo "X-Forwarded-For" (fst $ rqPeer rq)
forwardedHost = appendInfo "X-Forwarded-Host"
(B.unpack $ fromJust $ getHeader "host" rq)
csv v "" = v
csv v x = x++", " ++ v
unrproxify :: String -> [(String, String)] -> Request -> Request
unrproxify defaultHost list rq =
let host::String
host = fromMaybe defaultHost $ flip lookup list =<< B.unpack `liftM` getHeader "host" rq
newrq = rq {rqPaths = host: rqPaths rq}
in unproxify newrq