{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleContexts #-} -- | This module contains everything you need to initiate HTTP connections. If -- you want a simple interface based on URLs, you can use 'simpleHttp'. If you -- want raw power, 'http' is the underlying workhorse of this package. Some -- examples: -- -- > -- Just download an HTML document and print it. -- > import Network.HTTP.Conduit -- > import qualified Data.ByteString.Lazy as L -- > -- > main = simpleHttp "http://www.haskell.org/" >>= L.putStr -- -- This example uses interleaved IO to write the response body to a file in -- constant memory space. By using 'httpRedirect', it will automatically -- follow 3xx redirects. -- -- > import Data.Conduit.Binary (sinkFile) -- > import Network.HTTP.Conduit -- > import System.IO -- > -- > main :: IO () -- > main = do -- > request <- parseUrl "http://google.com/" -- > withManager $ \manager -> do -- > let handler _ _ bsrc = bsrc C.$$ sinkFile "google.html" -- > run_ $ httpRedirect request handler manager -- -- The following headers are automatically set by this module, and should not -- be added to 'requestHeaders': -- -- * Content-Length -- -- * Host -- -- * Accept-Encoding (not currently set, but client usage of this variable /will/ cause breakage). -- -- Any network code on Windows requires some initialization, and the network -- library provides withSocketsDo to perform it. Therefore, proper usage of -- this library will always involve calling that function at some point. The -- best approach is to simply call them at the beginning of your main function, -- such as: -- -- > import Network.HTTP.Conduit -- > import qualified Data.ByteString.Lazy as L -- > import Network (withSocketsDo) -- > -- > main = withSocketsDo -- > $ simpleHttp "http://www.haskell.org/" >>= L.putStr module Network.HTTP.Conduit ( -- * Perform a request simpleHttp , httpLbs , httpLbsRedirect , http , httpRedirect , redirectConsumer -- * Datatypes , Proxy (..) , RequestBody (..) , Response (..) , ResponseConsumer -- ** Request , Request , def , method , secure , checkCerts , host , port , path , queryString , requestHeaders , requestBody , proxy , rawBody , decompress -- *** Defaults , defaultCheckCerts -- * Manager , Manager , newManager , withManager -- * Utility functions , parseUrl , applyBasicAuth , addProxy , lbsConsumer -- * Decompression predicates , alwaysDecompress , browserDecompress -- * Request bodies , urlEncodedBody -- * Exceptions , HttpException (..) ) where import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Char8 as S8 import qualified Network.HTTP.Types as W import Data.Default (def) import Control.Exception.Lifted (throwIO) import Control.Monad.Base (liftBase) import qualified Data.Conduit as C import Data.Conduit.Blaze (builderToByteString) import Control.Monad.Trans.Resource (ResourceT, runResourceT, ResourceIO) import Network.HTTP.Conduit.Request import Network.HTTP.Conduit.Response import Network.HTTP.Conduit.Manager import Network.HTTP.Conduit.ConnInfo -- | The most low-level function for initiating an HTTP request. -- -- The first argument to this function gives a full specification on the -- request: the host to connect to, whether to use SSL, headers, etc. Please -- see 'Request' for full details. -- -- The second argument specifies how the response should be handled. It's a -- function that takes two arguments: the first is the HTTP status code of the -- response, and the second is a list of all response headers. This module -- exports 'lbsConsumer', which generates a 'Response' value. -- -- Note that this allows you to have fully interleaved IO actions during your -- HTTP download, making it possible to download very large responses in -- constant memory. http :: ResourceIO m => Request m -> ResponseConsumer m a -> Manager -> ResourceT m a http req consumer m = withConn req m $ \ci -> do bsrc <- C.bufferSource $ connSource ci requestBuilder req C.$$ builderToByteString C.=$ connSink ci getResponse req consumer bsrc -- | Download the specified 'Request', returning the results as a 'Response'. -- -- This is a simplified version of 'http' for the common case where you simply -- want the response data as a simple datatype. If you want more power, such as -- interleaved actions on the response body during download, you'll need to use -- 'http' directly. This function is defined as: -- -- @httpLbs = http lbsConsumer@ -- -- Please see 'lbsConsumer' for more information on how the 'Response' value is -- created. -- -- Even though a 'Response' contains a lazy bytestring, this function does -- /not/ utilize lazy I/O, and therefore the entire response body will live in -- memory. If you want constant memory usage, you'll need to write your own -- iteratee and use 'http' or 'httpRedirect' directly. httpLbs :: ResourceIO m => Request m -> Manager -> ResourceT m Response httpLbs req = http req lbsConsumer -- | Download the specified URL, following any redirects, and return the -- response body. -- -- This function will 'throwIO' an 'HttpException' for any response with a -- non-2xx status code. It uses 'parseUrl' to parse the input. This function -- essentially wraps 'httpLbsRedirect'. -- -- Note: Even though this function returns a lazy bytestring, it does /not/ -- utilize lazy I/O, and therefore the entire response body will live in -- memory. If you want constant memory usage, you'll need to write your own -- iteratee and use 'http' or 'httpRedirect' directly. simpleHttp :: ResourceIO m => String -> m L.ByteString simpleHttp url = runResourceT $ do url' <- liftBase $ parseUrl url man <- newManager Response sc _ b <- httpLbsRedirect url' { decompress = browserDecompress } man if 200 <= sc && sc < 300 then return b else liftBase $ throwIO $ StatusCodeException sc b -- | Same as 'http', but follows all 3xx redirect status codes that contain a -- location header. httpRedirect :: ResourceIO m => Request m -> (W.Status -> W.ResponseHeaders -> C.BufferedSource m S.ByteString -> ResourceT m a) -> Manager -> ResourceT m a httpRedirect req bodyStep manager = http req (redirectConsumer 10 req bodyStep manager) manager -- | Download the specified 'Request', returning the results as a 'Response' -- and automatically handling redirects. -- -- This is a simplified version of 'httpRedirect' for the common case where you -- simply want the response data as a simple datatype. If you want more power, -- such as interleaved actions on the response body during download, you'll -- need to use 'httpRedirect' directly. This function is defined as: -- -- @httpLbsRedirect = httpRedirect lbsConsumer@ -- -- Please see 'lbsConsumer' for more information on how the 'Response' value is -- created. -- -- Even though a 'Response' contains a lazy bytestring, this function does -- /not/ utilize lazy I/O, and therefore the entire response body will live in -- memory. If you want constant memory usage, you'll need to write your own -- iteratee and use 'http' or 'httpRedirect' directly. httpLbsRedirect :: ResourceIO m => Request m -> Manager -> ResourceT m Response httpLbsRedirect req m = httpRedirect req lbsConsumer m -- | Make a request automatically follow 3xx redirects. -- -- Used internally by 'httpRedirect' and family. redirectConsumer :: ResourceIO m => Int -- ^ number of redirects to attempt -> Request m -- ^ Original request -> ResponseConsumer m a -> Manager -> ResponseConsumer m a redirectConsumer redirects req bodyStep manager s@(W.Status code _) hs bsrc | 300 <= code && code < 400 = case lookup "location" hs of Just l'' -> do -- Prepend scheme, host and port if missing let l' = case S8.uncons l'' of Just ('/', _) -> concat [ "http" , if secure req then "s" else "" , "://" , S8.unpack $ host req , ":" , show $ port req , S8.unpack l'' ] _ -> S8.unpack l'' l <- liftBase $ parseUrl l' let req' = req { host = host l , port = port l , secure = secure l , path = path l , queryString = queryString l , method = if code == 303 then "GET" else method l } if redirects == 0 then liftBase $ throwIO TooManyRedirects else (http req') (redirectConsumer (redirects - 1) req' bodyStep manager) manager Nothing -> bodyStep s hs bsrc | otherwise = bodyStep s hs bsrc