X-Git-Url: http://git.cielonegro.org/gitweb.cgi?a=blobdiff_plain;f=Network%2FHTTP%2FLucu%2FPostprocess.hs;h=732c47a809002e39e08e522f2b5681e508b9143b;hb=ca338174155913a969808d7b20193973394e474e;hp=989ad164707ca9afb99f94f55dcc69fe2840e658;hpb=8510a3765130fb171c06b448c50a74e65ac8ae11;p=Lucu.git diff --git a/Network/HTTP/Lucu/Postprocess.hs b/Network/HTTP/Lucu/Postprocess.hs index 989ad16..732c47a 100644 --- a/Network/HTTP/Lucu/Postprocess.hs +++ b/Network/HTTP/Lucu/Postprocess.hs @@ -1,6 +1,7 @@ {-# LANGUAGE - BangPatterns + DoAndIfThenElse , OverloadedStrings + , RecordWildCards , UnicodeSyntax #-} module Network.HTTP.Lucu.Postprocess @@ -9,29 +10,27 @@ module Network.HTTP.Lucu.Postprocess ) where import Control.Applicative -import Control.Concurrent.STM -import Control.Monad +import Control.Concurrent.STM +import Control.Monad import Control.Monad.Unicode import Data.Ascii (Ascii, CIAscii) import qualified Data.Ascii as A -import qualified Data.ByteString as Strict (ByteString) -import qualified Data.ByteString.Char8 as C8 hiding (ByteString) -import Data.IORef -import Data.Maybe -import Data.Time +import Data.Monoid.Unicode +import Data.Time import qualified Data.Time.HTTP as HTTP -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 -import Network.HTTP.Lucu.Request -import Network.HTTP.Lucu.Response +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 +import Network.HTTP.Lucu.Request +import Network.HTTP.Lucu.Response import Prelude.Unicode -import System.IO.Unsafe {- + TODO: Tanslate this memo into English. It doesn't make sense to + non-Japanese speakers. * Response が未設定なら、200 OK にする。 @@ -65,46 +64,56 @@ import System.IO.Unsafe -} postprocess ∷ Interaction → STM () -postprocess !itr - = do reqM ← readItr itr itrRequest id - res ← readItr itr itrResponse id +postprocess (Interaction {..}) + = do res ← readTVar itrResponse let sc = resStatus res unless (any (\ p → p sc) [isSuccessful, isRedirection, isError]) - $ abortSTM InternalServerError [] - $ Just ("The status code is not good for a final status: " - ++ show sc) - - when (sc ≡ MethodNotAllowed ∧ getHeader (C8.pack "Allow") res ≡ Nothing) - $ abortSTM InternalServerError [] - $ Just ("The status was " ++ show sc ++ " but no Allow header.") - - when (sc /= NotModified ∧ isRedirection sc ∧ getHeader (C8.pack "Location") res ≡ Nothing) - $ abortSTM InternalServerError [] - $ Just ("The status code was " ++ show sc ++ " but no Location header.") - - when (reqM /= Nothing) relyOnRequest + $ abortSTM InternalServerError [] + $ Just + $ A.toText + $ A.fromAsciiBuilder + $ A.toAsciiBuilder "The status code is not good for a final status of a response: " + ⊕ printStatusCode sc + + when (sc ≡ MethodNotAllowed ∧ getHeader "Allow" res ≡ Nothing) + $ abortSTM InternalServerError [] + $ Just + $ A.toText + $ A.fromAsciiBuilder + $ A.toAsciiBuilder "The status was " + ⊕ printStatusCode sc + ⊕ A.toAsciiBuilder " but no Allow header." + + when (sc ≢ NotModified ∧ isRedirection sc ∧ getHeader "Location" res ≡ Nothing) + $ abortSTM InternalServerError [] + $ Just + $ A.toText + $ A.fromAsciiBuilder + $ A.toAsciiBuilder "The status code was " + ⊕ printStatusCode sc + ⊕ A.toAsciiBuilder " but no Location header." + + case itrRequest of + Just req → postprocessWithRequest sc req + Nothing → return () -- itrResponse の内容は relyOnRequest によって變へられてゐる可 -- 能性が高い。 - do oldRes ← readItr itr itrResponse id + do oldRes ← readTVar itrResponse newRes ← unsafeIOToSTM - $ completeUnconditionalHeaders (itrConfig itr) oldRes - writeItr itr itrResponse newRes + $ completeUnconditionalHeaders itrConfig oldRes + writeTVar itrResponse newRes where - relyOnRequest ∷ STM () - relyOnRequest - = do status ← readItr itr itrResponse resStatus - req ← readItr itr itrRequest fromJust - - let reqVer = reqVersion req - canHaveBody = if reqMethod req ≡ HEAD then + postprocessWithRequest ∷ StatusCode → Request → STM () + postprocessWithRequest sc (Request {..}) + = do let canHaveBody = if reqMethod ≡ HEAD then False else - not (isInformational status ∨ - status ≡ NoContent ∨ - status ≡ ResetContent ∨ - status ≡ NotModified ) + (¬) (isInformational sc ∨ + sc ≡ NoContent ∨ + sc ≡ ResetContent ∨ + sc ≡ NotModified ) updateRes $ deleteHeader "Content-Length" updateRes $ deleteHeader "Transfer-Encoding" @@ -114,34 +123,42 @@ postprocess !itr $ updateRes $ setHeader "Content-Type" defaultPageContentType if canHaveBody then - when (reqVer ≡ HttpVersion 1 1) - $ do updateRes $ setHeader "Transfer-Encoding" "chunked" - writeItr itr itrWillChunkBody True - else + when (reqVersion ≡ HttpVersion 1 1) + $ do updateRes $ setHeader "Transfer-Encoding" "chunked" + writeTVar itrWillChunkBody True + else -- body 関連のヘッダを削除。但し HEAD なら Content-* は殘す - when (reqMethod req /= HEAD) - $ do updateRes $ deleteHeader "Content-Type" - updateRes $ deleteHeader "Etag" - updateRes $ deleteHeader "Last-Modified" + when (reqMethod ≢ HEAD) + $ do updateRes $ deleteHeader "Content-Type" + updateRes $ deleteHeader "Etag" + updateRes $ deleteHeader "Last-Modified" - conn ← readHeader "Connection" + conn ← readCIHeader "Connection" case conn of Nothing → return () - Just value → when (A.toCIAscii value ≡ "close") - $ writeItr itr itrWillClose True + Just value → when (value ≡ "close") + $ writeTVar itrWillClose True - willClose ← readItr itr itrWillClose id + willClose ← readTVar itrWillClose when willClose - $ updateRes $ setHeader "Connection" "close" + $ updateRes $ setHeader "Connection" "close" - when (reqMethod req ≡ HEAD ∨ not canHaveBody) - $ writeTVar (itrWillDiscardBody itr) True + when (reqMethod ≡ HEAD ∨ not canHaveBody) + $ writeTVar itrWillDiscardBody True readHeader ∷ CIAscii → STM (Maybe Ascii) - readHeader = readItr itr itrResponse ∘ getHeader + {-# INLINE readHeader #-} + readHeader k = getHeader k <$> readTVar itrResponse + + readCIHeader ∷ CIAscii → STM (Maybe CIAscii) + {-# INLINE readCIHeader #-} + readCIHeader k = getCIHeader k <$> readTVar itrResponse updateRes ∷ (Response → Response) → STM () - updateRes = updateItr itr itrResponse + {-# INLINE updateRes #-} + updateRes f + = do old ← readTVar itrResponse + writeTVar itrResponse (f old) completeUnconditionalHeaders ∷ Config → Response → IO Response completeUnconditionalHeaders conf = (compDate =≪) ∘ compServer @@ -158,4 +175,4 @@ completeUnconditionalHeaders conf = (compDate =≪) ∘ compServer Just _ → return res' getCurrentDate ∷ IO Ascii -getCurrentDate = HTTP.format <$> getCurrentTime +getCurrentDate = HTTP.toAscii <$> getCurrentTime