X-Git-Url: http://git.cielonegro.org/gitweb.cgi?p=Lucu.git;a=blobdiff_plain;f=Network%2FHTTP%2FLucu%2FPostprocess.hs;h=7d7e147f93be84797d739ca976991a4954ea5929;hp=929413cd0ce86a19ce389067f98a8e61265aaa0b;hb=e624f0db8c4610b36da9e4463a656e0cb8a104dd;hpb=1e48e402adec79653203dc19a1800efa7b1c467b diff --git a/Network/HTTP/Lucu/Postprocess.hs b/Network/HTTP/Lucu/Postprocess.hs index 929413c..7d7e147 100644 --- a/Network/HTTP/Lucu/Postprocess.hs +++ b/Network/HTTP/Lucu/Postprocess.hs @@ -1,6 +1,6 @@ module Network.HTTP.Lucu.Postprocess ( postprocess -- Interaction -> STM () - , completeUnconditionalHeaders -- Response -> IO Response + , completeUnconditionalHeaders -- Config -> Response -> IO Response ) where @@ -9,6 +9,8 @@ import Control.Monad import Data.Char import Data.Maybe import GHC.Conc (unsafeIOToSTM) +import Network.HTTP.Lucu.Abortion +import Network.HTTP.Lucu.Config import Network.HTTP.Lucu.Headers import Network.HTTP.Lucu.HttpVersion import Network.HTTP.Lucu.Interaction @@ -20,7 +22,14 @@ import System.Time {- - * Response が未設定なら、HTTP/1.1 500 Internal Server Error にする。 + * Response が未設定なら、200 OK にする。 + + * ステータスが 2xx, 3xx, 4xx, 5xx のいずれでもなければ 500 にする。 + + * 405 Method Not Allowed なのに Allow ヘッダが無ければ 500 にする。 + + * 304 Not Modified 以外の 3xx なのに Location ヘッダが無ければ 500 に + する。 * Content-Length があれば、それを削除する。 @@ -33,7 +42,7 @@ import System.Time * body を持つ事が出來る時、Content-Type が無ければ application/octet-stream にする。出來ない時、HEAD でなければ - Content-Type を削除する。 + Content-Type, Etag, Last-Modified を削除する。 * body を持つ事が出來ない時、body 破棄フラグを立てる。 @@ -47,22 +56,41 @@ import System.Time -} -{- Postprocess は body を補完した後で實行する事 -} - postprocess :: Interaction -> STM () postprocess itr - = do res <- readItr itr itrResponse id - - when (res == Nothing) - $ setStatus itr InternalServerError + = do resM <- readItr itr itrResponse id + + case resM of + Nothing -> writeItr itr itrResponse + $ Just $ Response { + resVersion = HttpVersion 1 1 + , resStatus = Ok + , resHeaders = [] + } + Just res -> do let sc = resStatus res + + when (not $ any (\ p -> p sc) [isSuccessful, isRedirection, isError]) + $ abortSTM InternalServerError [] + ("The status code is not good for a final status: " + ++ show sc) + + when (sc == MethodNotAllowed && getHeader "Allow" res == Nothing) + $ abortSTM InternalServerError [] + ("The status was " ++ show sc ++ " but no Allow header.") + + when (sc /= NotModified && isRedirection sc && getHeader "Location" res == Nothing) + $ abortSTM InternalServerError [] + ("The status code was " ++ show sc ++ " but no Location header.") when (itrRequest itr /= Nothing) $ relyOnRequest itr do oldRes <- readItr itr itrResponse id - newRes <- unsafeIOToSTM $ completeUnconditionalHeaders $ fromJust oldRes + newRes <- unsafeIOToSTM + $ completeUnconditionalHeaders (itrConfig itr) (fromJust oldRes) writeItr itr itrResponse $ Just newRes where + relyOnRequest :: Interaction -> STM () relyOnRequest itr = do status <- readItr itr itrResponse (resStatus . fromJust) @@ -88,14 +116,20 @@ postprocess itr | x <- splitBy (== ',') (map toLower te)] in when (teList == [] || last teList /= "chunked") - $ setStatus itr InternalServerError + $ abortSTM InternalServerError [] + ("Transfer-Encoding must end with `chunked' " + ++ "because this is an HTTP/1.1 request: " + ++ te) writeItr itr itrWillChunkBody True else case fmap (map toLower) teM of Nothing -> return () Just "identity" -> return () - _ -> setStatus itr InternalServerError + Just te -> abortSTM InternalServerError [] + ("Transfer-Encoding must be `identity' because " + ++ "this is an HTTP/1.0 request: " + ++ te) cType <- readHeader itr "Content-Type" when (cType == Nothing) @@ -104,24 +138,22 @@ postprocess itr -- body 関連のヘッダを削除。但し HEAD なら Content-* は殘す do updateRes itr $ deleteHeader "Transfer-Encoding" when (reqMethod req /= HEAD) - $ updateRes itr $ deleteHeader "Content-Type" + $ do updateRes itr $ deleteHeader "Content-Type" + updateRes itr $ deleteHeader "Etag" + updateRes itr $ deleteHeader "Last-Modified" conn <- readHeader itr "Connection" case fmap (map toLower) conn of Just "close" -> writeItr itr itrWillClose True - _ -> updateRes itr $ setHeader "Connection" "close" + _ -> return () + + willClose <- readItr itr itrWillClose id + when willClose + $ updateRes itr $ setHeader "Connection" "close" when (reqMethod req == HEAD || not canHaveBody) $ writeTVar (itrWillDiscardBody itr) True - setStatus :: Interaction -> StatusCode -> STM () - setStatus itr status - = writeTVar (itrResponse itr) (Just $ Response { - resVersion = HttpVersion 1 1 - , resStatus = status - , resHeaders = [] - }) - readHeader :: Interaction -> String -> STM (Maybe String) readHeader itr name = do valueMM <- readItrF itr itrResponse $ getHeader name @@ -134,13 +166,13 @@ postprocess itr = updateItrF itr itrResponse updator -completeUnconditionalHeaders :: Response -> IO Response -completeUnconditionalHeaders res +completeUnconditionalHeaders :: Config -> Response -> IO Response +completeUnconditionalHeaders conf res = return res >>= compServer >>= compDate >>= return where compServer res = case getHeader "Server" res of - Nothing -> return $ addHeader "Server" "Lucu/1.0" res + Nothing -> return $ addHeader "Server" (cnfServerSoftware conf) res Just _ -> return res compDate res