6 module Network.HTTP.Lucu.Postprocess
8 , completeUnconditionalHeaders
11 import Control.Applicative
12 import Control.Concurrent.STM
14 import Control.Monad.Unicode
15 import Data.Ascii (Ascii, CIAscii)
16 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
33 * Response が未設定なら、200 OK にする。
35 * ステータスが 2xx, 3xx, 4xx, 5xx のいずれでもなければ 500 にする。
37 * 405 Method Not Allowed なのに Allow ヘッダが無ければ 500 にする。
39 * 304 Not Modified 以外の 3xx なのに Location ヘッダが無ければ 500 に
42 * Content-Length があれば、それを削除する。Transfer-Encoding があって
45 * HTTP/1.1 であり、body を持つ事が出來る時、Transfer-Encoding を
48 * body を持つ事が出來る時、Content-Type が無ければデフォルト値にする。
49 出來ない時、HEAD でなければContent-Type, Etag, Last-Modified を削除
52 * body を持つ事が出來ない時、body 破棄フラグを立てる。
54 * Connection: close が設定されてゐる時、切斷フラグを立てる。
56 * 切斷フラグが立ってゐる時、Connection: close を設定する。
64 postprocess ∷ Interaction → STM ()
66 = do reqM ← readItr itrRequest itr
67 res ← readItr itrResponse itr
68 let sc = resStatus res
70 unless (any (\ p → p sc) [isSuccessful, isRedirection, isError])
71 $ abortSTM InternalServerError []
75 $ A.toAsciiBuilder "The status code is not good for a final status of a response: "
78 when (sc ≡ MethodNotAllowed ∧ getHeader "Allow" res ≡ Nothing)
79 $ abortSTM InternalServerError []
83 $ A.toAsciiBuilder "The status was "
85 ⊕ A.toAsciiBuilder " but no Allow header."
87 when (sc ≢ NotModified ∧ isRedirection sc ∧ getHeader "Location" res ≡ Nothing)
88 $ abortSTM InternalServerError []
92 $ A.toAsciiBuilder "The status code was "
94 ⊕ A.toAsciiBuilder " but no Location header."
96 when (reqM ≢ Nothing) relyOnRequest
98 -- itrResponse の内容は relyOnRequest によって變へられてゐる可
100 do oldRes ← readItr itrResponse itr
101 newRes ← unsafeIOToSTM
102 $ completeUnconditionalHeaders (itrConfig itr) oldRes
103 writeItr itrResponse newRes itr
105 relyOnRequest ∷ STM ()
107 = do status ← resStatus <$> readItr itrResponse itr
108 req ← fromJust <$> readItr itrRequest itr
110 let reqVer = reqVersion req
111 canHaveBody = if reqMethod req ≡ HEAD then
114 not (isInformational status ∨
116 status ≡ ResetContent ∨
117 status ≡ NotModified )
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 (reqVer ≡ HttpVersion 1 1)
128 $ do updateRes $ setHeader "Transfer-Encoding" "chunked"
129 writeItr itrWillChunkBody True itr
131 -- body 関連のヘッダを削除。但し HEAD なら Content-* は殘す
132 when (reqMethod req ≢ HEAD)
133 $ do updateRes $ deleteHeader "Content-Type"
134 updateRes $ deleteHeader "Etag"
135 updateRes $ deleteHeader "Last-Modified"
137 conn ← readHeader "Connection"
140 Just value → when (A.toCIAscii value ≡ "close")
141 $ writeItr itrWillClose True itr
143 willClose ← readItr itrWillClose itr
145 $ updateRes $ setHeader "Connection" "close"
147 when (reqMethod req ≡ HEAD ∨ not canHaveBody)
148 $ writeTVar (itrWillDiscardBody itr) True
150 readHeader ∷ CIAscii → STM (Maybe Ascii)
151 {-# INLINE readHeader #-}
152 readHeader k = getHeader k <$> readItr itrResponse itr
154 updateRes ∷ (Response → Response) → STM ()
155 {-# INLINE updateRes #-}
156 updateRes f = updateItr itrResponse f itr
158 completeUnconditionalHeaders ∷ Config → Response → IO Response
159 completeUnconditionalHeaders conf = (compDate =≪) ∘ compServer
162 = case getHeader "Server" res' of
163 Nothing → return $ setHeader "Server" (cnfServerSoftware conf) res'
167 = case getHeader "Date" res' of
168 Nothing → do date ← getCurrentDate
169 return $ setHeader "Date" date res'
172 getCurrentDate ∷ IO Ascii
173 getCurrentDate = HTTP.toAscii <$> getCurrentTime