7 module Network.HTTP.Lucu.Postprocess
11 import Control.Applicative
12 import Control.Concurrent.STM
14 import Control.Monad.Unicode
15 import Data.Ascii (Ascii, CIAscii, AsciiBuilder)
16 import qualified Data.Ascii as A
18 import Data.Monoid.Unicode
19 import GHC.Conc (unsafeIOToSTM)
20 import Network.HTTP.Lucu.Abortion
21 import Network.HTTP.Lucu.Config
22 import Network.HTTP.Lucu.DefaultPage
23 import Network.HTTP.Lucu.Headers
24 import Network.HTTP.Lucu.Interaction
25 import Network.HTTP.Lucu.Request
26 import Network.HTTP.Lucu.Response
27 import Prelude.Unicode
29 postprocess ∷ NormalInteraction → STM ()
30 postprocess ni@(NI {..})
31 = do void $ tryPutTMVar niSendContinue False
32 abortOnCertainConditions ni
33 postprocessWithRequest ni
34 completeUnconditionalHeaders ni
36 abortOnCertainConditions ∷ NormalInteraction → STM ()
37 abortOnCertainConditions (NI {..})
38 = readTVar niResponse ≫= go
40 go ∷ Response → STM ()
41 go res@(Response {..})
42 = do unless (any (\ p → p resStatus) [ isSuccessful
47 $ A.toAsciiBuilder "Inappropriate status code for a response: "
48 ⊕ printStatusCode resStatus
50 when ( resStatus ≈ MethodNotAllowed ∧
51 hasHeader "Allow" res )
53 $ A.toAsciiBuilder "The status was "
54 ⊕ printStatusCode resStatus
55 ⊕ A.toAsciiBuilder " but no \"Allow\" header."
57 when ( resStatus ≉ NotModified ∧
58 isRedirection resStatus ∧
59 hasHeader "Location" res )
61 $ A.toAsciiBuilder "The status code was "
62 ⊕ printStatusCode resStatus
63 ⊕ A.toAsciiBuilder " but no Location header."
65 abort' ∷ AsciiBuilder → STM ()
67 ∘ mkAbortion' InternalServerError
71 postprocessWithRequest ∷ NormalInteraction → STM ()
72 postprocessWithRequest ni@(NI {..})
73 = do willDiscardBody ← readTVar niWillDiscardBody
74 canHaveBody ← if willDiscardBody then
77 resCanHaveBody <$> readTVar niResponse
80 $ deleteHeader "Content-Length"
81 ∘ deleteHeader "Transfer-Encoding"
84 do when niWillChunkBody $
85 writeHeader ni "Transfer-Encoding" (Just "chunked")
86 writeDefaultPageIfNeeded ni
88 do writeTVar niWillDiscardBody True
89 -- These headers make sense for HEAD requests even
90 -- when there won't be a response entity body.
91 when (reqMethod niRequest ≢ HEAD)
93 $ deleteHeader "Content-Type"
95 ∘ deleteHeader "Last-Modified"
97 hasConnClose ← (≡ Just "close") <$> readCIHeader ni "Connection"
98 willClose ← readTVar niWillClose
99 when (hasConnClose ∧ (¬) willClose)
100 $ writeTVar niWillClose True
101 when ((¬) hasConnClose ∧ willClose)
102 $ writeHeader ni "Connection" (Just "close")
104 writeDefaultPageIfNeeded ∷ NormalInteraction → STM ()
105 writeDefaultPageIfNeeded ni@(NI {..})
106 = do resHasCType ← readTVar niResponseHasCType
108 $ do writeHeader ni "Content-Type" $ Just defaultPageContentType
109 writeHeader ni "Content-Encoding" Nothing
110 res ← readTVar niResponse
111 let body = getDefaultPage niConfig (Just niRequest) res
112 putTMVar niBodyToSend body
114 completeUnconditionalHeaders ∷ NormalInteraction → STM ()
115 completeUnconditionalHeaders ni@(NI {..})
116 = do srv ← readHeader ni "Server"
117 when (isNothing srv) $
118 writeHeader ni "Server" $ Just $ cnfServerSoftware niConfig
120 date ← readHeader ni "Date"
121 when (isNothing date) $
122 do date' ← unsafeIOToSTM getCurrentDate
123 writeHeader ni "Date" $ Just date'
125 writeHeader ∷ NormalInteraction → CIAscii → Maybe Ascii → STM ()
126 {-# INLINE writeHeader #-}
129 Just v' → updateRes ni $ setHeader k v'
130 Nothing → updateRes ni $ deleteHeader k
132 readHeader ∷ NormalInteraction → CIAscii → STM (Maybe Ascii)
133 {-# INLINE readHeader #-}
134 readHeader (NI {..}) k
135 = getHeader k <$> readTVar niResponse
137 readCIHeader ∷ NormalInteraction → CIAscii → STM (Maybe CIAscii)
138 {-# INLINE readCIHeader #-}
139 readCIHeader (NI {..}) k
140 = getCIHeader k <$> readTVar niResponse
142 updateRes ∷ NormalInteraction → (Response → Response) → STM ()
143 {-# INLINE updateRes #-}
144 updateRes (NI {..}) f
145 = do old ← readTVar niResponse
146 writeTVar niResponse $ f old