packages feed

script-monad-0.0.4: src/Network/HTTP/Client/Extras.hs

{- |
Module      : Network.HTTP.Client.Extras
Description : Some stuff not included in Network.HTTP.Client
Copyright   : 2018, Automattic, Inc.
License     : BSD3
Maintainer  : Nathan Bloomfield (nbloomf@gmail.com)
Stability   : experimental
Portability : POSIX

HTTP helpers
-}

{-# LANGUAGE RecordWildCards, CPP #-}
module Network.HTTP.Client.Extras (
    Url
  , HttpResponse(..)
  , readHttpResponse
  , jsonResponseHeaders

  -- * Responses
  , _200ok
  , _400badRequest
  , _404notFound
  , _405methodNotAllowed
  , _408requestTimeout
  , _500internalServerError
) where



import qualified Data.ByteString as SB
  ( ByteString, unpack )
import Data.ByteString.Lazy
  ( ByteString, unpack )
import Data.Text
  ( Text )
import Data.Vector
  ( fromList )
import Network.HTTP.Client
  ( HttpException(..), CookieJar, HttpExceptionContent(StatusCodeException)
  , Response, responseCookieJar, responseBody, createCookieJar
  , responseHeaders, responseVersion, responseStatus )

#if MIN_VERSION_http_client(0,7,0)
-- http-client 0.7.0 removed the Eq instance for CookieJar in favor
-- of multiple explicit equivalence relations.
import Network.HTTP.Client
  ( equalCookieJar )
#endif

import Network.HTTP.Types
import Data.Aeson (Value(..), object, (.=))

#if MIN_VERSION_aeson(2,0,0)
-- aeson 2.0.0.0 introduced KeyMap over HashMap
import Data.Aeson.Key (fromString)
#endif

import qualified Data.Text as T (Text, pack)


-- | To make type signatures nicer
type Url = Text

-- | Non-opaque HTTP response type.
data HttpResponse = HttpResponse
  { _responseStatus :: Status
  , _responseVersion :: HttpVersion
  , _responseHeaders :: ResponseHeaders
  , _responseBody :: ByteString
  , _responseCookieJar :: CookieJar
  } deriving (Show)

instance Eq HttpResponse where
  r1 == r2 = and
    [ (==)           (_responseStatus    r1) (_responseStatus    r2)
    , (==)           (_responseVersion   r1) (_responseVersion   r2)
    , (==)           (_responseHeaders   r1) (_responseHeaders   r2)
    , (==)           (_responseBody      r1) (_responseBody      r2)

#if MIN_VERSION_http_client(0,7,0)
    , equalCookieJar (_responseCookieJar r1) (_responseCookieJar r2)
#else
    , (==)           (_responseCookieJar r1) (_responseCookieJar r2)
#endif

    ]

-- | Convert an opaque `Response ByteString` into an `HttpResponse`.
readHttpResponse :: Response ByteString -> HttpResponse
readHttpResponse r = HttpResponse
  { _responseStatus = responseStatus r
  , _responseVersion = responseVersion r
  , _responseHeaders = responseHeaders r
  , _responseBody = responseBody r
  , _responseCookieJar = responseCookieJar r
  }



-- | Convert response headers to a JSON value; specifically a list of objects, one for each header.
jsonResponseHeaders :: ResponseHeaders -> Value
jsonResponseHeaders =
  Array . fromList . map (\(k,v) -> object [ (key k) .= (val v) ])
  where

    key =
#if MIN_VERSION_aeson(2,0,0)
      fromString . concatMap esc . show
#else
      T.pack . concatMap esc . show
#endif

    val = T.pack . concatMap esc . show

    esc c = case c of
      '\\' -> "\\"
      '"'  -> "\\\""
      _    -> [c]



-- | Status 200; no headers
_200ok :: ByteString -> HttpResponse
_200ok body = HttpResponse
  { _responseStatus = status200
  , _responseVersion = http11
  , _responseHeaders = []
  , _responseBody = body
  , _responseCookieJar = createCookieJar []
  }

-- | Status 400; no headers
_400badRequest :: ByteString -> HttpResponse
_400badRequest body = HttpResponse
  { _responseStatus = status400
  , _responseVersion = http11
  , _responseHeaders = []
  , _responseBody = body
  , _responseCookieJar = createCookieJar []
  }

-- | Status 404; no headers
_404notFound :: ByteString -> HttpResponse
_404notFound body = HttpResponse
  { _responseStatus = status404
  , _responseVersion = http11
  , _responseHeaders = []
  , _responseBody = body
  , _responseCookieJar = createCookieJar []
  }

-- | Status 405; no headers
_405methodNotAllowed :: ByteString -> HttpResponse
_405methodNotAllowed body = HttpResponse
  { _responseStatus = status405
  , _responseVersion = http11
  , _responseHeaders = []
  , _responseBody = body
  , _responseCookieJar = createCookieJar []
  }

-- | Status 408; no headers
_408requestTimeout :: ByteString -> HttpResponse
_408requestTimeout body = HttpResponse
  { _responseStatus = status408
  , _responseVersion = http11
  , _responseHeaders = []
  , _responseBody = body
  , _responseCookieJar = createCookieJar []
  }

-- | Status 500; no headers
_500internalServerError :: ByteString -> HttpResponse
_500internalServerError body = HttpResponse
  { _responseStatus = status500
  , _responseVersion = http11
  , _responseHeaders = []
  , _responseBody = body
  , _responseCookieJar = createCookieJar []
  }