]> gitweb @ CieloNegro.org - Lucu.git/blobdiff - Network/HTTP/Lucu/Postprocess.hs
Still working on Postprocess...
[Lucu.git] / Network / HTTP / Lucu / Postprocess.hs
index 6e8a5e6753b5bafdf6bbebc2448b76730e07afd3..49c95e809be046489bed306c83db6f77eab12baf 100644 (file)
@@ -1,26 +1,37 @@
+{-# LANGUAGE
+    BangPatterns
+  , DoAndIfThenElse
+  , 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
 
 {-
   
@@ -55,124 +66,107 @@ 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)
-                  $ abortSTM InternalServerError []
-                        $ Just ("The status was " ++ show sc ++ " but no Allow header.")
+         when (sc ≡ MethodNotAllowed ∧ getHeader "Allow" res ≡ Nothing)
+             $ abortSTM InternalServerError []
+             $ Just
+             $ A.toText ( "The status was "
+                          ⊕ printStatusCode 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 (sc /= NotModified ∧ isRedirection sc ∧ getHeader "Location" res ≡ Nothing)
+             $ abortSTM InternalServerError []
+             $ Just
+             $ A.toText ( "The status code was "
+                          ⊕ printStatusCode 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")
-                                 writeItr itr itrWillChunkBody True
-                 else
+                   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")
-
-               when (reqMethod req == HEAD || not canHaveBody)
-                        $ writeTVar (itrWillDiscardBody itr) True
+                   $ updateRes $ setHeader "Connection" "close"
 
-      readHeader :: Interaction -> ByteString -> STM (Maybe ByteString)
-      readHeader itr name
-          = itr `seq` name `seq`
-            readItr itr itrResponse $ getHeader name
+               when (reqMethod req ≡ HEAD ∨ not canHaveBody)
+                   $ writeTVar (itrWillDiscardBody itr) True
 
-      updateRes :: Interaction -> (Response -> Response) -> STM ()
-      updateRes itr updator 
-          = itr `seq` updator `seq`
-            updateItr itr itrResponse updator
+      readHeader ∷ CIAscii → STM (Maybe Ascii)
+      {-# INLINE readHeader #-}
+      readHeader = readItr itr itrResponse ∘ getHeader
 
+      updateRes ∷ (Response → Response) → STM ()
+      {-# INLINE updateRes #-}
+      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