+-- #prune
+
+-- | Aborting the computation of 'Network.HTTP.Lucu.Resource.Resource'
+-- in any IO monads or arrows.
module Network.HTTP.Lucu.Abortion
( Abortion(..)
- , abort -- MonadIO m => StatusCode -> [ (String, String) ] -> String -> m a
- , abortIO -- StatusCode -> [ (String, String) ] -> String -> IO a
- , abortSTM -- StatusCode -> [ (String, String) ] -> String -> STM a
- , abortA -- ArrowIO a => StatusCode -> [ (String, String) ] -> String -> a b c
- , aboPage -- Config -> Abortion -> String
+ , abort
+ , abortSTM
+ , abortA
+ , abortPage
)
where
import Network.HTTP.Lucu.Config
import Network.HTTP.Lucu.DefaultPage
import Network.HTTP.Lucu.Headers
+import Network.HTTP.Lucu.Request
import Network.HTTP.Lucu.Response
+import {-# SOURCE #-} Network.HTTP.Lucu.Resource
import System.IO.Unsafe
import Text.XML.HXT.Arrow.WriteDocument
import Text.XML.HXT.Arrow.XmlArrow
data Abortion = Abortion {
- aboStatus :: StatusCode
- , aboHeaders :: Headers
- , aboMessage :: String
+ aboStatus :: !StatusCode
+ , aboHeaders :: !Headers
+ , aboMessage :: !(Maybe String)
} deriving (Show, Typeable)
-
-abort :: MonadIO m => StatusCode -> [ (String, String) ] -> String -> m a
+-- | Computation of @'abort' status headers msg@ aborts the
+-- 'Network.HTTP.Lucu.Resource.Resource' monad with given status,
+-- additional response headers, and optional message string.
+--
+-- What this really does is to just throw a special DynException. The
+-- exception will be caught by the system.
+--
+-- 1. If the 'Network.HTTP.Lucu.Resource.Resource' is in the /Deciding
+-- Header/ or any precedent states, it is possible to use the
+-- @status@ and such like as a HTTP response to be sent to the
+-- client.
+--
+-- 2. Otherwise the HTTP response can't be modified anymore so the
+-- only possible thing the system can do is to dump it to the
+-- stderr. See
+-- 'Network.HTTP.Lucu.Config.cnfDumpTooLateAbortionToStderr'.
+--
+-- Note that the status code doesn't have to be an error code so you
+-- can use this action for redirection as well as error reporting e.g.
+--
+-- > abort MovedPermanently
+-- > [("Location", "http://example.net/")]
+-- > (Just "It has been moved to example.net")
+abort :: MonadIO m => StatusCode -> [ (String, String) ] -> Maybe String -> m a
abort status headers msg
- = liftIO $ abortIO status headers msg
-
-
-abortIO :: StatusCode -> [ (String, String) ] -> String -> IO a
-abortIO status headers msg
- = let abo = Abortion status headers msg
+ = status `seq` headers `seq` msg `seq`
+ let abo = Abortion status headers msg
exc = DynException (toDyn abo)
in
- throwIO exc
+ liftIO $ throwIO exc
+{-# SPECIALIZE abort :: StatusCode -> [ (String, String) ] -> Maybe String -> Resource a #-}
-
-abortSTM :: StatusCode -> [ (String, String) ] -> String -> STM a
+-- | Computation of @'abortSTM' status headers msg@ just computes
+-- 'abort' in a STM monad.
+abortSTM :: StatusCode -> [ (String, String) ] -> Maybe String -> STM a
abortSTM status headers msg
- = unsafeIOToSTM $ abortIO status headers msg
-
+ = status `seq` headers `seq` msg `seq`
+ unsafeIOToSTM $! abort status headers msg
-abortA :: ArrowIO a => StatusCode -> [ (String, String) ] -> String -> a b c
-abortA status headers msg
- = arrIO0 $ abortIO status headers msg
+-- | Computation of @'abortA' -< (status, (headers, msg))@ just
+-- computes 'abort' in an ArrowIO.
+abortA :: ArrowIO a => a (StatusCode, ([ (String, String) ], Maybe String)) c
+abortA
+ = arrIO3 abort
+{-# SPECIALIZE abortA :: IOSArrow (StatusCode, ([ (String, String) ], Maybe String)) c #-}
-
-aboPage :: Config -> Abortion -> String
-aboPage conf abo
- = let [html] = unsafePerformIO
- $ runX ( mkDefaultPage conf (aboStatus abo) (txt $ aboMessage abo)
- >>>
- writeDocumentToString [(a_indent, v_1)]
- )
- in
- html
+-- aboMessage が Just なら單に mkDefaultPage に渡すだけで良いので樂だが、
+-- Nothing の場合は getDefaultPage を使ってデフォルトのメッセージを得な
+-- ければならない。
+abortPage :: Config -> Maybe Request -> Response -> Abortion -> String
+abortPage conf reqM res abo
+ = conf `seq` reqM `seq` res `seq` abo `seq`
+ case aboMessage abo of
+ Just msg
+ -> let [html] = unsafePerformIO
+ $ runX ( mkDefaultPage conf (aboStatus abo) (txt msg)
+ >>>
+ writeDocumentToString [(a_indent, v_1)]
+ )
+ in
+ html
+ Nothing
+ -> let res' = res { resStatus = aboStatus abo }
+ res'' = foldl (.) id [setHeader name value
+ | (name, value) <- aboHeaders abo]
+ $ res'
+ in
+ getDefaultPage conf reqM res''