2-- Module : Network.HTTP.Req
3-- Copyright : © 2016 Mark Karpov
4-- License : BSD 3 clause
6-- Maintainer : Mark Karpov <markkarpov@openmailbox.org>
7-- Stability : experimental
8-- Portability : portable
10-- This is an easy-to-use, type-safe, expandable, high-level HTTP library
11-- that just works without any fooling around.
13-- /(A modest intro goes here, click on 'req' to start making requests.)/
15-- What does the “easy-to-use” phrase mean? It means that the library is
16-- designed to be beginner-friendly, so it's simple to add it to your monad
17-- stack, intuitive to work with, well-documented, and does not get in your
18-- way. On this path certain compromises were made. For example one cannot
19-- currently modify 'L.ManagerSettings' of default manager because the
20-- library always use the same implicit global manager for simplicity and
21-- maximal connection sharing. There is a way to use your own manager with
22-- different settings, but it requires a bit more typing. Doing HTTP
23-- requests is a common task and Haskell library for this should be very
24-- approachable and clear to beginners.
26-- “Type-safe” means that the library is protective and eliminates certain
27-- class of errors compared to alternative libraries like @wreq@ or vanilla
28-- @http-client@. For example we have correct-by-construction 'Url's, it's
29-- guaranteed that user does not send request body when using methods like
30-- 'GET' or 'DELETE', amount of implicit assumptions is minimized by making
31-- user specify his\/her intentions in explicit form (for example it's not
32-- possible to avoid specifying body or method of a request). The library
33-- carefully hides underlying types from lower-level @http-client@ package
34-- because it's not type safe enough (for example 'L.Request' is an instance
35-- of 'Data.String.IsString' and if it's malformed, it will blow up at
38-- “Expandable” refers to the ability of the library to be expanded without
39-- ugly hacking. For example it's possible to define your own HTTP methods,
40-- new ways to construct body of request, new authorization options, new
41-- ways to actually perform request and how to represent\/parse its
42-- response. As user extends the library to satisfy his\/her special needs,
43-- the new solutions work just like built-ins. That said, all common cases
44-- are covered by the library out-of-the-box.
46-- “High-level” means that there are less details to worry about. The
47-- library is a result of my experiences as a Haskell consultant, working
48-- for several clients who have very different projects and so the library
49-- adapts easily to any particular style of writing Haskell applications.
50-- For example some people prefer throwing exceptions, while others are
51-- concerned with purity: just define 'handleHttpException' accordingly when
52-- making your monad instance of 'MonadHttp' and it will play seamlessly.
53-- Finally, the library cuts boilerplate considerably and helps write
54-- concise, easy to read code, thanks to its minimal and uniform API.
56-- The documentation below is structured in such a way that most important
57-- information goes first: you learn how to do HTTP requests, then how to
58-- embed them in any monad you have, then it goes on giving you details
59-- about less-common things you may want to know about. The documentation is
60-- written with sufficient coverage of details and examples, it's designed
61-- to be a complete tutorial on its own.
63-- The library uses the following well-known and mature packages under the
64-- hood to guarantee you best experience without bugs or other funny
67-- * <https://hackage.haskell.org/package/http-client> — low level HTTP
68-- client used everywhere in Haskell.
69-- * <https://hackage.haskell.org/package/http-client-tls> — TLS (HTTPS)
70-- support for @http-client@.
71-- * <https://hackage.haskell.org/package/http-conduit> — conduit
72-- interface to @http-client@.
74-- You won't need low-level interface of @http-client@ most of the time, but
75-- when you do, it's better import it qualified because it has naming
76-- conflicts with @req@.
78{-# LANGUAGE DataKinds #-}
79{-# LANGUAGE DeriveDataTypeable #-}
80{-# LANGUAGE DeriveGeneric #-}
81{-# LANGUAGE FlexibleInstances #-}
82{-# LANGUAGE GeneralizedNewtypeDeriving #-}
83{-# LANGUAGE KindSignatures #-}
84{-# LANGUAGE OverloadedStrings #-}
85{-# LANGUAGE RecordWildCards #-}
86{-# LANGUAGE ScopedTypeVariables #-}
87{-# LANGUAGE TypeFamilies #-}
88{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
90module Network.HTTP.Req
91 ( -- * Making a request
94 -- * Embedding requests into your monad
95 -- $embedding-requests
119 , HttpBody (..) -- TODO more stuff here
120 -- ** Optional parameters
121 -- $request-optional-parameters
123 -- *** Query parameters
130 -- *** Authentication
131 -- $request-optional-parameters-authentication
149import Control.Applicative ((<|>))
150import Control.Arrow (first, second)
151import Control.Exception (try)
153import Control.Monad.IO.Class
155import Data.ByteString (ByteString)
156import Data.Data (Data)
157import Data.Default.Class
159import Data.List.NonEmpty (NonEmpty (..))
161import Data.Semigroup hiding (Option, option)
162import Data.Text (Text)
163import Data.Typeable (Typeable)
166import System.IO.Unsafe (unsafePerformIO)
167import qualified Data.Binary.Builder as R
168import qualified Data.ByteString as B
169import qualified Data.ByteString.Lazy as BL
170import qualified Data.CaseInsensitive as CI
171import qualified Data.List.NonEmpty as NE
172import qualified Data.Text.Encoding as T
173import qualified Network.Connection as NC
174import qualified Network.HTTP.Client as L
175import qualified Network.HTTP.Client.Internal as LI
176import qualified Network.HTTP.Client.TLS as L
177import qualified Network.HTTP.Req.AWS as AWS
178import qualified Network.HTTP.Req.OAuth1 as OAuth1
179import qualified Network.HTTP.Types as Y
181----------------------------------------------------------------------------
186-- To make an HTTP request you need only one function: 'req'.
188-- | Make an HTTP request.
190-- TODO Finish docs of this function when the package is more developed.
193 :: forall m method body response scheme.
197 , HttpResponse response
198 , AllowsBody method ~ ProvidesBody body )
199 => method -- ^ HTTP method
200 -> Url scheme -- ^ 'Url' — location of resource
201 -> body -- ^ Body of the request
202 -> Option scheme -- ^ Collection of optional parameters
203 -> m response -- ^ Response
204req method url body options = do
205 config <- getHttpConfig
206 manager <- liftIO (readIORef globalManager)
207 let request = flip appEndo L.defaultRequest $
208 -- NOTE Order of 'mappend's matters, here method is overwritten
209 -- first and 'config' takes its effect last. In particular, this
210 -- means that 'options' can overwrite things set by 'url' and
211 -- 'body', which is useful for setting port number, "Content-Type"
213 getRequestMod config <>
214 getRequestMod options <>
215 getRequestMod (Womb body :: Womb "body" body) <>
217 getRequestMod (Womb method :: Womb "method" method)
218 liftIO (try $ getHttpResponse manager request)
219 >>= either handleHttpException return
221-- | Global 'L.Manager' that 'req' uses. Here we just go with the default
222-- settings, so users don't need to deal with this manager stuff at all, but
223-- when we create a request, instance 'HttpConfig' can affect the default
224-- settings via 'getHttpConfig'.
226-- A note about safety, in case 'unsafePerformIO' looks suspicious to you.
227-- The value of 'globalManager' is named and lives on top level. This means
228-- it will be shared, i.e. computed only once on first use of manager. From
229-- that moment on the 'IORef' will be just reused — exactly the behaviour we
230-- want here in order to maximize connection sharing. GHC could spoil the
231-- plan by inlining the definition, hence the @NOINLINE@ pragma.
233globalManager :: IORef L.Manager
234globalManager = unsafePerformIO $ do
235 context <- NC.initConnectionContext
236 let settings = L.mkManagerSettingsContext (Just context) def Nothing
237 manager <- L.newManager settings
239{-# NOINLINE globalManager #-}
241----------------------------------------------------------------------------
242-- Embedding requests into your monad
244-- $embedding-requests
246-- To use 'req' in your monad, all you need to do is to make the monad an
247-- instance of the 'MonadHttp' type class, which see.
249-- | A type class for monads that support performing HTTP requests.
250-- Typically, you only need to define the 'handleHttpException' method
251-- unless you want to tweak 'HttpConfig'.
253class MonadIO m => MonadHttp m where
255 {-# MINIMAL handleHttpException #-}
257 -- | This method describes how to deal with 'L.HttpException' that was
258 -- caught by the library. One option is to re-throw it if you are OK with
259 -- exceptions, but if you prefer working with something like
260 -- 'Control.Monad.Error.MonadError', this is the right place to pass it to
261 -- 'Control.Monad.Error.throwError' for example.
263 handleHttpException :: L.HttpException -> m a
265 -- | Return 'HttpConfig' to be used when performing HTTP requests. Default
266 -- implementation returns its 'def' value, which is described in the
267 -- documentation for the type. Common usage pattern with manually defined
268 -- 'getHttpConfig' is to return some hard-coded value, or value extracted
269 -- from 'Control.Monad.Reader.MonadReader' if a more flexible approach to
270 -- configuration is desirable.
272 getHttpConfig :: m HttpConfig
273 getHttpConfig = return def
275-- | 'HttpConfig' contains general and default settings to be used when
276-- making HTTP requests.
278data HttpConfig = HttpConfig
279 { httpConfigProxy :: !(Maybe L.Proxy)
280 -- ^ Proxy to use. By default values of @HTTP_PROXY@ and @HTTPS_PROXY@
281 -- environment variables are respected, this setting overwrites them.
282 -- Default value: 'Nothing'.
283 , httpConfigRedirectCount :: !Word
284 -- ^ How many redirects to follow when getting a resource. Default
286 , httpConfigAltManager :: !(Maybe L.Manager)
287 -- ^ Alternative 'L.Manager' to use. 'Nothing' (default value) means
288 -- that default implicit manager will be used (that's what you want in
290 , httpConfigCheckResponse :: L.Request -> L.Response L.BodyReader -> IO ()
291 -- ^ Check the response immediately after receiving the status and
292 -- headers. This is used for throwing exceptions on non-success status
293 -- codes by default. Throwing is better then just returning a request
294 -- with non-2xx status code because in that case something is wrong and
295 -- we need a way to short-cut execution. The thrown exception is caught
296 -- by the library though and is available in 'handleHttpException',
300instance Default HttpConfig where
302 { httpConfigProxy = Nothing
303 , httpConfigRedirectCount = 10
304 , httpConfigAltManager = Nothing
305 , httpConfigCheckResponse = \_ response ->
306 let Y.Status statusCode _ = L.responseStatus response in
307 unless (200 <= statusCode && statusCode < 300) $ do
308 chunk <- BL.toStrict <$> L.brReadSome (L.responseBody response) 1024
309 LI.throwHttp (L.StatusCodeException (() <$ response) chunk) }
311instance RequestComponent HttpConfig where
312 getRequestMod HttpConfig {..} = Endo $ \x ->
313 x { L.proxy = httpConfigProxy
314 , L.redirectCount = fromIntegral httpConfigRedirectCount
315 , LI.requestManagerOverride = httpConfigAltManager
316 , LI.checkResponse = httpConfigCheckResponse }
318----------------------------------------------------------------------------
323-- The package provides all methods as defined by RFC 2616, and 'PATCH'
324-- which is defined by RFC 5789 — that should be enough to talk to RESTful
325-- APIs. In some cases however, you may want to add more methods (e.g. you
326-- work with WebDAV <https://en.wikipedia.org/wiki/WebDAV>); no need to
327-- compromise on type safety and hack, it only takes a couple of seconds to
328-- define a new method that will works seamlessly, see 'HttpMethod'.
334instance HttpMethod GET where
335 type AllowsBody GET = 'NoBody
336 httpMethodName Proxy = Y.methodGet
342instance HttpMethod POST where
343 type AllowsBody POST = 'CanHaveBody
344 httpMethodName Proxy = Y.methodPost
350instance HttpMethod HEAD where
351 type AllowsBody HEAD = 'NoBody
352 httpMethodName Proxy = Y.methodHead
358instance HttpMethod PUT where
359 type AllowsBody PUT = 'CanHaveBody
360 httpMethodName Proxy = Y.methodPut
366instance HttpMethod DELETE where
367 type AllowsBody DELETE = 'NoBody
368 httpMethodName Proxy = Y.methodDelete
374instance HttpMethod TRACE where
375 type AllowsBody TRACE = 'CanHaveBody
376 httpMethodName Proxy = Y.methodTrace
378-- | 'CONNECT' method.
380data CONNECT = CONNECT
382instance HttpMethod CONNECT where
383 type AllowsBody CONNECT = 'CanHaveBody
384 httpMethodName Proxy = Y.methodConnect
386-- | 'OPTIONS' method.
388data OPTIONS = OPTIONS
390instance HttpMethod OPTIONS where
391 type AllowsBody OPTIONS = 'NoBody
392 httpMethodName Proxy = Y.methodOptions
398instance HttpMethod PATCH where
399 type AllowsBody PATCH = 'CanHaveBody
400 httpMethodName Proxy = Y.methodPatch
402-- | A type class for types that can be used as an HTTP method. To define a
403-- non-standard method, follow this example that defines COPY:
407-- > instance HttpMethod COPY where
408-- > type AllowsBody COPY = 'CanHaveBody
409-- > httpMethodName Proxy = "COPY"
411class HttpMethod a where
413 -- | Type function 'AllowsBody' returns type of kind 'CanHaveBody' which
414 -- tells the rest of the library whether the method can have a body or
415 -- not. We use the special type 'CanHaveBody' “lifted” into kind instead
416 -- of 'Bool' to get more user-friendly compiler messages.
418 type AllowsBody a :: CanHaveBody
420 -- | Return name of the method as a 'ByteString'.
422 httpMethodName :: Proxy a -> Y.Method
424instance HttpMethod method => RequestComponent (Womb "method" method) where
425 getRequestMod _ = Endo $ \x ->
426 x { L.method = httpMethodName (Proxy :: Proxy method) }
428----------------------------------------------------------------------------
431-- | Request's 'Url'. Start constructing your 'Url' with 'http' or 'https'
432-- specifying the scheme and host at the same time. Then use the @('/:')@
433-- constructor to grow path one piece at a time. Every single piece of path
434-- will be url(percent)-encoded, so @('/:')@ is the only way to have forward
435-- slashes between path segments. This approach makes working with dynamic
436-- path segments easy and safe. See examples below how to represent various
437-- 'Url's (make sure the @OverloadedStrings@ language extension is enabled).
441-- > http "httpbin.org"
442-- > -- http://httpbin.org
444-- > https "httpbin.org"
445-- > -- https://httpbin.org
447-- > https "httpbin.org" /: "encoding" /: "utf8"
448-- > -- https://httpbin.org/encoding/utf8
450-- > https "httpbin.org" /: "foo" /: "bar/baz"
451-- > -- https://httpbin.org/foo/bar%2Fbaz
453-- > https "юникод.рф"
454-- > -- https://%D1%8E%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4.%D1%80%D1%84
456data Url (scheme :: Scheme) = Url Scheme (NonEmpty Text)
457 -- NOTE The second value is path segments in reversed order.
458 deriving (Eq, Ord, Show, Data, Typeable, Generic)
460-- | Given host name, produce a 'Url' which have “http” as its scheme and
461-- empty path. This also sets port to @80@.
463http :: Text -> Url 'Http
464http = Url Http . pure
466-- | Given host name, produce a 'Url' which have “https” as its scheme and
467-- empty path. This also sets port to @443@.
469https :: Text -> Url 'Https
470https = Url Https . pure
472-- | Grow given 'Url' appending a single path segment to it.
475(/:) :: Url scheme -> Text -> Url scheme
476Url secure path /: segment = Url secure (NE.cons segment path)
478-- | The 'parseUrlHttp' function provides an alternative method to get 'Url'
479-- (possibly with some 'Option's) from a 'ByteString'. This is useful when
480-- you are given an URL to query dynamically and don't know it beforehand.
481-- The function parses 'ByteString' because it's a correct type to hold an
482-- URL, as 'Url' cannot contain characters outside of ASCII range, thus we
483-- can consider every character a 'Data.Word.Word8' value.
485-- This function only parses 'Url' (scheme, host, path) and optional query
486-- parameters that are returned as 'Option'. It does not parse method name
487-- or authentication info from given 'ByteString'.
489parseUrlHttp :: ByteString -> Maybe (Url 'Http, Option scheme)
490parseUrlHttp url' = do
491 url <- B.stripPrefix "http://" url'
492 (host :| path, option) <- parseUrlHelper url
493 return (foldl (/:) (http host) path, option)
495-- | Just like 'parseUrlHttp', but expects “https” scheme.
497parseUrlHttps :: ByteString -> Maybe (Url 'Https, Option scheme)
498parseUrlHttps url' = do
499 url <- B.stripPrefix "https://" url'
500 (host :| path, option) <- parseUrlHelper url
501 return (foldl (/:) (https host) path, option)
503-- | Get host\/collection of path pieces and possibly query parameters
504-- already converted to 'Option'. This function is not public.
506parseUrlHelper :: ByteString -> Maybe (NonEmpty Text, Option scheme)
507parseUrlHelper url = do
508 let (path', query') = B.break (== 0x3f) url
509 query = mconcat (uncurry queryParam <$> Y.parseQueryText query')
510 path <- NE.nonEmpty (Y.decodePathSegments path')
513instance RequestComponent (Url scheme) where
514 getRequestMod (Url scheme segments) = Endo $ \x ->
515 let (host :| path) = NE.reverse segments in
516 x { L.secure = case scheme of
519 , L.port = case scheme of
522 , L.host = Y.urlEncode False (T.encodeUtf8 host)
524 (BL.toStrict . R.toLazyByteString . Y.encodePathSegments) path }
526----------------------------------------------------------------------------
529class HttpBody b where
530 type ProvidesBody b :: CanHaveBody
531 getReqestBody :: b -> ByteString -- FIXME should use a conduit here
533data FormUrlEncodedParam = FormUrlEncodedParam Text (Maybe Text)
535instance QueryParam FormUrlEncodedParam where
536 queryParam = FormUrlEncodedParam
538instance HttpBody b => RequestComponent (Womb "body" b) where
539 getRequestMod = undefined -- FIXME
541----------------------------------------------------------------------------
542-- Request — Optional parameters
544-- $request-optional-parameters
546-- Optional parameters to a request include things like query parameters,
547-- headers, port number, etc. All optional parameters have the type
548-- 'Option', which is a 'Monoid'. This means that you can use 'mempty' as
549-- the last argument of 'req' to specify no optional parameters, or combine
550-- 'Option's using 'mappend' (or @('<>')@) to have several of them at once.
552-- | Opaque 'Option' type is a 'Monoid' you can use to pack collection of
553-- optional parameters like query parameters and headers. See sections below
554-- to learn which 'Option' primitives are available.
556-- TODO We need examples here.
558data Option (scheme :: Scheme) =
559 Option (Endo (Y.QueryText, L.Request)) (Maybe (Endo L.Request))
560 -- NOTE 'QueryText' is just [(Text, Maybe Text)], we keep it along with
561 -- Request to avoid appending to existing query string in request every
562 -- time new parameter is added. Additional Maybe (Endo Request) is a
563 -- finalizer that will be applied after all over options. This is for
564 -- authentication methods that sign requests based on data in Request.
566instance Semigroup (Option scheme) where
567 Option er0 mr0 <> Option er1 mr1 = Option
568 (er0 <> er1) (mr0 <|> mr1)
570instance Monoid (Option scheme) where
571 mempty = Option mempty Nothing
574-- | A helper to create an 'Option' that modifies only collection of query
575-- parameters. This helper is not a part of public API.
577withQueryParams :: (Y.QueryText -> Y.QueryText) -> Option scheme
578withQueryParams f = Option (Endo (first f)) Nothing
580-- | A helper to create an 'Option' that modifies only 'L.Request'. This
581-- helper is not a part of public API.
583withRequest :: (L.Request -> L.Request) -> Option scheme
584withRequest f = Option (Endo (second f)) Nothing
586-- | A helper to create an 'Option' that adds a finalier (request
587-- endomorphism that is run after all other modifications).
589withFinalizer :: (L.Request -> L.Request) -> Option scheme
590withFinalizer f = Option mempty (Just (Endo f))
592instance RequestComponent (Option scheme) where
593 getRequestMod (Option f finalizer) = Endo $ \x ->
594 let (qparams, x') = appEndo f ([], x)
595 query = Y.renderQuery True (Y.queryTextToQuery qparams)
596 in maybe id appEndo finalizer x' { L.queryString = query }
598----------------------------------------------------------------------------
599-- Request — Optional parameters — Query Parameters
601-- | This operator builds a query parameter that will be included in URL of
602-- your request after question sign @?@. This is the same syntax you use
603-- with form URL encoded request bodies.
605-- This operator is defined in terms of 'queryParam':
607-- > name =: value = queryParam name (pure value)
610(=:) :: QueryParam a => Text -> Text -> a
611name =: value = queryParam name (pure value)
613-- | Construct a flag, that is, valueless query parameter. For example, in
614-- the following URL @a@ is a flag, @b@ is a query parameter with a value:
616-- > https://httpbin.org/foo/bar?a&b=10
618-- This operator is defined in terms of 'queryParam':
620-- > queryFlag name = queryParam name Nothing
622queryFlag :: QueryParam a => Text -> a
623queryFlag name = queryParam name Nothing
625-- | A type class for query-parameter-like things. The reason to have
626-- overloaded 'queryParam' is to be able to use as an 'Option' and as a
627-- 'FormUrlEncodedParam' when constructing form URL encoded request bodies.
628-- Having the same syntax for these cases seems natural and user-friendly.
630class QueryParam a where
632 -- | Create a query parameter with given name and value. If value is
633 -- 'Nothing', it won't be included at all (i.e. you create a flag this
634 -- way). It's recommended to use @('=:')@ and 'queryFlag' instead of this
635 -- method, because they are easier to read.
637 queryParam :: Text -> Maybe Text -> a
639instance QueryParam (Option scheme) where
640 queryParam name mvalue = withQueryParams ((:) (name, mvalue))
642----------------------------------------------------------------------------
643-- Request — Optional parameters — Headers
645-- | Create an 'Option' that adds a header.
648 :: ByteString -- ^ Header name
649 -> ByteString -- ^ Header value
651header name value = withRequest $ \x ->
652 x { L.requestHeaders = (CI.mk name, value) : L.requestHeaders x }
654----------------------------------------------------------------------------
655-- Request — Optional parameters — Cookies
657-- TODO No idea right now.
659----------------------------------------------------------------------------
660-- Request — Optional parameters — Authentication
662-- $request-optional-parameters-authentication
664-- TODO Something. You should always prefer authentication 'Option's to
665-- manual construction of headers, etc., because it's a better style and
666-- provides additional type safety that prevents leaking of credentials.
668-- | OAuth 1 authentication 'Option'.
671 :: ByteString -- ^ Consumer token
672 -> ByteString -- ^ Consumer secret
673 -> ByteString -- ^ OAuth token
674 -> ByteString -- ^ OAuth token secret
676oAuth1 ctoken csecret otoken osecret = withFinalizer
677 (OAuth1.signRequest ctoken csecret otoken osecret)
679-- | The 'Option' adds basic authentication. The 'Text' values will be UTF-8
682-- See also: <https://en.wikipedia.org/wiki/Basic_access_authentication>
685 :: ByteString -- ^ Username
686 -> ByteString -- ^ Password
687 -> Option 'Https -- ^ Auth 'Option'
688basicAuth username password = withRequest
689 (L.applyBasicAuth username password)
691-- | The 'Option' adds an OAuth2 bearer token. This is treated by many
692-- services as the equivalent of a username and password.
694-- The 'Option' is defined as:
696-- > oAuth2Bearer token = header "Authorization" ("Bearer " <> token)
699 :: ByteString -- ^ Token
700 -> Option 'Https -- ^ Auth 'Option'
701oAuth2Bearer token = header "Authorization" ("Bearer " <> token)
703-- | The 'Option' adds a not-quite-standard OAuth2 bearer token (that seems
704-- to be used only by GitHub). This will be treated by whatever services
705-- accept it as the equivalent of a username and password.
707-- The 'Option' is defined as:
709-- > oAuth2Token token = header "Authorization" ("token" <> token)
712 :: ByteString -- ^ Token
713 -> Option 'Https -- ^ Auth 'Option'
714oAuth2Token token = header "Authorization" ("token" <> token)
716-- | The 'Option' adds AWS v4 request signature.
719 :: ByteString -- ^ AWS access key
720 -> ByteString -- ^ AWS secret access key
721 -> Option 'Https -- ^ Auth 'Option'
722awsAuth accessKey secretKey = withFinalizer
723 (AWS.signRequest accessKey secretKey)
725----------------------------------------------------------------------------
726-- Request — Optional parameters — Other
728-- | Specify the port to connect to explicitly. Normally, 'Url' you use
729-- determines default port, @80@ for HTTP and @443@ for HTTPS, this 'Option'
730-- allows to choose arbitrary port overwriting the defaults.
732port :: Word -> Option scheme
733port n = withRequest $ \x ->
734 x { L.port = fromIntegral n }
736-- | This 'Option' controls whether gzipped data should be decompressed on
737-- the fly. By default everything except for @application\/x-tar@ is
738-- decompressed, i.e. we have:
740-- > decompress (/= "application/x-tar")
742-- You can also choose to decompress everything like this:
744-- > decompress (const True)
747 :: (ByteString -> Bool) -- ^ Predicate that is given MIME type, it
748 -- returns 'True' when content should be decompressed on the fly.
750decompress f = withRequest $ \x ->
751 x { L.decompress = f }
753-- | Specify number of microseconds to wait for response. Default is 30
757 :: Word -- ^ Number of microseconds to wait
759responseTimeout n = withRequest $ \x ->
760 x { L.responseTimeout = LI.ResponseTimeoutMicro (fromIntegral n) }
762-- | HTTP version to send to server, default is HTTP 1.1.
765 :: Word -- ^ Major version number
766 -> Word -- ^ Minor version number
768httpVersion major minor = withRequest $ \x ->
769 x { L.requestVersion =
770 Y.HttpVersion (fromIntegral major) (fromIntegral minor) }
772----------------------------------------------------------------------------
775-- Here we need to provide various options how to consume responses.
777class HttpResponse response where
778 getHttpResponse :: L.Manager -> L.Request -> IO response
782----------------------------------------------------------------------------
785-- | The main class for things that are “parts” of 'L.Request' in the sense
786-- that if we have a 'L.Request', then we know how to apply an instance of
787-- 'RequestComponent' changing\/overwriting something in it. 'Endo' is
788-- endomorphism of functions under composition, it's used to chain different
789-- request component easier using @('<>')@.
791class RequestComponent a where
793 -- | Get a function that takes a 'L.Request' and changes it somehow
794 -- returning another 'L.Request'. For example HTTP method instance of
795 -- 'RequestComponent' just overwrites method. The function is wrapped in
796 -- 'Endo' so it's easier to chain such “modifying applications” together
797 -- building bigger and bigger 'RequestComponent's.
799 getRequestMod :: a -> Endo L.Request
801-- | This wrapper is only used to attach a type-level tag to given type.
802-- This is necessary to define instances of 'RequestComponent' for any thing
803-- that implements 'HttpMethod' or 'HttpBody'. Without the tag, GHC is not
804-- able to see difference between @'HttpMethod' method => 'RequestComponent'
805-- method@ and @'HttpBody' body => 'RequestComponent' body@ when it decides
806-- which instance to use (i.e. constraints are taken into account later,
807-- when instance is already chosen).
809newtype Womb (tag :: Symbol) a = Womb a
811-- | A simple 'Bool'-like type we only have for better error messages. We
812-- use it as a kind and its data constructors as type-level tags.
814-- See also: 'HttpMethod' and 'HttpBody'.
817 = CanHaveBody -- ^ Indeed can have a body
818 | NoBody -- ^ Should not have a body
820-- | A type-level tag that specifies URL scheme used (and thus if TLS is
821-- enabled). This is used to force TLS requirement for some authentication
825 = Http -- ^ HTTP, no TLS
827 deriving (Eq, Ord, Show, Data, Typeable, Generic)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list