7 module Network.HTTP.Lucu.Postprocess
9 , completeUnconditionalHeaders
12 import Control.Applicative
13 import Control.Concurrent.STM
15 import Control.Monad.Unicode
16 import Data.Ascii (Ascii, CIAscii)
17 import qualified Data.Ascii as A
18 import Data.Monoid.Unicode
20 import qualified Data.Time.HTTP as HTTP
21 import GHC.Conc (unsafeIOToSTM)
22 import Network.HTTP.Lucu.Abortion
23 import Network.HTTP.Lucu.Config
24 import Network.HTTP.Lucu.Headers
25 import Network.HTTP.Lucu.HttpVersion
26 import Network.HTTP.Lucu.Interaction
27 import Network.HTTP.Lucu.Request
28 import Network.HTTP.Lucu.Response
29 import Prelude.Unicode
32 TODO: Tanslate this memo into English. It doesn't make sense to
33 non-Japanese speakers.
35 * Response が未設定なら、200 OK にする。
37 * ステータスが 2xx, 3xx, 4xx, 5xx のいずれでもなければ 500 にする。
39 * 405 Method Not Allowed なのに Allow ヘッダが無ければ 500 にする。
41 * 304 Not Modified 以外の 3xx なのに Location ヘッダが無ければ 500 に
44 * Content-Length があれば、それを削除する。Transfer-Encoding があって
47 * HTTP/1.1 であり、body を持つ事が出來る時、Transfer-Encoding を
50 * body を持つ事が出來る時、Content-Type が無ければデフォルト値にする。
51 出來ない時、HEAD でなければContent-Type, Etag, Last-Modified を削除
54 * body を持つ事が出來ない時、body 破棄フラグを立てる。
56 * Connection: close が設定されてゐる時、切斷フラグを立てる。
58 * 切斷フラグが立ってゐる時、Connection: close を設定する。
66 postprocess ∷ Interaction → STM ()
67 postprocess (Interaction {..})
68 = do res ← readTVar itrResponse
69 let sc = resStatus res
71 unless (any (\ p → p sc) [isSuccessful, isRedirection, isError])
72 $ abortSTM InternalServerError []
76 $ A.toAsciiBuilder "The status code is not good for a final status of a response: "
79 when (sc ≡ MethodNotAllowed ∧ getHeader "Allow" res ≡ Nothing)
80 $ abortSTM InternalServerError []
84 $ A.toAsciiBuilder "The status was "
86 ⊕ A.toAsciiBuilder " but no Allow header."
88 when (sc ≢ NotModified ∧ isRedirection sc ∧ getHeader "Location" res ≡ Nothing)
89 $ abortSTM InternalServerError []
93 $ A.toAsciiBuilder "The status code was "
95 ⊕ A.toAsciiBuilder " but no Location header."
97 reqM ← readTVar itrRequest
99 Just req → postprocessWithRequest sc req
102 -- itrResponse の内容は relyOnRequest によって變へられてゐる可
104 do oldRes ← readTVar itrResponse
105 newRes ← unsafeIOToSTM
106 $ completeUnconditionalHeaders itrConfig oldRes
107 writeTVar itrResponse newRes
109 postprocessWithRequest ∷ StatusCode → Request → STM ()
110 postprocessWithRequest sc (Request {..})
111 = do let canHaveBody = if reqMethod ≡ HEAD then
114 (¬) (isInformational sc ∨
119 updateRes $ deleteHeader "Content-Length"
120 updateRes $ deleteHeader "Transfer-Encoding"
122 cType ← readHeader "Content-Type"
123 when (cType ≡ Nothing)
124 $ updateRes $ setHeader "Content-Type" defaultPageContentType
127 when (reqVersion ≡ HttpVersion 1 1)
128 $ do updateRes $ setHeader "Transfer-Encoding" "chunked"
129 writeTVar itrWillChunkBody True
131 -- body 関連のヘッダを削除。但し HEAD なら Content-* は殘す
132 when (reqMethod ≢ HEAD)
133 $ do updateRes $ deleteHeader "Content-Type"
134 updateRes $ deleteHeader "Etag"
135 updateRes $ deleteHeader "Last-Modified"
137 conn ← readCIHeader "Connection"
140 Just value → when (value ≡ "close")
141 $ writeTVar itrWillClose True
143 willClose ← readTVar itrWillClose
145 $ updateRes $ setHeader "Connection" "close"
147 when (reqMethod ≡ HEAD ∨ not canHaveBody)
148 $ writeTVar itrWillDiscardBody True
150 readHeader ∷ CIAscii → STM (Maybe Ascii)
151 {-# INLINE readHeader #-}
152 readHeader k = getHeader k <$> readTVar itrResponse
154 readCIHeader ∷ CIAscii → STM (Maybe CIAscii)
155 {-# INLINE readCIHeader #-}
156 readCIHeader k = getCIHeader k <$> readTVar itrResponse
158 updateRes ∷ (Response → Response) → STM ()
159 {-# INLINE updateRes #-}
161 = do old ← readTVar itrResponse
162 writeTVar itrResponse (f old)
164 completeUnconditionalHeaders ∷ Config → Response → IO Response
165 completeUnconditionalHeaders conf = (compDate =≪) ∘ compServer
168 = case getHeader "Server" res' of
169 Nothing → return $ setHeader "Server" (cnfServerSoftware conf) res'
173 = case getHeader "Date" res' of
174 Nothing → do date ← getCurrentDate
175 return $ setHeader "Date" date res'
178 getCurrentDate ∷ IO Ascii
179 getCurrentDate = HTTP.toAscii <$> getCurrentTime