on master help sign in
Network.Wai.Internal
commit2c681c80ea501718542b291932eba30fb6552ba7 authorMichael Snoyman <michael@snoyman.com> dateMon 19 Aug 2013, 10:56 (13 years ago) parentf71fa94
3 files changed+149−110
wai/Network/Wai.hs+46−110 wai/Network/Wai/Internal.hs+102 wai/wai.cabal+1
wai/Network/Wai.hs+46−110
@@ -1,6 +1,4 @@
11{-# LANGUAGE Rank2Types #-}
2−{-# LANGUAGE ExistentialQuantification #-}
3−{-# LANGUAGE DeriveDataTypeable #-}
42{-|
53
64This module defines a generic web application interface. It is a common
@@ -37,108 +35,57 @@ include:
3735-}
3836module Network.Wai
3937 ( -- * WAI interface
40− Request (..)
41− , Response (..)
38+ -- ** Request
39+ Request
40+ , requestMethod
41+ , httpVersion
42+ , rawPathInfo
43+ , rawQueryString
44+ , requestHeaders
45+ , isSecure
46+ , remoteHost
47+ , pathInfo
48+ , queryString
49+ , requestBody
50+ , vault
51+ , requestBodyLength
52+ , resourceInternalState
53+ -- ** Response
54+ , Response
55+ , responseFile
56+ , responseBuilder
4257 , responseSource
58+ , responseLBS
59+ -- ** Other types
4360 , Application
4461 , Middleware
4562 , FilePart (..)
4663 , RequestBodyLength (..)
47− -- * Response body smart constructors
48− , responseLBS
64+ -- ** Helper functions
65+ , responseToSource
4966 , responseStatus
5067 ) where
5168
52−import qualified Data.ByteString as B
53−import qualified Data.ByteString.Lazy as L
54−import Data.Typeable (Typeable)
55−import qualified Data.Conduit as C
56−import qualified Data.Conduit.List as CL
57−import qualified Data.Conduit.Binary as CB
58−import Blaze.ByteString.Builder (Builder, fromLazyByteString)
59−import Network.Socket (SockAddr)
60−import qualified Network.HTTP.Types as H
61−import Data.Text (Text)
62−import Data.ByteString.Lazy.Char8 () -- makes it easier to use responseLBS
63−import Blaze.ByteString.Builder (fromByteString)
64−import Data.Vault (Vault)
65−import Data.Word (Word64)
69+import Blaze.ByteString.Builder (Builder, fromLazyByteString)
70+import Blaze.ByteString.Builder (fromByteString)
6671import qualified Control.Monad.Trans.Resource as Res
72+import qualified Data.ByteString as B
73+import qualified Data.ByteString.Lazy as L
74+import Data.ByteString.Lazy.Char8 ()
75+import qualified Data.Conduit as C
76+import qualified Data.Conduit.Binary as CB
77+import qualified Data.Conduit.List as CL
78+import qualified Network.HTTP.Types as H
79+import Network.Wai.Internal
6780
68−-- | Information on the request sent by the client. This abstracts away the
69−-- details of the underlying implementation.
70−data Request = Request
71− { requestMethod :: H.Method
72− , httpVersion :: H.HttpVersion
73− -- | Extra path information sent by the client. The meaning varies slightly
74− -- depending on backend; in a standalone server setting, this is most likely
75− -- all information after the domain name. In a CGI application, this would be
76− -- the information following the path to the CGI executable itself.
77− -- Do not modify this raw value- modify pathInfo instead.
78− , rawPathInfo :: B.ByteString
79− -- | If no query string was specified, this should be empty. This value
80− -- /will/ include the leading question mark.
81− -- Do not modify this raw value- modify queryString instead.
82− , rawQueryString :: B.ByteString
83− , requestHeaders :: H.RequestHeaders
84− -- | Was this request made over an SSL connection?
85− --
86− -- Note that this value will /not/ tell you if the client originally made
87− -- this request over SSL, but rather whether the current connection is SSL.
88− -- The distinction lies with reverse proxies. In many cases, the client will
89− -- connect to a load balancer over SSL, but connect to the WAI handler
90− -- without SSL. In such a case, @isSecure@ will be @False@, but from a user
91− -- perspective, there is a secure connection.
92− , isSecure :: Bool
93− -- | The client\'s host information.
94− , remoteHost :: SockAddr
95− -- | Path info in individual pieces- the url without a hostname/port and without a query string, split on forward slashes,
96− , pathInfo :: [Text]
97− -- | Parsed query string information
98− , queryString :: H.Query
99− , requestBody :: C.Source IO B.ByteString
100− -- | A location for arbitrary data to be shared by applications and middleware.
101− , vault :: Vault
102− -- | The size of the request body. In the case of a chunked request body, this may be unknown.
103− --
104− -- Since 1.4.0
105− , requestBodyLength :: RequestBodyLength
106− -- | The internal @ResourceT@ state, used for allocating scarce resources
107− -- will in an application.
108− --
109− -- Since 1.5.0
110− , resourceInternalState :: Res.InternalState
111− }
112− deriving (Typeable)
113−
114−-- |
115−--
116−-- Some questions and answers about the usage of 'Builder' here:
117−--
118−-- Q1. Shouldn't it be at the user's discretion to use Builders internally and
119−-- then create a stream of ByteStrings?
120−--
121−-- A1. That would be less efficient, as we wouldn't get cheap concatenation
122−-- with the response headers.
123−--
124−-- Q2. Isn't it really inefficient to convert from ByteString to Builder, and
125−-- then right back to ByteString?
126−--
127−-- A2. No. If the ByteStrings are small, then they will be copied into a larger
128−-- buffer, which should be a performance gain overall (less system calls). If
129−-- they are already large, then blaze-builder uses an InsertByteString
130−-- instruction to avoid copying.
131−--
132−-- Q3. Doesn't this prevent us from creating comet-style servers, since data
133−-- will be cached?
134−--
135−-- A3. You can force blaze-builder to output a ByteString before it is an
136−-- optimal size by sending a flush command.
137−data Response
138− = ResponseFile H.Status H.ResponseHeaders FilePath (Maybe FilePart)
139− | ResponseBuilder H.Status H.ResponseHeaders Builder
140− | ResponseSource H.Status H.ResponseHeaders (C.Source IO (C.Flush Builder))
141− deriving Typeable
81+responseFile :: H.Status -> H.ResponseHeaders -> FilePath -> Maybe FilePart -> Response
82+responseFile = ResponseFile
83+
84+responseBuilder :: H.Status -> H.ResponseHeaders -> Builder -> Response
85+responseBuilder = ResponseBuilder
86+
87+responseSource :: H.Status -> H.ResponseHeaders -> C.Source IO (C.Flush Builder) -> Response
88+responseSource = ResponseSource
14289
14390responseStatus :: Response -> H.Status
14491responseStatus rsp =
@@ -147,24 +94,19 @@ responseStatus rsp =
14794 ResponseBuilder s _ _ -> s
14895 ResponseSource s _ _ -> s
14996
150−data FilePart = FilePart
151− { filePartOffset :: Integer
152− , filePartByteCount :: Integer
153− } deriving Show
154−
155−responseSource :: Request
97+responseToSource :: Request
15698 -> Response
15799 -> (H.Status, H.ResponseHeaders, C.Source IO (C.Flush Builder))
158−responseSource _ (ResponseSource s h b) = (s, h, b)
159−responseSource req (ResponseFile s h fp (Just part)) =
100+responseToSource _ (ResponseSource s h b) = (s, h, b)
101+responseToSource req (ResponseFile s h fp (Just part)) =
160102 (s, h, transRes (sourceFilePart part fp) C.$= CL.map (C.Chunk . fromByteString))
161103 where
162104 transRes = C.transPipe (flip Res.runInternalState (resourceInternalState req))
163−responseSource req (ResponseFile s h fp Nothing) =
105+responseToSource req (ResponseFile s h fp Nothing) =
164106 (s, h, transRes (CB.sourceFile fp) C.$= CL.map (C.Chunk . fromByteString))
165107 where
166108 transRes = C.transPipe (flip Res.runInternalState (resourceInternalState req))
167−responseSource _ (ResponseBuilder s h b) =
109+responseToSource _ (ResponseBuilder s h b) =
168110 (s, h, CL.sourceList [C.Chunk b])
169111
170112sourceFilePart :: C.MonadResource m => FilePart -> FilePath -> C.Source m B.ByteString
@@ -191,9 +133,3 @@ type Application = Request -> IO Response
191133-- Here, instead of taking a standard 'Application' as its first argument, the
192134-- middleware takes a function which consumes the session information as well.
193135type Middleware = Application -> Application
194−
195−-- | The size of the request body. In the case of chunked bodies, the size will
196−-- not be known.
197−--
198−-- Since 1.4.0
199−data RequestBodyLength = ChunkedBody | KnownLength Word64
wai/Network/Wai/Internal.hsnew file+102
@@ -0,0 +1,102 @@
1+{-# OPTIONS_HADDOCK not-home #-}
2+{-# LANGUAGE DeriveDataTypeable #-}
3+-- | Internal constructors and helper functions. Note that no guarantees are
4+-- given for stability of these interfaces.
5+module Network.Wai.Internal where
6+
7+import Blaze.ByteString.Builder (Builder)
8+import qualified Control.Monad.Trans.Resource as Res
9+import qualified Data.ByteString as B
10+import qualified Data.Conduit as C
11+import Data.Text (Text)
12+import Data.Typeable (Typeable)
13+import Data.Vault (Vault)
14+import Data.Word (Word64)
15+import qualified Network.HTTP.Types as H
16+import Network.Socket (SockAddr)
17+
18+-- | Information on the request sent by the client. This abstracts away the
19+-- details of the underlying implementation.
20+data Request = Request
21+ { requestMethod :: H.Method
22+ , httpVersion :: H.HttpVersion
23+ -- | Extra path information sent by the client. The meaning varies slightly
24+ -- depending on backend; in a standalone server setting, this is most likely
25+ -- all information after the domain name. In a CGI application, this would be
26+ -- the information following the path to the CGI executable itself.
27+ -- Do not modify this raw value- modify pathInfo instead.
28+ , rawPathInfo :: B.ByteString
29+ -- | If no query string was specified, this should be empty. This value
30+ -- /will/ include the leading question mark.
31+ -- Do not modify this raw value- modify queryString instead.
32+ , rawQueryString :: B.ByteString
33+ , requestHeaders :: H.RequestHeaders
34+ -- | Was this request made over an SSL connection?
35+ --
36+ -- Note that this value will /not/ tell you if the client originally made
37+ -- this request over SSL, but rather whether the current connection is SSL.
38+ -- The distinction lies with reverse proxies. In many cases, the client will
39+ -- connect to a load balancer over SSL, but connect to the WAI handler
40+ -- without SSL. In such a case, @isSecure@ will be @False@, but from a user
41+ -- perspective, there is a secure connection.
42+ , isSecure :: Bool
43+ -- | The client\'s host information.
44+ , remoteHost :: SockAddr
45+ -- | Path info in individual pieces- the url without a hostname/port and without a query string, split on forward slashes,
46+ , pathInfo :: [Text]
47+ -- | Parsed query string information
48+ , queryString :: H.Query
49+ , requestBody :: C.Source IO B.ByteString
50+ -- | A location for arbitrary data to be shared by applications and middleware.
51+ , vault :: Vault
52+ -- | The size of the request body. In the case of a chunked request body, this may be unknown.
53+ --
54+ -- Since 1.4.0
55+ , requestBodyLength :: RequestBodyLength
56+ -- | The internal @ResourceT@ state, used for allocating scarce resources
57+ -- will in an application.
58+ --
59+ -- Since 1.5.0
60+ , resourceInternalState :: Res.InternalState
61+ }
62+ deriving (Typeable)
63+
64+-- |
65+--
66+-- Some questions and answers about the usage of 'Builder' here:
67+--
68+-- Q1. Shouldn't it be at the user's discretion to use Builders internally and
69+-- then create a stream of ByteStrings?
70+--
71+-- A1. That would be less efficient, as we wouldn't get cheap concatenation
72+-- with the response headers.
73+--
74+-- Q2. Isn't it really inefficient to convert from ByteString to Builder, and
75+-- then right back to ByteString?
76+--
77+-- A2. No. If the ByteStrings are small, then they will be copied into a larger
78+-- buffer, which should be a performance gain overall (less system calls). If
79+-- they are already large, then blaze-builder uses an InsertByteString
80+-- instruction to avoid copying.
81+--
82+-- Q3. Doesn't this prevent us from creating comet-style servers, since data
83+-- will be cached?
84+--
85+-- A3. You can force blaze-builder to output a ByteString before it is an
86+-- optimal size by sending a flush command.
87+data Response
88+ = ResponseFile H.Status H.ResponseHeaders FilePath (Maybe FilePart)
89+ | ResponseBuilder H.Status H.ResponseHeaders Builder
90+ | ResponseSource H.Status H.ResponseHeaders (C.Source IO (C.Flush Builder))
91+ deriving Typeable
92+
93+-- | The size of the request body. In the case of chunked bodies, the size will
94+-- not be known.
95+--
96+-- Since 1.4.0
97+data RequestBodyLength = ChunkedBody | KnownLength Word64
98+
99+data FilePart = FilePart
100+ { filePartOffset :: Integer
101+ , filePartByteCount :: Integer
102+ } deriving Show
wai/wai.cabal+1
@@ -28,4 +28,5 @@ Library
2828 , vault >= 0.1 && < 0.3
2929 , resourcet >= 0.4.7
3030 Exposed-modules: Network.Wai
31+ Network.Wai.Internal
3132 ghc-options: -Wall