packages feed

wai-extra 0.4.3 → 0.4.4

raw patch · 4 files changed

+110/−29 lines, 4 filesdep +data-defaultdep +zlib-enumPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: data-default, zlib-enum

API changes (from Hackage documentation)

+ Network.Wai.Middleware.Gzip: GzipCacheFolder :: FilePath -> GzipFiles
+ Network.Wai.Middleware.Gzip: GzipCompress :: GzipFiles
+ Network.Wai.Middleware.Gzip: GzipIgnore :: GzipFiles
+ Network.Wai.Middleware.Gzip: data GzipFiles
+ Network.Wai.Middleware.Gzip: data GzipSettings
+ Network.Wai.Middleware.Gzip: def :: Default a => a
+ Network.Wai.Middleware.Gzip: defaultCheckMime :: ByteString -> Bool
+ Network.Wai.Middleware.Gzip: gzip' :: GzipSettings -> Middleware
+ Network.Wai.Middleware.Gzip: gzipFiles :: GzipSettings -> GzipFiles
+ Network.Wai.Middleware.Gzip: instance Default GzipSettings
+ Network.Wai.Middleware.Gzip: instance Eq GzipFiles
+ Network.Wai.Middleware.Gzip: instance Read GzipFiles
+ Network.Wai.Middleware.Gzip: instance Show GzipFiles
- Network.Wai.Middleware.Rewrite: autoHtmlRewrite :: String -> [Text] -> IO [Text]
+ Network.Wai.Middleware.Rewrite: autoHtmlRewrite :: String -> [Text] -> RequestHeaders -> IO [Text]
- Network.Wai.Middleware.Rewrite: rewrite :: ([Text] -> IO [Text]) -> Middleware
+ Network.Wai.Middleware.Rewrite: rewrite :: ([Text] -> RequestHeaders -> IO [Text]) -> Middleware

Files

