@@ -36,6 +36,9 @@ module Network.Wai.Handler.Warp
36 , settingsTimeout
36 , settingsTimeout
37 , settingsIntercept
37 , settingsIntercept
38 , settingsManager
38 , settingsManager
41+ , runSettingsConnection
39 -- * Datatypes
42 -- * Datatypes
41 , InvalidRequest (..)
44 , InvalidRequest (..)
@@ -117,6 +120,7 @@ import qualified Control.Concurrent.MVar as MV
117import Network.Socket (withSocketsDo)
120import Network.Socket (withSocketsDo)
120#ifndef MEGA
124#ifndef MEGA
121import Data.Version (showVersion)
125import Data.Version (showVersion)
122import qualified Paths_warp
126import qualified Paths_warp
@@ -126,6 +130,26 @@ warpVersion = "0.4.6"
127warpVersion :: String
131warpVersion :: String
133+data Connection = Connection
134+ { connSendMany :: [B.ByteString] -> IO ()
135+ , connSendAll :: B.ByteString -> IO ()
136+ , connSendFile :: FilePath -> Integer -> Integer -> IO () -> IO () -- ^ offset, length
137+ , connSink :: T.Handle -> C.Sink B.ByteString IO ()
138+ , connClose :: IO ()
139+ , connSource :: T.Handle -> C.Source IO B.ByteString
142+socketConnection :: Socket -> Connection
143+socketConnection s = Connection
144+ { connSendMany = Sock.sendMany s
145+ , connSendAll = Sock.sendAll s
146+ , connSendFile = \fp off len act -> sendfile s fp (PartOfFile off len) act
147+ , connSink = flip sinkSocket s
148+ , connClose = sClose s
149+ , connSource = \th -> C.Source $ return $ sourceSocket th bytesPerRead s
129bindPort :: Int -> String -> IO Socket
153bindPort :: Int -> String -> IO Socket
130bindPort p s = do
154bindPort p s = do
131 let hints = defaultHints { addrFlags = [AI_PASSIVE
155 let hints = defaultHints { addrFlags = [AI_PASSIVE
@@ -185,32 +209,40 @@ type Port = Int
185-- 'serverPort' record.
209-- 'serverPort' record.
186runSettingsSocket :: Settings -> Socket -> Application -> IO ()
210runSettingsSocket :: Settings -> Socket -> Application -> IO ()
187runSettingsSocket set socket app = do
211runSettingsSocket set socket app = do
212+ runSettingsConnection set getter app
215+ (conn, sa) <- accept socket
216+ return (socketConnection conn, sa)
218+runSettingsConnection :: Settings -> IO (Connection, SockAddr) -> Application -> IO ()
219+runSettingsConnection set getConn app = do
188 let onE = settingsOnException set
220 let onE = settingsOnException set
189 port = settingsPort set
221 port = settingsPort set
190 tm <- maybe (T.initialize $ settingsTimeout set * 1000000) return
222 tm <- maybe (T.initialize $ settingsTimeout set * 1000000) return
191 $ settingsManager set
223 $ settingsManager set
192 forever $ do
224 forever $ do
193− (conn, sa) <- accept socket
225+ (conn, addr) <- getConn
194 _ <- forkIO $ do
226 _ <- forkIO $ do
195 th <- T.registerKillThread tm
227 th <- T.registerKillThread tm
196− serveConnection set th onE port app conn sa
228+ serveConnection set th onE port app conn addr
197 T.cancel th
229 T.cancel th
198 return ()
230 return ()
200serveConnection :: Settings
232serveConnection :: Settings
201 -> T.Handle
233 -> T.Handle
202 -> (SomeException -> IO ())
234 -> (SomeException -> IO ())
203− -> Port -> Application -> Socket -> SockAddr -> IO ()
235+ -> Port -> Application -> Connection -> SockAddr -> IO ()
204serveConnection settings th onException port app conn remoteHost' = do
236serveConnection settings th onException port app conn remoteHost' = do
207 (runResourceT serveConnection')
239 (runResourceT serveConnection')
208− (sClose conn))
240+ (connClose conn))
209 onException
241 onException
211 serveConnection' :: ResourceT IO ()
243 serveConnection' :: ResourceT IO ()
212 serveConnection' = do
244 serveConnection' = do
213− fromClient <- C.bufferSource $ sourceSocket th bytesPerRead conn
245+ fromClient <- C.bufferSource $ connSource conn th
214 env <- parseRequest port remoteHost' fromClient
246 env <- parseRequest port remoteHost' fromClient
215 case settingsIntercept settings env of
247 case settingsIntercept settings env of
216 Nothing -> do
248 Nothing -> do
@@ -371,8 +403,8 @@ hasBody s req = s /= (H.Status 204 "") && s /= H.status304 &&
371 H.statusCode s >= 200 && requestMethod req /= "HEAD"
403 H.statusCode s >= 200 && requestMethod req /= "HEAD"
373sendResponse :: T.Handle
405sendResponse :: T.Handle
374− -> Request -> Socket -> Response -> ResourceT IO Bool
406+ -> Request -> Connection -> Response -> ResourceT IO Bool
375−sendResponse th req socket r = sendResponse' r
407+sendResponse th req conn r = sendResponse' r
377 version = httpVersion req
409 version = httpVersion req
378 isPersist = checkPersist req
410 isPersist = checkPersist req
@@ -392,19 +424,13 @@ sendResponse th req socket r = sendResponse' r
392 (Nothing, Just part) -> do
424 (Nothing, Just part) -> do
393 let cl = filePartByteCount part
425 let cl = filePartByteCount part
394 return $ addClToHeaders cl
426 return $ addClToHeaders cl
395− Sock.sendMany socket $ L.toChunks $ toLazyByteString $
427+ connSendMany conn $ L.toChunks $ toLazyByteString $
396 headers version s lengthyHeaders False
428 headers version s lengthyHeaders False
398 if not (hasBody s req) then return isPersist else do
430 if not (hasBody s req) then return isPersist else do
399 case mpart of
431 case mpart of
400− Nothing -> sendfile socket fp PartOfFile {
432+ Nothing -> connSendFile conn fp 0 cl (T.tickle th)
401− rangeOffset = 0
433+ Just part -> connSendFile conn fp (filePartOffset part) (filePartByteCount part) (T.tickle th)
404− Just part -> sendfile socket fp PartOfFile {
405− rangeOffset = filePartOffset part
406− , rangeLength = filePartByteCount part
408 T.tickle th
434 T.tickle th
409 return isPersist
435 return isPersist
@@ -413,11 +439,11 @@ sendResponse th req socket r = sendResponse' r
413 sendResponse' (ResponseBuilder s hs b)
439 sendResponse' (ResponseBuilder s hs b)
414 | hasBody s req = liftIO $ do
440 | hasBody s req = liftIO $ do
415 toByteStringIO (\bs -> do
441 toByteStringIO (\bs -> do
416− Sock.sendAll socket bs
442+ connSendAll conn bs
417 T.tickle th) body
443 T.tickle th) body
418 return (isKeepAlive hs)
444 return (isKeepAlive hs)
419 | otherwise = liftIO $ do
445 | otherwise = liftIO $ do
420− Sock.sendMany socket
446+ connSendMany conn
421 $ L.toChunks
447 $ L.toChunks
422 $ toLazyByteString
448 $ toLazyByteString
423 $ headers' False
449 $ headers' False
@@ -440,7 +466,7 @@ sendResponse th req socket r = sendResponse' r
440 -- functions below. Should lessen greatly the GC burden (I hope)
466 -- functions below. Should lessen greatly the GC burden (I hope)
442 | not (hasBody s req) = do
468 | not (hasBody s req) = do
443− liftIO $ Sock.sendMany socket
469+ liftIO $ connSendMany conn
444 $ L.toChunks $ toLazyByteString
470 $ L.toChunks $ toLazyByteString
445 $ headers' False
471 $ headers' False
446 return (checkPersist req)
472 return (checkPersist req)
@@ -448,7 +474,7 @@ sendResponse th req socket r = sendResponse' r
448 let src =
474 let src =
449 CL.sourceList [headers' needsChunked'] `mappend`
475 CL.sourceList [headers' needsChunked'] `mappend`
450 (if needsChunked' then body C.$= chunk else body)
476 (if needsChunked' then body C.$= chunk else body)
451− src C.$$ builderToByteString C.=$ sinkSocket th socket
477+ src C.$$ builderToByteString C.=$ connSink conn th
452 return $ isKeepAlive hs
478 return $ isKeepAlive hs
453 needsChunked' = needsChunked hs
479 needsChunked' = needsChunked hs
454 chunk :: C.Conduit Builder IO Builder
480 chunk :: C.Conduit Builder IO Builder
@@ -516,7 +542,7 @@ data Settings = Settings
516 , settingsHost :: String -- ^ Host to bind to, or * for all. Default value: *
542 , settingsHost :: String -- ^ Host to bind to, or * for all. Default value: *
517 , settingsOnException :: SomeException -> IO () -- ^ What to do with exceptions thrown by either the application or server. Default: ignore server-generated exceptions (see 'InvalidRequest') and print application-generated applications to stderr.
543 , settingsOnException :: SomeException -> IO () -- ^ What to do with exceptions thrown by either the application or server. Default: ignore server-generated exceptions (see 'InvalidRequest') and print application-generated applications to stderr.
518 , settingsTimeout :: Int -- ^ Timeout value in seconds. Default value: 30
544 , settingsTimeout :: Int -- ^ Timeout value in seconds. Default value: 30
519− , settingsIntercept :: Request -> Maybe (C.BufferedSource IO S.ByteString -> Socket -> ResourceT IO ())
545+ , settingsIntercept :: Request -> Maybe (C.BufferedSource IO S.ByteString -> Connection -> ResourceT IO ())
520 , settingsManager :: Maybe Manager -- ^ Use an existing timeout manager instead of spawning a new one. If used, 'settingsTimeout' is ignored. Default is 'Nothing'
546 , settingsManager :: Maybe Manager -- ^ Use an existing timeout manager instead of spawning a new one. If used, 'settingsTimeout' is ignored. Default is 'Nothing'