2 module Network.HTTP.Lucu.Postprocess
4 , completeUnconditionalHeaders
8 import Control.Concurrent.STM
12 import GHC.Conc (unsafeIOToSTM)
13 import Network.HTTP.Lucu.Abortion
14 import Network.HTTP.Lucu.Config
15 import Network.HTTP.Lucu.Headers
16 import Network.HTTP.Lucu.HttpVersion
17 import Network.HTTP.Lucu.Interaction
18 import Network.HTTP.Lucu.RFC1123DateTime
19 import Network.HTTP.Lucu.Request
20 import Network.HTTP.Lucu.Response
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 があれば、それを削除する。Transfer-Encoding があって
37 * HTTP/1.1 であり、body を持つ事が出來る時、Transfer-Encoding を
40 * body を持つ事が出來る時、Content-Type が無ければデフォルト値にする。
41 出來ない時、HEAD でなければContent-Type, Etag, Last-Modified を削除
44 * body を持つ事が出來ない時、body 破棄フラグを立てる。
46 * Connection: close が設定されてゐる時、切斷フラグを立てる。
48 * 切斷フラグが立ってゐる時、Connection: close を設定する。
56 postprocess :: Interaction -> STM ()
59 do reqM <- readItr itr itrRequest id
60 res <- readItr itr itrResponse id
61 let sc = resStatus res
63 when (not $ any (\ p -> p sc) [isSuccessful, isRedirection, isError])
64 $ abortSTM InternalServerError []
65 $ Just ("The status code is not good for a final status: "
68 when (sc == MethodNotAllowed && getHeader "Allow" res == Nothing)
69 $ abortSTM InternalServerError []
70 $ Just ("The status was " ++ show sc ++ " but no Allow header.")
72 when (sc /= NotModified && isRedirection sc && getHeader "Location" res == Nothing)
73 $ abortSTM InternalServerError []
74 $ Just ("The status code was " ++ show sc ++ " but no Location header.")
76 when (reqM /= Nothing)
79 -- itrResponse の内容は relyOnRequest によって變へられてゐる可
81 do oldRes <- readItr itr itrResponse id
82 newRes <- unsafeIOToSTM
83 $ completeUnconditionalHeaders (itrConfig itr) oldRes
84 writeItr itr itrResponse newRes
86 relyOnRequest :: Interaction -> STM ()
89 do status <- readItr itr itrResponse resStatus
90 req <- readItr itr itrRequest fromJust
92 let reqVer = reqVersion req
93 canHaveBody = if reqMethod req == HEAD then
96 not (isInformational status ||
97 status == NoContent ||
98 status == ResetContent ||
99 status == NotModified )
101 updateRes itr $! deleteHeader "Content-Length"
102 updateRes itr $! deleteHeader "Transfer-Encoding"
104 cType <- readHeader itr "Content-Type"
105 when (cType == Nothing)
106 $ updateRes itr $ setHeader "Content-Type" defaultPageContentType
109 when (reqVer == HttpVersion 1 1)
110 $ do updateRes itr $! setHeader "Transfer-Encoding" "chunked"
111 writeItr itr itrWillChunkBody True
113 -- body 関連のヘッダを削除。但し HEAD なら Content-* は殘す
114 when (reqMethod req /= HEAD)
115 $ do updateRes itr $! deleteHeader "Content-Type"
116 updateRes itr $! deleteHeader "Etag"
117 updateRes itr $! deleteHeader "Last-Modified"
119 conn <- readHeader itr "Connection"
120 case fmap (map toLower) conn of
121 Just "close" -> writeItr itr itrWillClose True
124 willClose <- readItr itr itrWillClose id
126 $ updateRes itr $! setHeader "Connection" "close"
128 when (reqMethod req == HEAD || not canHaveBody)
129 $ writeTVar (itrWillDiscardBody itr) True
131 readHeader :: Interaction -> String -> STM (Maybe String)
133 = itr `seq` name `seq`
134 readItr itr itrResponse $ getHeader name
136 updateRes :: Interaction -> (Response -> Response) -> STM ()
137 updateRes itr updator
138 = itr `seq` updator `seq`
139 updateItr itr itrResponse updator
142 completeUnconditionalHeaders :: Config -> Response -> IO Response
143 completeUnconditionalHeaders conf res
144 = conf `seq` res `seq`
145 return res >>= compServer >>= compDate >>= return
148 = case getHeader "Server" res of
149 Nothing -> return $ addHeader "Server" (cnfServerSoftware conf) res
153 = case getHeader "Date" res of
154 Nothing -> do time <- getClockTime
155 return $ addHeader "Date" (formatHTTPDateTime time) res