wai 1.4.1 → 2.0.0
raw patch · 3 files changed
+244/−111 lines, 3 filesdep ~conduit
Dependency ranges changed: conduit
Files
- Network/Wai.hs +133/−109
- Network/Wai/Internal.hs +108/−0
- wai.cabal +3/−2
Network/Wai.hs view
@@ -1,7 +1,5 @@ {-# LANGUAGE Rank2Types #-} {-# LANGUAGE CPP #-}-{-# LANGUAGE ExistentialQuantification #-}-{-# LANGUAGE DeriveDataTypeable #-} {-| This module defines a generic web application interface. It is a common@@ -37,89 +35,69 @@ -} module Network.Wai- ( -- * WAI interface- Request (..)- , Response (..)- , responseSource- , Application+ (+ -- * Ttypes+ Application , Middleware- , FilePart (..)+ -- * Request+ , Request+ , defaultRequest , RequestBodyLength (..)+ -- ** Request accessors+ , requestMethod+ , httpVersion+ , rawPathInfo+ , rawQueryString+ , requestHeaders+ , isSecure+ , remoteHost+ , pathInfo+ , queryString+ , requestBody+ , vault+ , requestBodyLength+ , requestHeaderHost+ , requestHeaderRange , lazyRequestBody- -- * Response body smart constructors+ -- * Response+ , Response+ , FilePart (..)+ , WithSource+ -- ** Response composers+ , responseFile+ , responseBuilder , responseLBS+ , responseSource+ , responseSourceBracket+ -- * Response accessors , responseStatus+ , responseHeaders+ , responseToSource ) where -import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as L-import Data.Typeable (Typeable)-import qualified Data.Conduit as C-import qualified Data.Conduit.List as CL-import Data.Conduit.Lazy (lazyConsume)-import qualified Data.Conduit.Binary as CB-import Blaze.ByteString.Builder (Builder, fromLazyByteString)-import Network.Socket (SockAddr)-import qualified Network.HTTP.Types as H-import Data.Text (Text)-import Data.ByteString.Lazy.Char8 () -- makes it easier to use responseLBS-import Blaze.ByteString.Builder (fromByteString)-#if MIN_VERSION_vault(0,3,0)-import Data.Vault.Lazy (Vault)-#else-import Data.Vault (Vault)-#endif-import Data.Word (Word64)+import Blaze.ByteString.Builder (Builder, fromLazyByteString)+import Blaze.ByteString.Builder (fromByteString)+import Control.Exception (bracket, bracketOnError)+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as L+import Data.ByteString.Lazy.Char8 ()+import qualified Data.Conduit as C+import qualified Data.Conduit.Binary as CB+import Data.Conduit.Lazy (lazyConsume)+import qualified Data.Conduit.List as CL+import Data.Monoid (mempty)+import qualified Network.HTTP.Types as H+import Network.Socket (SockAddr (SockAddrInet))+import Network.Wai.Internal+import qualified System.IO as IO --- | Information on the request sent by the client. This abstracts away the--- details of the underlying implementation.-data Request = Request- { requestMethod :: H.Method- , httpVersion :: H.HttpVersion- -- | Extra path information sent by the client. The meaning varies slightly- -- depending on backend; in a standalone server setting, this is most likely- -- all information after the domain name. In a CGI application, this would be- -- the information following the path to the CGI executable itself.- -- Do not modify this raw value- modify pathInfo instead.- , rawPathInfo :: B.ByteString- -- | If no query string was specified, this should be empty. This value- -- /will/ include the leading question mark.- -- Do not modify this raw value- modify queryString instead.- , rawQueryString :: B.ByteString- -- | Generally the host requested by the user via the Host request header.- -- Backends are free to provide alternative values as necessary. This value- -- should not be used to construct URLs.- , serverName :: B.ByteString- -- | The listening port that the server received this request on. It is- -- possible for a server to listen on a non-numeric port (i.e., Unix named- -- socket), in which case this value will be arbitrary. Like 'serverName',- -- this value should not be used in URL construction.- , serverPort :: Int- , requestHeaders :: H.RequestHeaders- -- | Was this request made over an SSL connection?- --- -- This value should /not/ be used, and will be removed in future revisions- -- of WAI. There is no meaningful way that a backend can indicate whether the- -- request is actually over a secure channel, due to issues of reverse- -- proxying.- , isSecure :: Bool- -- | The client\'s host information.- , remoteHost :: SockAddr- -- | Path info in individual pieces- the url without a hostname/port and without a query string, split on forward slashes,- , pathInfo :: [Text]- -- | Parsed query string information- , queryString :: H.Query- , requestBody :: C.Source (C.ResourceT IO) B.ByteString- -- | A location for arbitrary data to be shared by applications and middleware.- , vault :: Vault- -- | The size of the request body. In the case of a chunked request body, this may be unknown.- --- -- Since 1.4.0- , requestBodyLength :: RequestBodyLength- }- deriving (Typeable)+---------------------------------------------------------------- --- |+-- | Creating 'Response' from a file.+responseFile :: H.Status -> H.ResponseHeaders -> FilePath -> Maybe FilePart -> Response+responseFile = ResponseFile++-- | Creating 'Response' from 'Builder'. -- -- Some questions and answers about the usage of 'Builder' here: --@@ -142,41 +120,72 @@ -- -- A3. You can force blaze-builder to output a ByteString before it is an -- optimal size by sending a flush command.-data Response- = ResponseFile H.Status H.ResponseHeaders FilePath (Maybe FilePart)- | ResponseBuilder H.Status H.ResponseHeaders Builder- | ResponseSource H.Status H.ResponseHeaders (C.Source (C.ResourceT IO) (C.Flush Builder))- deriving Typeable+responseBuilder :: H.Status -> H.ResponseHeaders -> Builder -> Response+responseBuilder = ResponseBuilder +-- | Creating 'Response' from 'L.ByteString'. This is a wrapper for+-- 'responseBuilder'.+responseLBS :: H.Status -> H.ResponseHeaders -> L.ByteString -> Response+responseLBS s h = ResponseBuilder s h . fromLazyByteString++-- | Creating 'Response' from 'C.Source'.+responseSource :: H.Status -> H.ResponseHeaders -> C.Source IO (C.Flush Builder) -> Response+responseSource st hs src = ResponseSource st hs ($ src)++-- | Creating 'Response' with allocated resource safely released.+--+-- * The first argument is an action to allocate resource.+--+-- * The second argument is a function to release the resource.+--+-- * The third argument is a function to create+-- ('H.Status','H.ResponseHeaders','C.Source' 'IO' ('C.Flush' 'Builder'))+-- from the resource.+responseSourceBracket :: IO a+ -> (a -> IO ())+ -> (a -> IO (H.Status+ ,H.ResponseHeaders+ ,C.Source IO (C.Flush Builder)))+ -> IO Response+responseSourceBracket setup teardown action =+ bracketOnError setup teardown $ \resource -> do+ (st,hdr,src) <- action resource+ return $ ResponseSource st hdr $ \f ->+ bracket (return resource) teardown (\_ -> f src)++----------------------------------------------------------------++-- | Accessing 'H.Status' in 'Response'. responseStatus :: Response -> H.Status-responseStatus rsp =- case rsp of- ResponseFile s _ _ _ -> s- ResponseBuilder s _ _ -> s- ResponseSource s _ _ -> s+responseStatus (ResponseFile s _ _ _) = s+responseStatus (ResponseBuilder s _ _ ) = s+responseStatus (ResponseSource s _ _ ) = s -data FilePart = FilePart- { filePartOffset :: Integer- , filePartByteCount :: Integer- } deriving Show+-- | Accessing 'H.Status' in 'Response'.+responseHeaders :: Response -> H.ResponseHeaders+responseHeaders (ResponseFile _ hs _ _) = hs+responseHeaders (ResponseBuilder _ hs _ ) = hs+responseHeaders (ResponseSource _ hs _ ) = hs -responseSource :: Response -> (H.Status, H.ResponseHeaders, C.Source (C.ResourceT IO) (C.Flush Builder))-responseSource (ResponseSource s h b) = (s, h, b)-responseSource (ResponseFile s h fp (Just part)) =- (s, h, sourceFilePart part fp C.$= CL.map (C.Chunk . fromByteString))-responseSource (ResponseFile s h fp Nothing) =- (s, h, CB.sourceFile fp C.$= CL.map (C.Chunk . fromByteString))-responseSource (ResponseBuilder s h b) =- (s, h, CL.sourceList [C.Chunk b])+-- | Converting the body information in 'Response' to 'Source'.+responseToSource :: Response+ -> (H.Status, H.ResponseHeaders, WithSource IO (C.Flush Builder) b)+responseToSource (ResponseSource s h b) = (s, h, b)+responseToSource (ResponseFile s h fp (Just part)) =+ (s, h, \f -> IO.withFile fp IO.ReadMode $ \handle -> f $ sourceFilePart handle part C.$= CL.map (C.Chunk . fromByteString))+responseToSource (ResponseFile s h fp Nothing) =+ (s, h, \f -> IO.withFile fp IO.ReadMode $ \handle -> f $ CB.sourceHandle handle C.$= CL.map (C.Chunk . fromByteString))+responseToSource (ResponseBuilder s h b) =+ (s, h, ($ CL.sourceList [C.Chunk b])) -sourceFilePart :: C.MonadResource m => FilePart -> FilePath -> C.Source m B.ByteString-sourceFilePart (FilePart offset count) fp =- CB.sourceFileRange fp (Just offset) (Just count)+sourceFilePart :: IO.Handle -> FilePart -> C.Source IO B.ByteString+sourceFilePart handle (FilePart offset count _) =+ CB.sourceHandleRange handle (Just offset) (Just count) -responseLBS :: H.Status -> H.ResponseHeaders -> L.ByteString -> Response-responseLBS s h = ResponseBuilder s h . fromLazyByteString+---------------------------------------------------------------- -type Application = Request -> C.ResourceT IO Response+-- | The WAI application.+type Application = Request -> 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@@ -194,15 +203,30 @@ -- middleware takes a function which consumes the session information as well. type Middleware = Application -> Application --- | The size of the request body. In the case of chunked bodies, the size will--- not be known.+-- | A default, blank request. ----- Since 1.4.0-data RequestBodyLength = ChunkedBody | KnownLength Word64+-- Since 2.0.0+defaultRequest :: Request+defaultRequest = Request+ { requestMethod = H.methodGet+ , httpVersion = H.http10+ , rawPathInfo = B.empty+ , rawQueryString = B.empty+ , requestHeaders = []+ , isSecure = False+ , remoteHost = SockAddrInet 0 0+ , pathInfo = []+ , queryString = []+ , requestBody = return ()+ , vault = mempty+ , requestBodyLength = KnownLength 0+ , requestHeaderHost = Nothing+ , requestHeaderRange = Nothing+ } -- | Get the request body as a lazy ByteString. This uses lazy I\/O under the -- surface, and therefore all typical warnings regarding lazy I/O apply. -- -- Since 1.4.1-lazyRequestBody :: Request -> C.ResourceT IO L.ByteString+lazyRequestBody :: Request -> IO L.ByteString lazyRequestBody = fmap L.fromChunks . lazyConsume . requestBody
+ Network/Wai/Internal.hs view
@@ -0,0 +1,108 @@+{-# OPTIONS_HADDOCK not-home #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE RankNTypes #-}+-- | Internal constructors and helper functions. Note that no guarantees are+-- given for stability of these interfaces.+module Network.Wai.Internal where++import Blaze.ByteString.Builder (Builder)+import qualified Data.ByteString as B+import qualified Data.Conduit as C+import Data.Text (Text)+import Data.Typeable (Typeable)+#if MIN_VERSION_vault(0,3,0)+import Data.Vault.Lazy (Vault)+#else+import Data.Vault (Vault)+#endif+import Data.Word (Word64)+import qualified Network.HTTP.Types as H+import Network.Socket (SockAddr)++-- | Information on the request sent by the client. This abstracts away the+-- details of the underlying implementation.+data Request = Request {+ -- | Request method such as GET.+ requestMethod :: H.Method+ -- | HTTP version such as 1.1.+ , httpVersion :: H.HttpVersion+ -- | Extra path information sent by the client. The meaning varies slightly+ -- depending on backend; in a standalone server setting, this is most likely+ -- all information after the domain name. In a CGI application, this would be+ -- the information following the path to the CGI executable itself.+ -- Do not modify this raw value- modify pathInfo instead.+ , rawPathInfo :: B.ByteString+ -- | If no query string was specified, this should be empty. This value+ -- /will/ include the leading question mark.+ -- Do not modify this raw value- modify queryString instead.+ , rawQueryString :: B.ByteString+ -- | A list of header (a pair of key and value) in an HTTP request.+ , requestHeaders :: H.RequestHeaders+ -- | Was this request made over an SSL connection?+ --+ -- Note that this value will /not/ tell you if the client originally made+ -- this request over SSL, but rather whether the current connection is SSL.+ -- The distinction lies with reverse proxies. In many cases, the client will+ -- connect to a load balancer over SSL, but connect to the WAI handler+ -- without SSL. In such a case, @isSecure@ will be @False@, but from a user+ -- perspective, there is a secure connection.+ , isSecure :: Bool+ -- | The client\'s host information.+ , remoteHost :: SockAddr+ -- | Path info in individual pieces- the url without a hostname/port and without a query string, split on forward slashes,+ , pathInfo :: [Text]+ -- | Parsed query string information+ , queryString :: H.Query+ -- | A request body provided as 'Source'.+ , requestBody :: C.Source IO B.ByteString+ -- | A location for arbitrary data to be shared by applications and middleware.+ , vault :: Vault+ -- | The size of the request body. In the case of a chunked request body, this may be unknown.+ --+ -- Since 1.4.0+ , requestBodyLength :: RequestBodyLength+ -- | The value of the Host header in a HTTP request.+ --+ -- Since 2.0.0+ , requestHeaderHost :: Maybe B.ByteString+ -- | The value of the Range header in a HTTP request.+ --+ -- Since 2.0.0+ , requestHeaderRange :: Maybe B.ByteString+ }+ deriving (Typeable)++-- | The strange structure of the third field or ResponseSource is to allow for+-- exception-safe resource allocation. As an example:+--+-- > app :: Application+-- > app _ = return $ ResponseSource status200 [] $ \f -> bracket+-- > (putStrLn "Allocation" >> return 5)+-- > (\i -> putStrLn $ "Cleaning up: " ++ show i)+-- > (\_ -> f $ do+-- > yield $ Chunk $ fromByteString "Hello "+-- > yield $ Chunk $ fromByteString "World!")+data Response+ = ResponseFile H.Status H.ResponseHeaders FilePath (Maybe FilePart)+ | ResponseBuilder H.Status H.ResponseHeaders Builder+ | ResponseSource H.Status H.ResponseHeaders (forall b. WithSource IO (C.Flush Builder) b)+ deriving Typeable++-- | Auxiliary type for 'ResponseSource'.+type WithSource m a b = (C.Source m a -> m b) -> m b++-- | The size of the request body. In the case of chunked bodies, the size will+-- not be known.+--+-- Since 1.4.0+data RequestBodyLength = ChunkedBody | KnownLength Word64++-- | Information on which part to be sent.+-- Sophisticated application handles Range (and If-Range) then+-- create 'FilePart'.+data FilePart = FilePart+ { filePartOffset :: Integer+ , filePartByteCount :: Integer+ , filePartFileSize :: Integer+ } deriving Show
wai.cabal view
@@ -1,5 +1,5 @@ Name: wai-Version: 1.4.1+Version: 2.0.0 Synopsis: Web Application Interface. Description: Provides a common protocol for communication between web applications and web servers. License: MIT@@ -20,11 +20,12 @@ Build-Depends: base >= 4 && < 5 , bytestring >= 0.9.1.4 , blaze-builder >= 0.2.1.4 && < 0.4- , conduit >= 0.5 && < 1.1+ , conduit >= 1.0.8 && < 1.1 , network >= 2.2.1.5 , http-types >= 0.7 , text >= 0.7 , transformers >= 0.2.2 , vault >= 0.1 && < 0.4 Exposed-modules: Network.Wai+ Network.Wai.Internal ghc-options: -Wall