packages feed

hyena 0.1 → 0.1.0.1

raw patch · 6 files changed

+99/−55 lines, 6 filesdep ~bytestringdep ~containersdep ~mtlPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: bytestring, containers, mtl

API changes (from Hackage documentation)

- Hyena.Config: instance (Show a) => Show (Flag a)
+ Hyena.Config: instance Show a => Show (Flag a)

Files

Data/Enumerator.hs view
@@ -27,11 +27,15 @@  -- | Enumerates a 'ByteString'. bytesEnum :: Monad m => S.ByteString -> EnumeratorM m-bytesEnum bs f seed = do-  seed' <- f seed bs-  case seed' of-    Left seed''  -> return seed''-    Right seed'' -> return seed''+bytesEnum bs f seed =+  let block = S.take blockSize bs+  in if S.null block+     then return seed+     else do+       seed' <- f seed block+       case seed' of+         Left seed''  -> return seed''+         Right seed'' -> bytesEnum (S.drop blockSize bs) f seed''   nl :: Word8
Hyena/Http.hs view
@@ -20,6 +20,7 @@     ( -- * The request and response data types       Request(..),       Response(..),+      ReceiveResult(..),        -- * Sending and receiving       sendResponse,@@ -33,16 +34,19 @@       parseRequest     ) where +import Prelude hiding (catch) import qualified Data.ByteString as S import qualified Data.ByteString.Char8 as C (map, pack, unpack) import Data.Char (chr, digitToInt, isAlpha, isDigit, isSpace, ord, toLower) import Data.Either (either) import Control.Arrow+import Control.Exception (IOException, catch, throw)+import Control.Monad (when) import qualified Data.Map as M-import Data.Maybe (fromJust)+import Data.Maybe (isJust, fromJust) import Data.Word (Word8) import Network.Socket (Socket)-import Network.Socket.ByteString (recv, send)+import Network.Socket.ByteString (recv, sendAll) import Network.Wai (Enumerator, Headers, Method(..))  import Data.Enumerator@@ -77,6 +81,13 @@     , responseBody    :: Enumerator     } +-- | A data structure used to keep track of the result of parsing a+-- request from the client.+data ReceiveResult a = ParseSuccess a+                     | ParseError+                     | ClientDisconnect++ -- --------------------------------------------------------------------- -- Sending and receiving @@ -87,44 +98,50 @@ -- | Send response over socket. sendResponse :: Socket -> Response -> IO () sendResponse sock resp = do-  -- TODO: Check if all data was sent.-  send sock $ S.concat [C.pack "HTTP/1.1 "-                       ,(C.pack $ show (statusCode resp) ++ " "+  sendAll sock $ S.concat [C.pack "HTTP/1.1 "+                          ,(C.pack $ show (statusCode resp) ++ " "                               ++(C.unpack $ reasonPhrase resp))-                       ,C.pack "\r\n"]+                          ,C.pack "\r\n"]   sendHeaders sock (responseHeaders resp)-  send sock $ C.pack "\r\n"-  responseBody resp (sendMessageBody sock) ()+  sendAll sock $ C.pack "\r\n"+  r <- responseBody resp (sendMessageBody sock) Nothing+  when (isJust r) $ throw (fromJust r)   -- TODO: Flush the socket. --- TODO: Check if all bytes were sent, otherwise retry.- -- | Iteratee used for sending message body over socket.-sendMessageBody :: Socket -> () -> S.ByteString -> IO (Either () ())-sendMessageBody sock _ bs = send sock bs >> return (Right ())+sendMessageBody :: Socket+                -> Maybe IOException+                -> S.ByteString+                -> IO (Either (Maybe IOException) (Maybe IOException))+sendMessageBody sock _ bs =+  catch (sendAll sock bs >> return (Right Nothing))+        (\e -> return $ Left (Just e))  -- | Send headers over socket. sendHeaders :: Socket -> Headers -> IO () sendHeaders sock headers = do-  send sock $ S.concat $ map go headers+  sendAll sock $ S.concat $ map go headers   return ()     where go (k, v) = S.concat [k, C.pack ": "                                ,v, C.pack "\r\n"] --- | Receive request from socket.  If the request is malformed--- 'Nothing' is returned.-receiveRequest :: Socket -> IO (Maybe Request)+-- | Receive request from socket.  Returns @ParseError@ on parse+-- failure, @ClientDisconnect@ if the client disconnected+-- unexpectedly, and @ParseSuccess request@ on success.+receiveRequest :: Socket -> IO (ReceiveResult Request) receiveRequest sock = do   x <- parseIRequest sock   case x of-    Nothing  -> return Nothing-    Just (req, bs) ->+    ClientDisconnect -> return ClientDisconnect+    ParseError       -> return ParseError++    ParseSuccess (req, bs) ->         let len  = contentLength req             rest = bytesEnum bs             enum = case len of                      Just n  -> partialSocketEnum sock (n - S.length bs)                      Nothing -> chunkEnum $ socketEnum sock-        in return $ Just+        in return $ ParseSuccess            Request            { method         = iMethod req            , requestUri     = iRequestUri req@@ -195,17 +212,23 @@  -- | Parses the header part (i.e. everything expect for the request -- body) of an HTTP request.  Returns any bytes read that were not--- used when parsing.  Returns @Nothing@ on failure and @Just--- (request, remaining)@ on success.-parseIRequest :: Socket -> IO (Maybe (IRequest, S.ByteString))+-- used when parsing.  Returns @ParseError@ on parse failure,+-- @ClientDisconnect@ if the client disconnected unexpectedly, and+-- @ParseSuccess (request, remaining)@ on success.+parseIRequest :: Socket -> IO (ReceiveResult (IRequest, S.ByteString)) parseIRequest sock = do   initial <- recv sock blockSize-  go $ runParser pIRequest initial+  if S.null initial then+    return ClientDisconnect+    else go $ runParser pIRequest initial     where-      go (Finished req bs) = return $ Just (req, bs)-      go (Failed _)        = return Nothing-      -- TODO: Detect end of input.-      go (Partial k)       = recv sock blockSize >>= go . k . Just+      go (Finished req bs) = return $ ParseSuccess (req, bs)+      go (Failed _)        = return ParseError+      go (Partial k)       = do+        received <- recv sock blockSize+        if S.null received then do+          return ClientDisconnect+          else (go . k . Just) received  -- | Parser for the internal request data type. pIRequest :: Parser IRequest
Hyena/Logging.hs view
@@ -58,7 +58,7 @@ startLogger writer logHandle = do   chan <- newChan   finished' <- newEmptyMVar-  forkIO $ logMessages chan finished'+  _ <- forkIO $ logMessages chan finished'   return Logger { channel  = chan                 , finished = finished'                 }
Hyena/Server.hs view
@@ -111,7 +111,7 @@ serveWithConfig :: Config -> Application -> IO () serveWithConfig conf application = do #ifndef mingw32_HOST_OS-  installHandler sigPIPE Ignore Nothing+  _ <- installHandler sigPIPE Ignore Nothing #endif   when (daemonize conf) $ do     hPutStrLn stderr "Daemonized mode not supported at the moment."@@ -162,11 +162,11 @@ acceptConnections :: Application -> Socket -> Server () acceptConnections application serverSock = do   (sock, SockAddrInet _ haddr) <- io $ accept serverSock-  forkServer ((talk sock haddr application `finallyServer`-               (io $ sClose sock))-              `catchServer`-              (\e -> do logger <- asks errorLogger-                        io $ logError logger $ show e))+  _ <- forkServer ((talk sock haddr application `finallyServer`+                    (io $ sClose sock))+                   `catchServer`+                   (\e -> do logger <- asks errorLogger+                             io $ logError logger $ show e))   acceptConnections application serverSock  -- | Read the client input, parse the request, run the application,@@ -175,8 +175,9 @@ talk sock haddr application = do   req <- io $ receiveRequest sock   case req of-    Nothing  -> io $ sendResponse sock $ errorResponse 400-    Just req' ->+    ClientDisconnect  -> return ()+    ParseError        -> io $ sendResponse sock $ errorResponse 400+    ParseSuccess req' ->         -- TODO: Validate the request:         --    * If HTTP 1.1 Host MUST be present.         do errorLogger' <- asks errorLogger
Network/Wai.hs view
@@ -14,19 +14,20 @@ -- -- Example application: --+-- > {-# LANGUAGE Rank2Types, ImpredicativeTypes #-} -- > module Main where--- >+-- -- > import qualified Data.ByteString as S -- > import qualified Data.ByteString.Char8 as C (pack, unpack)--- > import Network.Wai (Application(..), Enumerator(..))+-- > import Hyena.Server+-- > import Network.Wai (Application, Enumerator, pathInfo) -- > import System.Directory (getCurrentDirectory) -- > import System.FilePath ((</>), makeRelative) -- > import System.IO--- >+-- -- > sendFile :: FilePath -> IO Enumerator--- > sendFile fname = do--- >   cwd <- getCurrentDirectory--- >   h <- openBinaryFile (cwd </> makeRelative "/" fname) ReadMode+-- > sendFile path = do+-- >   h <- openBinaryFile path ReadMode -- >   let yieldBlock f z = do -- >              block <- S.hGetNonBlocking h 1024 -- >              if S.null block then hClose h >> return z@@ -36,14 +37,25 @@ -- >                    Left z''  -> hClose h >> return z'' -- >                    Right z'' -> yieldBlock f z'' -- >   return yieldBlock--- >+-- -- > fileServer :: Application -- > fileServer environ = do+-- >   cwd <- getCurrentDirectory+-- >   let path = (cwd </> makeRelative "/" (C.unpack $ pathInfo environ))+-- >   size <- getFileSize path -- >   -- Here you should add security checks, etc. -- >   let contentType = (C.pack "Content-Type",--- >                      C.pack "application/octet-stream")--- >   enumerator <- sendFile $ C.unpack $ pathInfo environ--- >   return (200, pack "OK", [contentType], enumerator)+-- >                      C.pack "text/plain")+-- >       contentLength = (C.pack "Content-Length",+-- >                        C.pack (show size))+-- >   enumerator <- sendFile path+-- >   return (200, C.pack "OK", [contentType,contentLength], enumerator)+--+-- > getFileSize :: String -> IO Integer+-- > getFileSize fn = withFile fn ReadMode hFileSize+--+-- > main :: IO ()+-- > main = serve fileServer -- ------------------------------------------------------------------------ 
hyena.cabal view
@@ -1,5 +1,5 @@ name:           hyena-version:        0.1+version:        0.1.0.1 synopsis:       Simple web application server description:   A simple web application server using iteratees.@@ -25,12 +25,12 @@    build-depends:     base               == 4.*,-    bytestring,-    containers,+    bytestring         < 1.0,+    containers         >= 0.3 && < 0.4,     directory,     extensible-exceptions,     filepath,-    mtl                >= 1 && < 1.2,+    mtl                >= 1 && < 2.0.2,     network            >= 2.1 && < 2.3,     network-bytestring >= 0.1.1.2 && < 0.2 @@ -42,3 +42,7 @@   ghc-options: -funbox-strict-fields -Wall   if impl(ghc >= 6.8)     ghc-options: -fwarn-tabs++source-repository head+  type:     git+  location: git://github.com/tibbe/hyena.git