http-monad 0.0.1 → 0.1
raw patch · 8 files changed
+96/−70 lines, 8 filesdep ~explicit-exceptiondep ~transformersPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: explicit-exception, transformers
API changes (from Hackage documentation)
- Network.Monad.Body: instance (CharType char) => C [char]
- Network.Monad.HTTP: instance (Monoid body) => Monoid (ChunkedResponse body)
- Network.Monad.HTTP: instance (Show body) => Show (ChunkedResponse body)
- Network.Monad.Transfer.ChunkyLazyIO: instance (CharType char) => Body [char]
- Network.Monad.Transfer.Offline: instance (CharType char) => Body [char]
+ Network.Monad.Body: instance CharType char => C [char]
+ Network.Monad.HTTP: instance Monoid body => Monoid (ChunkedResponse body)
+ Network.Monad.HTTP: instance Show body => Show (ChunkedResponse body)
+ Network.Monad.Transfer.ChunkyLazyIO: instance CharType char => Body [char]
+ Network.Monad.Transfer.Offline: instance CharType char => Body [char]
- Network.Monad.Body: class (Monoid body) => C body
+ Network.Monad.Body: class Monoid body => C body
- Network.Monad.Body: fromChar :: (CharType char) => Char -> char
+ Network.Monad.Body: fromChar :: CharType char => Char -> char
- Network.Monad.Body: fromString :: (C body) => String -> body
+ Network.Monad.Body: fromString :: C body => String -> body
- Network.Monad.Body: isEmpty :: (C body) => body -> Bool
+ Network.Monad.Body: isEmpty :: C body => body -> Bool
- Network.Monad.Body: isLineTerm :: (C body) => body -> Bool
+ Network.Monad.Body: isLineTerm :: C body => body -> Bool
- Network.Monad.Body: toChar :: (CharType char) => char -> Char
+ Network.Monad.Body: toChar :: CharType char => char -> Char
- Network.Monad.Body: toString :: (C body) => body -> String
+ Network.Monad.Body: toString :: C body => body -> String
- Network.Monad.HTTP.Header: find :: (HasHeaders a) => Name -> a -> Maybe String
+ Network.Monad.HTTP.Header: find :: HasHeaders a => Name -> a -> Maybe String
- Network.Monad.HTTP.Header: findMany :: (HasHeaders a) => Name -> a -> [String]
+ Network.Monad.HTTP.Header: findMany :: HasHeaders a => Name -> a -> [String]
- Network.Monad.HTTP.Header: getHeaders :: (HasHeaders x) => x -> [Header]
+ Network.Monad.HTTP.Header: getHeaders :: HasHeaders x => x -> [Header]
- Network.Monad.HTTP.Header: getMany :: (HasHeaders x) => x -> [T]
+ Network.Monad.HTTP.Header: getMany :: HasHeaders x => x -> [T]
- Network.Monad.HTTP.Header: insert :: (HasHeaders a) => Name -> String -> a -> a
+ Network.Monad.HTTP.Header: insert :: HasHeaders a => Name -> String -> a -> a
- Network.Monad.HTTP.Header: insertIfMissing :: (HasHeaders a) => Name -> String -> a -> a
+ Network.Monad.HTTP.Header: insertIfMissing :: HasHeaders a => Name -> String -> a -> a
- Network.Monad.HTTP.Header: insertMany :: (HasHeaders a) => [T] -> a -> a
+ Network.Monad.HTTP.Header: insertMany :: HasHeaders a => [T] -> a -> a
- Network.Monad.HTTP.Header: modifyMany :: (HasHeaders x) => ([T] -> [T]) -> x -> x
+ Network.Monad.HTTP.Header: modifyMany :: HasHeaders x => ([T] -> [T]) -> x -> x
- Network.Monad.HTTP.Header: replace :: (HasHeaders a) => Name -> String -> a -> a
+ Network.Monad.HTTP.Header: replace :: HasHeaders a => Name -> String -> a -> a
- Network.Monad.HTTP.Header: retrieveMany :: (HasHeaders a) => Name -> a -> [T]
+ Network.Monad.HTTP.Header: retrieveMany :: HasHeaders a => Name -> a -> [T]
- Network.Monad.HTTP.Header: setHeaders :: (HasHeaders x) => x -> [Header] -> x
+ Network.Monad.HTTP.Header: setHeaders :: HasHeaders x => x -> [Header] -> x
- Network.Monad.HTTP.Header: setMany :: (HasHeaders x) => x -> [T] -> x
+ Network.Monad.HTTP.Header: setMany :: HasHeaders x => x -> [T] -> x
- Network.Monad.Reader: readBlock :: (Monad m) => Int -> AsyncExceptional body m body
+ Network.Monad.Reader: readBlock :: Monad m => Int -> AsyncExceptional body m body
- Network.Monad.Reader: readLine :: (Monad m) => AsyncExceptional body m body
+ Network.Monad.Reader: readLine :: Monad m => AsyncExceptional body m body
- Network.Monad.Reader: type AsyncExceptional body m a = (T body m) (Exceptional ConnError a)
+ Network.Monad.Reader: type AsyncExceptional body m = ExceptionalT ConnError (T body m)
- Network.Monad.Reader: type SyncExceptional body m a = ExceptionalT ConnError (T body m) a
+ Network.Monad.Reader: type SyncExceptional body m = ExceptionalT ConnError (T body m)
- Network.Monad.Reader: writeBlock :: (Monad m) => body -> SyncExceptional body m ()
+ Network.Monad.Reader: writeBlock :: Monad m => body -> SyncExceptional body m ()
- Network.Monad.Transfer: liftIOSync :: (MonadIO io) => IO (Result a) -> SyncExceptional io a
+ Network.Monad.Transfer: liftIOSync :: MonadIO io => IO (Result a) -> SyncExceptional io a
- Network.Monad.Transfer: type AsyncExceptional m a = m (Exceptional ConnError a)
+ Network.Monad.Transfer: type AsyncExceptional m = ExceptionalT ConnError m
- Network.Monad.Transfer.ChunkyLazyIO: class (C body) => Body body
+ Network.Monad.Transfer.ChunkyLazyIO: class C body => Body body
- Network.Monad.Transfer.ChunkyLazyIO: length :: (Body body) => body -> Int
+ Network.Monad.Transfer.ChunkyLazyIO: length :: Body body => body -> Int
- Network.Monad.Transfer.Offline: breakAfter :: (Body body) => (Char -> Bool) -> body -> (body, body)
+ Network.Monad.Transfer.Offline: breakAfter :: Body body => (Char -> Bool) -> body -> (body, body)
- Network.Monad.Transfer.Offline: class (C body) => Body body
+ Network.Monad.Transfer.Offline: class C body => Body body
- Network.Monad.Transfer.Offline: run :: (Body body) => T body (T body) a -> ConnError -> body -> (a, body, [body])
+ Network.Monad.Transfer.Offline: run :: Body body => T body (T body) a -> ConnError -> body -> (a, body, [body])
- Network.Monad.Transfer.Offline: splitAt :: (Body body) => Int -> body -> (body, body)
+ Network.Monad.Transfer.Offline: splitAt :: Body body => Int -> body -> (body, body)
- Network.Monad.Transfer.Offline: transfer :: (Body body) => T (T body) body
+ Network.Monad.Transfer.Offline: transfer :: Body body => T (T body) body
- Network.Monad.Transfer.Offline: withBuffer :: (C body) => (body -> (a, body)) -> AsyncExceptional (T body) a
+ Network.Monad.Transfer.Offline: withBuffer :: C body => (body -> (a, body)) -> AsyncExceptional (T body) a
Files
- http-monad.cabal +5/−5
- src/Network/Monad/Exception.hs +13/−0
- src/Network/Monad/HTTP.hs +39/−37
- src/Network/Monad/Reader.hs +9/−7
- src/Network/Monad/Transfer.hs +18/−8
- src/Network/Monad/Transfer/ChunkyLazyIO.hs +9/−11
- src/Network/Monad/Transfer/IO.hs +1/−1
- src/Network/Monad/Transfer/Offline.hs +2/−1
http-monad.cabal view
@@ -1,5 +1,5 @@ Name: http-monad-Version: 0.0.1+Version: 0.1 Cabal-Version: >= 1.6 Build-type: Simple License: BSD3@@ -9,7 +9,7 @@ Author: Henning Thielemann <http@henning-thielemann.de> Maintainer: Henning Thielemann <http@henning-thielemann.de> -- Homepage: http://www.haskell.org/http/-Synopsis: Monad abstraction for HTTP communication allowing lazy transfer and non-I/O simulation+Synopsis: Monad abstraction for HTTP allowing lazy transfer and non-I/O simulation Description: This library implements a monad class with various interesting instances: .@@ -30,7 +30,7 @@ Source-Repository this type: darcs location: http://code.haskell.org/~thielema/http-monad/- tag: 0.0.1+ tag: 0.0.2 Flag splitBase description: Old, monolithic base@@ -59,8 +59,8 @@ Build-depends: HTTP >=4000 && <4001 Build-depends: network >=2.1 && <3, parsec >=2.1 && <3 Build-depends: bytestring >=0.9 && <0.10- Build-depends: transformers >=0.0.1 && <0.2- Build-depends: explicit-exception >=0.1 && <0.2+ Build-depends: transformers >=0.2 && <0.3+ Build-depends: explicit-exception >=0.1.4 && <0.2 Build-depends: utility-ht >=0.0.4 && <0.1 Build-depends: lazyio >=0.0.2 && <0.1
src/Network/Monad/Exception.hs view
@@ -14,12 +14,25 @@ import qualified Control.Monad.Exception.Asynchronous as Async import qualified Control.Monad.Exception.Synchronous as Sync+import Control.Applicative (WrappedMonad(WrapMonad), unwrapMonad, ) import Control.Monad (liftM, ) import Data.Monoid (Monoid, mappend, ) +import Prelude hiding (map, ) + type AsyncExceptionalT e m a = m (Async.Exceptional e a)++-- in contrast to 'fmap' it does require Monad instance, not Functor+map ::+ (Monad m) =>+ (a -> b) ->+ Async.ExceptionalT body m a -> Async.ExceptionalT body m b+map f =+ Async.mapExceptionalT unwrapMonad .+ fmap f .+ Async.mapExceptionalT WrapMonad infixr 1 `bind`, `append`, `continue`
src/Network/Monad/HTTP.hs view
@@ -27,7 +27,7 @@ Response(..), ResponseData, ResponseCode, ) import Network.Monad.Reader (readLine, readBlock, writeBlock, )-import Control.Monad.Trans (lift, )+import Control.Monad.Trans.Class (lift, ) import qualified Control.Monad.Exception.Asynchronous as Async import qualified Control.Monad.Exception.Synchronous as Sync@@ -47,7 +47,7 @@ Sync.ExceptionalT ConnError (StreamMonad.T body m) a type AsynchronousExceptional body m a =- (StreamMonad.T body m) (Async.Exceptional ConnError a)+ Async.ExceptionalT ConnError (StreamMonad.T body m) a @@ -178,7 +178,8 @@ getResponseHead :: (Monad m, Body.C body) => SynchronousExceptional body m (Async.Exceptional ConnError ResponseData) getResponseHead = Sync.ExceptionalT $- liftM (Async.sequence . fmap (parseResponseHead . map Body.toString)) readTillEmpty1+ liftM (Async.sequence . fmap (parseResponseHead . map Body.toString)) $+ Async.runExceptionalT readTillEmpty1 withResponseHead :: (Monad m, Body.C body) => (ResponseData -> SynchronousExceptional body m (Async.Exceptional ConnError (Response body))) -> SynchronousExceptional body m (Async.Exceptional ConnError (Response body)) withResponseHead =@@ -226,7 +227,8 @@ ExpectEntity -> let tc = Header.lookup Header.HdrTransferEncoding hdrs cl = Header.lookup Header.HdrContentLength hdrs- in lift $ assembleHeaderBody (Response cd rn) hdrs $+ in lift $ Async.runExceptionalT $+ assembleHeaderBody (Response cd rn) hdrs $ case tc of Nothing -> case cl of@@ -268,13 +270,14 @@ receive = Exc.switchM getRequestHead (\(rm,uri,hdrs) -> return $ Request uri rm hdrs mempty)- (lift . processRequest)+ (lift . Async.runExceptionalT . processRequest) -- | Reads and parses request headers. getRequestHead :: (Monad m, Body.C body) => SynchronousExceptional body m (Async.Exceptional ConnError RequestData) getRequestHead = Sync.ExceptionalT $- liftM (Async.sequence . fmap (parseRequestHead . map Body.toString)) readTillEmpty1+ liftM (Async.sequence . fmap (parseRequestHead . map Body.toString)) $+ Async.runExceptionalT readTillEmpty1 -- | Process request body (called after successful getRequestHead) processRequest :: (Monad m, Body.C body) => RequestData -> AsynchronousExceptional body m (Request body)@@ -287,7 +290,7 @@ Nothing -> case cl of Just x -> linearTransferStrLen x- Nothing -> return $ Async.pure ([], mempty)+ Nothing -> mempty -- hopefulTransfer Just x -> case map toLower (trim x) of@@ -301,8 +304,8 @@ -} assembleHeaderBody :: (Monad m) => ([Header.T] -> body -> a) -> [Header.T] -> AsynchronousExceptional body m ([Header.T], body) -> AsynchronousExceptional body m a assembleHeaderBody make hdrs =- liftM (fmap (\(_ftrs,bdy) -> make hdrs bdy))--- liftM (fmap (\(ftrs,bdy) -> make (hdrs++ftrs) bdy))+ Exc.map (\(_ftrs,bdy) -> make hdrs bdy)+-- Exc.map (\(ftrs,bdy) -> make (hdrs++ftrs) bdy) {- | Very simple function, send a HTTP response over the given stream.@@ -324,12 +327,12 @@ linearTransferStrLen ns = case reads ns of [(n,"")] -> linearTransfer n- _ -> return $ Async.throwMonoid $ ErrorParse $ "Content-Length header contains not a number: " ++ show ns+ _ -> Async.throwMonoidT $ ErrorParse $ "Content-Length header contains not a number: " ++ show ns -- | Used when we know exactly how many bytes to expect. linearTransfer :: Monad m => Int -> AsynchronousExceptional body m ([Header.T],body) linearTransfer n =- liftM (fmap ((,) [])) $ readBlock n+ Exc.map ((,) []) $ readBlock n -- | Used when nothing about data is known, -- Unfortunately waiting for a socket closure@@ -340,9 +343,9 @@ let go = readLineSwitch $ \line -> if Body.isEmpty line- then return $ Async.pure mempty- else liftM (fmap (mappend line)) go- in liftM (fmap ((,) [])) go+ then mempty+ else Exc.map (mappend line) go+ in Exc.map ((,) []) go -- | in contrast to built-in @(,,)@, its mappend implementation is lazy@@ -366,11 +369,11 @@ -} chunkedTransfer :: (Monad m, Body.C body) => Bool -> AsynchronousExceptional body m ([Header.T],body) chunkedTransfer attachLength =- liftM (fmap (\(ChunkedResponse ftrs sizes info) ->+ Exc.map (\(ChunkedResponse ftrs sizes info) -> ((if attachLength then (Header.Header Header.HdrContentLength (show $ sum sizes) :) else id) ftrs,- info))) $+ info)) $ chunkedTransferLoop {- we do not sum up the chunk size here@@ -382,20 +385,20 @@ [(size,_)] -> if size == 0 then- liftM- (fmap (\strs -> ChunkedResponse (Header.parseManyStraight $ map Body.toString strs) [0] mempty))+ Exc.map (\strs -> ChunkedResponse (Header.parseManyStraight $ map Body.toString strs) [0] mempty) readTillEmpty2 else- liftM (fmap (\block -> ChunkedResponse [] [0] block)) (readBlock size)- `Async.appendM`- (liftM- (\newLineE ->- mplus- (Async.exception newLineE)- (toMaybe- (not $ Body.isLineTerm $ Async.result newLineE)- (ErrorParse $ "no CR+LF after chunk"))) $- readBlock 2)+ Exc.map (\block -> ChunkedResponse [] [0] block) (readBlock size)+ `mappend`+ Async.ExceptionalT+ ((liftM+ (\newLineE ->+ mplus+ (Async.exception newLineE)+ (toMaybe+ (not $ Body.isLineTerm $ Async.result newLineE)+ (ErrorParse $ "no CR+LF after chunk"))) $+ Async.runExceptionalT (readBlock 2)) {- less efficient since it reads an entire line (liftM (\newLineE ->@@ -408,12 +411,12 @@ )) readLine) -}- `Async.continueM`- liftM (fmap forceCR) chunkedTransferLoop+ `Async.continueM`+ Async.runExceptionalT (Exc.map forceCR chunkedTransferLoop)) _ -> {- old implementation continued reading anyway in this case as if the Chunk length was 0 -}- return $ Async.throwMonoid+ Async.throwMonoidT (ErrorParse $ "Chunk-Length is not a number: " ++ show (Body.toString line)) @@ -422,8 +425,7 @@ -- the name. uglyDeathTransfer :: (Monad m, Monoid body) => AsynchronousExceptional body m ([Header.T],body) uglyDeathTransfer =- return $- Async.throwMonoid $+ Async.throwMonoidT $ ErrorParse "Unknown Transfer-Encoding" @@ -436,7 +438,7 @@ readLineSwitch $ \s -> if Body.isLineTerm s then readTillEmpty1- else liftM (fmap (s:)) readTillEmpty2+ else Exc.map (s:) readTillEmpty2 -- | Read lines until an empty line (CRLF), -- also accepts a connection close as end of@@ -447,8 +449,8 @@ readTillEmpty2 = readLineSwitch $ \s -> if Body.isLineTerm s || Body.isEmpty s- then return $ Async.pure []- else liftM (fmap (s:)) readTillEmpty2+ then mempty+ else Exc.map (s:) readTillEmpty2 {- |@@ -459,7 +461,7 @@ -} readLineSwitch :: (Monad m, Monoid a) => (body -> AsynchronousExceptional body m a) -> AsynchronousExceptional body m a readLineSwitch next =- Exc.bind (Sync.ExceptionalT $ liftM Async.toSynchronous readLine) next+ Async.bindT readLine next {- strict variant do lineE <- readLine maybe
src/Network/Monad/Reader.hs view
@@ -7,7 +7,7 @@ import qualified Network.Stream as Stream import Control.Monad.Trans.Reader (ReaderT, asks, )-import Control.Monad.Trans (lift, )+import Control.Monad.Trans.Class (lift, ) import qualified Control.Monad.Exception.Asynchronous as Async import qualified Control.Monad.Exception.Synchronous as Sync@@ -16,24 +16,26 @@ type T body m = ReaderT (Transfer.T m body) m -type SyncExceptional body m a =- Sync.ExceptionalT Stream.ConnError (T body m) a+type SyncExceptional body m =+ Sync.ExceptionalT Stream.ConnError (T body m) -type AsyncExceptional body m a =- (T body m) (Async.Exceptional Stream.ConnError a)+type AsyncExceptional body m =+ Async.ExceptionalT Stream.ConnError (T body m) {-# INLINE readLine #-} readLine :: (Monad m) => AsyncExceptional body m body readLine =+ Async.ExceptionalT $ do action <- asks Transfer.readLine- lift action+ lift $ Async.runExceptionalT action {-# INLINE readBlock #-} readBlock :: (Monad m) => Int -> AsyncExceptional body m body readBlock n =+ Async.ExceptionalT $ do action <- asks Transfer.readBlock- lift $ action n+ lift $ Async.runExceptionalT $ action n {-# INLINE writeBlock #-} writeBlock :: (Monad m) => body -> SyncExceptional body m ()
src/Network/Monad/Transfer.hs view
@@ -18,21 +18,21 @@ module Network.Monad.Transfer where import qualified Network.Stream as Stream-import Control.Monad.Trans (MonadIO(liftIO), )-import Control.Monad (liftM, )+import Control.Monad.IO.Class (MonadIO(liftIO), ) import qualified Control.Monad.Exception.Asynchronous as Async import qualified Control.Monad.Exception.Synchronous as Sync+import Control.Applicative (WrappedMonad(WrapMonad), unwrapMonad, ) -import Data.Monoid (Monoid)+import Data.Monoid (Monoid, ) type SyncExceptional m = Sync.ExceptionalT Stream.ConnError m -type AsyncExceptional m a =- m (Async.Exceptional Stream.ConnError a)+type AsyncExceptional m =+ Async.ExceptionalT Stream.ConnError m data T m body = Cons {@@ -49,6 +49,16 @@ liftIOAsync :: (MonadIO io, Monoid a) => IO (Stream.Result a) -> AsyncExceptional io a-liftIOAsync m =- liftM (Async.fromSynchronousMonoid . Sync.fromEither) $- liftIO m+liftIOAsync =+ Async.mapExceptionalT unwrapMonad .+ Async.fromSynchronousMonoidT .+ Sync.mapExceptionalT WrapMonad .+ liftIOSync+{-+liftIOAsync =+ Async.ExceptionalT .+ liftM (Async.fromSynchronous mempty) .+ Sync.runExceptionalT .+ liftIOSync+-}+-- Async.fromSynchronousMonoidT . liftIOSync
src/Network/Monad/Transfer/ChunkyLazyIO.hs view
@@ -10,7 +10,6 @@ import qualified Network.TCP as TCP import Control.Monad.Trans.Reader (ReaderT, runReaderT, )-import Control.Monad (liftM, ) import qualified Control.Monad.Exception.Asynchronous as Async @@ -68,15 +67,14 @@ readBlockChunky :: (TCP.HStream body, Body body) => Int -> TCP.HandleStream body -> Int -> Transfer.AsyncExceptional LazyIO.T body-readBlockChunky chunkSize h n =+readBlockChunky chunkSize h = let go todo = if todo>0- then -- we cannot use Async.appendM because 'length str' is needed- do ~(Async.Exceptional e str) <-- Transfer.liftIOAsync $- TCP.readBlock h (min chunkSize todo)- liftM (fmap (mappend str)) $- maybe (go (max 0 (todo - length str)))- (return . Async.throwMonoid) e- else return $ Async.pure mempty- in go n+ then -- we cannot use 'mappend' because 'length str' is needed+ do (Transfer.liftIOAsync $+ TCP.readBlock h (min chunkSize todo))+ `Async.bindT`+ (\str ->+ fmap (mappend str) $ go (max 0 (todo - length str)))+ else mempty+ in go
src/Network/Monad/Transfer/IO.hs view
@@ -5,7 +5,7 @@ import qualified Network.TCP as TCP import Control.Monad.Trans.Reader (ReaderT, runReaderT, )-import Control.Monad.Trans (MonadIO, )+import Control.Monad.IO.Class (MonadIO, ) import Data.Monoid (Monoid, )
src/Network/Monad/Transfer/Offline.hs view
@@ -11,7 +11,7 @@ import qualified Network.Stream as Stream import Control.Monad.Trans.Reader (ReaderT, runReaderT, ) import Control.Monad.Trans.RWS (RWS, runRWS, tell, )-import Control.Monad.Trans (lift, )+import Control.Monad.Trans.Class (lift, ) import qualified Control.Monad.Trans.RWS as RWS @@ -67,6 +67,7 @@ (body -> (a, body)) -> Transfer.AsyncExceptional (T body) a withBuffer f =+ Async.ExceptionalT $ {- It is important to run all monadic actions independent from the exceptional case of an empty buffer,