+{-# LANGUAGE
+ BangPatterns
+ , OverloadedStrings
+ , UnicodeSyntax
+ #-}
module Network.HTTP.Lucu.Postprocess
( postprocess
, completeUnconditionalHeaders
)
where
-
-import Control.Concurrent.STM
-import Control.Monad
-import Data.ByteString.Base (ByteString)
-import qualified Data.ByteString.Char8 as C8
-import Data.IORef
-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
-import Network.HTTP.Lucu.RFC1123DateTime
-import Network.HTTP.Lucu.Request
-import Network.HTTP.Lucu.Response
-import System.Time
-import System.IO.Unsafe
+import Control.Applicative
+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.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 Prelude.Unicode
+import System.IO.Unsafe
{-
-}
-postprocess :: Interaction -> STM ()
-postprocess itr
- = itr `seq`
- do reqM <- readItr itr itrRequest id
- res <- readItr itr itrResponse id
+postprocess ∷ Interaction → STM ()
+postprocess !itr
+ = do reqM ← readItr itr itrRequest id
+ res ← readItr itr itrResponse id
let sc = resStatus res
- when (not $ any (\ p -> p sc) [isSuccessful, isRedirection, isError])
- $ abortSTM InternalServerError []
- $ Just ("The status code is not good for a final status: "
- ++ show sc)
+ unless (any (\ p → p sc) [isSuccessful, isRedirection, isError])
+ $ abortSTM InternalServerError []
+ $ Just
+ $ A.toText ( "The status code is not good for a final status of a response: "
+ ⊕ printStatusCode sc )
- when (sc == MethodNotAllowed && getHeader (C8.pack "Allow") res == Nothing)
+ when (sc ≡ MethodNotAllowed ∧ getHeader "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)
+ 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 itr
+ when (reqM /= Nothing) relyOnRequest
-- itrResponse の内容は relyOnRequest によって變へられてゐる可
-- 能性が高い。
- do oldRes <- readItr itr itrResponse id
- newRes <- unsafeIOToSTM
- $ completeUnconditionalHeaders (itrConfig itr) oldRes
+ do oldRes ← readItr itr itrResponse id
+ newRes ← unsafeIOToSTM
+ $ completeUnconditionalHeaders (itrConfig itr) oldRes
writeItr itr itrResponse newRes
where
- relyOnRequest :: Interaction -> STM ()
- relyOnRequest itr
- = itr `seq`
- do status <- readItr itr itrResponse resStatus
- req <- readItr itr itrRequest fromJust
+ relyOnRequest ∷ STM ()
+ relyOnRequest
+ = do status ← readItr itr itrResponse resStatus
+ req ← readItr itr itrRequest fromJust
let reqVer = reqVersion req
- canHaveBody = if reqMethod req == HEAD then
+ canHaveBody = if reqMethod req ≡ HEAD then
False
else
- not (isInformational status ||
- status == NoContent ||
- status == ResetContent ||
- status == NotModified )
+ not (isInformational status ∨
+ status ≡ NoContent ∨
+ status ≡ ResetContent ∨
+ status ≡ NotModified )
- updateRes itr $! deleteHeader (C8.pack "Content-Length")
- updateRes itr $! deleteHeader (C8.pack "Transfer-Encoding")
+ updateRes $ deleteHeader "Content-Length"
+ updateRes $ deleteHeader "Transfer-Encoding"
- cType <- readHeader itr (C8.pack "Content-Type")
- when (cType == Nothing)
- $ updateRes itr $ setHeader (C8.pack "Content-Type") defaultPageContentType
+ cType ← readHeader "Content-Type"
+ when (cType ≡ Nothing)
+ $ updateRes $ setHeader "Content-Type" defaultPageContentType
if canHaveBody then
- when (reqVer == HttpVersion 1 1)
- $ do updateRes itr $! setHeader (C8.pack "Transfer-Encoding") (C8.pack "chunked")
+ when (reqVer ≡ HttpVersion 1 1)
+ $ do updateRes $ setHeader "Transfer-Encoding" "chunked"
writeItr itr itrWillChunkBody True
else
-- body 関連のヘッダを削除。但し HEAD なら Content-* は殘す
when (reqMethod req /= HEAD)
- $ do updateRes itr $! deleteHeader (C8.pack "Content-Type")
- updateRes itr $! deleteHeader (C8.pack "Etag")
- updateRes itr $! deleteHeader (C8.pack "Last-Modified")
+ $ do updateRes $ deleteHeader "Content-Type"
+ updateRes $ deleteHeader "Etag"
+ updateRes $ deleteHeader "Last-Modified"
- conn <- readHeader itr (C8.pack "Connection")
+ conn ← readHeader "Connection"
case conn of
- Nothing -> return ()
- Just value -> if value `noCaseEq` C8.pack "close" then
- writeItr itr itrWillClose True
- else
- return ()
+ Nothing → return ()
+ Just value → when (A.toCIAscii value ≡ "close")
+ $ writeItr itr itrWillClose True
- willClose <- readItr itr itrWillClose id
+ willClose ← readItr itr itrWillClose id
when willClose
- $ updateRes itr $! setHeader (C8.pack "Connection") (C8.pack "close")
+ $ updateRes $ setHeader "Connection" "close"
- when (reqMethod req == HEAD || not canHaveBody)
+ when (reqMethod req ≡ HEAD ∨ not canHaveBody)
$ writeTVar (itrWillDiscardBody itr) True
- readHeader :: Interaction -> ByteString -> STM (Maybe ByteString)
- readHeader itr name
- = itr `seq` name `seq`
- readItr itr itrResponse $ getHeader name
-
- updateRes :: Interaction -> (Response -> Response) -> STM ()
- updateRes itr updator
- = itr `seq` updator `seq`
- updateItr itr itrResponse updator
+ readHeader ∷ CIAscii → STM (Maybe Ascii)
+ readHeader = readItr itr itrResponse ∘ getHeader
+ updateRes ∷ (Response → Response) → STM ()
+ updateRes = updateItr itr itrResponse
-completeUnconditionalHeaders :: Config -> Response -> IO Response
-completeUnconditionalHeaders conf res
- = conf `seq` res `seq`
- return res >>= compServer >>= compDate >>= return
+completeUnconditionalHeaders ∷ Config → Response → IO Response
+completeUnconditionalHeaders conf = (compDate =≪) ∘ compServer
where
- compServer res
- = case getHeader (C8.pack "Server") res of
- Nothing -> return $ setHeader (C8.pack "Server") (cnfServerSoftware conf) res
- Just _ -> return res
-
- compDate res
- = case getHeader (C8.pack "Date") res of
- Nothing -> do date <- getCurrentDate
- return $ setHeader (C8.pack "Date") date res
- Just _ -> return res
-
-
-cache :: IORef (ClockTime, ByteString)
-cache = unsafePerformIO $
- newIORef (TOD 0 0, undefined)
-{-# NOINLINE cache #-}
-
-getCurrentDate :: IO ByteString
-getCurrentDate = do now@(TOD curSec _) <- getClockTime
- (TOD cachedSec _, cachedStr) <- readIORef cache
-
- if curSec == cachedSec then
- return cachedStr
- else
- do let dateStr = C8.pack $ formatHTTPDateTime now
- writeIORef cache (now, dateStr)
- return dateStr
\ No newline at end of file
+ compServer res'
+ = case getHeader "Server" res' of
+ Nothing → return $ setHeader "Server" (cnfServerSoftware conf) res'
+ Just _ → return res'
+
+ compDate res'
+ = case getHeader "Date" res' of
+ Nothing → do date ← getCurrentDate
+ return $ setHeader "Date" date res'
+ Just _ → return res'
+
+getCurrentDate ∷ IO Ascii
+getCurrentDate = HTTP.format <$> getCurrentTime