{-# language OverloadedStrings #-}
{-|
Module: HTTP.Client

Thin wrapper around wreq and http-client to provide a robust HTTP(S) connection manager
and re-export the subset of wreq and http-types symbols used throughout RESTman.

The key entry point is 'robustSettings', which configures an 'HC.ManagerSettings' with
a 30-second response timeout to prevent hangs on slow proxies or captive portals.

Note: The underlying http-client library has a hardcoded 8 KB limit per header line that
cannot be configured in this version. Some sites (e.g. slashdot.org with its extensive
@Content-Security-Policy@ header listing hundreds of domains) may exceed this limit and
will fail with an @OverlongHeaders@ exception. This is a known limitation of the
http-client library version in use.
-}
module HTTP.Client
    ( -- * HTTP.Client helpers
      UseDefaultHeaders(..)
    , knownMethods
    , Method
    , robustSettings
      -- * wreq re-exports
    , customMethodWith
    , customPayloadMethodWith
    , defaults
    , headers
    , responseBody
    , responseHeader
      -- * http-types re-exports
    , Header
    ) where

-- http-client
import qualified Network.HTTP.Client as HC
-- http-client-tls
import qualified Network.HTTP.Client.TLS as HCT
-- http-types
import Network.HTTP.Types.Header (Header)
-- wreq
import Network.Wreq
  ( customMethodWith
  , customPayloadMethodWith
  , defaults
  , headers
  , responseBody
  , responseHeader
  )

-- | Helper type for clarity of intention.
type Method = String

-- | Bool-ish (for now) with more descriptive names.
data UseDefaultHeaders
  = ReplaceDefaultHeaders          -- ^ Discard wreq's default headers; use only custom headers.
  | AppendCustomToDefaultHeaders   -- ^ Keep wreq's default headers and append custom headers.
  deriving (UseDefaultHeaders -> UseDefaultHeaders -> Bool
(UseDefaultHeaders -> UseDefaultHeaders -> Bool)
-> (UseDefaultHeaders -> UseDefaultHeaders -> Bool)
-> Eq UseDefaultHeaders
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: UseDefaultHeaders -> UseDefaultHeaders -> Bool
== :: UseDefaultHeaders -> UseDefaultHeaders -> Bool
$c/= :: UseDefaultHeaders -> UseDefaultHeaders -> Bool
/= :: UseDefaultHeaders -> UseDefaultHeaders -> Bool
Eq, Eq UseDefaultHeaders
Eq UseDefaultHeaders =>
(UseDefaultHeaders -> UseDefaultHeaders -> Ordering)
-> (UseDefaultHeaders -> UseDefaultHeaders -> Bool)
-> (UseDefaultHeaders -> UseDefaultHeaders -> Bool)
-> (UseDefaultHeaders -> UseDefaultHeaders -> Bool)
-> (UseDefaultHeaders -> UseDefaultHeaders -> Bool)
-> (UseDefaultHeaders -> UseDefaultHeaders -> UseDefaultHeaders)
-> (UseDefaultHeaders -> UseDefaultHeaders -> UseDefaultHeaders)
-> Ord UseDefaultHeaders
UseDefaultHeaders -> UseDefaultHeaders -> Bool
UseDefaultHeaders -> UseDefaultHeaders -> Ordering
UseDefaultHeaders -> UseDefaultHeaders -> UseDefaultHeaders
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: UseDefaultHeaders -> UseDefaultHeaders -> Ordering
compare :: UseDefaultHeaders -> UseDefaultHeaders -> Ordering
$c< :: UseDefaultHeaders -> UseDefaultHeaders -> Bool
< :: UseDefaultHeaders -> UseDefaultHeaders -> Bool
$c<= :: UseDefaultHeaders -> UseDefaultHeaders -> Bool
<= :: UseDefaultHeaders -> UseDefaultHeaders -> Bool
$c> :: UseDefaultHeaders -> UseDefaultHeaders -> Bool
> :: UseDefaultHeaders -> UseDefaultHeaders -> Bool
$c>= :: UseDefaultHeaders -> UseDefaultHeaders -> Bool
>= :: UseDefaultHeaders -> UseDefaultHeaders -> Bool
$cmax :: UseDefaultHeaders -> UseDefaultHeaders -> UseDefaultHeaders
max :: UseDefaultHeaders -> UseDefaultHeaders -> UseDefaultHeaders
$cmin :: UseDefaultHeaders -> UseDefaultHeaders -> UseDefaultHeaders
min :: UseDefaultHeaders -> UseDefaultHeaders -> UseDefaultHeaders
Ord, Int -> UseDefaultHeaders
UseDefaultHeaders -> Int
UseDefaultHeaders -> [UseDefaultHeaders]
UseDefaultHeaders -> UseDefaultHeaders
UseDefaultHeaders -> UseDefaultHeaders -> [UseDefaultHeaders]
UseDefaultHeaders
-> UseDefaultHeaders -> UseDefaultHeaders -> [UseDefaultHeaders]
(UseDefaultHeaders -> UseDefaultHeaders)
-> (UseDefaultHeaders -> UseDefaultHeaders)
-> (Int -> UseDefaultHeaders)
-> (UseDefaultHeaders -> Int)
-> (UseDefaultHeaders -> [UseDefaultHeaders])
-> (UseDefaultHeaders -> UseDefaultHeaders -> [UseDefaultHeaders])
-> (UseDefaultHeaders -> UseDefaultHeaders -> [UseDefaultHeaders])
-> (UseDefaultHeaders
    -> UseDefaultHeaders -> UseDefaultHeaders -> [UseDefaultHeaders])
-> Enum UseDefaultHeaders
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
$csucc :: UseDefaultHeaders -> UseDefaultHeaders
succ :: UseDefaultHeaders -> UseDefaultHeaders
$cpred :: UseDefaultHeaders -> UseDefaultHeaders
pred :: UseDefaultHeaders -> UseDefaultHeaders
$ctoEnum :: Int -> UseDefaultHeaders
toEnum :: Int -> UseDefaultHeaders
$cfromEnum :: UseDefaultHeaders -> Int
fromEnum :: UseDefaultHeaders -> Int
$cenumFrom :: UseDefaultHeaders -> [UseDefaultHeaders]
enumFrom :: UseDefaultHeaders -> [UseDefaultHeaders]
$cenumFromThen :: UseDefaultHeaders -> UseDefaultHeaders -> [UseDefaultHeaders]
enumFromThen :: UseDefaultHeaders -> UseDefaultHeaders -> [UseDefaultHeaders]
$cenumFromTo :: UseDefaultHeaders -> UseDefaultHeaders -> [UseDefaultHeaders]
enumFromTo :: UseDefaultHeaders -> UseDefaultHeaders -> [UseDefaultHeaders]
$cenumFromThenTo :: UseDefaultHeaders
-> UseDefaultHeaders -> UseDefaultHeaders -> [UseDefaultHeaders]
enumFromThenTo :: UseDefaultHeaders
-> UseDefaultHeaders -> UseDefaultHeaders -> [UseDefaultHeaders]
Enum, UseDefaultHeaders
UseDefaultHeaders -> UseDefaultHeaders -> Bounded UseDefaultHeaders
forall a. a -> a -> Bounded a
$cminBound :: UseDefaultHeaders
minBound :: UseDefaultHeaders
$cmaxBound :: UseDefaultHeaders
maxBound :: UseDefaultHeaders
Bounded, Int -> UseDefaultHeaders -> [Char] -> [Char]
[UseDefaultHeaders] -> [Char] -> [Char]
UseDefaultHeaders -> [Char]
(Int -> UseDefaultHeaders -> [Char] -> [Char])
-> (UseDefaultHeaders -> [Char])
-> ([UseDefaultHeaders] -> [Char] -> [Char])
-> Show UseDefaultHeaders
forall a.
(Int -> a -> [Char] -> [Char])
-> (a -> [Char]) -> ([a] -> [Char] -> [Char]) -> Show a
$cshowsPrec :: Int -> UseDefaultHeaders -> [Char] -> [Char]
showsPrec :: Int -> UseDefaultHeaders -> [Char] -> [Char]
$cshow :: UseDefaultHeaders -> [Char]
show :: UseDefaultHeaders -> [Char]
$cshowList :: [UseDefaultHeaders] -> [Char] -> [Char]
showList :: [UseDefaultHeaders] -> [Char] -> [Char]
Show, ReadPrec [UseDefaultHeaders]
ReadPrec UseDefaultHeaders
Int -> ReadS UseDefaultHeaders
ReadS [UseDefaultHeaders]
(Int -> ReadS UseDefaultHeaders)
-> ReadS [UseDefaultHeaders]
-> ReadPrec UseDefaultHeaders
-> ReadPrec [UseDefaultHeaders]
-> Read UseDefaultHeaders
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
$creadsPrec :: Int -> ReadS UseDefaultHeaders
readsPrec :: Int -> ReadS UseDefaultHeaders
$creadList :: ReadS [UseDefaultHeaders]
readList :: ReadS [UseDefaultHeaders]
$creadPrec :: ReadPrec UseDefaultHeaders
readPrec :: ReadPrec UseDefaultHeaders
$creadListPrec :: ReadPrec [UseDefaultHeaders]
readListPrec :: ReadPrec [UseDefaultHeaders]
Read)

{-|
TLS-enabled 'HC.ManagerSettings' with a 30-second response timeout.

Use this when creating an 'HC.Manager' for RESTman so that requests do not
hang indefinitely on unresponsive proxies or captive portals.

> manager <- HC.newManager robustSettings
-}
robustSettings :: HC.ManagerSettings
robustSettings :: ManagerSettings
robustSettings = ManagerSettings
HCT.tlsManagerSettings
        { HC.managerResponseTimeout = HC.responseTimeoutMicro 30000000  -- 30 seconds
        -- Note: managerRawConnection can be used to customize the connection,
        -- but header size limits are hardcoded in the http-client parser.
        -- The default limit is 8KB per header line. Sites like slashdot.org
        -- may exceed this with long cookie headers.
        }

{-|
Lifted from
https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol&oldid=954964381#Request_methods
-}
knownMethods :: [Method]
knownMethods :: [[Char]]
knownMethods =
  [ [Char]
"GET"
  , [Char]
"HEAD"
  , [Char]
"POST"
  , [Char]
"PUT"
  , [Char]
"DELETE"
  , [Char]
"TRACE"
  , [Char]
"OPTIONS"
  , [Char]
"CONNECT"
  , [Char]
"PATCH"
  ]