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
17 import qualified Data.ByteString as Strict (ByteString)
18 import qualified Data.ByteString.Char8 as C8 hiding (ByteString)
22 import qualified Data.Time.HTTP as HTTP
23 import GHC.Conc (unsafeIOToSTM)
24 import Network.HTTP.Lucu.Abortion
25 import Network.HTTP.Lucu.Config
26 import Network.HTTP.Lucu.Headers
27 import Network.HTTP.Lucu.HttpVersion
28 import Network.HTTP.Lucu.Interaction
29 import Network.HTTP.Lucu.Request
30 import Network.HTTP.Lucu.Response
31 import Prelude.Unicode
32 import System.IO.Unsafe
36 * Response が未設定なら、200 OK にする。
38 * ステータスが 2xx, 3xx, 4xx, 5xx のいずれでもなければ 500 にする。
40 * 405 Method Not Allowed なのに Allow ヘッダが無ければ 500 にする。
42 * 304 Not Modified 以外の 3xx なのに Location ヘッダが無ければ 500 に
45 * Content-Length があれば、それを削除する。Transfer-Encoding があって
48 * HTTP/1.1 であり、body を持つ事が出來る時、Transfer-Encoding を
51 * body を持つ事が出來る時、Content-Type が無ければデフォルト値にする。
52 出來ない時、HEAD でなければContent-Type, Etag, Last-Modified を削除
55 * body を持つ事が出來ない時、body 破棄フラグを立てる。
57 * Connection: close が設定されてゐる時、切斷フラグを立てる。
59 * 切斷フラグが立ってゐる時、Connection: close を設定する。
67 postprocess ∷ Interaction → STM ()
69 = do reqM ← readItr itr itrRequest id
70 res ← readItr itr itrResponse id
71 let sc = resStatus res
73 unless (any (\ p → p sc) [isSuccessful, isRedirection, isError])
74 $ abortSTM InternalServerError []
75 $ Just ("The status code is not good for a final status: "
78 when (sc ≡ MethodNotAllowed ∧ getHeader (C8.pack "Allow") res ≡ Nothing)
79 $ abortSTM InternalServerError []
80 $ Just ("The status was " ++ show sc ++ " but no Allow header.")
82 when (sc /= NotModified ∧ isRedirection sc ∧ getHeader (C8.pack "Location") res ≡ Nothing)
83 $ abortSTM InternalServerError []
84 $ Just ("The status code was " ++ show sc ++ " but no Location header.")
86 when (reqM /= Nothing) relyOnRequest
88 -- itrResponse の内容は relyOnRequest によって變へられてゐる可
90 do oldRes ← readItr itr itrResponse id
91 newRes ← unsafeIOToSTM
92 $ completeUnconditionalHeaders (itrConfig itr) oldRes
93 writeItr itr itrResponse newRes
95 relyOnRequest ∷ STM ()
97 = do status ← readItr itr itrResponse resStatus
98 req ← readItr itr itrRequest fromJust
100 let reqVer = reqVersion req
101 canHaveBody = if reqMethod req ≡ HEAD then
104 not (isInformational status ∨
106 status ≡ ResetContent ∨
107 status ≡ NotModified )
109 updateRes $ deleteHeader "Content-Length"
110 updateRes $ deleteHeader "Transfer-Encoding"
112 cType ← readHeader "Content-Type"
113 when (cType ≡ Nothing)
114 $ updateRes $ setHeader "Content-Type" defaultPageContentType
117 when (reqVer ≡ HttpVersion 1 1)
118 $ do updateRes $ setHeader "Transfer-Encoding" "chunked"
119 writeItr itr itrWillChunkBody True
121 -- body 関連のヘッダを削除。但し HEAD なら Content-* は殘す
122 when (reqMethod req /= HEAD)
123 $ do updateRes $ deleteHeader "Content-Type"
124 updateRes $ deleteHeader "Etag"
125 updateRes $ deleteHeader "Last-Modified"
127 conn ← readHeader "Connection"
130 Just value → when (A.toCIAscii value ≡ "close")
131 $ writeItr itr itrWillClose True
133 willClose ← readItr itr itrWillClose id
135 $ updateRes $ setHeader "Connection" "close"
137 when (reqMethod req ≡ HEAD ∨ not canHaveBody)
138 $ writeTVar (itrWillDiscardBody itr) True
140 readHeader ∷ CIAscii → STM (Maybe Ascii)
141 readHeader = readItr itr itrResponse ∘ getHeader
143 updateRes ∷ (Response → Response) → STM ()
144 updateRes = updateItr itr itrResponse
146 completeUnconditionalHeaders ∷ Config → Response → IO Response
147 completeUnconditionalHeaders conf = (compDate =≪) ∘ compServer
150 = case getHeader "Server" res' of
151 Nothing → return $ setHeader "Server" (cnfServerSoftware conf) res'
155 = case getHeader "Date" res' of
156 Nothing → do date ← getCurrentDate
157 return $ setHeader "Date" date res'
160 getCurrentDate ∷ IO Ascii
161 getCurrentDate = HTTP.format <$> getCurrentTime