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
19 import Data.Monoid.Unicode
21 import qualified Data.Time.HTTP as HTTP
22 import GHC.Conc (unsafeIOToSTM)
23 import Network.HTTP.Lucu.Abortion
24 import Network.HTTP.Lucu.Config
25 import Network.HTTP.Lucu.Headers
26 import Network.HTTP.Lucu.HttpVersion
27 import Network.HTTP.Lucu.Interaction
28 import Network.HTTP.Lucu.Request
29 import Network.HTTP.Lucu.Response
30 import Prelude.Unicode
34 * Response が未設定なら、200 OK にする。
36 * ステータスが 2xx, 3xx, 4xx, 5xx のいずれでもなければ 500 にする。
38 * 405 Method Not Allowed なのに Allow ヘッダが無ければ 500 にする。
40 * 304 Not Modified 以外の 3xx なのに Location ヘッダが無ければ 500 に
43 * Content-Length があれば、それを削除する。Transfer-Encoding があって
46 * HTTP/1.1 であり、body を持つ事が出來る時、Transfer-Encoding を
49 * body を持つ事が出來る時、Content-Type が無ければデフォルト値にする。
50 出來ない時、HEAD でなければContent-Type, Etag, Last-Modified を削除
53 * body を持つ事が出來ない時、body 破棄フラグを立てる。
55 * Connection: close が設定されてゐる時、切斷フラグを立てる。
57 * 切斷フラグが立ってゐる時、Connection: close を設定する。
65 postprocess ∷ Interaction → STM ()
67 = do reqM ← readItr itrRequest id itr
68 res ← readItr itrResponse id itr
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 when (reqM ≢ Nothing) relyOnRequest
99 -- itrResponse の内容は relyOnRequest によって變へられてゐる可
101 do oldRes ← readItr itrResponse id itr
102 newRes ← unsafeIOToSTM
103 $ completeUnconditionalHeaders (itrConfig itr) oldRes
104 writeItr itrResponse newRes itr
106 relyOnRequest ∷ STM ()
108 = do status ← readItr itrResponse resStatus itr
109 req ← readItr itrRequest fromJust itr
111 let reqVer = reqVersion req
112 canHaveBody = if reqMethod req ≡ HEAD then
115 not (isInformational status ∨
117 status ≡ ResetContent ∨
118 status ≡ NotModified )
120 updateRes $ deleteHeader "Content-Length"
121 updateRes $ deleteHeader "Transfer-Encoding"
123 cType ← readHeader "Content-Type"
124 when (cType ≡ Nothing)
125 $ updateRes $ setHeader "Content-Type" defaultPageContentType
128 when (reqVer ≡ HttpVersion 1 1)
129 $ do updateRes $ setHeader "Transfer-Encoding" "chunked"
130 writeItr itrWillChunkBody True itr
132 -- body 関連のヘッダを削除。但し HEAD なら Content-* は殘す
133 when (reqMethod req ≢ HEAD)
134 $ do updateRes $ deleteHeader "Content-Type"
135 updateRes $ deleteHeader "Etag"
136 updateRes $ deleteHeader "Last-Modified"
138 conn ← readHeader "Connection"
141 Just value → when (A.toCIAscii value ≡ "close")
142 $ writeItr itrWillClose True itr
144 willClose ← readItr itrWillClose id itr
146 $ updateRes $ setHeader "Connection" "close"
148 when (reqMethod req ≡ HEAD ∨ not canHaveBody)
149 $ writeTVar (itrWillDiscardBody itr) True
151 readHeader ∷ CIAscii → STM (Maybe Ascii)
152 {-# INLINE readHeader #-}
153 readHeader k = readItr itrResponse (getHeader k) itr
155 updateRes ∷ (Response → Response) → STM ()
156 {-# INLINE updateRes #-}
157 updateRes f = updateItr itrResponse f itr
159 completeUnconditionalHeaders ∷ Config → Response → IO Response
160 completeUnconditionalHeaders conf = (compDate =≪) ∘ compServer
163 = case getHeader "Server" res' of
164 Nothing → return $ setHeader "Server" (cnfServerSoftware conf) res'
168 = case getHeader "Date" res' of
169 Nothing → do date ← getCurrentDate
170 return $ setHeader "Date" date res'
173 getCurrentDate ∷ IO Ascii
174 getCurrentDate = HTTP.toAscii <$> getCurrentTime