]> gitweb @ CieloNegro.org - Lucu.git/blobdiff - Network/HTTP/Lucu/Resource.hs
Optimization
[Lucu.git] / Network / HTTP / Lucu / Resource.hs
index 44db0dc2a36633787f87707a0cc96ec72063cfd1..bf75de8a5f6b5bf4ad5b5a9060713282c833788c 100644 (file)
@@ -134,8 +134,9 @@ module Network.HTTP.Lucu.Resource
 import           Control.Concurrent.STM
 import           Control.Monad.Reader
 import           Data.Bits
-import           Data.ByteString.Base (LazyByteString)
-import qualified Data.ByteString.Lazy.Char8 as B
+import           Data.ByteString.Base (ByteString, LazyByteString(..))
+import qualified Data.ByteString.Char8 as C8
+import qualified Data.ByteString.Lazy.Char8 as L8
 import           Data.List
 import           Data.Maybe
 import           Network.HTTP.Lucu.Abortion
@@ -291,7 +292,7 @@ getQueryForm = do reqURI <- getRequestURI
 -- case-insensitive. Note that this action is not intended to be used
 -- so frequently: there should be actions like 'getContentType' for
 -- every common headers.
-getHeader :: String -> Resource (Maybe String)
+getHeader :: ByteString -> Resource (Maybe ByteString)
 getHeader name = name `seq`
                  do req <- getRequest
                     return $! H.getHeader name req
@@ -299,22 +300,22 @@ getHeader name = name `seq`
 -- |Get a list of 'Network.HTTP.Lucu.MIMEType.MIMEType' enumerated on
 -- header \"Accept\".
 getAccept :: Resource [MIMEType]
-getAccept = do acceptM <- getHeader "Accept"
+getAccept = do acceptM <- getHeader (C8.pack "Accept")
                case acceptM of
                  Nothing 
                      -> return []
                  Just accept
