@@ -59,6 +59,7 @@ import Control.Monad.Except (MonadError (..))
5959import Control.Monad.Error (MonadError (.. ))
6060#endif
6161
62+ import Control.Monad (when )
6263import Control.Monad.Catch (MonadCatch (.. ), MonadThrow )
6364import Control.Monad.Trans.Class (lift )
6465import Control.Monad.Trans.Except (ExceptT (.. ), runExceptT )
@@ -67,13 +68,13 @@ import Data.List (find)
6768
6869import Network.HTTP.Client
6970 (HttpException (.. ), Manager , RequestBody (.. ), Response (.. ),
70- applyBasicAuth , httpLbs , method , newManager , requestBody ,
71- requestHeaders , setQueryString )
71+ applyBasicAuth , getUri , httpLbs , method , newManager , redirectCount ,
72+ requestBody , requestHeaders , setQueryString , setRequestIgnoreStatus )
7273import Network.HTTP.Client.TLS (tlsManagerSettings )
7374import Network.HTTP.Link.Parser (parseLinkHeaderBS )
7475import Network.HTTP.Link.Types (Link (.. ), LinkParam (.. ), href , linkParams )
7576import Network.HTTP.Types (Method , RequestHeaders , Status (.. ))
76- import Network.URI (URI )
77+ import Network.URI (URI , parseURIReference , relativeTo )
7778
7879#if !MIN_VERSION_http_client(0,5,0)
7980import qualified Control.Exception as E
@@ -82,6 +83,7 @@ import Network.HTTP.Types (ResponseHeaders)
8283
8384import qualified Data.ByteString.Lazy as LBS
8485import qualified Data.Text as T
86+ import qualified Data.Text.Encoding as TE
8587import qualified Data.Vector as V
8688import qualified Network.HTTP.Client as HTTP
8789import qualified Network.HTTP.Client.Internal as HTTP
@@ -125,6 +127,9 @@ executeRequestWithMgr mgr auth req = runExceptT $ do
125127 performHttpReq httpReq (StatusQuery sm _) = do
126128 res <- httpLbs' httpReq
127129 parseStatus sm . responseStatus $ res
130+ performHttpReq httpReq (RedirectQuery _) = do
131+ res <- httpLbs' httpReq
132+ parseRedirect (getUri httpReq) res
128133
129134 performHttpReq' :: FromJSON b => HTTP. Request -> SimpleRequest k b -> ExceptT Error IO b
130135 performHttpReq' httpReq Query {} = do
@@ -172,6 +177,9 @@ executeRequestWithMgr' mgr req = runExceptT $ do
172177 performHttpReq httpReq (StatusQuery sm _) = do
173178 res <- httpLbs' httpReq
174179 parseStatus sm . responseStatus $ res
180+ performHttpReq httpReq (RedirectQuery _) = do
181+ res <- httpLbs' httpReq
182+ parseRedirect (getUri httpReq) res
175183
176184 performHttpReq' :: FromJSON b => HTTP. Request -> SimpleRequest 'RO b -> ExceptT Error IO b
177185 performHttpReq' httpReq Query {} = do
@@ -222,6 +230,9 @@ makeHttpRequest auth r = case r of
222230 HeaderQuery h req -> do
223231 req' <- makeHttpSimpleRequest auth req
224232 return $ req' { requestHeaders = h <> requestHeaders req' }
233+ RedirectQuery req -> do
234+ req' <- makeHttpSimpleRequest auth req
235+ return $ setRequestIgnoreStatus $ req' { redirectCount = 0 }
225236
226237makeHttpSimpleRequest
227238 :: MonadThrow m
@@ -328,6 +339,24 @@ parseStatus m (Status sci _) =
328339 where
329340 err = throwError $ JsonError $ " invalid status: " <> T. pack (show sci)
330341
342+ -- | Helper for handling of 'RequestRedirect'.
343+ --
344+ -- @
345+ -- parseRedirect :: 'Response' 'LBS.ByteString' -> 'Either' 'Error' a
346+ -- @
347+ parseRedirect :: MonadError Error m => URI -> Response LBS. ByteString -> m URI
348+ parseRedirect originalUri rsp = do
349+ let status = responseStatus rsp
350+ when (statusCode status /= 302 ) $
351+ throwError $ ParseError $ " invalid status: " <> T. pack (show status)
352+ loc <- maybe noLocation return $ lookup " Location" $ responseHeaders rsp
353+ case parseURIReference $ T. unpack $ TE. decodeUtf8 loc of
354+ Nothing -> throwError $ ParseError $
355+ " location header does not contain a URI: " <> T. pack (show loc)
356+ Just uri -> return $ uri `relativeTo` originalUri
357+ where
358+ noLocation = throwError $ ParseError " no location header in response"
359+
331360-- | Helper for making paginated requests. Responses, @a@ are combined monoidally.
332361--
333362-- @
0 commit comments