packages feed

request-0.2.0.0: src/Network/HTTP/Request.hs

{-# LANGUAGE DatatypeContexts #-}
{-# LANGUAGE OverloadedStrings #-}

module Network.HTTP.Request
  ( Header
  , Headers
  , Method (..)
  , Request
  , Response
  , get
  , post
  , put
  , requestBody
  , requestHeaders
  , requestMethod
  , requestUrl
  , responseBody
  , responseHeaders
  , responseStatus
  , send
  ) where

import qualified Data.String               as S
import qualified Data.ByteString           as BS
import qualified Data.ByteString.Char8     as C
import qualified Data.ByteString.Lazy      as LBS
import qualified Data.CaseInsensitive      as CI
import qualified Data.List                 as List
import qualified Network.HTTP.Client       as LowLevelClient
import qualified Network.HTTP.Client.TLS   as LowLevelTLSClient
import qualified Network.HTTP.Types.Status as LowLevelStatus

type Header = (BS.ByteString, BS.ByteString)

type Headers = [Header]

data Method
  = DELETE
  | GET
  | HEAD
  | OPTIONS
  | PATCH
  | POST
  | PUT
  | TRACE
  | Method String

instance Show Method where
  show GET = "GET"
  show HEAD = "HEAD"
  show OPTIONS = "OPTIONS"
  show PATCH = "PATCH"
  show POST = "POST"
  show PUT = "PUT"
  show TRACE = "TRACE"
  show (Method method) = method

data (S.IsString a) => Request a = Request
  { requestMethod  :: Method
  , requestUrl     :: String
  , requestHeaders :: Headers
  , requestBody    :: Maybe a
  } deriving (Show)

toLowlevelRequest :: (S.IsString a) => Request a -> IO LowLevelClient.Request
toLowlevelRequest req = do
  initReq <- LowLevelClient.parseRequest $ requestUrl req
  return $ initReq { LowLevelClient.method = C.pack . show $ requestMethod req
                   , LowLevelClient.requestHeaders = map (\(k, v) -> (CI.mk k, v)) $ requestHeaders req
                   }

data Response = Response
  { responseStatus  :: Int
  , responseHeaders :: Headers
  , responseBody    :: BS.ByteString
  } deriving (Show)

fromLowLevelRequest :: LowLevelClient.Response LBS.ByteString -> Response
fromLowLevelRequest res =
  let status = LowLevelStatus.statusCode . LowLevelClient.responseStatus $ res
      body = LBS.toStrict $ LowLevelClient.responseBody res
      headers = LowLevelClient.responseHeaders res
  in
  Response status (map (\(k,v) ->
                         let hk = CI.original k
                         in
                         (hk, v)) headers) body

getManagerForUrl :: String -> IO LowLevelClient.Manager
getManagerForUrl url =
    if "https" `List.isPrefixOf` url then LowLevelClient.newManager LowLevelTLSClient.tlsManagerSettings
                                     else LowLevelClient.newManager LowLevelClient.defaultManagerSettings

send :: (S.IsString a) => Request a -> IO Response
send req = do
  manager <- getManagerForUrl $ requestUrl req
  llreq <- toLowlevelRequest req
  llres <- LowLevelClient.httpLbs llreq manager
  return $ fromLowLevelRequest llres

get :: String -> IO Response
get url =
  send $ Request GET url [] Nothing

delete :: String -> IO Response
delete url =
  send $ Request DELETE url [] Nothing

post :: (String, Maybe BS.ByteString) -> IO Response
post (url, body) =
  send $ Request POST url [] body

put :: (String, Maybe BS.ByteString) -> IO Response
put (url, body) =
  send $ Request PUT url [] body