wai 3.2.4 → 3.2.5
raw patch · 5 files changed
+319/−274 lines, 5 filesnew-uploaderPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Network.Wai: requestSendEarlyHints :: Request -> ResponseHeaders -> IO ()
+ Network.Wai.Internal: [requestSendEarlyHints] :: Request -> ResponseHeaders -> IO ()
- Network.Wai: type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
+ Network.Wai: type Application = Request -> Response -> IO ResponseReceived -> IO ResponseReceived
- Network.Wai: type StreamingBody = (Builder -> IO ()) -> IO () -> IO ()
+ Network.Wai: type StreamingBody = Builder -> IO () -> IO () -> IO ()
- Network.Wai.Internal: Request :: Method -> HttpVersion -> ByteString -> ByteString -> RequestHeaders -> Bool -> SockAddr -> [Text] -> Query -> IO ByteString -> Vault -> RequestBodyLength -> Maybe ByteString -> Maybe ByteString -> Maybe ByteString -> Maybe ByteString -> Request
+ Network.Wai.Internal: Request :: Method -> HttpVersion -> ByteString -> ByteString -> RequestHeaders -> Bool -> SockAddr -> [Text] -> Query -> IO ByteString -> Vault -> RequestBodyLength -> Maybe ByteString -> Maybe ByteString -> Maybe ByteString -> Maybe ByteString -> (ResponseHeaders -> IO ()) -> Request
- Network.Wai.Internal: type StreamingBody = (Builder -> IO ()) -> IO () -> IO ()
+ Network.Wai.Internal: type StreamingBody = Builder -> IO () -> IO () -> IO ()
Files
- ChangeLog.md +4/−0
- Network/Wai.hs +169/−144
- Network/Wai/Internal.hs +120/−109
- test/Network/WaiSpec.hs +23/−15
- wai.cabal +3/−6
ChangeLog.md view
@@ -1,5 +1,9 @@ # ChangeLog for wai +## 3.2.5++* Add a `requestSendEarlyHints :: [Header] -> IO ()` field to `Request` for sending `103 Early Hints` responses. [#1085](https://github.com/yesodweb/wai/pull/1085)+ ## 3.2.4 * Add helpers for modifying request headers: `modifyRequest` and `mapRequestHeaders`. [#710](https://github.com/yesodweb/wai/pull/710) [#952](https://github.com/yesodweb/wai/pull/952)
Network/Wai.hs view
@@ -1,116 +1,136 @@-{-|+-- Ignore deprecations, because this module needs to use the deprecated requestBody to construct a response.+{-# OPTIONS_GHC -fno-warn-deprecations #-} -This module defines a generic web application interface. It is a common-protocol between web servers and web applications.+-- |+--+-- This module defines a generic web application interface. It is a common+-- protocol between web servers and web applications.+--+-- The overriding design principles here are performance and generality. To+-- address performance, this library uses a streaming interface for request and+-- response bodies, paired with bytestring's 'Builder' type. The advantages of a+-- streaming API over lazy IO have been debated elsewhere and so will not be+-- addressed here. However, helper functions like 'responseLBS' allow you to+-- continue using lazy IO if you so desire.+--+-- Generality is achieved by removing many variables commonly found in similar+-- projects that are not universal to all servers. The goal is that the 'Request'+-- object contains only data which is meaningful in all circumstances.+--+-- Please remember when using this package that, while your application may+-- compile without a hitch against many different servers, there are other+-- considerations to be taken when moving to a new backend. For example, if you+-- transfer from a CGI application to a FastCGI one, you might suddenly find you+-- have a memory leak. Conversely, a FastCGI application would be well served to+-- preload all templates from disk when first starting; this would kill the+-- performance of a CGI application.+--+-- This package purposely provides very little functionality. You can find various+-- middlewares, backends and utilities on Hackage. Some of the most commonly used+-- include:+--+-- [warp] <http://hackage.haskell.org/package/warp>+--+-- [wai-extra] <http://hackage.haskell.org/package/wai-extra>+module Network.Wai (+ -- * Types+ Application,+ Middleware,+ ResponseReceived, -The overriding design principles here are performance and generality. To-address performance, this library uses a streaming interface for request and-response bodies, paired with bytestring's 'Builder' type. The advantages of a-streaming API over lazy IO have been debated elsewhere and so will not be-addressed here. However, helper functions like 'responseLBS' allow you to-continue using lazy IO if you so desire.+ -- * Request+ Request,+ defaultRequest,+ RequestBodyLength (..), -Generality is achieved by removing many variables commonly found in similar-projects that are not universal to all servers. The goal is that the 'Request'-object contains only data which is meaningful in all circumstances.+ -- ** Request accessors+ requestMethod,+ httpVersion,+ rawPathInfo,+ rawQueryString,+ requestHeaders,+ isSecure,+ remoteHost,+ pathInfo,+ queryString,+ getRequestBodyChunk,+ requestBody,+ vault,+ requestBodyLength,+ requestHeaderHost,+ requestHeaderRange,+ requestHeaderReferer,+ requestHeaderUserAgent,+ requestSendEarlyHints,+ -- $streamingRequestBodies+ strictRequestBody,+ consumeRequestBodyStrict,+ lazyRequestBody,+ consumeRequestBodyLazy, -Please remember when using this package that, while your application may-compile without a hitch against many different servers, there are other-considerations to be taken when moving to a new backend. For example, if you-transfer from a CGI application to a FastCGI one, you might suddenly find you-have a memory leak. Conversely, a FastCGI application would be well served to-preload all templates from disk when first starting; this would kill the-performance of a CGI application.+ -- ** Request modifiers+ setRequestBodyChunks,+ mapRequestHeaders, -This package purposely provides very little functionality. You can find various-middlewares, backends and utilities on Hackage. Some of the most commonly used-include:+ -- * Response+ Response,+ StreamingBody,+ FilePart (..), -[warp] <http://hackage.haskell.org/package/warp>+ -- ** Response composers+ responseFile,+ responseBuilder,+ responseLBS,+ responseStream,+ responseRaw, -[wai-extra] <http://hackage.haskell.org/package/wai-extra>+ -- ** Response accessors+ responseStatus,+ responseHeaders, --}--- Ignore deprecations, because this module needs to use the deprecated requestBody to construct a response.-{-# OPTIONS_GHC -fno-warn-deprecations #-}-module Network.Wai- (- -- * Types- Application- , Middleware- , ResponseReceived- -- * Request- , Request- , defaultRequest- , RequestBodyLength (..)- -- ** Request accessors- , requestMethod- , httpVersion- , rawPathInfo- , rawQueryString- , requestHeaders- , isSecure- , remoteHost- , pathInfo- , queryString- , getRequestBodyChunk- , requestBody- , vault- , requestBodyLength- , requestHeaderHost- , requestHeaderRange- , requestHeaderReferer- , requestHeaderUserAgent- -- $streamingRequestBodies- , strictRequestBody- , consumeRequestBodyStrict- , lazyRequestBody- , consumeRequestBodyLazy- -- ** Request modifiers- , setRequestBodyChunks- , mapRequestHeaders- -- * Response- , Response- , StreamingBody- , FilePart (..)- -- ** Response composers- , responseFile- , responseBuilder- , responseLBS- , responseStream- , responseRaw- -- ** Response accessors- , responseStatus- , responseHeaders- -- ** Response modifiers- , responseToStream- , mapResponseHeaders- , mapResponseStatus- -- * Middleware composition- , ifRequest- , modifyRequest- , modifyResponse- ) where+ -- ** Response modifiers+ responseToStream,+ mapResponseHeaders,+ mapResponseStatus, -import Data.ByteString.Builder (Builder, byteString, lazyByteString)-import Control.Monad (unless)-import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as L+ -- * Middleware composition+ ifRequest,+ modifyRequest,+ modifyResponse,+) where++import Control.Monad (unless)+import qualified Data.ByteString as B+import Data.ByteString.Builder (+ Builder,+ byteString,+ lazyByteString,+ )+import qualified Data.ByteString.Lazy as L+import Data.ByteString.Lazy.Internal (defaultChunkSize) import qualified Data.ByteString.Lazy.Internal as LI-import Data.ByteString.Lazy.Internal (defaultChunkSize)-import Data.Function (fix)-import qualified Network.HTTP.Types as H-import Network.Socket (SockAddr (SockAddrInet))-import Network.Wai.Internal-import qualified System.IO as IO-import System.IO.Unsafe (unsafeInterleaveIO)+import Data.Function (fix)+import qualified Network.HTTP.Types as H+import Network.Socket (SockAddr (SockAddrInet))+import Network.Wai.Internal+import qualified System.IO as IO+import System.IO.Unsafe (unsafeInterleaveIO) ---------------------------------------------------------------- -- | Creating 'Response' from a file. --+-- Server implementations like @warp@ might disregard the t'Status' when+-- using 'responseFile', since the server might rework the response based on+-- headers or presence/absence of the file.+-- @warp@ does not do do any extra processing when sending file parts, though,+-- so be mindful of how each server implementation handles file responses.+--+-- /The above was written when @warp-3.4.14@ was the newest version/+-- -- @since 2.0.0-responseFile :: H.Status -> H.ResponseHeaders -> FilePath -> Maybe FilePart -> Response+responseFile+ :: H.Status -> H.ResponseHeaders -> FilePath -> Maybe FilePart -> Response responseFile = ResponseFile -- | Creating 'Response' from 'Builder'.@@ -170,10 +190,11 @@ -- and response headers to depend on the scarce resource. -- -- @since 3.0.0-responseStream :: H.Status- -> H.ResponseHeaders- -> StreamingBody- -> Response+responseStream+ :: H.Status+ -> H.ResponseHeaders+ -> StreamingBody+ -> Response responseStream = ResponseStream -- | Create a response for a raw application. This is useful for \"upgrade\"@@ -187,9 +208,10 @@ -- @responseRaw@, behavior is undefined. -- -- @since 2.1.0-responseRaw :: (IO B.ByteString -> (B.ByteString -> IO ()) -> IO ())- -> Response- -> Response+responseRaw+ :: (IO B.ByteString -> (B.ByteString -> IO ()) -> IO ())+ -> Response+ -> Response responseRaw = ResponseRaw ----------------------------------------------------------------@@ -198,28 +220,29 @@ -- -- @since 1.2.0 responseStatus :: Response -> H.Status-responseStatus (ResponseFile s _ _ _) = s-responseStatus (ResponseBuilder s _ _ ) = s-responseStatus (ResponseStream s _ _ ) = s-responseStatus (ResponseRaw _ res ) = responseStatus res+responseStatus (ResponseFile s _ _ _) = s+responseStatus (ResponseBuilder s _ _) = s+responseStatus (ResponseStream s _ _) = s+responseStatus (ResponseRaw _ res) = responseStatus res -- | Accessing 'H.ResponseHeaders' in 'Response'. -- -- @since 2.0.0 responseHeaders :: Response -> H.ResponseHeaders-responseHeaders (ResponseFile _ hs _ _) = hs-responseHeaders (ResponseBuilder _ hs _ ) = hs-responseHeaders (ResponseStream _ hs _ ) = hs-responseHeaders (ResponseRaw _ res) = responseHeaders res+responseHeaders (ResponseFile _ hs _ _) = hs+responseHeaders (ResponseBuilder _ hs _) = hs+responseHeaders (ResponseStream _ hs _) = hs+responseHeaders (ResponseRaw _ res) = responseHeaders res -- | Converting the body information in 'Response' to a 'StreamingBody'. -- -- @since 3.0.0-responseToStream :: Response- -> ( H.Status- , H.ResponseHeaders- , (StreamingBody -> IO a) -> IO a- )+responseToStream+ :: Response+ -> ( H.Status+ , H.ResponseHeaders+ , (StreamingBody -> IO a) -> IO a+ ) responseToStream (ResponseStream s h b) = (s, h, ($ b)) responseToStream (ResponseFile s h fp (Just part)) = ( s@@ -239,7 +262,7 @@ ( s , h , \withBody -> IO.withBinaryFile fp IO.ReadMode $ \handle ->- withBody $ \sendChunk _flush -> fix $ \loop -> do+ withBody $ \sendChunk _flush -> fix $ \loop -> do bs <- B.hGetSome handle defaultChunkSize unless (B.null bs) $ do sendChunk $ byteString bs@@ -252,7 +275,8 @@ -- | Apply the provided function to the response header list of the Response. -- -- @since 3.0.3.0-mapResponseHeaders :: (H.ResponseHeaders -> H.ResponseHeaders) -> Response -> Response+mapResponseHeaders+ :: (H.ResponseHeaders -> H.ResponseHeaders) -> Response -> Response mapResponseHeaders f (ResponseFile s h b1 b2) = ResponseFile s (f h) b1 b2 mapResponseHeaders f (ResponseBuilder s h b) = ResponseBuilder s (f h) b mapResponseHeaders f (ResponseStream s h b) = ResponseStream s (f h) b@@ -282,32 +306,33 @@ -- (putStrLn \"Cleaning up\") -- (respond $ responseLBS status200 [] \"Hello World\") -- @-type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived-+type Application =+ Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived -- | A default, blank request. -- -- @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 B.empty- , vault = mempty- , requestBodyLength = KnownLength 0- , requestHeaderHost = Nothing- , requestHeaderRange = Nothing- , requestHeaderReferer = Nothing- , requestHeaderUserAgent = Nothing- }-+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 B.empty+ , vault = mempty+ , requestBodyLength = KnownLength 0+ , requestHeaderHost = Nothing+ , requestHeaderRange = Nothing+ , requestHeaderReferer = Nothing+ , requestHeaderUserAgent = Nothing+ , requestSendEarlyHints = \_ -> return ()+ } -- | A @Middleware@ is a component that sits between the server and application. --@@ -451,7 +476,6 @@ -- However, modifying the response (especially the response body) is not trivial, -- so in order to get a sense of how to do it (dealing with the type of 'responseToStream'), -- it’s best to look at an example, for example <https://hackage.haskell.org/package/wai-extra/docs/src/Network.Wai.Middleware.Gzip.html#gzip the GZIP middleware of wai-extra>.- type Middleware = Application -> Application -- | Apply a function that modifies a request as a 'Middleware'@@ -472,7 +496,7 @@ ifRequest :: (Request -> Bool) -> Middleware -> Middleware ifRequest rpred middle app req | rpred req = middle app req- | otherwise = app req+ | otherwise = app req -- $streamingRequestBodies --@@ -576,5 +600,6 @@ -- | Apply the provided function to the request header list of the 'Request'. -- -- @since 3.2.4-mapRequestHeaders :: (H.RequestHeaders -> H.RequestHeaders) -> Request -> Request-mapRequestHeaders f request = request { requestHeaders = f (requestHeaders request) }+mapRequestHeaders+ :: (H.RequestHeaders -> H.RequestHeaders) -> Request -> Request+mapRequestHeaders f request = request{requestHeaders = f (requestHeaders request)}
Network/Wai/Internal.hs view
@@ -1,95 +1,108 @@-{-# OPTIONS_HADDOCK not-home #-} {-# LANGUAGE RecordWildCards #-}+{-# OPTIONS_HADDOCK not-home #-}+ -- | Internal constructors and helper functions. Note that no guarantees are -- given for stability of these interfaces. module Network.Wai.Internal where -import Data.ByteString.Builder (Builder)-import qualified Data.ByteString as B-import Data.Text (Text)-import Data.Typeable (Typeable)-import Data.Vault.Lazy (Vault)-import Data.Word (Word64)-import qualified Network.HTTP.Types as H-import Network.Socket (SockAddr)-import Data.List (intercalate)+import qualified Data.ByteString as B+import Data.ByteString.Builder (Builder)+import Data.List (intercalate)+import Data.Text (Text)+import Data.Vault.Lazy (Vault)+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.-{-# DEPRECATED requestBody "requestBody's name is misleading because it only gets a partial chunk of the body. Use getRequestBodyChunk instead when getting the field, and setRequestBodyChunks when setting the field." #-}-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.- --- -- Middlewares and routing tools should not modify this raw value, as it may- -- be used for such things as creating redirect destinations by applications.- -- Instead, if you are writing a middleware or routing framework, modify the- -- @pathInfo@ instead. This is the approach taken by systems like Yesod- -- subsites.- --- -- /Note/: At the time of writing this documentation, there is at least one- -- system (@Network.Wai.UrlMap@ from @wai-extra@) that does not follow the- -- above recommendation. Therefore, it is recommended that you test the- -- behavior of your application when using @rawPathInfo@ and any form of- -- library that might modify the @Request@.- , 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 headers (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- -- | Get the next chunk of the body. Returns 'B.empty' when the- -- body is fully consumed. Since 3.2.2, this is deprecated in favor of 'getRequestBodyChunk'.- , requestBody :: 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- -- | The value of the Referer header in a HTTP request.- --- -- @since 3.2.0- , requestHeaderReferer :: Maybe B.ByteString- -- | The value of the User-Agent header in a HTTP request.- --- -- @since 3.2.0- , requestHeaderUserAgent :: Maybe B.ByteString- }- deriving (Typeable)+{-# DEPRECATED+ requestBody+ "requestBody's name is misleading because it only gets a partial chunk of the body. Use getRequestBodyChunk instead when getting the field, and setRequestBodyChunks when setting the field."+ #-} +data Request = Request+ { requestMethod :: H.Method+ -- ^ Request method such as GET.+ , httpVersion :: H.HttpVersion+ -- ^ HTTP version such as 1.1.+ , rawPathInfo :: B.ByteString+ -- ^ 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.+ --+ -- Middlewares and routing tools should not modify this raw value, as it may+ -- be used for such things as creating redirect destinations by applications.+ -- Instead, if you are writing a middleware or routing framework, modify the+ -- @pathInfo@ instead. This is the approach taken by systems like Yesod+ -- subsites.+ --+ -- /Note/: At the time of writing this documentation, there is at least one+ -- system (@Network.Wai.UrlMap@ from @wai-extra@) that does not follow the+ -- above recommendation. Therefore, it is recommended that you test the+ -- behavior of your application when using @rawPathInfo@ and any form of+ -- library that might modify the @Request@.+ , rawQueryString :: 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.+ , requestHeaders :: H.RequestHeaders+ -- ^ A list of headers (a pair of key and value) in an HTTP request.+ , isSecure :: Bool+ -- ^ 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.+ , remoteHost :: SockAddr+ -- ^ The client\'s host information.+ , pathInfo :: [Text]+ -- ^ Path info in individual pieces - the URL without a hostname/port and+ -- without a query string, split on forward slashes.+ , queryString :: H.Query+ -- ^ Parsed query string information.+ , requestBody :: IO B.ByteString+ -- ^ Get the next chunk of the body. Returns 'B.empty' when the+ -- body is fully consumed. Since 3.2.2, this is deprecated in favor of 'getRequestBodyChunk'.+ , vault :: Vault+ -- ^ A location for arbitrary data to be shared by applications and middleware.+ , requestBodyLength :: RequestBodyLength+ -- ^ The size of the request body. In the case of a chunked request body,+ -- this may be unknown.+ --+ -- @since 1.4.0+ , requestHeaderHost :: Maybe B.ByteString+ -- ^ The value of the Host header in a HTTP request.+ --+ -- @since 2.0.0+ , requestHeaderRange :: Maybe B.ByteString+ -- ^ The value of the Range header in a HTTP request.+ --+ -- @since 2.0.0+ , requestHeaderReferer :: Maybe B.ByteString+ -- ^ The value of the Referer header in a HTTP request.+ --+ -- @since 3.2.0+ , requestHeaderUserAgent :: Maybe B.ByteString+ -- ^ The value of the User-Agent header in a HTTP request.+ --+ -- @since 3.2.0+ , requestSendEarlyHints :: H.ResponseHeaders -> IO ()+ -- ^ Send a @103 Early Hints@ informational response carrying the given+ -- headers, ahead of the final response. This lets a client (typically over+ -- HTTP\/2) start fetching resources named in @Link@ headers while the final+ -- response is still being produced.+ --+ -- A handler (e.g. Warp over HTTP\/2) that supports early hints installs an+ -- action here; otherwise it is a no-op.+ --+ -- @since 3.2.5+ }+ -- | Get the next chunk of the body. Returns 'B.empty' when the -- body is fully consumed. --@@ -106,35 +119,33 @@ -- @since 3.2.4 setRequestBodyChunks :: IO B.ByteString -> Request -> Request setRequestBodyChunks requestBody r =- r {requestBody = requestBody}+ r{requestBody = requestBody} instance Show Request where- show Request{..} = "Request {" ++ intercalate ", " [a ++ " = " ++ b | (a,b) <- fields] ++ "}"- where- fields =- [("requestMethod",show requestMethod)- ,("httpVersion",show httpVersion)- ,("rawPathInfo",show rawPathInfo)- ,("rawQueryString",show rawQueryString)- ,("requestHeaders",show requestHeaders)- ,("isSecure",show isSecure)- ,("remoteHost",show remoteHost)- ,("pathInfo",show pathInfo)- ,("queryString",show queryString)- ,("requestBody","<IO ByteString>")- ,("vault","<Vault>")- ,("requestBodyLength",show requestBodyLength)- ,("requestHeaderHost",show requestHeaderHost)- ,("requestHeaderRange",show requestHeaderRange)- ]-+ show Request{..} = "Request {" ++ intercalate ", " [a ++ " = " ++ b | (a, b) <- fields] ++ "}"+ where+ fields =+ [ ("requestMethod", show requestMethod)+ , ("httpVersion", show httpVersion)+ , ("rawPathInfo", show rawPathInfo)+ , ("rawQueryString", show rawQueryString)+ , ("requestHeaders", show requestHeaders)+ , ("isSecure", show isSecure)+ , ("remoteHost", show remoteHost)+ , ("pathInfo", show pathInfo)+ , ("queryString", show queryString)+ , ("requestBody", "<IO ByteString>")+ , ("vault", "<Vault>")+ , ("requestBodyLength", show requestBodyLength)+ , ("requestHeaderHost", show requestHeaderHost)+ , ("requestHeaderRange", show requestHeaderRange)+ ] data Response = ResponseFile H.Status H.ResponseHeaders FilePath (Maybe FilePart) | ResponseBuilder H.Status H.ResponseHeaders Builder | ResponseStream H.Status H.ResponseHeaders StreamingBody | ResponseRaw (IO B.ByteString -> (B.ByteString -> IO ()) -> IO ()) Response- deriving Typeable -- | Represents a streaming HTTP response body. It's a function of two -- parameters; the first parameter provides a means of sending another chunk of@@ -148,7 +159,7 @@ -- not be known. -- -- @since 1.4.0-data RequestBodyLength = ChunkedBody | KnownLength Word64 deriving Show+data RequestBodyLength = ChunkedBody | KnownLength Word64 deriving (Show) -- | Information on which part to be sent. -- Sophisticated application handles Range (and If-Range) then@@ -156,10 +167,11 @@ -- -- @since 0.4.0 data FilePart = FilePart- { filePartOffset :: Integer+ { filePartOffset :: Integer , filePartByteCount :: Integer- , filePartFileSize :: Integer- } deriving Show+ , filePartFileSize :: Integer+ }+ deriving (Show) -- | A special datatype to indicate that the WAI handler has received the -- response. This is to avoid the need for Rank2Types in the definition of@@ -170,4 +182,3 @@ -- -- @since 3.0.0 data ResponseReceived = ResponseReceived- deriving Typeable
test/Network/WaiSpec.hs view
@@ -1,15 +1,16 @@ {-# LANGUAGE LambdaCase #-}+ module Network.WaiSpec (spec) where -import Test.Hspec-import Test.Hspec.QuickCheck (prop)-import Network.Wai-import Data.Word (Word8)-import Data.IORef+import Control.Monad (forM_) import qualified Data.ByteString as S-import qualified Data.ByteString.Lazy as L import Data.ByteString.Builder (Builder, toLazyByteString, word8)-import Control.Monad (forM_)+import qualified Data.ByteString.Lazy as L+import Data.IORef+import Data.Word (Word8)+import Network.Wai+import Test.Hspec+import Test.Hspec.QuickCheck (prop) spec :: Spec spec = do@@ -29,8 +30,11 @@ body <- getBody $ responseLBS undefined undefined $ L.pack bytes body `shouldBe` S.pack bytes prop "responseBuilder" $ \bytes -> do- body <- getBody $ responseBuilder undefined undefined- $ mconcat $ map word8 bytes+ body <-+ getBody $+ responseBuilder undefined undefined $+ mconcat $+ map word8 bytes body `shouldBe` S.pack bytes prop "responseStream" $ \chunks -> do body <- getBody $ responseStream undefined undefined $ \sendChunk _ ->@@ -47,11 +51,15 @@ let total = S.length totalBS offset = abs offset' `mod` total count = abs count' `mod` (total - offset)- body <- getBody $ responseFile undefined undefined fp $ Just FilePart- { filePartOffset = fromIntegral offset- , filePartByteCount = fromIntegral count- , filePartFileSize = fromIntegral total- }+ body <-+ getBody $+ responseFile undefined undefined fp $+ Just+ FilePart+ { filePartOffset = fromIntegral offset+ , filePartByteCount = fromIntegral count+ , filePartFileSize = fromIntegral total+ } let expected = S.take count $ S.drop offset totalBS body `shouldBe` expected describe "lazyRequestBody" $ do@@ -76,4 +84,4 @@ flip setRequestBodyChunks defaultRequest $ atomicModifyIORef ref $ \case [] -> ([], S.empty)- x:y -> (y, x)+ x : y -> (y, x)
wai.cabal view
@@ -1,6 +1,6 @@ Cabal-Version: >=1.10 Name: wai-Version: 3.2.4+Version: 3.2.5 Synopsis: Web Application Interface. Description: Provides a common protocol for communication between web applications and web servers. .@@ -17,7 +17,8 @@ Source-repository head type: git- location: git://github.com/yesodweb/wai.git+ location: https://github.com/yesodweb/wai.git+ subdir: wai Library default-language: Haskell2010@@ -44,7 +45,3 @@ , bytestring other-modules: Network.WaiSpec build-tool-depends: hspec-discover:hspec-discover--source-repository head- type: git- location: git://github.com/yesodweb/wai.git