1{-# LANGUAGE CPP #-}
2{-# LANGUAGE DeriveDataTypeable #-}
3{-# LANGUAGE DeriveTraversable #-}
4{-# LANGUAGE RankNTypes #-}
5{-# LANGUAGE OverloadedStrings #-}
6module Network.HTTP.Client.Types
7    ( BodyReader
8    , Connection (..)
9    , StatusHeaders (..)
10    , HttpException (..)
11    , HttpExceptionContent (..)
12    , unHttpExceptionContentWrapper
13    , throwHttp
14    , toHttpException
15    , Cookie (..)
16    , equalCookie
17    , equivCookie
18    , compareCookies
19    , CookieJar (..)
20    , equalCookieJar
21    , equivCookieJar
22    , Proxy (..)
23    , RequestBody (..)
24    , Popper
25    , NeedsPopper
26    , GivesPopper
27    , Request (..)
28    , Response (..)
29    , ResponseClose (..)
30    , Manager (..)
31    , HasHttpManager (..)
32    , ConnsMap (..)
33    , ManagerSettings (..)
34    , NonEmptyList (..)
35    , ConnHost (..)
36    , ConnKey (..)
37    , ProxyOverride (..)
38    , StreamFileStatus (..)
39    , ResponseTimeout (..)
40    ) where
41
42import qualified Data.Typeable as T (Typeable)
43import Network.HTTP.Types
44import Control.Exception (Exception, SomeException, throwIO)
45import Data.Word (Word64)
46import qualified Data.ByteString as S
47import qualified Data.ByteString.Lazy as L
48import Blaze.ByteString.Builder (Builder, fromLazyByteString, fromByteString, toLazyByteString)
49import Data.Int (Int64)
50import Data.Foldable (Foldable)
51import Data.Monoid (Monoid(..))
52import Data.Semigroup (Semigroup(..))
53import Data.String (IsString, fromString)
54import Data.Time (UTCTime)
55import Data.Traversable (Traversable)
56import qualified Data.List as DL
57import Network.Socket (HostAddress)
58import Data.IORef
59import qualified Network.Socket as NS
60import qualified Data.Map as Map
61import Data.Text (Text)
62import Data.Streaming.Zlib (ZlibException)
63import Data.CaseInsensitive as CI
64import Data.KeyedPool (KeyedPool)
65
66-- | An @IO@ action that represents an incoming response body coming from the
67-- server. Data provided by this action has already been gunzipped and
68-- de-chunked, and respects any content-length headers present.
69--
70-- The action gets a single chunk of data from the response body, or an empty
71-- bytestring if no more data is available.
72--
73-- Since 0.4.0
74type BodyReader = IO S.ByteString
75
76data Connection = Connection
77    { connectionRead :: IO S.ByteString
78      -- ^ If no more data, return empty.
79    , connectionUnread :: S.ByteString -> IO ()
80      -- ^ Return data to be read next time.
81    , connectionWrite :: S.ByteString -> IO ()
82      -- ^ Send data to server
83    , connectionClose :: IO ()
84      -- ^ Close connection. Any successive operation on the connection
85      -- (except closing) should fail with `ConnectionClosed` exception.
86      -- It is allowed to close connection multiple times.
87    }
88    deriving T.Typeable
89
90data StatusHeaders = StatusHeaders Status HttpVersion RequestHeaders
91    deriving (Show, Eq, Ord, T.Typeable)
92
93-- | A newtype wrapper which is not exported from this library but is an
94-- instance of @Exception@. This allows @HttpExceptionContent@ to be thrown
95-- (via this wrapper), but users of the library can't accidentally try to catch
96-- it (when they /should/ be trying to catch 'HttpException').
97--
98-- @since 0.5.0
99newtype HttpExceptionContentWrapper = HttpExceptionContentWrapper
100    { unHttpExceptionContentWrapper :: HttpExceptionContent
101    }
102    deriving (Show, T.Typeable)
103instance Exception HttpExceptionContentWrapper
104
105throwHttp :: HttpExceptionContent -> IO a
106throwHttp = throwIO . HttpExceptionContentWrapper
107
108toHttpException :: Request -> HttpExceptionContentWrapper -> HttpException
109toHttpException req (HttpExceptionContentWrapper e) = HttpExceptionRequest req e
110
111-- | An exception which may be generated by this library
112--
113-- @since 0.5.0
114data HttpException
115    = HttpExceptionRequest Request HttpExceptionContent
116    -- ^ Most exceptions are specific to a 'Request'. Inspect the
117    -- 'HttpExceptionContent' value for details on what occurred.
118    --
119    -- @since 0.5.0
120    | InvalidUrlException String String
121    -- ^ A URL (first field) is invalid for a given reason
122    -- (second argument).
123    --
124    -- @since 0.5.0
125    deriving (Show, T.Typeable)
126instance Exception HttpException
127
128data HttpExceptionContent
129                   = StatusCodeException (Response ()) S.ByteString
130                   -- ^ Generated by the @parseUrlThrow@ function when the
131                   -- server returns a non-2XX response status code.
132                   --
133                   -- May include the beginning of the response body.
134                   --
135                   -- @since 0.5.0
136                   | TooManyRedirects [Response L.ByteString]
137                   -- ^ The server responded with too many redirects for a
138                   -- request.
139                   --
140                   -- Contains the list of encountered responses containing
141                   -- redirects in reverse chronological order; including last
142                   -- redirect, which triggered the exception and was not
143                   -- followed.
144                   --
145                   -- @since 0.5.0
146                   | OverlongHeaders
147                   -- ^ Either too many headers, or too many total bytes in a
148                   -- single header, were returned by the server, and the
149                   -- memory exhaustion protection in this library has kicked
150                   -- in.
151                   --
152                   -- @since 0.5.0
153                   | ResponseTimeout
154                   -- ^ The server took too long to return a response. This can
155                   -- be altered via 'responseTimeout' or
156                   -- 'managerResponseTimeout'.
157                   --
158                   -- @since 0.5.0
159                   | ConnectionTimeout
160                   -- ^ Attempting to connect to the server timed out.
161                   --
162                   -- @since 0.5.0
163                   | ConnectionFailure SomeException
164                   -- ^ An exception occurred when trying to connect to the
165                   -- server.
166                   --
167                   -- @since 0.5.0
168                   | InvalidStatusLine S.ByteString
169                   -- ^ The status line returned by the server could not be parsed.
170                   --
171                   -- @since 0.5.0
172                   | InvalidHeader S.ByteString
173                   -- ^ The given response header line could not be parsed
174                   --
175                   -- @since 0.5.0
176                   | InvalidRequestHeader S.ByteString
177                   -- ^ The given request header is not compliant (e.g. has newlines)
178                   --
179                   -- @since 0.5.14
180                   | InternalException SomeException
181                   -- ^ An exception was raised by an underlying library when
182                   -- performing the request. Most often, this is caused by a
183                   -- failing socket action or a TLS exception.
184                   --
185                   -- @since 0.5.0
186                   | ProxyConnectException S.ByteString Int Status
187                   -- ^ A non-200 status code was returned when trying to
188                   -- connect to the proxy server on the given host and port.
189                   --
190                   -- @since 0.5.0
191                   | NoResponseDataReceived
192                   -- ^ No response data was received from the server at all.
193                   -- This exception may deserve special handling within the
194                   -- library, since it may indicate that a pipelining has been
195                   -- used, and a connection thought to be open was in fact
196                   -- closed.
197                   --
198                   -- @since 0.5.0
199                   | TlsNotSupported
200                   -- ^ Exception thrown when using a @Manager@ which does not
201                   -- have support for secure connections. Typically, you will
202                   -- want to use @tlsManagerSettings@ from @http-client-tls@
203                   -- to overcome this.
204                   --
205                   -- @since 0.5.0
206                   | WrongRequestBodyStreamSize Word64 Word64
207                   -- ^ The request body provided did not match the expected size.
208                   --
209                   -- Provides the expected and actual size.
210                   --
211                   -- @since 0.4.31
212                   | ResponseBodyTooShort Word64 Word64
213                   -- ^ The returned response body is too short. Provides the
214                   -- expected size and actual size.
215                   --
216                   -- @since 0.5.0
217                   | InvalidChunkHeaders
218                   -- ^ A chunked response body had invalid headers.
219                   --
220                   -- @since 0.5.0
221                   | IncompleteHeaders
222                   -- ^ An incomplete set of response headers were returned.
223                   --
224                   -- @since 0.5.0
225                   | InvalidDestinationHost S.ByteString
226                   -- ^ The host we tried to connect to is invalid (e.g., an
227                   -- empty string).
228                   | HttpZlibException ZlibException
229                   -- ^ An exception was thrown when inflating a response body.
230                   --
231                   -- @since 0.5.0
232                   | InvalidProxyEnvironmentVariable Text Text
233                   -- ^ Values in the proxy environment variable were invalid.
234                   -- Provides the environment variable name and its value.
235                   --
236                   -- @since 0.5.0
237                   | ConnectionClosed
238                   -- ^ Attempted to use a 'Connection' which was already closed
239                   --
240                   -- @since 0.5.0
241                   | InvalidProxySettings Text
242                   -- ^ Proxy settings are not valid (Windows specific currently)
243                   -- @since 0.5.7
244    deriving (Show, T.Typeable)
245
246-- Purposely not providing this instance, since we don't want users to
247-- accidentally try and catch these exceptions instead of HttpException
248--
249-- instance Exception HttpExceptionContent
250
251
252-- This corresponds to the description of a cookie detailed in Section 5.3 \"Storage Model\"
253data Cookie = Cookie
254  { cookie_name :: S.ByteString
255  , cookie_value :: S.ByteString
256  , cookie_expiry_time :: UTCTime
257  , cookie_domain :: S.ByteString
258  , cookie_path :: S.ByteString
259  , cookie_creation_time :: UTCTime
260  , cookie_last_access_time :: UTCTime
261  , cookie_persistent :: Bool
262  , cookie_host_only :: Bool
263  , cookie_secure_only :: Bool
264  , cookie_http_only :: Bool
265  }
266  deriving (Read, Show, T.Typeable)
267
268newtype CookieJar = CJ { expose :: [Cookie] }
269  deriving (Read, Show, T.Typeable)
270
271-- | Instead of '(==)'.
272--
273-- Since there was some confusion in the history of this library about how the 'Eq' instance
274-- should work, it was removed for clarity, and replaced by 'equal' and 'equiv'.  'equal'
275-- gives you equality of all fields of the 'Cookie' record.
276--
277-- @since 0.7.0
278equalCookie :: Cookie -> Cookie -> Bool
279equalCookie a b = and
280  [ cookie_name a == cookie_name b
281  , cookie_value a == cookie_value b
282  , cookie_expiry_time a == cookie_expiry_time b
283  , cookie_domain a == cookie_domain b
284  , cookie_path a == cookie_path b
285  , cookie_creation_time a == cookie_creation_time b
286  , cookie_last_access_time a == cookie_last_access_time b
287  , cookie_persistent a == cookie_persistent b
288  , cookie_host_only a == cookie_host_only b
289  , cookie_secure_only a == cookie_secure_only b
290  , cookie_http_only a == cookie_http_only b
291  ]
292
293-- | Equality of name, domain, path only.  This corresponds to step 11 of the algorithm
294-- described in Section 5.3 \"Storage Model\".  See also: 'equal'.
295--
296-- @since 0.7.0
297equivCookie :: Cookie -> Cookie -> Bool
298equivCookie a b = name_matches && domain_matches && path_matches
299  where name_matches = cookie_name a == cookie_name b
300        domain_matches = CI.foldCase (cookie_domain a) == CI.foldCase (cookie_domain b)
301        path_matches = cookie_path a == cookie_path b
302
303-- | Instead of @instance Ord Cookie@.  See 'equalCookie', 'equivCookie'.
304--
305-- @since 0.7.0
306compareCookies :: Cookie -> Cookie -> Ordering
307compareCookies c1 c2
308    | S.length (cookie_path c1) > S.length (cookie_path c2) = LT
309    | S.length (cookie_path c1) < S.length (cookie_path c2) = GT
310    | cookie_creation_time c1 > cookie_creation_time c2 = GT
311    | otherwise = LT
312
313-- | See 'equalCookie'.
314--
315-- @since 0.7.0
316equalCookieJar :: CookieJar -> CookieJar -> Bool
317equalCookieJar (CJ cj1) (CJ cj2) = and $ zipWith equalCookie cj1 cj2
318
319-- | See 'equalCookieJar', 'equalCookie'.
320--
321-- @since 0.7.0
322equivCookieJar :: CookieJar -> CookieJar -> Bool
323equivCookieJar cj1 cj2 = and $
324  zipWith equivCookie (DL.sortBy compareCookies $ expose cj1) (DL.sortBy compareCookies $ expose cj2)
325
326instance Semigroup CookieJar where
327  (CJ a) <> (CJ b) = CJ (DL.nubBy equivCookie $ DL.sortBy mostRecentFirst $ a <> b)
328    where mostRecentFirst c1 c2 =
329            -- inverse so that recent cookies are kept by nub over older
330            if cookie_creation_time c1 > cookie_creation_time c2
331                then LT
332                else GT
333
334-- | Since 1.9
335instance Data.Monoid.Monoid CookieJar where
336  mempty = CJ []
337#if !(MIN_VERSION_base(4,11,0))
338  mappend = (<>)
339#endif
340
341-- | Define a HTTP proxy, consisting of a hostname and port number.
342
343data Proxy = Proxy
344    { proxyHost :: S.ByteString -- ^ The host name of the HTTP proxy.
345    , proxyPort :: Int -- ^ The port number of the HTTP proxy.
346    }
347    deriving (Show, Read, Eq, Ord, T.Typeable)
348
349-- | When using one of the 'RequestBodyStream' \/ 'RequestBodyStreamChunked'
350-- constructors, you must ensure that the 'GivesPopper' can be called multiple
351-- times.  Usually this is not a problem.
352--
353-- The 'RequestBodyStreamChunked' will send a chunked request body. Note that
354-- not all servers support this. Only use 'RequestBodyStreamChunked' if you
355-- know the server you're sending to supports chunked request bodies.
356--
357-- Since 0.1.0
358data RequestBody
359    = RequestBodyLBS L.ByteString
360    | RequestBodyBS S.ByteString
361    | RequestBodyBuilder Int64 Builder
362    | RequestBodyStream Int64 (GivesPopper ())
363    | RequestBodyStreamChunked (GivesPopper ())
364    | RequestBodyIO (IO RequestBody)
365    -- ^ Allows creation of a @RequestBody@ inside the @IO@ monad, which is
366    -- useful for making easier APIs (like @setRequestBodyFile@).
367    --
368    -- @since 0.4.28
369    deriving T.Typeable
370-- |
371--
372-- Since 0.4.12
373instance IsString RequestBody where
374    fromString str = RequestBodyBS (fromString str)
375instance Monoid RequestBody where
376    mempty = RequestBodyBS S.empty
377#if !(MIN_VERSION_base(4,11,0))
378    mappend = (<>)
379#endif
380
381instance Semigroup RequestBody where
382    x0 <> y0 =
383        case (simplify x0, simplify y0) of
384            (Left (i, x), Left (j, y)) -> RequestBodyBuilder (i + j) (x <> y)
385            (Left x, Right y) -> combine (builderToStream x) y
386            (Right x, Left y) -> combine x (builderToStream y)
387            (Right x, Right y) -> combine x y
388      where
389        combine (Just i, x) (Just j, y) = RequestBodyStream (i + j) (combine' x y)
390        combine (_, x) (_, y) = RequestBodyStreamChunked (combine' x y)
391
392        combine' :: GivesPopper () -> GivesPopper () -> GivesPopper ()
393        combine' x y f = x $ \x' -> y $ \y' -> combine'' x' y' f
394
395        combine'' :: Popper -> Popper -> NeedsPopper () -> IO ()
396        combine'' x y f = do
397            istate <- newIORef $ Left (x, y)
398            f $ go istate
399
400        go istate = do
401            state <- readIORef istate
402            case state of
403                Left (x, y) -> do
404                    bs <- x
405                    if S.null bs
406                        then do
407                            writeIORef istate $ Right y
408                            y
409                        else return bs
410                Right y -> y
411
412simplify :: RequestBody -> Either (Int64, Builder) (Maybe Int64, GivesPopper ())
413simplify (RequestBodyLBS lbs) = Left (L.length lbs, fromLazyByteString lbs)
414simplify (RequestBodyBS bs) = Left (fromIntegral $ S.length bs, fromByteString bs)
415simplify (RequestBodyBuilder len b) = Left (len, b)
416simplify (RequestBodyStream i gp) = Right (Just i, gp)
417simplify (RequestBodyStreamChunked gp) = Right (Nothing, gp)
418simplify (RequestBodyIO _mbody) = error "FIXME No support for Monoid on RequestBodyIO"
419
420builderToStream :: (Int64, Builder) -> (Maybe Int64, GivesPopper ())
421builderToStream (len, builder) =
422    (Just len, gp)
423  where
424    gp np = do
425        ibss <- newIORef $ L.toChunks $ toLazyByteString builder
426        np $ do
427            bss <- readIORef ibss
428            case bss of
429                [] -> return S.empty
430                bs:bss' -> do
431                    writeIORef ibss bss'
432                    return bs
433
434-- | A function which generates successive chunks of a request body, provider a
435-- single empty bytestring when no more data is available.
436--
437-- Since 0.1.0
438type Popper = IO S.ByteString
439
440-- | A function which must be provided with a 'Popper'.
441--
442-- Since 0.1.0
443type NeedsPopper a = Popper -> IO a
444
445-- | A function which will provide a 'Popper' to a 'NeedsPopper'. This
446-- seemingly convoluted structure allows for creation of request bodies which
447-- allocate scarce resources in an exception safe manner.
448--
449-- Since 0.1.0
450type GivesPopper a = NeedsPopper a -> IO a
451
452-- | All information on how to connect to a host and what should be sent in the
453-- HTTP request.
454--
455-- If you simply wish to download from a URL, see 'parseRequest'.
456--
457-- The constructor for this data type is not exposed. Instead, you should use
458-- either the 'defaultRequest' value, or 'parseRequest' to
459-- construct from a URL, and then use the records below to make modifications.
460-- This approach allows http-client to add configuration options without
461-- breaking backwards compatibility.
462--
463-- For example, to construct a POST request, you could do something like:
464--
465-- > initReq <- parseRequest "http://www.example.com/path"
466-- > let req = initReq
467-- >             { method = "POST"
468-- >             }
469--
470-- For more information, please see
471-- <http://www.yesodweb.com/book/settings-types>.
472--
473-- Since 0.1.0
474data Request = Request
475    { method :: Method
476    -- ^ HTTP request method, eg GET, POST.
477    --
478    -- Since 0.1.0
479    , secure :: Bool
480    -- ^ Whether to use HTTPS (ie, SSL).
481    --
482    -- Since 0.1.0
483    , host :: S.ByteString
484    -- ^ Requested host name, used for both the IP address to connect to and
485    -- the @host@ request header.
486    --
487    -- Since 0.1.0
488    , port :: Int
489    -- ^ The port to connect to. Also used for generating the @host@ request header.
490    --
491    -- Since 0.1.0
492    , path :: S.ByteString
493    -- ^ Everything from the host to the query string.
494    --
495    -- Since 0.1.0
496    , queryString :: S.ByteString
497    -- ^ Query string appended to the path.
498    --
499    -- Since 0.1.0
500    , requestHeaders :: RequestHeaders
501    -- ^ Custom HTTP request headers
502    --
503    -- The Content-Length and Transfer-Encoding headers are set automatically
504    -- by this module, and shall not be added to @requestHeaders@.
505    --
506    -- If not provided by the user, @Host@ will automatically be set based on
507    -- the @host@ and @port@ fields.
508    --
509    -- Moreover, the Accept-Encoding header is set implicitly to gzip for
510    -- convenience by default. This behaviour can be overridden if needed, by
511    -- setting the header explicitly to a different value. In order to omit the
512    -- Accept-Header altogether, set it to the empty string \"\". If you need an
513    -- empty Accept-Header (i.e. requesting the identity encoding), set it to a
514    -- non-empty white-space string, e.g. \" \". See RFC 2616 section 14.3 for
515    -- details about the semantics of the Accept-Header field. If you request a
516    -- content-encoding not supported by this module, you will have to decode
517    -- it yourself (see also the 'decompress' field).
518    --
519    -- Note: Multiple header fields with the same field-name will result in
520    -- multiple header fields being sent and therefore it\'s the responsibility
521    -- of the client code to ensure that the rules from RFC 2616 section 4.2
522    -- are honoured.
523    --
524    -- Since 0.1.0
525    , requestBody :: RequestBody
526    -- ^ Request body to be sent to the server.
527    --
528    -- Since 0.1.0
529    , proxy :: Maybe Proxy
530    -- ^ Optional HTTP proxy.
531    --
532    -- Since 0.1.0
533    , hostAddress :: Maybe HostAddress
534    -- ^ Optional resolved host address. May not be used by all backends.
535    --
536    -- Since 0.1.0
537    , rawBody :: Bool
538    -- ^ If @True@, a chunked and\/or gzipped body will not be
539    -- decoded. Use with caution.
540    --
541    -- Since 0.1.0
542    , decompress :: S.ByteString -> Bool
543    -- ^ Predicate to specify whether gzipped data should be
544    -- decompressed on the fly (see 'alwaysDecompress' and
545    -- 'browserDecompress'). Argument is the mime type.
546    -- Default: browserDecompress.
547    --
548    -- Since 0.1.0
549    , redirectCount :: Int
550    -- ^ How many redirects to follow when getting a resource. 0 means follow
551    -- no redirects. Default value: 10.
552    --
553    -- Since 0.1.0
554    , checkResponse :: Request -> Response BodyReader -> IO ()
555    -- ^ Check the response immediately after receiving the status and headers.
556    -- This can be useful for throwing exceptions on non-success status codes.
557    --
558    -- In previous versions of http-client, this went under the name
559    -- @checkStatus@, but was renamed to avoid confusion about the new default
560    -- behavior (doing nothing).
561    --
562    -- @since 0.5.0
563    , responseTimeout :: ResponseTimeout
564    -- ^ Number of microseconds to wait for a response (see 'ResponseTimeout'
565    -- for more information). Default: use 'managerResponseTimeout' (which by
566    -- default is 30 seconds).
567    --
568    -- Since 0.1.0
569    , cookieJar :: Maybe CookieJar
570    -- ^ A user-defined cookie jar.
571    -- If 'Nothing', no cookie handling will take place, \"Cookie\" headers
572    -- in 'requestHeaders' will be sent raw, and 'responseCookieJar' will be
573    -- empty.
574    --
575    -- Since 0.1.0
576
577    , requestVersion :: HttpVersion
578    -- ^ HTTP version to send to server.
579    --
580    -- Default: HTTP 1.1
581    --
582    -- Since 0.4.3
583
584    , onRequestBodyException :: SomeException -> IO ()
585    -- ^ How to deal with exceptions thrown while sending the request.
586    --
587    -- Default: ignore @IOException@s, rethrow all other exceptions.
588    --
589    -- Since: 0.4.6
590
591    , requestManagerOverride :: Maybe Manager
592    -- ^ A 'Manager' value that should override whatever @Manager@ value was
593    -- passed in to the HTTP request function manually. This is useful when
594    -- dealing with implicit global managers, such as in @Network.HTTP.Simple@
595    --
596    -- @since 0.4.28
597
598    , shouldStripHeaderOnRedirect :: HeaderName -> Bool
599    -- ^ Decide whether a header must be stripped from the request
600    -- when following a redirect. Default: keep all headers intact.
601    --
602    -- @since 0.6.2
603    }
604    deriving T.Typeable
605
606-- | How to deal with timing out on retrieval of response headers.
607--
608-- @since 0.5.0
609data ResponseTimeout
610    = ResponseTimeoutMicro !Int
611    -- ^ Wait the given number of microseconds for response headers to
612    -- load, then throw an exception
613    | ResponseTimeoutNone
614    -- ^ Wait indefinitely
615    | ResponseTimeoutDefault
616    -- ^ Fall back to the manager setting ('managerResponseTimeout') or, in its
617    -- absence, Wait 30 seconds and then throw an exception.
618    deriving (Eq, Show)
619
620instance Show Request where
621    show x = unlines
622        [ "Request {"
623        , "  host                 = " ++ show (host x)
624        , "  port                 = " ++ show (port x)
625        , "  secure               = " ++ show (secure x)
626        , "  requestHeaders       = " ++ show (DL.map redactSensitiveHeader (requestHeaders x))
627        , "  path                 = " ++ show (path x)
628        , "  queryString          = " ++ show (queryString x)
629        --, "  requestBody          = " ++ show (requestBody x)
630        , "  method               = " ++ show (method x)
631        , "  proxy                = " ++ show (proxy x)
632        , "  rawBody              = " ++ show (rawBody x)
633        , "  redirectCount        = " ++ show (redirectCount x)
634        , "  responseTimeout      = " ++ show (responseTimeout x)
635        , "  requestVersion       = " ++ show (requestVersion x)
636        , "}"
637        ]
638
639redactSensitiveHeader :: Header -> Header
640redactSensitiveHeader ("Authorization", _) = ("Authorization", "<REDACTED>")
641redactSensitiveHeader h = h
642
643-- | A simple representation of the HTTP response.
644--
645-- Since 0.1.0
646data Response body = Response
647    { responseStatus :: Status
648    -- ^ Status code of the response.
649    --
650    -- Since 0.1.0
651    , responseVersion :: HttpVersion
652    -- ^ HTTP version used by the server.
653    --
654    -- Since 0.1.0
655    , responseHeaders :: ResponseHeaders
656    -- ^ Response headers sent by the server.
657    --
658    -- Since 0.1.0
659    , responseBody :: body
660    -- ^ Response body sent by the server.
661    --
662    -- Since 0.1.0
663    , responseCookieJar :: CookieJar
664    -- ^ Cookies set on the client after interacting with the server. If
665    -- cookies have been disabled by setting 'cookieJar' to @Nothing@, then
666    -- this will always be empty.
667    --
668    -- Since 0.1.0
669    , responseClose' :: ResponseClose
670    -- ^ Releases any resource held by this response. If the response body
671    -- has not been fully read yet, doing so after this call will likely
672    -- be impossible.
673    --
674    -- Since 0.1.0
675    }
676    deriving (Show, T.Typeable, Functor, Data.Foldable.Foldable, Data.Traversable.Traversable)
677
678-- Purposely not providing this instance.  It used to use 'equivCookieJar'
679-- semantics before 0.7.0, but should, if anything, use 'equalCookieJar'
680-- semantics.
681--
682-- instance Exception Eq
683
684newtype ResponseClose = ResponseClose { runResponseClose :: IO () }
685    deriving T.Typeable
686instance Show ResponseClose where
687    show _ = "ResponseClose"
688
689-- | Settings for a @Manager@. Please use the 'defaultManagerSettings' function and then modify
690-- individual settings. For more information, see <http://www.yesodweb.com/book/settings-types>.
691--
692-- Since 0.1.0
693data ManagerSettings = ManagerSettings
694    { managerConnCount :: Int
695      -- ^ Number of connections to a single host to keep alive. Default: 10.
696      --
697      -- Since 0.1.0
698    , managerRawConnection :: IO (Maybe NS.HostAddress -> String -> Int -> IO Connection)
699      -- ^ Create an insecure connection.
700      --
701      -- Since 0.1.0
702    , managerTlsConnection :: IO (Maybe NS.HostAddress -> String -> Int -> IO Connection)
703      -- ^ Create a TLS connection. Default behavior: throw an exception that TLS is not supported.
704      --
705      -- Since 0.1.0
706    , managerTlsProxyConnection :: IO (S.ByteString -> (Connection -> IO ()) -> String -> Maybe NS.HostAddress -> String -> Int -> IO Connection)
707      -- ^ Create a TLS proxy connection. Default behavior: throw an exception that TLS is not supported.
708      --
709      -- Since 0.2.2
710    , managerResponseTimeout :: ResponseTimeout
711      -- ^ Default timeout to be applied to requests which do not provide a
712      -- timeout value.
713      --
714      -- Default is 30 seconds
715      --
716      -- @since 0.5.0
717    , managerRetryableException :: SomeException -> Bool
718    -- ^ Exceptions for which we should retry our request if we were reusing an
719    -- already open connection. In the case of IOExceptions, for example, we
720    -- assume that the connection was closed on the server and therefore open a
721    -- new one.
722    --
723    -- Since 0.1.0
724    , managerWrapException :: forall a. Request -> IO a -> IO a
725    -- ^ Action wrapped around all attempted @Request@s, usually used to wrap
726    -- up exceptions in library-specific types.
727    --
728    -- Default: wrap all @IOException@s in the @InternalException@ constructor.
729    --
730    -- @since 0.5.0
731    , managerIdleConnectionCount :: Int
732    -- ^ Total number of idle connection to keep open at a given time.
733    --
734    -- This limit helps deal with the case where you are making a large number
735    -- of connections to different hosts. Without this limit, you could run out
736    -- of file descriptors. Additionally, it can be set to zero to prevent
737    -- reuse of any connections. Doing this is useful when the server your application
738    -- is talking to sits behind a load balancer.
739    --
740    -- Default: 512
741    --
742    -- Since 0.3.7
743    , managerModifyRequest :: Request -> IO Request
744    -- ^ Perform the given modification to a @Request@ before performing it.
745    --
746    -- This function may be called more than once during request processing.
747    -- see https://github.com/snoyberg/http-client/issues/350
748    --
749    -- Default: no modification
750    --
751    -- Since 0.4.4
752    , managerModifyResponse :: Response BodyReader -> IO (Response BodyReader)
753    -- ^ Perform the given modification to a @Response@ after receiving it.
754    --
755    -- Default: no modification
756    --
757    -- @since 0.5.5
758    , managerProxyInsecure :: ProxyOverride
759    -- ^ How HTTP proxy server settings should be discovered.
760    --
761    -- Default: respect the @proxy@ value on the @Request@ itself.
762    --
763    -- Since 0.4.7
764    , managerProxySecure :: ProxyOverride
765    -- ^ How HTTPS proxy server settings should be discovered.
766    --
767    -- Default: respect the @proxy@ value on the @Request@ itself.
768    --
769    -- Since 0.4.7
770    }
771    deriving T.Typeable
772
773-- | How the HTTP proxy server settings should be discovered.
774--
775-- Since 0.4.7
776newtype ProxyOverride = ProxyOverride
777    { runProxyOverride :: Bool -> IO (Request -> Request)
778    }
779    deriving T.Typeable
780
781-- | Keeps track of open connections for keep-alive.
782--
783-- If possible, you should share a single 'Manager' between multiple threads and requests.
784--
785-- Since 0.1.0
786data Manager = Manager
787    { mConns :: KeyedPool ConnKey Connection
788    , mResponseTimeout :: ResponseTimeout
789    -- ^ Copied from 'managerResponseTimeout'
790    , mRetryableException :: SomeException -> Bool
791    , mWrapException :: forall a. Request -> IO a -> IO a
792    , mModifyRequest :: Request -> IO Request
793    , mSetProxy :: Request -> Request
794    , mModifyResponse      :: Response BodyReader -> IO (Response BodyReader)
795    -- ^ See 'managerProxy'
796    }
797    deriving T.Typeable
798
799class HasHttpManager a where
800    getHttpManager :: a -> Manager
801instance HasHttpManager Manager where
802    getHttpManager = id
803
804data ConnsMap
805    = ManagerClosed
806    | ManagerOpen {-# UNPACK #-} !Int !(Map.Map ConnKey (NonEmptyList Connection))
807
808data NonEmptyList a =
809    One a UTCTime |
810    Cons a Int UTCTime (NonEmptyList a)
811    deriving T.Typeable
812
813-- | Hostname or resolved host address.
814data ConnHost =
815    HostName Text |
816    HostAddress NS.HostAddress
817    deriving (Eq, Show, Ord, T.Typeable)
818
819-- | @ConnKey@ consists of a hostname, a port and a @Bool@
820-- specifying whether to use SSL.
821data ConnKey
822    = CKRaw (Maybe HostAddress) {-# UNPACK #-} !S.ByteString !Int
823    | CKSecure (Maybe HostAddress) {-# UNPACK #-} !S.ByteString !Int
824    | CKProxy
825        {-# UNPACK #-} !S.ByteString
826        !Int
827
828        -- Proxy-Authorization request header
829        (Maybe S.ByteString)
830
831        -- ultimate host
832        {-# UNPACK #-} !S.ByteString
833
834        -- ultimate port
835        !Int
836    deriving (Eq, Show, Ord, T.Typeable)
837
838-- | Status of streaming a request body from a file.
839--
840-- Since 0.4.9
841data StreamFileStatus = StreamFileStatus
842    { fileSize :: Int64
843    , readSoFar :: Int64
844    , thisChunkSize :: Int
845    }
846    deriving (Eq, Show, Ord, T.Typeable)
847