on master help sign in
Switch to Sockets from Handles
f94a767Michael Snoyman
Network/Wai/Handler/Warp.hs+32−24
@@ -28,8 +28,12 @@ import qualified Data.ByteString as S
28import qualified Data.ByteString.Char8 as B
28import qualified Data.ByteString.Char8 as B
29import qualified Data.ByteString.Lazy as L
29import qualified Data.ByteString.Lazy as L
30import Network
30import Network
31− ( listenOn, accept, sClose, PortID(PortNumber), Socket
31+ ( listenOn, sClose, PortID(PortNumber), Socket
32 , withSocketsDo)
32 , withSocketsDo)
33+import Network.Socket
34+ ( accept
35+ )
36+import qualified Network.Socket.ByteString as Sock
33import Control.Exception (bracket, finally, Exception)
37import Control.Exception (bracket, finally, Exception)
34import System.IO (Handle, hClose, hFlush)
38import System.IO (Handle, hClose, hFlush)
35import System.IO.Error (isEOFError, ioeGetHandle)
39import System.IO.Error (isEOFError, ioeGetHandle)
@@ -50,7 +54,7 @@ import Blaze.ByteString.Builder.HTTP
50import Blaze.ByteString.Builder (fromByteString, Builder, toLazyByteString)
54import Blaze.ByteString.Builder (fromByteString, Builder, toLazyByteString)
51import Blaze.ByteString.Builder.Char8 (fromChar, fromString)
55import Blaze.ByteString.Builder.Char8 (fromChar, fromString)
52import Data.Monoid (mconcat)
56import Data.Monoid (mconcat)
53−import Network.Socket.SendFile (unsafeSendFile)
57+import Network.Socket.SendFile (sendFile)
54
58
55import Control.Monad.IO.Class (liftIO)
59import Control.Monad.IO.Class (liftIO)
56
60
@@ -64,35 +68,26 @@ type Port = Int
64
68
65serveConnections :: Port -> Application -> Socket -> IO ()
69serveConnections :: Port -> Application -> Socket -> IO ()
66serveConnections port app socket = do
70serveConnections port app socket = do
67− (conn, remoteHost', _) <- accept socket -- FIXME use sockets directly instead of Handlers?
71+ (conn, _) <- accept socket
68− -- FIXME disable buffering
72+ let remoteHost' = undefined
69 _ <- forkIO $ serveConnection port app conn remoteHost'
73 _ <- forkIO $ serveConnection port app conn remoteHost'
70 serveConnections port app socket
74 serveConnections port app socket
71
75
72−serveConnection :: Port -> Application -> Handle -> String -> IO ()
76+serveConnection :: Port -> Application -> Socket -> String -> IO ()
73serveConnection port app conn remoteHost' = do
77serveConnection port app conn remoteHost' = do
74 catch
78 catch
75 (finally
79 (finally
76 (E.run_ $ fromClient $$ serveConnection')
80 (E.run_ $ fromClient $$ serveConnection')
77− (hClose conn))
81+ (sClose conn))
78− catchEOFError
82+ undefined
79 where
83 where
80− fromClient = enumHandle 4096 conn
84+ fromClient = enumSocket 4096 conn
81 serveConnection' = do
85 serveConnection' = do
82 (enumeratee, env) <- parseRequest port remoteHost'
86 (enumeratee, env) <- parseRequest port remoteHost'
83 res <- E.joinI $ enumeratee $$ app env
87 res <- E.joinI $ enumeratee $$ app env
84 keepAlive <- liftIO $ sendResponse env (httpVersion env) conn res
88 keepAlive <- liftIO $ sendResponse env (httpVersion env) conn res
85− liftIO $ hFlush conn
86 when keepAlive serveConnection'
89 when keepAlive serveConnection'
87
90
88− catchEOFError :: IOError -> IO ()
89− catchEOFError e
90− | isEOFError e =
91− case ioeGetHandle e of
92− Just h -> unless (h == conn) (ioError e)
93− Nothing -> ioError e
94− | otherwise = ioError e
95−
96parseRequest :: Port -> String -> E.Iteratee S.ByteString IO (E.Enumeratee ByteString ByteString IO a, Request)
91parseRequest :: Port -> String -> E.Iteratee S.ByteString IO (E.Enumeratee ByteString ByteString IO a, Request)
97parseRequest port remoteHost' = do
92parseRequest port remoteHost' = do
98 headers' <- takeUntilBlank 0 id
93 headers' <- takeUntilBlank 0 id
@@ -222,20 +217,20 @@ isChunked = (==) http11
222hasBody :: Status -> Request -> Bool
217hasBody :: Status -> Request -> Bool
223hasBody s req = s /= (Status 204 "") && requestMethod req /= "HEAD"
218hasBody s req = s /= (Status 204 "") && requestMethod req /= "HEAD"
224
219
225−sendResponse :: Request -> HttpVersion -> Handle -> Response -> IO Bool
220+sendResponse :: Request -> HttpVersion -> Socket -> Response -> IO Bool
226−sendResponse req hv handle (ResponseFile s hs fp) = do
221+sendResponse req hv socket (ResponseFile s hs fp) = do
227− mapM_ (S.hPut handle) $ L.toChunks $ toLazyByteString $ headers hv s hs False
222+ Sock.sendMany socket $ L.toChunks $ toLazyByteString $ headers hv s hs False
228 if hasBody s req
223 if hasBody s req
229 then do
224 then do
230− unsafeSendFile handle fp
225+ sendFile socket fp
231 return $ lookup "content-length" hs /= Nothing
226 return $ lookup "content-length" hs /= Nothing
232 else return True
227 else return True
233−sendResponse req hv handle (ResponseEnumerator res) =
228+sendResponse req hv socket (ResponseEnumerator res) =
234 res go
229 res go
235 where
230 where
236 go s hs
231 go s hs
237 | not (hasBody s req) = do
232 | not (hasBody s req) = do
238− liftIO $ mapM_ (S.hPut handle)
233+ liftIO $ Sock.sendMany socket
239 $ L.toChunks $ toLazyByteString
234 $ L.toChunks $ toLazyByteString
240 $ headers hv s hs False
235 $ headers hv s hs False
241 return True
236 return True
@@ -243,7 +238,7 @@ sendResponse req hv handle (ResponseEnumerator res) =
243 chunk'
238 chunk'
244 $ E.enumList 1 [headers hv s hs isChunked']
239 $ E.enumList 1 [headers hv s hs isChunked']
245 $$ E.joinI $ builderToByteString
240 $$ E.joinI $ builderToByteString
246− $$ (iterHandle handle >> return isKeepAlive)
241+ $$ (iterSocket socket >> return isKeepAlive)
247 where
242 where
248 hasLength = lookup "content-length" hs /= Nothing
243 hasLength = lookup "content-length" hs /= Nothing
249 isChunked' = isChunked hv && not hasLength
244 isChunked' = isChunked hv && not hasLength
@@ -298,3 +293,16 @@ requestBodyHandle initLen =
298 if newlen <= 0
293 if newlen <= 0
299 then return ()
294 then return ()
300 else drain newlen
295 else drain newlen
296+
297+iterSocket socket =
298+ E.continue go
299+ where
300+ go E.EOF = E.yield () E.EOF
301+ go (E.Chunks cs) = liftIO (Sock.sendMany socket cs) >> E.continue go
302+
303+enumSocket len socket (E.Continue k) = do
304+ bs <- liftIO $ Sock.recv socket len
305+ if S.length bs == 0
306+ then E.continue k
307+ else k (E.Chunks [bs]) >>== enumSocket len socket
308+enumSocket _ _ step = E.returnI step
warp.cabal+1−1
@@ -15,7 +15,7 @@ Library
15 Build-Depends: base >= 3 && < 5
15 Build-Depends: base >= 3 && < 5
16 , bytestring >= 0.9 && < 0.10
16 , bytestring >= 0.9 && < 0.10
17 , wai >= 0.3.0 && < 0.4
17 , wai >= 0.3.0 && < 0.4
18− , network >= 2.2.1.5 && < 2.4
18+ , network >= 2.3 && < 2.4
19 , blaze-builder-enumerator >= 0.2 && < 0.3
19 , blaze-builder-enumerator >= 0.2 && < 0.3
20 , transformers >= 0.2 && < 0.3
20 , transformers >= 0.2 && < 0.3
21 , enumerator >= 0.4 && < 0.5
21 , enumerator >= 0.4 && < 0.5