packages feed

wai 0.2.2.1 → 0.3.0

raw patch · 4 files changed

+42/−282 lines, 4 filesdep +blaze-builderdep +enumeratordep +networkdep −directorydep ~base

Dependencies added: blaze-builder, enumerator, network

Dependencies removed: directory

Dependency ranges changed: base

Files

Network/Wai.hs view
@@ -43,8 +43,10 @@     , mkCIByteString       -- ** Request header names     , RequestHeader+    , RequestHeaders       -- ** Response header names     , ResponseHeader+    , ResponseHeaders       -- ** Response status code     , Status (..)     , status200@@ -57,17 +59,15 @@     , status404     , status405     , status500-      -- ** Response body-    , ResponseBody (..)-      -- ** Source-    , Source (..)-      -- * Enumerator-    , Enumerator (..)       -- * WAI interface     , Request (..)     , Response (..)+    , ResponseEnumerator+    , responseEnumerator     , Application     , Middleware+      -- * Response body smart constructors+    , responseLBS     ) where  import qualified Data.ByteString as B@@ -76,6 +76,12 @@ import Data.Char (toLower) import Data.String (IsString (..)) import Data.Typeable (Typeable)+import Data.Enumerator (Iteratee, ($$), joinI, run_)+import qualified Data.Enumerator as E+import Data.Enumerator.IO (enumFile)+import Blaze.ByteString.Builder (Builder, fromByteString, fromLazyByteString)+import Data.Data (Data)+import Network.Socket (SockAddr)  -- | HTTP request method. Since the HTTP protocol allows arbitrary request -- methods, we leave this open as a 'B.ByteString'. Please note the request@@ -111,6 +117,7 @@     { ciOriginal :: !B.ByteString     , ciLowerCase :: !B.ByteString     }+    deriving (Data, Typeable)  -- | Convert a regular bytestring to a case-insensitive bytestring. mkCIByteString :: B.ByteString -> CIByteString@@ -118,6 +125,8 @@  instance Show CIByteString where     show = show . ciOriginal+instance Read CIByteString where+    readsPrec i = map (\(x, y) -> (mkCIByteString x, y)) . readsPrec i instance Eq CIByteString where     x == y = ciLowerCase x == ciLowerCase y instance Ord CIByteString where@@ -128,10 +137,12 @@ -- | Headers sent from the client to the server. Note that this is a -- case-insensitive string, as the HTTP spec specifies. type RequestHeader = CIByteString+type RequestHeaders = [(RequestHeader, B.ByteString)]  -- | Headers sent from the server to the client. Note that this is a -- case-insensitive string, as the HTTP spec specifies. type ResponseHeader = CIByteString+type ResponseHeaders = [(ResponseHeader, B.ByteString)]  -- | HTTP status code; a combination of the integral code and a status message. -- Equality is determined solely on the basis of the integral code.@@ -181,60 +192,6 @@ status500 :: Status status500 = Status 500 $ B8.pack "Internal Server Error" --- | This is a source for 'B.ByteString's. It is a function (wrapped in a--- newtype) that will return Nothing if the data has been completely consumed,--- or return the next 'B.ByteString' from the source along with a new 'Source'--- to continue reading from.------ Be certain not to reuse a 'Source'! It might work fine with some--- implementations of 'Source', while causing bugs with others.------ This datatype is used by WAI to represent a request body. We choose this--- over an enumerator in that it gives the application power over control flow.--- This not only makes it easier to use in many situations, but also allows--- implementation of some features such as a backtracking parser which doesn't--- read the entire body into memory.-newtype Source = Source { runSource :: IO (Maybe (B.ByteString, Source)) }---- | An enumerator is a data producer. It takes two arguments: a function to--- enumerate over (the iteratee) and an accumulating parameter. As the--- enumerator produces output, it calls the iteratee, thereby avoiding the need--- to allocate large amounts of memory for storing the entire piece of data.------ Normally in Haskell, we can achieve the same results with laziness. For--- example, an inifinite list does not require inifinite memory storage; we--- simply get away with thunks. However, when operating in the IO monad, we do--- not have this luxury. There are other approaches, such as lazy I\/O. If you--- would like to program in this manner, please see--- "Network.Wai.Enumerator", in particular toLBS.------ That said, let's address the details of this particular enumerator--- implementation. You'll notice that the iteratee is a function that takes two--- arguments and returns an 'Either' value. The second argument is simply the--- piece of data generated by the enumerator. The 'Either' value at the end is--- a means to alert the enumerator whether to continue or not. If it returns--- 'Left', then the enumeration should cease. If it returns 'Right', it should--- continue.------ The accumulating parameter (a) has meaning only to the iteratee; the--- enumerator simply passes it around. The enumerator itself also returns an--- 'Either' value; a 'Right' means the enumerator ran to completion, while a--- 'Left' indicates early termination was requested by the iteratee.------ 'Enumerator's are not required to be resumable. That is to say, the--- 'Enumerator' may only be called once. While this requirement puts a bit of a--- strain on the caller in some situations, it saves a large amount of--- complication- and thus performance- on the producer.------ In WAI, an Enumerator is used to represent the response body. We have--- specifically chosen one of the simplest representations of an enumerator to--- avoid coding complication and performance overhead.-newtype Enumerator = Enumerator { runEnumerator :: forall a.-              (a -> B.ByteString -> IO (Either a a))-                 -> a-                 -> IO (Either a a)-}- -- | Information on the request sent by the client. This abstracts away the -- details of the underlying implementation. data Request = Request@@ -252,45 +209,35 @@   ,  requestHeaders :: [(RequestHeader, B.ByteString)]   -- ^ Was this request made over an SSL connection?   ,  isSecure       :: Bool-  ,  requestBody    :: Source   -- ^ Log the given line in some method; how this is accomplished is   -- server-dependant.   ,  errorHandler   :: String -> IO ()   -- | The client\'s host information.-  ,  remoteHost     :: B.ByteString+  ,  remoteHost     :: SockAddr   }   deriving Typeable --- | The response body returned to the server from the application. We provide--- three separate constructors as optimizations:------ * 'ResponseEnumerator' is the most general type, allowing constant-memory--- production of a response, even in the presence of interleaved I\/O actions.------ * 'ResponseFile' serves a static file from the filesystem. Many servers use--- a sendfile system call to optimize this type of serving, making this a huge--- performance gain.------ * 'ResponseLBS'. Often times, we wish to return a response that includes no--- interleaved I\/O. In this case, we can use Haskell's natural laziness to our--- advantage, and represent the response as a lazy bytestring.-data ResponseBody = ResponseFile FilePath-                  | ResponseEnumerator Enumerator-                  | ResponseLBS L.ByteString+data Response+    = ResponseFile Status ResponseHeaders FilePath+    | ResponseBuilder Status ResponseHeaders Builder+    | ResponseEnumerator (forall a. ResponseEnumerator a)   deriving Typeable -data Response = Response-  { status          :: Status-  , responseHeaders :: [(ResponseHeader, B.ByteString)]-  -- | A common optimization is to use the sendfile system call when sending-  -- files from the disk. This datatype facilitates this optimization; if-  -- 'Left' is returned, the server will send the file from the disk by-  -- whatever means it wishes. If 'Right', it will call the 'Enumerator'.-  , responseBody    :: ResponseBody-  }-  deriving Typeable+type ResponseEnumerator a =+    (Status -> ResponseHeaders -> Iteratee Builder IO a) -> IO a -type Application = Request -> IO Response+responseEnumerator :: Response -> ResponseEnumerator a+responseEnumerator (ResponseEnumerator e) f = e f+responseEnumerator (ResponseFile s h fp) f =+    run_ $ enumFile fp $$ joinI $ E.map fromByteString $$ f s h+responseEnumerator (ResponseBuilder s h b) f = run_ $ do+    E.yield () $ E.Chunks [b]+    f s h++responseLBS :: Status -> ResponseHeaders -> L.ByteString -> Response+responseLBS s h = ResponseBuilder s h . fromLazyByteString++type Application = Request -> Iteratee B.ByteString IO Response  -- | Middleware is a component that sits between the server and application. It -- can do such tasks as GZIP encoding or response caching. What follows is the
− Network/Wai/Enumerator.hs
@@ -1,141 +0,0 @@-{-# LANGUAGE Rank2Types #-}--- | A collection of utility functions for dealing with 'Enumerator's.-module Network.Wai.Enumerator-    ( -- * Utilities-      mapE-      -- * Conversions-    , -- ** Lazy byte strings-      toLBS-    , fromLBS-    , fromLBS'-      -- ** Source-    , toSource-      -- ** Handle-    , fromHandle-    , fromHandleFinally-      -- ** FilePath-    , fromFile-    , fromFileFinally-    , fromTempFile-    , fromResponseBody-    ) where--import Network.Wai (Enumerator (..), Source (..), ResponseBody (..))-import qualified Network.Wai.Source as Source-import qualified Data.ByteString.Lazy as L-import qualified Data.ByteString as B-import System.Directory (removeFile)-import System.IO (withBinaryFile, IOMode (ReadMode), Handle, hIsEOF)-import Data.ByteString.Lazy.Internal (defaultChunkSize)-import Control.Concurrent (forkIO)-import Control.Concurrent.MVar-import Control.Exception-import Control.Monad ((<=<))---- | Performs a specified conversion on each 'B.ByteString' output by an--- enumerator.-mapE :: (B.ByteString -> B.ByteString) -> Enumerator -> Enumerator-mapE f (Enumerator e) = Enumerator $ \iter -> e (iter' iter) where-    iter' iter a = iter a . f---- | This uses 'unsafeInterleaveIO' to lazily read from an enumerator. All--- normal lazy I/O warnings apply. In addition, since it is based on--- 'toSource', please observe all precautions for that function.-toLBS :: Enumerator -> IO L.ByteString-toLBS = Source.toLBS <=< toSource---- | This function safely converts a lazy bytestring into an enumerator.-fromLBS :: L.ByteString -> Enumerator-fromLBS lbs = Enumerator $ \iter a0 -> helper iter a0 $ L.toChunks lbs where-    helper _ a [] = return $ Right a-    helper iter a (x:xs) = do-        ea <- iter a x-        case ea of-            Left a' -> return $ Left a'-            Right a' -> helper iter a' xs---- | Same as 'fromLBS', but the lazy bytestring is in the IO monad. This allows--- you to lazily read a file into memory, perform some mapping on the data and--- convert it into an enumerator.-fromLBS' :: IO L.ByteString -> Enumerator-fromLBS' lbs' = Enumerator $ \iter a0 -> lbs' >>= \lbs ->-    runEnumerator (fromLBS lbs) iter a0---- | This function uses another thread to convert an 'Enumerator' to a--- 'Source'. In essence, this allows you to write code which \"pulls\" instead--- of code which is pushed to. While this can be a useful technique, some--- caveats apply:------ * It will be more resource heavy than using the 'Enumerator' directly.------ * You *must* consume all input. If you do not, then the other thread will be--- deadlocked.-toSource :: Enumerator -> IO Source-toSource (Enumerator e) = do-    buff <- newEmptyMVar-    _ <- forkIO $ e (helper buff) () >> putMVar buff Nothing-    return $ source buff-      where-        helper :: MVar (Maybe B.ByteString)-               -> ()-               -> B.ByteString-               -> IO (Either () ())-        helper buff _ bs = do-            putMVar buff $ Just bs-            return $ Right ()-        source :: MVar (Maybe B.ByteString)-               -> Source-        source mmbs = Source $ do-            mbs <- takeMVar mmbs-            case mbs of-                Nothing -> do-                    -- By putting Nothing back in, the source can be called-                    -- again without causing a deadlock.-                    putMVar mmbs Nothing-                    return Nothing-                Just bs -> return $ Just (bs, source mmbs)---- | Read a chunk of data from the given 'Handle' at a time. We use--- 'defaultChunkSize' from the bytestring package to determine the largest--- chunk to take.-fromHandle :: Handle -> Enumerator-fromHandle h = Enumerator $ \iter a -> do-    eof <- hIsEOF h-    if eof-        then return $ Right a-        else do-            bs <- B.hGet h defaultChunkSize-            ea' <- iter a bs-            case ea' of-                Left a' -> return $ Left a'-                Right a' -> runEnumerator (fromHandle h) iter a'---- | Wrapper around fromHandle to perform an action after EOF or an exception.-fromHandleFinally :: Handle -> IO a -> Enumerator-fromHandleFinally h onEOF = Enumerator $ \iter a0 ->-                            finally (runEnumerator (fromHandle h) iter a0)-                                    onEOF---- | A little wrapper around 'fromHandle' which first opens a file for reading.-fromFile :: FilePath -> Enumerator-fromFile fp = Enumerator $ \iter a0 -> withBinaryFile fp ReadMode $ \h ->-    runEnumerator (fromHandle h) iter a0---- | Wrapper around fromFile to perform an action after the file is closed.-fromFileFinally :: FilePath -> IO a -> Enumerator-fromFileFinally fp onClose = Enumerator $ \iter a0 ->-                             finally (runEnumerator (fromFile fp) iter a0)-                                     onClose---- | Enumerator to read and remove a file. Being based on 'fromFileFinally', it--- ensures the file is removed, even in the presence of exceptions.-fromTempFile :: FilePath -> Enumerator-fromTempFile fp = fromFileFinally fp $ removeFile fp---- | Since the response body is defined as a 'ResponseBody', this function--- simply reduces the whole value to an enumerator. This can be convenient for--- server implementations not optimizing file sending.-fromResponseBody :: ResponseBody -> Enumerator-fromResponseBody (ResponseEnumerator e) = e-fromResponseBody (ResponseLBS lbs) = fromLBS lbs-fromResponseBody (ResponseFile fp) = fromFile fp
− Network/Wai/Source.hs
@@ -1,46 +0,0 @@-module Network.Wai.Source-    (-    -- * Conversions-      toEnumerator-    , toLBS-    , fromLBS-    ) where--import Network.Wai-import qualified Data.ByteString.Lazy as L-import System.IO.Unsafe (unsafeInterleaveIO)---- | This function safely converts a 'Source' (where you pull data) to an--- 'Enumerator' (which pushes the data to you). There should be no significant--- performance impact from its use, and it uses no unsafe functions.-toEnumerator :: Source -> Enumerator-toEnumerator source0 = Enumerator $ helper source0 where-    helper source iter a = do-        next <- runSource source-        case next of-            Nothing -> return $ Right a-            Just (bs, source') -> do-                res <- iter a bs-                case res of-                    Left a' -> return $ Left a'-                    Right a' -> helper source' iter a'---- | Uses lazy I\/O (via 'unsafeInterleaveIO') to provide a lazy interface to--- the given 'Source'. Normal lazy I\/O warnings apply.-toLBS :: Source -> IO L.ByteString-toLBS source0 = L.fromChunks `fmap` helper source0 where-    helper source = unsafeInterleaveIO $ do-        next <- runSource source-        case next of-            Nothing -> return []-            Just (bs, source') -> do-                rest <- helper source'-                return $ bs : rest---- | Convert a lazy bytestring to a 'Source'. This operation does not request lazy I\/O.-fromLBS :: L.ByteString -> Source-fromLBS =-    go . L.toChunks-  where-    go [] = Source $ return Nothing-    go (x:xs) = Source $ return $ Just (x, go xs)
wai.cabal view
@@ -1,5 +1,5 @@ Name:                wai-Version:             0.2.2.1+Version:             0.3.0 Synopsis:            Web Application Interface. Description:         Provides a common protocol for communication between web aplications and web servers. License:             BSD3@@ -17,10 +17,10 @@     location:        git://github.com/snoyberg/wai.git  Library-  Build-Depends:     base >= 3 && < 5,-                     bytestring >= 0.9 && < 0.10,-                     directory >= 1.0 && < 1.2+  Build-Depends:     base                   >= 3        && < 5+                   , bytestring             >= 0.9      && < 0.10+                   , blaze-builder          >= 0.2      && < 0.3+                   , enumerator             >= 0.4      && < 0.5+                   , network                >= 2.2      && < 2.4   Exposed-modules:   Network.Wai-                     Network.Wai.Enumerator-                     Network.Wai.Source   ghc-options:       -Wall