]> gitweb @ CieloNegro.org - Lucu.git/blob - Network/HTTP/Lucu/Abortion.hs
33f22abb9045ca962e4ef9a62449b34d56662296
[Lucu.git] / Network / HTTP / Lucu / Abortion.hs
1 {-# OPTIONS_HADDOCK prune #-}
2
3 -- |Aborting the computation of 'Network.HTTP.Lucu.Resource.Resource'
4 -- in any 'Prelude.IO' monads or arrows.
5 module Network.HTTP.Lucu.Abortion
6     ( Abortion(..)
7     , abort
8     , abortPurely
9     , abortSTM
10     , abortA
11     , abortPage
12     )
13     where
14
15 import           Control.Arrow
16 import           Control.Arrow.ArrowIO
17 import           Control.Concurrent.STM
18 import           Control.Exception
19 import           Control.Monad.Trans
20 import qualified Data.ByteString.Char8 as C8
21 import           Data.Dynamic
22 import           GHC.Conc (unsafeIOToSTM)
23 import           Network.HTTP.Lucu.Config
24 import           Network.HTTP.Lucu.DefaultPage
25 import           Network.HTTP.Lucu.Headers
26 import           Network.HTTP.Lucu.Request
27 import           Network.HTTP.Lucu.Response
28 import           System.IO.Unsafe
29 import           Text.XML.HXT.Arrow.WriteDocument
30 import           Text.XML.HXT.Arrow.XmlArrow
31 import           Text.XML.HXT.Arrow.XmlIOStateArrow
32 import           Text.XML.HXT.DOM.XmlKeywords
33
34
35 data Abortion = Abortion {
36       aboStatus  :: !StatusCode
37     , aboHeaders :: !Headers
38     , aboMessage :: !(Maybe String)
39     } deriving (Show, Typeable)
40
41 instance Exception Abortion
42
43 -- |Computation of @'abort' status headers msg@ aborts the
44 -- 'Network.HTTP.Lucu.Resource.Resource' monad with given status,
45 -- additional response headers, and optional message string.
46 --
47 -- What this really does is to throw a special
48 -- 'Control.Exception.Exception'. The exception will be caught by the
49 -- Lucu system.
50 --
51 -- 1. If the 'Network.HTTP.Lucu.Resource.Resource' is in the /Deciding
52 --    Header/ or any precedent states, it is possible to use the
53 --    @status@ and such like as a HTTP response to be sent to the
54 --    client.
55 --
56 -- 2. Otherwise the HTTP response can't be modified anymore so the
57 --    only possible thing the system can do is to dump it to the
58 --    stderr. See
59 --    'Network.HTTP.Lucu.Config.cnfDumpTooLateAbortionToStderr'.
60 --
61 -- Note that the status code doesn't have to be an error code so you
62 -- can use this action for redirection as well as error reporting e.g.
63 --
64 -- > abort MovedPermanently
65 -- >       [("Location", "http://example.net/")]
66 -- >       (Just "It has been moved to example.net")
67 abort :: MonadIO m => StatusCode -> [ (String, String) ] -> Maybe String -> m a
68 abort status headers msg
69     = status `seq` headers `seq` msg `seq`
70       let abo = Abortion status (toHeaders $ map pack headers) msg
71       in
72         liftIO $ throwIO abo
73     where
74       pack (x, y) = (C8.pack x, C8.pack y)
75
76 -- |This is similar to 'abort' but computes it with
77 -- 'System.IO.Unsafe.unsafePerformIO'.
78 abortPurely :: StatusCode -> [ (String, String) ] -> Maybe String -> a
79 abortPurely = ((unsafePerformIO .) .) . abort
80
81 -- |Computation of @'abortSTM' status headers msg@ just computes
82 -- 'abort' in a 'Control.Monad.STM.STM' monad.
83 abortSTM :: StatusCode -> [ (String, String) ] -> Maybe String -> STM a
84 abortSTM status headers msg
85     = status `seq` headers `seq` msg `seq`
86       unsafeIOToSTM $! abort status headers msg
87
88 -- | Computation of @'abortA' -< (status, (headers, msg))@ just
89 -- computes 'abort' in an 'Control.Arrow.ArrowIO.ArrowIO'.
90 abortA :: ArrowIO a => a (StatusCode, ([ (String, String) ], Maybe String)) c
91 abortA 
92     = arrIO3 abort
93
94 -- aboMessage が Just なら單に mkDefaultPage に渡すだけで良いので樂だが、
95 -- Nothing の場合は getDefaultPage を使ってデフォルトのメッセージを得な
96 -- ければならない。
97 abortPage :: Config -> Maybe Request -> Response -> Abortion -> String
98 abortPage conf reqM res abo
99     = conf `seq` reqM `seq` res `seq` abo `seq`
100       case aboMessage abo of
101         Just msg
102             -> let [html] = unsafePerformIO 
103                             $ runX ( mkDefaultPage conf (aboStatus abo) (txt msg)
104                                      >>>
105                                      writeDocumentToString [(a_indent, v_1)]
106                                    )
107                in
108                  html
109         Nothing
110             -> let res'  = res { resStatus = aboStatus abo }
111                    res'' = foldl (.) id [setHeader name value
112                                              | (name, value) <- fromHeaders $ aboHeaders abo]
113                            $ res'
114                in
115                  getDefaultPage conf reqM res''