module Network.HTTP.HandleStream
( simpleHTTP
, simpleHTTP_
, sendHTTP
, sendHTTP_notify
, receiveHTTP
, respondHTTP
, simpleHTTP_debug
) where
import Network.BufferType
import Network.Stream ( fmapE, Result )
import Network.StreamDebugger ( debugByteStream )
import Network.TCP (HStream(..), HandleStream )
import Network.HTTP.Base
import Network.HTTP.Headers
import Network.HTTP.Utils ( trim, readsOne )
import Data.Char (toLower)
import Data.Maybe (fromMaybe)
import Control.Exception (onException)
import Control.Monad (when)
simpleHTTP :: HStream ty => Request ty -> IO (Result (Response ty))
simpleHTTP r = do
auth <- getAuth r
failHTTPS (rqURI r)
c <- openStream (host auth) (fromMaybe 80 (port auth))
simpleHTTP_ c r
simpleHTTP_debug :: HStream ty => FilePath -> Request ty -> IO (Result (Response ty))
simpleHTTP_debug httpLogFile r = do
auth <- getAuth r
failHTTPS (rqURI r)
c0 <- openStream (host auth) (fromMaybe 80 (port auth))
c <- debugByteStream httpLogFile c0
simpleHTTP_ c r
simpleHTTP_ :: HStream ty => HandleStream ty -> Request ty -> IO (Result (Response ty))
simpleHTTP_ s r = sendHTTP s r
sendHTTP :: HStream ty => HandleStream ty -> Request ty -> IO (Result (Response ty))
sendHTTP conn rq = sendHTTP_notify conn rq (return ())
sendHTTP_notify :: HStream ty
=> HandleStream ty
-> Request ty
-> IO ()
-> IO (Result (Response ty))
sendHTTP_notify conn rq onSendComplete = do
when providedClose $ (closeOnEnd conn True)
onException (sendMain conn rq onSendComplete)
(close conn)
where
providedClose = findConnClose (rqHeaders rq)
sendMain :: HStream ty
=> HandleStream ty
-> Request ty
-> (IO ())
-> IO (Result (Response ty))
sendMain conn rqst onSendComplete = do
_ <- writeBlock conn (buf_fromStr bufferOps $ show rqst)
_ <- writeBlock conn (rqBody rqst)
onSendComplete
rsp <- getResponseHead conn
switchResponse conn True False rsp rqst
switchResponse :: HStream ty
=> HandleStream ty
-> Bool
-> Bool
-> Result ResponseData
-> Request ty
-> IO (Result (Response ty))
switchResponse _ _ _ (Left e) _ = return (Left e)
switchResponse conn allow_retry bdy_sent (Right (cd,rn,hdrs)) rqst =
case matchResponse (rqMethod rqst) cd of
Continue
| not bdy_sent -> do
writeBlock conn (rqBody rqst) >>= either (return . Left)
(\ _ -> do
rsp <- getResponseHead conn
switchResponse conn allow_retry True rsp rqst)
| otherwise -> do
rsp <- getResponseHead conn
switchResponse conn allow_retry bdy_sent rsp rqst
Retry -> do
_ <- writeBlock conn ((buf_append bufferOps)
(buf_fromStr bufferOps (show rqst))
(rqBody rqst))
rsp <- getResponseHead conn
switchResponse conn False bdy_sent rsp rqst
Done -> do
when (findConnClose hdrs)
(closeOnEnd conn True)
return (Right $ Response cd rn hdrs (buf_empty bufferOps))
DieHorribly str -> do
close conn
return (responseParseError "Invalid response:" str)
ExpectEntity -> do
r <- fmapE (\ (ftrs,bdy) -> Right (Response cd rn (hdrs++ftrs) bdy)) $
maybe (maybe (hopefulTransfer bo (readLine conn) [])
(\ x ->
readsOne (linearTransfer (readBlock conn))
(return$responseParseError "unrecognized content-length value" x)
x)
cl)
(ifChunked (chunkedTransfer bo (readLine conn) (readBlock conn))
(uglyDeathTransfer "sendHTTP"))
tc
case r of
Left{} -> do
close conn
return r
Right (Response _ _ hs _) -> do
when (findConnClose hs)
(closeOnEnd conn True)
return r
where
tc = lookupHeader HdrTransferEncoding hdrs
cl = lookupHeader HdrContentLength hdrs
bo = bufferOps
getResponseHead :: HStream ty => HandleStream ty -> IO (Result ResponseData)
getResponseHead conn =
fmapE (\es -> parseResponseHead (map (buf_toStr bufferOps) es))
(readTillEmpty1 bufferOps (readLine conn))
receiveHTTP :: HStream bufTy => HandleStream bufTy -> IO (Result (Request bufTy))
receiveHTTP conn = getRequestHead >>= either (return . Left) processRequest
where
getRequestHead :: IO (Result RequestData)
getRequestHead = do
fmapE (\es -> parseRequestHead (map (buf_toStr bufferOps) es))
(readTillEmpty1 bufferOps (readLine conn))
processRequest (rm,uri,hdrs) =
fmapE (\ (ftrs,bdy) -> Right (Request uri rm (hdrs++ftrs) bdy)) $
maybe
(maybe (return (Right ([], buf_empty bo)))
(\ x -> readsOne (linearTransfer (readBlock conn))
(return$responseParseError "unrecognized Content-Length value" x)
x)
cl)
(ifChunked (chunkedTransfer bo (readLine conn) (readBlock conn))
(uglyDeathTransfer "receiveHTTP"))
tc
where
tc = lookupHeader HdrTransferEncoding hdrs
cl = lookupHeader HdrContentLength hdrs
bo = bufferOps
respondHTTP :: HStream ty => HandleStream ty -> Response ty -> IO ()
respondHTTP conn rsp = do
_ <- writeBlock conn (buf_fromStr bufferOps $ show rsp)
_ <- writeBlock conn (rspBody rsp)
return ()
headerName :: String -> String
headerName x = map toLower (trim x)
ifChunked :: a -> a -> String -> a
ifChunked a b s =
case headerName s of
"chunked" -> a
_ -> b