orpleT
  :: forall a b e m s. (Monad m, FromJSON a, ToJSON b, FromJSON s, ToJSON s, Eq s)
  => (forall x. m x -> IO x)  -- ^The runner for the inner monad
  -> Options                  -- ^Options
  -> e                        -- ^The initial environment
  -> s                        -- ^The default session state
  -> (a -> Vorple e s m b)    -- ^The request handler
  -> W.Application            -- ^The application
vorpleT runner opts env emptySession handler req = liftIO $ do
  appKey <- maybe (randomKey 32) return $ optAppKey opts
  (result, log) <- flip (flip runInternal opts) env $ do
    when (W.requestMethod req /= H.methodPost) $ throwStatus H.status405
    $(say "Got a POST")
    maybeInput <- liftIO $ decodeJSONSource $ W.requestBody req
    input <- maybe (throwStatus H.status400) return maybeInput
    $(say "Got JSON data")
    cookie <- getCookie appKey (W.requestHeaders req)
    (setCsrfKey, session) <- case cookie of
      Nothing -> do
        $(say "No cookie; generating CSRF key")
        csrfKey <- liftIO (randomKey 32) >>= return . encodeBase64
        return (csrfKey, emptySession)
      Just cookie -> do
        $(say "Got cookie")
        session <- requireJust $ decodeJSON $ cAppData cookie
        return (cCsrfKey cookie, session)
    (response, nextSession) <-
      case csrfData input of
        Nothing -> return (Nothing, session)
        Just inputData -> do
          require $ csrfKey input == setCsrfKey
          $(say "CSRF key matches")
          (response, nextSession) <-
            mapInternal (liftIO . runner)
            $ runVorpleInternal (handler inputData) session
          return (Just response, nextSession)
    $(say "Ran request handler")
    cookie <-
      if isNothing cookie || session /= nextSession
      then setCookie appKey setCsrfKey (encodeJSON nextSession) >>= return . Just
      else return Nothing
    $(say "About to return response")
    return (Csrf setCsrfKey response, cookie)
  BS.hPutStr stderr log
  let
  { (status, body, cookie) = case result of
      Left status          -> (getStatus status, encodeJSONBuilder (), Nothing)
      Right (body, cookie) -> (H.status200, encodeJSONBuilder body, cookie)
  }
  return $ W.ResponseBuilder status (maybeToList cookie) body

-- |Make an 'Application' from an 'IO' request handler

vorpleT
  :: forall a b e m s. (Monad m, FromJSON a, ToJSON b, FromJSON s, ToJSON s, Eq s)
  => (forall x. m x -> IO x)  -- ^The runner for the inner monad
  -> Options                  -- ^Options
  -> e                        -- ^The initial environment
  -> s                        -- ^The default session state
  -> (a -> Vorple e s m b)    -- ^The request handler
  -> W.Application
vorpleT runner opts env emptySession handler req = liftIO $ do
  appKey <- maybe (randomKey 32) return $ optAppKey opts
  (result, log) <- flip (flip runInternal opts) env $ do
    when (W.requestMethod req /= H.methodPost) $ throwStatus H.status405
    $(say "Got a POST")
    maybeInput <- liftIO $ decodeJSONSource $ W.requestBody req
    input <- maybe (throwStatus H.status400) return maybeInput
    $(say "Got JSON data")
    cookie <- getCookie appKey (W.requestHeaders req)
    (setCsrfKey, session) <- case cookie of
      Nothing -> do
        $(say "No cookie; generating CSRF key")
        csrfKey <- liftIO (randomKey 32) >>= return . encodeBase64
        return (csrfKey, emptySession)
      Just cookie -> do
        $(say "Got cookie")
        session <- requireJust $ decodeJSON $ cAppData cookie
        return (cCsrfKey cookie, session)
    (response, nextSession) <-
      case csrfData input of
        Nothing -> return (Nothing, session)
        Just inputData -> do
          require $ csrfKey input == setCsrfKey
          $(say "CSRF key matches")
          (response, nextSession) <-
            mapInternal (liftIO . runner)
            $ runVorpleInternal (handler inputData) session
          return (Just response, nextSession)
    $(say "Ran request handler")
    cookie <-
      if isNothing cookie || session /= nextSession
      then setCookie appKey setCsrfKey (encodeJSON nextSession) >>= return . Just
      else return Nothing
    $(say "About to return response")
    return (Csrf setCsrfKey response, cookie)
  BS.hPutStr stderr log
  let
  { (status, body, cookie) = case result of
      Left status          -> (getStatus status, encodeJSONBuilder (), Nothing)
      Right (body, cookie) -> (H.status200, encodeJSONBuilder body, cookie)
  }
  return $ W.ResponseBuilder status (maybeToList cookie) body

-- |Make an 'Application' from an 'IO' request handler
vorpleT runner opts env emptySession handler req = liftIO $ do
  appKey <- maybe (randomKey 32) return $ optAppKey opts
  (result, log) <- flip (flip runInternal opts) env $ do
    when (W.requestMethod req /= H.methodPost) $ throwStatus H.status405
    $(say "Got a POST")
    maybeInput <- liftIO $ decodeJSONSource $ W.requestBody req
    input <- maybe (throwStatus H.status400) return maybeInput
    $(say "Got JSON data")
    cookie <- getCookie appKey (W.requestHeaders req)
    (setCsrfKey, session) <- case cookie of
      Nothing -> do
        $(say "No cookie; generating CSRF key")
        csrfKey <- liftIO (randomKey 32) >>= return . encodeBase64
        return (csrfKey, emptySession)
      Just cookie -> do
        $(say "Got cookie")
        session <- requireJust $ decodeJSON $ cAppData cookie
        return (cCsrfKey cookie, session)
    (response, nextSession) <-
      case csrfData input of
        Nothing -> return (Nothing, session)
        Just inputData -> do
          require $ csrfKey input == setCsrfKey
          $(say "CSRF key matches")
          (response, nextSession) <-
            mapInternal (liftIO . runner)
            $ runVorpleInternal (handler inputData) session
          return (Just response, nextSession)
    $(say "Ran request handler")
    cookie <-
      if isNothing cookie || session /= nextSession
      then setCookie appKey setCsrfKey (encodeJSON nextSession) >>= return . Just
      else return Nothing
    $(say "About to return response")
    return (Csrf setCsrfKey response, cookie)
  BS.hPutStr stderr log
  let
  { (status, body, cookie) = case result of
      Left status          -> (getStatus status, encodeJSONBuilder (), Nothing)
      Right (body, cookie) -> (H.status200, encodeJSONBuilder body, cookie)
  }
  return $ W.ResponseBuilder status (maybeToList cookie) body

-- |Make an 'Application' from an 'IO' request handler