1 module Network.HTTP.Lucu.Postprocess
2 ( postprocess -- Interaction -> STM ()
3 , completeUnconditionalHeaders -- Config -> Response -> IO Response
7 import Control.Concurrent.STM
11 import GHC.Conc (unsafeIOToSTM)
12 import Network.HTTP.Lucu.Abortion
13 import Network.HTTP.Lucu.Config
14 import Network.HTTP.Lucu.Headers
15 import Network.HTTP.Lucu.HttpVersion
16 import Network.HTTP.Lucu.Interaction
17 import Network.HTTP.Lucu.RFC1123DateTime
18 import Network.HTTP.Lucu.Request
19 import Network.HTTP.Lucu.Response
20 import Network.HTTP.Lucu.Utils
25 * Response が未設定なら、200 OK にする。
27 * ステータスが 2xx, 3xx, 4xx, 5xx のいずれでもなければ 500 にする。
29 * 405 Method Not Allowed なのに Allow ヘッダが無ければ 500 にする。
31 * 304 Not Modified 以外の 3xx なのに Location ヘッダが無ければ 500 に
34 * Content-Length があれば、それを削除する。
36 * HTTP/1.1 であり、且つ body を持つ事が出來る時、Transfer-Encoding の
37 最後の要素が chunked でなければ 500 Internal Error にする。
38 Transfer-Encoding が未設定であれば、chunked に設定する。
40 * HTTP/1.0 なのに Transfer-Encoding が設定濟なら 500 Internal Server
41 Error にする。但し identity だけは許す。
43 * body を持つ事が出來る時、Content-Type が無ければ
44 application/octet-stream にする。出來ない時、HEAD でなければ
45 Content-Type, Etag, Last-Modified を削除する。
47 * body を持つ事が出來ない時、body 破棄フラグを立てる。
49 * Connection: close が設定されてゐる時、切斷フラグを立てる。
51 * 切斷フラグが立ってゐる時、Connection: close を設定する。
59 postprocess :: Interaction -> STM ()
61 = do resM <- readItr itr itrResponse id
64 Nothing -> writeItr itr itrResponse
66 resVersion = HttpVersion 1 1
70 Just res -> do let sc = resStatus res
72 when (not $ any (\ p -> p sc) [isSuccessful, isRedirection, isError])
73 $ abortSTM InternalServerError []
74 $ Just ("The status code is not good for a final status: "
77 when (sc == MethodNotAllowed && getHeader "Allow" res == Nothing)
78 $ abortSTM InternalServerError []
79 $ Just ("The status was " ++ show sc ++ " but no Allow header.")
81 when (sc /= NotModified && isRedirection sc && getHeader "Location" res == Nothing)
82 $ abortSTM InternalServerError []
83 $ Just ("The status code was " ++ show sc ++ " but no Location header.")
85 when (itrRequest itr /= Nothing)
88 do oldRes <- readItr itr itrResponse id
89 newRes <- unsafeIOToSTM
90 $ completeUnconditionalHeaders (itrConfig itr) (fromJust oldRes)
91 writeItr itr itrResponse $ Just newRes
93 relyOnRequest :: Interaction -> STM ()
95 = do status <- readItr itr itrResponse (resStatus . fromJust)
97 let req = fromJust $ itrRequest itr
98 reqVer = reqVersion req
99 canHaveBody = if reqMethod req == HEAD then
102 not (isInformational status ||
103 status == NoContent ||
104 status == ResetContent ||
105 status == NotModified )
107 updateRes itr $ deleteHeader "Content-Length"
109 cType <- readHeader itr "Content-Type"
110 when (cType == Nothing)
111 $ updateRes itr $ setHeader "Content-Type" "application/octet-stream"
114 do teM <- readHeader itr "Transfer-Encoding"
115 if reqVer == HttpVersion 1 1 then
118 Nothing -> updateRes itr $ setHeader "Transfer-Encoding" "chunked"
119 Just te -> let teList = [trim isWhiteSpace x
120 | x <- splitBy (== ',') (map toLower te)]
122 when (teList == [] || last teList /= "chunked")
123 $ abortSTM InternalServerError []
124 $ Just ("Transfer-Encoding must end with `chunked' "
125 ++ "because this is an HTTP/1.1 request: "
128 writeItr itr itrWillChunkBody True
130 case fmap (map toLower) teM of
132 Just "identity" -> return ()
133 Just te -> abortSTM InternalServerError []
134 $ Just ("Transfer-Encoding must be `identity' because "
135 ++ "this is an HTTP/1.0 request: "
138 -- body 関連のヘッダを削除。但し HEAD なら Content-* は殘す
139 do updateRes itr $ deleteHeader "Transfer-Encoding"
140 when (reqMethod req /= HEAD)
141 $ do updateRes itr $ deleteHeader "Content-Type"
142 updateRes itr $ deleteHeader "Etag"
143 updateRes itr $ deleteHeader "Last-Modified"
145 conn <- readHeader itr "Connection"
146 case fmap (map toLower) conn of
147 Just "close" -> writeItr itr itrWillClose True
150 willClose <- readItr itr itrWillClose id
152 $ updateRes itr $ setHeader "Connection" "close"
154 when (reqMethod req == HEAD || not canHaveBody)
155 $ writeTVar (itrWillDiscardBody itr) True
157 readHeader :: Interaction -> String -> STM (Maybe String)
159 = do valueMM <- readItrF itr itrResponse $ getHeader name
161 Just (Just val) -> return $ Just val
164 updateRes :: Interaction -> (Response -> Response) -> STM ()
165 updateRes itr updator
166 = updateItrF itr itrResponse updator
169 completeUnconditionalHeaders :: Config -> Response -> IO Response
170 completeUnconditionalHeaders conf res
171 = return res >>= compServer >>= compDate >>= return
174 = case getHeader "Server" res of
175 Nothing -> return $ addHeader "Server" (cnfServerSoftware conf) res
179 = case getHeader "Date" res of
180 Nothing -> do time <- getClockTime
181 return $ addHeader "Date" (formatHTTPDateTime time) res