-                     -> case parseStr mimeTypeListP accept of
+                     -> case parse mimeTypeListP (LPS [accept]) of
                           (# Success xs, _ #) -> return xs
                           (# _         , _ #) -> abort BadRequest []
-                                                 (Just $ "Unparsable Accept: " ++ accept)
+                                                 (Just $ "Unparsable Accept: " ++ C8.unpack accept)
 
 -- |Get a list of @(contentCoding, qvalue)@ enumerated on header
 -- \"Accept-Encoding\". The list is sorted in descending order by
 -- qvalue.
 getAcceptEncoding :: Resource [(String, Maybe Double)]
 getAcceptEncoding
-    = do accEncM <- getHeader "Accept-Encoding"
+    = do accEncM <- getHeader (C8.pack "Accept-Encoding")
          case accEncM of
            Nothing
                -- HTTP/1.0 には Accept-Encoding が無い場合の規定が無い
@@ -325,36 +326,37 @@ getAcceptEncoding
                      case ver of
                        HttpVersion 1 0 -> return [("identity", Nothing)]
                        HttpVersion 1 1 -> return [("*"       , Nothing)]
-           Just ""
-               -- identity のみが許される。
-               -> return [("identity", Nothing)]
-           Just accEnc
-               -> case parseStr acceptEncodingListP accEnc of
-                    (# Success x, _ #) -> return $ reverse $ sortBy orderAcceptEncodings x
-                    (# _        , _ #) -> abort BadRequest []
-                                          (Just $ "Unparsable Accept-Encoding: " ++ accEnc)
+           Just value
+               -> if C8.null value then
+                      -- identity のみが許される。
+                      return [("identity", Nothing)]
+                  else
+                      case parse acceptEncodingListP (LPS [value]) of
+                        (# Success x, _ #) -> return $ reverse $ sortBy orderAcceptEncodings x
+                        (# _        , _ #) -> abort BadRequest []
+                                              (Just $ "Unparsable Accept-Encoding: " ++ C8.unpack value)
 
 -- |Check whether a given content-coding is acceptable.
 isEncodingAcceptable :: String -> Resource Bool
 isEncodingAcceptable coding
     = do accList <- getAcceptEncoding
          return (flip any accList $ \ (c, q) ->
-                     (c == "*" || c `noCaseEq` coding) && q /= Just 0)
+                     (c == "*" || C8.pack c `H.noCaseEq` C8.pack coding) && q /= Just 0)
 
 
 -- |Get the header \"Content-Type\" as
 -- 'Network.HTTP.Lucu.MIMEType.MIMEType'.
 getContentType :: Resource (Maybe MIMEType)
 getContentType
-    = do cTypeM <- getHeader "Content-Type"
+    = do cTypeM <- getHeader (C8.pack "Content-Type")
          case cTypeM of
            Nothing
                -> return Nothing
            Just cType
-               -> case parseStr mimeTypeP cType of
+               -> case parse mimeTypeP (LPS [cType]) of
                     (# Success t, _ #) -> return $ Just t
                     (# _        , _ #) -> abort BadRequest []
-                                          (Just $ "Unparsable Content-Type: " ++ cType)
+                                          (Just $ "Unparsable Content-Type: " ++ C8.unpack cType)
 
 
 {- ExaminingRequest 時に使用するアクション群 -}
@@ -380,7 +382,7 @@ foundEntity tag timeStamp
 
          method <- getMethod
          when (method == GET || method == HEAD)
-                  $ setHeader' "Last-Modified" $! formatHTTPDateTime timeStamp
+                  $ setHeader' (C8.pack "Last-Modified") (C8.pack $ formatHTTPDateTime timeStamp)
          when (method == POST)
                   $ abort InternalServerError []
                         (Just "Illegal computation of foundEntity for POST request.")
@@ -402,25 +404,27 @@ foundETag tag
       
          method <- getMethod
          when (method == GET || method == HEAD)
-                  $ setHeader' "ETag" $! show tag
+                  $ setHeader' (C8.pack "ETag") (C8.pack $ show tag)
          when (method == POST)
                   $ abort InternalServerError []
                         (Just "Illegal computation of foundETag for POST request.")
 
          -- If-Match があればそれを見る。
-         ifMatch <- getHeader "If-Match"
+         ifMatch <- getHeader (C8.pack "If-Match")
          case ifMatch of
-           Nothing   -> return ()
-           Just "*"  -> return ()
-           Just list -> case parseStr eTagListP list of
-                          (# Success tags, _ #)
-                              -- tags の中に一致するものが無ければ
-                              -- PreconditionFailed で終了。
-                              -> when (not $ any (== tag) tags)
-                                 $ abort PreconditionFailed []
-                                       $! Just ("The entity tag doesn't match: " ++ list)
-                          (# _, _ #)
-                              -> abort BadRequest [] $! Just ("Unparsable If-Match: " ++ fromJust ifMatch)
+           Nothing    -> return ()
+           Just value -> if value == C8.pack "*" then
+                             return ()
+                         else
+                             case parse eTagListP (LPS [value]) of
+                               (# Success tags, _ #)
+                                 -- tags の中に一致するものが無ければ
+                                 -- PreconditionFailed で終了。
+                                 -> when (not $ any (== tag) tags)
+                                    $ abort PreconditionFailed []
+                                          $! Just ("The entity tag doesn't match: " ++ C8.unpack value)
+                               (# _, _ #)
+                                   -> abort BadRequest [] $! Just ("Unparsable If-Match: " ++ C8.unpack value)
 
          let statusForNoneMatch = if method == GET || method == HEAD then
                                       NotModified
@@ -428,16 +432,18 @@ foundETag tag
                                       PreconditionFailed
 
          -- If-None-Match があればそれを見る。
-         ifNoneMatch <- getHeader "If-None-Match"
+         ifNoneMatch <- getHeader (C8.pack "If-None-Match")
          case ifNoneMatch of
-           Nothing   -> return ()
-           Just "*"  -> abort statusForNoneMatch [] $! Just ("The entity tag matches: *")
-           Just list -> case parseStr eTagListP list of
-                          (# Success tags, _ #)
-                              -> when (any (== tag) tags)
-                                 $ abort statusForNoneMatch [] $! Just ("The entity tag matches: " ++ list)
-                          (# _, _ #)
-                              -> abort BadRequest [] $! Just ("Unparsable If-None-Match: " ++ list)
+           Nothing    -> return ()
+           Just value -> if value == C8.pack "*" then
+                             abort statusForNoneMatch [] $! Just ("The entity tag matches: *")
+                         else
+                             case parse eTagListP (LPS [value]) of
+                               (# Success tags, _ #)
+                                   -> when (any (== tag) tags)
+                                      $ abort statusForNoneMatch [] $! Just ("The entity tag matches: " ++ C8.unpack value)
+                               (# _, _ #)
+                                   -> abort BadRequest [] $! Just ("Unparsable If-None-Match: " ++ C8.unpack value)
 
          driftTo GettingBody
 
@@ -458,7 +464,7 @@ foundTimeStamp timeStamp
 
          method <- getMethod
          when (method == GET || method == HEAD)
-                  $ setHeader' "Last-Modified" $! formatHTTPDateTime timeStamp
+                  $ setHeader' (C8.pack "Last-Modified") (C8.pack $ formatHTTPDateTime timeStamp)
          when (method == POST)
                   $ abort InternalServerError []
                         (Just "Illegal computation of foundTimeStamp for POST request.")
@@ -469,25 +475,25 @@ foundTimeStamp timeStamp
                                        PreconditionFailed
 
          -- If-Modified-Since があればそれを見る。
-         ifModSince <- getHeader "If-Modified-Since"
+         ifModSince <- getHeader (C8.pack "If-Modified-Since")
          case ifModSince of
-           Just str -> case parseHTTPDateTime str of
+           Just str -> case parseHTTPDateTime (LPS [str]) of
                          Just lastTime
                              -> when (timeStamp <= lastTime)
                                 $ abort statusForIfModSince []
-                                      $! Just ("The entity has not been modified since " ++ str)
+                                      $! Just ("The entity has not been modified since " ++ C8.unpack str)
                          Nothing
                              -> return () -- 不正な時刻は無視
            Nothing  -> return ()
 
          -- If-Unmodified-Since があればそれを見る。
-         ifUnmodSince <- getHeader "If-Unmodified-Since"
+         ifUnmodSince <- getHeader (C8.pack "If-Unmodified-Since")
          case ifUnmodSince of
-           Just str -> case parseHTTPDateTime str of
+           Just str -> case parseHTTPDateTime (LPS [str]) of
                          Just lastTime
                              -> when (timeStamp > lastTime)
                                 $ abort PreconditionFailed []
-                                      $! Just  ("The entity has not been modified since " ++ str)
+                                      $! Just  ("The entity has not been modified since " ++ C8.unpack str)
                          Nothing
                              -> return () -- 不正な時刻は無視
            Nothing  -> return ()
@@ -513,7 +519,7 @@ foundNoEntity msgM
 
          -- エンティティが存在しないと云ふ事は、"*" も含めたどのやうな
          -- If-Match: 條件も滿たさない。
-         ifMatch <- getHeader "If-Match"
+         ifMatch <- getHeader (C8.pack "If-Match")
          when (ifMatch /= Nothing)
                   $ abort PreconditionFailed [] msgM
 
@@ -538,7 +544,7 @@ foundNoEntity msgM
 -- use it whenever possible.
 input :: Int -> Resource String
 input limit = limit `seq`
-              inputLBS limit >>= return . B.unpack
+              inputLBS limit >>= return . L8.unpack
 
 
 -- | This is mostly the same as 'input' but is more
@@ -558,7 +564,7 @@ inputLBS limit
                         askForInput itr
                     else
                         do driftTo DecidingHeader
-                           return B.empty
+                           return L8.empty
          return chunk
     where
       askForInput :: Interaction -> Resource LazyByteString
@@ -584,7 +590,7 @@ inputLBS limit
                chunk <- liftIO $! atomically
                         $! do chunk       <- readItr itr itrReceivedBody id
                               chunkIsOver <- readItr itr itrReqChunkIsOver id
-                              if B.length chunk < fromIntegral actualLimit then
+                              if L8.length chunk < fromIntegral actualLimit then
                                   -- 要求された量に滿たなくて、まだ殘り
                                   -- があるなら再試行。
                                   unless chunkIsOver
@@ -597,7 +603,7 @@ inputLBS limit
                                              $ tooLarge actualLimit
                               -- 成功。itr 内にチャンクを置いたままにす
                               -- るとメモリの無駄になるので除去。
-                              writeItr itr itrReceivedBody B.empty
+                              writeItr itr itrReceivedBody L8.empty
                               return chunk
                driftTo DecidingHeader
                return chunk
@@ -623,7 +629,7 @@ inputLBS limit
 -- should use it whenever possible.
 inputChunk :: Int -> Resource String
 inputChunk limit = limit `seq`
-                   inputChunkLBS limit >>= return . B.unpack
+                   inputChunkLBS limit >>= return . L8.unpack
 
 
 -- | This is mostly the same as 'inputChunk' but is more
@@ -638,7 +644,7 @@ inputChunkLBS limit
                         askForInput itr
                     else
                         do driftTo DecidingHeader
-                           return B.empty
+                           return L8.empty
          return chunk
     where
       askForInput :: Interaction -> Resource LazyByteString
@@ -660,14 +666,14 @@ inputChunkLBS limit
                         $ do chunk <- readItr itr itrReceivedBody id
                              -- 要求された量に滿たなくて、まだ殘りがあ
                              -- るなら再試行。
-                             when (B.length chunk < fromIntegral actualLimit)
+                             when (L8.length chunk < fromIntegral actualLimit)
                                       $ do chunkIsOver <- readItr itr itrReqChunkIsOver id
                                            unless chunkIsOver
                                                       $ retry
                              -- 成功
-                             writeItr itr itrReceivedBody B.empty
+                             writeItr itr itrReceivedBody L8.empty
                              return chunk
-               when (B.null chunk)
+               when (L8.null chunk)
                         $ driftTo DecidingHeader
                return chunk
 
@@ -740,13 +746,13 @@ setStatus code
 -- 20 bytes long. In this case the client shall only accept the first
 -- 10 bytes of response body and thinks that the residual 10 bytes is
 -- a part of header of the next response.
-setHeader :: String -> String -> Resource ()
+setHeader :: ByteString -> ByteString -> Resource ()
 setHeader name value
     = name `seq` value `seq`
       driftTo DecidingHeader >> setHeader' name value
          
 
-setHeader' :: String -> String -> Resource ()
+setHeader' :: ByteString -> ByteString -> Resource ()
 setHeader' name value
     = name `seq` value `seq`
       do itr <- getInteraction
@@ -772,13 +778,13 @@ redirect code uri
 -- \"Content-Type\" to @mType@.
 setContentType :: MIMEType -> Resource ()
 setContentType mType
-    = setHeader "Content-Type" $! show mType
+    = setHeader (C8.pack "Content-Type") (C8.pack $ show mType)
 
 -- | Computation of @'setLocation' uri@ sets the response header
 -- \"Location\" to @uri@.
 setLocation :: URI -> Resource ()
 setLocation uri
-    = setHeader "Location" $ uriToString id uri $ ""
+    = setHeader (C8.pack "Location") (C8.pack $ uriToString id uri $ "")
 
 -- |Computation of @'setContentEncoding' codings@ sets the response
 -- header \"Content-Encoding\" to @codings@.
@@ -788,7 +794,7 @@ setContentEncoding codings
          let tr = case ver of
                     HttpVersion 1 0 -> unnormalizeCoding
                     HttpVersion 1 1 -> id
-         setHeader "Content-Encoding" $ joinWith ", " $ map tr codings
+         setHeader (C8.pack "Content-Encoding") (C8.pack $ joinWith ", " $ map tr codings)
 
 
 {- DecidingBody 時に使用するアクション群 -}
@@ -801,7 +807,7 @@ setContentEncoding codings
 -- Note that 'outputLBS' is more efficient than 'output' so you should
 -- use it whenever possible.
 output :: String -> Resource ()
-output str = outputLBS $! B.pack str
+output str = outputLBS $! L8.pack str
 {-# INLINE output #-}
 
 -- | This is mostly the same as 'output' but is more efficient.
@@ -818,7 +824,7 @@ outputLBS str = do outputChunkLBS str
 -- Note that 'outputChunkLBS' is more efficient than 'outputChunk' so
 -- you should use it whenever possible.
 outputChunk :: String -> Resource ()
-outputChunk str = outputChunkLBS $! B.pack str
+outputChunk str = outputChunkLBS $! L8.pack str
 {-# INLINE outputChunk #-}
 
 -- | This is mostly the same as 'outputChunk' but is more efficient.
@@ -839,30 +845,30 @@ outputChunkLBS str
          unless (discardBody)
                     $ sendChunks str limit
 
-         unless (B.null str)
+         unless (L8.null str)
                     $ liftIO $ atomically $
                       writeItr itr itrBodyIsNull False
     where
       -- チャンクの大きさは Config で制限されてゐる。もし例へば
-      -- "/dev/zero" を B.readFile して作った LazyByteString をそのまま
+      -- "/dev/zero" を L8.readFile して作った LazyByteString をそのまま
       -- ResponseWriter に渡したりすると大變な事が起こる。何故なら
       -- ResponseWriter は Transfer-Encoding: chunked の時、ヘッダを書
       -- く爲にチャンクの大きさを測る。
       sendChunks :: LazyByteString -> Int -> Resource ()
       sendChunks str limit
-          | B.null str = return ()
-          | otherwise  = do let (chunk, remaining) = B.splitAt (fromIntegral limit) str
-                            itr <- getInteraction
-                            liftIO $ atomically $ 
-                                   do buf <- readItr itr itrBodyToSend id
-                                      if B.null buf then
-                                          -- バッファが消化された
-                                          writeItr itr itrBodyToSend chunk
-                                        else
-                                          -- 消化されるのを待つ
-                                          retry
-                            -- 殘りのチャンクについて繰り返す
-                            sendChunks remaining limit
+          | L8.null str = return ()
+          | otherwise   = do let (chunk, remaining) = L8.splitAt (fromIntegral limit) str
+                             itr <- getInteraction
+                             liftIO $ atomically $ 
+                                    do buf <- readItr itr itrBodyToSend id
+                                       if L8.null buf then
+                                           -- バッファが消化された
+                                           writeItr itr itrBodyToSend chunk
+                                         else
+                                           -- 消化されるのを待つ
+                                           retry
+                             -- 殘りのチャンクについて繰り返す
+                             sendChunks remaining limit
 
 {-