@@ -197,6 +197,10 @@ module Network.HTTP.Req
197197 , bsResponse
198198 , LbsResponse
199199 , lbsResponse
200+ , TextResponse
201+ , textResponse
202+ , LTextResponse
203+ , ltextResponse
200204 -- ** Inspecting a response
201205 , responseBody
202206 , responseStatusCode
@@ -246,6 +250,9 @@ import qualified Data.CaseInsensitive as CI
246250import qualified Data.List.NonEmpty as NE
247251import qualified Data.Text as T
248252import qualified Data.Text.Encoding as T
253+import qualified Data.Text.Encoding.Error as TE
254+import qualified Data.Text.Lazy as TL
255+import qualified Data.Text.Lazy.Encoding as TL
249256import qualified Data.Text.Read as TR
250257import qualified Network.Connection as NC
251258import qualified Network.HTTP.Client as L
@@ -292,6 +299,8 @@ import GHC.Exts (Constraint)
292299-- * 'jsonResponse'
293300-- * 'bsResponse' (to get a strict 'ByteString')
294301-- * 'lbsResponse' (to get a lazy 'BL.ByteString')
302+-- * 'textResponse' (to get a strict 'Text', UTF-8 decoded)
303+-- * 'ltextResponse' (to get a lazy 'TL.Text', UTF-8 decoded)
295304--
296305-- Finally, @options@ is a 'Monoid' that holds a composite 'Option' for all
297306-- other optional settings like query parameters, headers, non-standard port
@@ -1337,7 +1346,7 @@ httpVersion major minor = withRequest $ \x ->
13371346----------------------------------------------------------------------------
13381347-- Response interpretations
13391348
1340−-- | Make a request and ignore body of response.
1349+-- | Make a request and ignore the body of the response.
13411350
13421351data IgnoreResponse = IgnoreResponse (L.Response ())
13431352
@@ -1349,12 +1358,12 @@ instance HttpResponse IgnoreResponse where
13491358 makeResponseBodyPreview _ = "<ignored response>"
13501359
13511360-- | Use this as the fourth argument of 'req' to specify that you want it to
1352−-- return the 'IgnoreResponse' interpretation.
1361+-- ignore the response body.
13531362
13541363ignoreResponse :: Proxy IgnoreResponse
13551364ignoreResponse = Proxy
13561365
1357−-- | Make a request and interpret body of response as JSON. The
1366+-- | Make a request and interpret the body of the response as JSON. The
13581367-- 'handleHttpException' method of 'MonadHttp' instance corresponding to
13591368-- monad in which you use 'req' will determine what to do in the case when
13601369-- parsing fails (the 'JsonHttpException' constructor will be used).
@@ -1377,13 +1386,14 @@ instance FromJSON a => HttpResponse (JsonResponse a) where
13771386 return $ JsonResponse response { L.responseBody = x } preview
13781387 makeResponseBodyPreview (JsonResponse _ preview) = preview
13791388
1380−-- | Use this as the forth argument of 'req' to specify that you want it to
1389+-- | Use this as the fourth argument of 'req' to specify that you want it to
13811390-- return the 'JsonResponse' interpretation.
13821391
13831392jsonResponse :: Proxy (JsonResponse a)
13841393jsonResponse = Proxy
13851394
1386−-- | Make a request and interpret body of response as a strict 'ByteString'.
1395+-- | Make a request and interpret the body of the response as a strict
1396+-- 'ByteString'.
13871397
13881398newtype BsResponse = BsResponse (L.Response ByteString)
13891399
@@ -1391,18 +1401,17 @@ instance HttpResponse BsResponse where
13911401 type HttpResponseBody BsResponse = ByteString
13921402 toVanillaResponse (BsResponse response) = response
13931403 getHttpResponse request manager =
1394− L.withResponse request manager $ \response -> do
1395− chunks <- L.brConsume (L.responseBody response)
1396− return $ BsResponse response { L.responseBody = B.concat chunks }
1397− makeResponseBodyPreview = B.take bodyPreviewLength . responseBody
1404+ BsResponse <$> httpBs request manager
1405+ makeResponseBodyPreview =
1406+ B.take bodyPreviewLength . responseBody
13981407
1399−-- | Use this as the forth argument of 'req' to specify that you want to
1400−-- interpret response body as a strict 'ByteString'.
1408+-- | Use this as the fourth argument of 'req' to specify that you want to
1409+-- interpret the response body as a strict 'ByteString'.
14011410
14021411bsResponse :: Proxy BsResponse
14031412bsResponse = Proxy
14041413
1405−-- | Make a request and interpret body of response as a lazy
1414+-- | Make a request and interpret the body of the response as a lazy
14061415-- 'BL.ByteString'.
14071416
14081417newtype LbsResponse = LbsResponse (L.Response BL.ByteString)
@@ -1412,14 +1421,87 @@ instance HttpResponse LbsResponse where
14121421 toVanillaResponse (LbsResponse response) = response
14131422 getHttpResponse request manager =
14141423 LbsResponse <$> L.httpLbs request manager
1415− makeResponseBodyPreview = BL.toStrict . BL.take 1027 . responseBody
1424+ makeResponseBodyPreview =
1425+ BL.toStrict . BL.take bodyPreviewLength . responseBody
14161426
1417−-- | Use this as the forth argument of 'req' to specify that you want to
1418−-- interpret response body as a lazy 'BL.ByteString'.
1427+-- | Use this as the fourth argument of 'req' to specify that you want to
1428+-- interpret the response body as a lazy 'BL.ByteString'.
14191429
14201430lbsResponse :: Proxy LbsResponse
14211431lbsResponse = Proxy
14221432
1433+-- | Make a request and interpret the body of the response as strict 'Text'.
1434+-- The 'handleHttpException' method of 'MonadHttp' instance corresponding to
1435+-- monad in which you use 'req' will determine what to do in the case when
1436+-- decoding fails (the 'TextEncHttpException' constructor will be used).
1437+--
1438+-- @since 0.5.0
1439+
1440+newtype TextResponse = TextResponse (L.Response Text)
1441+
1442+instance HttpResponse TextResponse where
1443+ type HttpResponseBody TextResponse = Text
1444+ toVanillaResponse (TextResponse r) = r
1445+ getHttpResponse request manager = do
1446+ r <- httpBs request manager
1447+ let bs = L.responseBody r
1448+ case T.decodeUtf8' bs of
1449+ Left uexc ->
1450+ (throwIO . TextEncHttpException uexc . B.take bodyPreviewLength) bs
1451+ Right txt -> return (TextResponse r { L.responseBody = txt })
1452+ makeResponseBodyPreview =
1453+ B.take bodyPreviewLength . T.encodeUtf8 . responseBody
1454+
1455+-- | Use this as the fourth argument of 'req' to specify that you want to
1456+-- interpret the response body as 'T.Text'.
1457+--
1458+-- @since 0.5.0
1459+
1460+textResponse :: Proxy TextResponse
1461+textResponse = Proxy
1462+
1463+-- | Make a request and interpret the body of the response as lazy
1464+-- 'TL.Text'. The 'handleHttpException' method of 'MonadHttp' instance
1465+-- corresponding to monad in which you use 'req' will determine what to do
1466+-- in the case when decoding fails (the 'TextEncHttpException' constructor
1467+-- will be used).
1468+--
1469+-- @since 0.5.0
1470+
1471+newtype LTextResponse = LTextResponse (L.Response TL.Text)
1472+
1473+instance HttpResponse LTextResponse where
1474+ type HttpResponseBody LTextResponse = TL.Text
1475+ toVanillaResponse (LTextResponse r) = r
1476+ getHttpResponse request manager = do
1477+ r <- L.httpLbs request manager
1478+ case TL.decodeUtf8' (L.responseBody r) of
1479+ Left uexc -> throwIO
1480+ . TextEncHttpException uexc
1481+ . BL.toStrict
1482+ . BL.take bodyPreviewLength
1483+ . L.responseBody
1484+ $ r
1485+ Right txt -> return (LTextResponse r { L.responseBody = txt })
1486+ makeResponseBodyPreview =
1487+ BL.toStrict . BL.take bodyPreviewLength . TL.encodeUtf8 . responseBody
1488+
1489+-- | Use this as the fourth argument of 'req' to specify that you want to
1490+-- interpret the response body as a lazy 'TL.Text'.
1491+--
1492+-- @since 0.5.0
1493+
1494+ltextResponse :: Proxy LTextResponse
1495+ltextResponse = Proxy
1496+
1497+-- | Perform a 'L.Request' using given 'L.Manager' and return the response
1498+-- as a strict 'ByteString'.
1499+
1500+httpBs :: L.Request -> L.Manager -> IO (L.Response ByteString)
1501+httpBs request manager = L.withResponse request manager $ \response -> do
1502+ chunks <- L.brConsume (L.responseBody response)
1503+ return response { L.responseBody = B.concat chunks }
1504+
14231505----------------------------------------------------------------------------
14241506-- Inspecting a response
14251507
@@ -1540,7 +1622,14 @@ data HttpException
15401622 = VanillaHttpException L.HttpException
15411623 -- ^ A wrapper with an 'L.HttpException' from "Network.HTTP.Client"
15421624 | JsonHttpException String
1543− -- ^ A wrapper with Aeson-produced 'String' describing why decoding failed
1625+ -- ^ A wrapper with Aeson-produced 'String' describing why decoding
1626+ -- failed
1627+ | TextEncHttpException TE.UnicodeException ByteString
1628+ -- ^ This value indicates that a response that was expected to be in the
1629+ -- UTF-8 encoding could not be decoded; the 'ByteString' is the
1630+ -- beginning of response in raw binary form
1631+ --
1632+ -- @since 0.5.0
15441633 deriving (Show, Typeable, Generic)
15451634
15461635instance Exception HttpException