Network/Wai/Middleware/Gzip.hs view
@@ -14,15 +14,44 @@ -- Automatic gzip compression of responses. -- ----------------------------------------------------------module Network.Wai.Middleware.Gzip (gzip) where+module Network.Wai.Middleware.Gzip+    ( gzip+    , gzip'+    , GzipSettings+    , gzipFiles+    , GzipFiles (..)+    , def+    , defaultCheckMime+    ) where  import Network.Wai import Network.Wai.Zlib import Data.Maybe (fromMaybe)-import Data.Enumerator (($$), joinI)+import Data.Enumerator (($$), joinI, (=$), run)+import Data.Enumerator.Binary (enumFile, iterHandle) import qualified Data.ByteString.Char8 as S8 import qualified Data.ByteString as S+import Data.Default+import Network.HTTP.Types (Status, Header)+import Control.Monad.IO.Class (liftIO)+import qualified Codec.Zlib.Enum as CZE+import qualified System.IO as SIO+import System.Directory (doesFileExist, createDirectoryIfMissing) +data GzipSettings = GzipSettings+    { gzipFiles :: GzipFiles+    , gzipCheckMime :: S.ByteString -> Bool+    }++data GzipFiles = GzipIgnore | GzipCompress | GzipCacheFolder FilePath+    deriving (Show, Eq, Read)++instance Default GzipSettings where+    def = GzipSettings GzipIgnore defaultCheckMime++defaultCheckMime :: S.ByteString -> Bool+defaultCheckMime = S8.isPrefixOf "text/"+ -- | Use gzip to compress the body of the response. -- -- Analyzes the \"Accept-Encoding\" header from the client to determine@@ -31,37 +60,77 @@ -- Possible future enhancements: -- -- * Only compress if the response is above a certain size.------ * I've read that IE can\'t support compression for Javascript files. gzip :: Bool -- ^ should we gzip files?      -> Middleware-gzip files app env = do+gzip files = gzip' def+    { gzipFiles = if files then GzipCompress else GzipIgnore+    }++gzip' :: GzipSettings -> Middleware+gzip' set app env = do     res <- app env-    return $-        case res of-            ResponseFile{} | not files -> res-            _ -> if "gzip" `elem` enc && not isMSIE6-                    then ResponseEnumerator $ compressE $ responseEnumerator res-                    else res+    case res of+        ResponseFile{} | gzipFiles set == GzipIgnore -> return res+        _ -> if "gzip" `elem` enc && not isMSIE6+                then+                    case (res, gzipFiles set) of+                        (ResponseFile s hs file Nothing, GzipCacheFolder cache) ->+                            case lookup "content-type" hs of+                                Just m+                                    | gzipCheckMime set m -> liftIO $ compressFile s hs file cache+                                _ -> return res+                        _ -> return $ ResponseEnumerator $ compressE set $ responseEnumerator res+                else return res   where     enc = fromMaybe [] $ (splitCommas . S8.unpack)                     `fmap` lookup "Accept-Encoding" (requestHeaders env)     ua = fromMaybe "" $ lookup "user-agent" $ requestHeaders env     isMSIE6 = "MSIE 6" `S.isInfixOf` ua -compressE :: (forall a. ResponseEnumerator a)+compressFile :: Status -> [Header] -> FilePath -> FilePath -> IO Response+compressFile s hs file cache = do+    e <- doesFileExist tmpfile+    if e+        then onSucc+        else do+            createDirectoryIfMissing True cache+            x <- SIO.withFile tmpfile SIO.WriteMode $ \h ->+                   run+                 $ enumFile file+                $$ CZE.gzip+                =$ iterHandle h+            either (const onErr) (const onSucc) x+  where+    onSucc = return $ ResponseFile s (fixHeaders hs) tmpfile Nothing+    onErr = return $ ResponseFile s hs file Nothing+    tmpfile = cache ++ '/' : map safe file+    safe c+        | 'A' <= c && c <= 'Z' = c+        | 'a' <= c && c <= 'z' = c+        | '0' <= c && c <= '9' = c+    safe '-' = '-'+    safe '_' = '_'+    safe _ = '_'++compressE :: GzipSettings           -> (forall a. ResponseEnumerator a)-compressE re f =+          -> (forall a. ResponseEnumerator a)+compressE set re f =     re f'     --e s hs'   where     f' s hs =-        joinI $ compress $$ f s hs'-      where-        -- Remove Content-Length header, since we will certainly have a-        -- different length after gzip compression.-        hs' = ("Content-Encoding", "gzip") : filter notLength hs-        notLength (x, _) = x /= "content-length"+        case lookup "content-type" hs of+            Just m | gzipCheckMime set m -> joinI $ compress $$ f s (fixHeaders hs)+            _ -> f s hs++-- Remove Content-Length header, since we will certainly have a+-- different length after gzip compression.+fixHeaders :: [Header] -> [Header]+fixHeaders =+    (("Content-Encoding", "gzip") :) . filter notLength+  where+    notLength (x, _) = x /= "content-length"  splitCommas :: String -> [String] splitCommas [] = []
Network/Wai/Middleware/Rewrite.hs view
@@ -7,21 +7,27 @@ import System.Directory (doesFileExist) import Control.Monad.IO.Class (liftIO) import Data.Text (Text, unpack, pack)+import qualified Data.Text.Encoding as TE+import qualified Data.Text as T+import Network.HTTP.Types as H + -- | rewrite based on your own conversion rules -- Example usage: rewrite (autoHtmlRewrite "static")-rewrite :: ([Text] -> IO [Text]) -> Middleware+rewrite :: ([Text] -> H.RequestHeaders -> IO [Text]) -> Middleware rewrite convert app req = do-  newPathInfo <- liftIO $ convert $ pathInfo req-  app req { pathInfo = newPathInfo }+  newPathInfo <- liftIO $ convert (pathInfo req) (requestHeaders req)+  let rawPInfo = TE.encodeUtf8 $ T.intercalate "/" newPathInfo+  app req { pathInfo = newPathInfo, rawPathInfo =  rawPInfo }  -- | example rewriter---   implements 2 rules for static html re-writes+-- We don't recommend normally checking the file system on every request - this is just an example.+-- Implements 2 rules for static html re-writes --   1) for a directory foo/, check for foo/index.html --   2) for a non-directory bar, check for bar.html---   if the file exists, do the rewrite-autoHtmlRewrite :: String -> [Text] -> IO [Text]-autoHtmlRewrite staticDir pieces' = do+-- Do the rewrite only if the html file exists.+autoHtmlRewrite :: String -> [Text] -> H.RequestHeaders -> IO [Text]+autoHtmlRewrite staticDir pieces' _ = do     fe <- doesFileExist $ staticDir ++ "/" ++ reWritePath     return $ if fe then map pack reWritePieces else pieces'   where
runtests.hs view
@@ -255,7 +255,9 @@     assertBody "{\"foo\":\"bar\"}" sres3  gzipApp :: Application-gzipApp = gzip True $ const $ return $ responseLBS status200 [] "test"+gzipApp = gzip True $ const $ return $ responseLBS status200+    [("Content-Type", "text/plain")]+    "test"  caseGzip :: Assertion caseGzip = flip runSession gzipApp $ do
wai-extra.cabal view
@@ -1,5 +1,5 @@ Name:                wai-extra-Version:             0.4.3+Version:             0.4.4 Synopsis:            Provides some basic WAI handlers and middleware. Description:         The goal here is to provide common features without many dependencies. License:             BSD3@@ -33,7 +33,9 @@                      blaze-builder >= 0.2.1.3 && < 0.4,                      http-types >= 0.6 && < 0.7,                      text >= 0.5 && < 1.0,-                     case-insensitive >= 0.2 && < 0.4+                     case-insensitive >= 0.2 && < 0.4,+                     zlib-enum >= 0.2.1 && < 0.3,+                     data-default >= 0.3 && < 0.4   Exposed-modules:   Network.Wai.Handler.CGI                      Network.Wai.Middleware.AcceptOverride                      Network.Wai.Middleware.Autohead@@ -71,6 +73,8 @@                    , zlib-bindings                    , blaze-builder-enumerator                    , blaze-builder+                   , zlib-enum+                   , data-default  source-repository head   type:     git