@@ -5,30 +5,22 @@ module Network.Wai.Handler.DevelServer (run) where
6import Language.Haskell.Interpreter
6import Language.Haskell.Interpreter
7import Network.Wai
7import Network.Wai
8−import qualified System.IO
10−import Data.ByteString (ByteString)
11−import Data.ByteString.Lazy.Internal (defaultChunkSize)
12−import qualified Data.ByteString.Char8 as B
13−import qualified Data.ByteString.Lazy as L
14import qualified Data.ByteString.Lazy.Char8 as L8
9import qualified Data.ByteString.Lazy.Char8 as L8
15import Network
10import Network
16 ( listenOn, accept, sClose, PortID(PortNumber), Socket
11 ( listenOn, accept, sClose, PortID(PortNumber), Socket
17 , withSocketsDo)
12 , withSocketsDo)
18−import Control.Exception (bracket, finally, Exception, throwIO,
13+import Control.Exception (bracket, finally, Exception,
19− handle, SomeException, toException)
14+ SomeException, toException)
15+import qualified Control.Exception as E
20import System.IO (Handle, hClose)
16import System.IO (Handle, hClose)
21import Control.Concurrent (forkIO, threadDelay)
17import Control.Concurrent (forkIO, threadDelay)
22−import Control.Monad (unless, forM)
18+import Control.Monad (forM)
23−import Data.Maybe (isJust, fromJust, fromMaybe)
25−import Data.Typeable (Typeable)
27−import Network.Socket.SendFile
28−import Control.Arrow (first)
29import qualified Control.Concurrent.MVar as M
20import qualified Control.Concurrent.MVar as M
30import qualified Control.Concurrent.Chan as C
21import qualified Control.Concurrent.Chan as C
31import System.Directory (getModificationTime)
22import System.Directory (getModificationTime)
23+import Network.Wai.Handler.SimpleServer (parseRequest, sendResponse)
33type FunctionName = String
25type FunctionName = String
@@ -82,7 +74,7 @@ fillApp modu func mqueue =
82 return (Just $ toException err, [])
74 return (Just $ toException err, [])
83 Right (app, files) -> do
75 Right (app, files) -> do
84 putStrLn "Interpreting success, new app loaded"
76 putStrLn "Interpreting success, new app loaded"
85− handle onInitErr $ do
77+ E.handle onInitErr $ do
86 swapApp app mqueue
78 swapApp app mqueue
87 files' <- forM files $ \f -> do
79 files' <- forM files $ \f -> do
88 t <- getModificationTime f
80 t <- getModificationTime f
@@ -96,6 +88,7 @@ fillApp modu func mqueue =
96loadingApp' :: Maybe SomeException -> M.MVar Queue -> IO ()
88loadingApp' :: Maybe SomeException -> M.MVar Queue -> IO ()
97loadingApp' err mqueue = swapApp (loadingApp err) mqueue
89loadingApp' err mqueue = swapApp (loadingApp err) mqueue
91+swapApp :: Handler -> M.MVar Queue -> IO ()
99swapApp app mqueue = do
92swapApp app mqueue = do
100 oldqueue <- M.takeMVar mqueue
93 oldqueue <- M.takeMVar mqueue
101 C.writeChan oldqueue Nothing
94 C.writeChan oldqueue Nothing
@@ -114,7 +107,6 @@ loadingApp err f =
114 toMessage (Just err') = "Error loading code: " ++ show err'
107 toMessage (Just err') = "Error loading code: " ++ show err'
116type Handler = (Application -> IO ()) -> IO ()
109type Handler = (Application -> IO ()) -> IO ()
117−type MHandler = M.MVar Application
119theapp :: String -> String -> IO (Either InterpreterError (Handler, [FilePath]))
111theapp :: String -> String -> IO (Either InterpreterError (Handler, [FilePath]))
120theapp modu func =
112theapp modu func =
@@ -147,142 +139,10 @@ type Queue = C.Chan (Maybe (Request, Response -> IO ()))
148serveConnection :: Port -> M.MVar Queue -> Handle -> String -> IO ()
140serveConnection :: Port -> M.MVar Queue -> Handle -> String -> IO ()
149serveConnection port mqueue conn remoteHost' = do
141serveConnection port mqueue conn remoteHost' = do
150− env <- hParseRequest port conn remoteHost'
142+ env <- parseRequest port conn remoteHost'
151 let onRes res =
143 let onRes res =
153 (sendResponse (httpVersion env) conn res)
145 (sendResponse (httpVersion env) conn res)
154 (hClose conn)
146 (hClose conn)
155 queue <- M.readMVar mqueue
147 queue <- M.readMVar mqueue
156 C.writeChan queue $ Just (env, onRes)
148 C.writeChan queue $ Just (env, onRes)
158−hParseRequest :: Port -> Handle -> String -> IO Request
159−hParseRequest port conn remoteHost' = do
160− headers' <- takeUntilBlank conn id
161− parseRequest port headers' conn remoteHost'
163−takeUntilBlank :: Handle
164− -> ([ByteString] -> [ByteString])
166−takeUntilBlank h front = do
167− l <- stripCR `fmap` B.hGetLine h
169− then return $ front []
170− else takeUntilBlank h $ front . (:) l
172−stripCR :: ByteString -> ByteString
175− | B.last bs == '\r' = B.init bs
178−data InvalidRequest =
179− NotEnoughLines [String]
181− | BadFirstLine String
183− deriving (Show, Typeable)
184−instance Exception InvalidRequest
186−-- | Parse a set of header lines and body into a 'Request'.
192−parseRequest port lines' handle remoteHost' = do
194− (_:_:_) -> return ()
195− _ -> throwIO $ NotEnoughLines $ map B.unpack lines'
196− (method, rpath', gets, httpversion) <- parseFirst $ head lines'
197− let rpath = '/' : case B.unpack rpath' of
199− _ -> B.unpack rpath'
200− let heads = map (first mkCIByteString . parseHeaderNoAttr) $ tail lines'
201− let host' = lookup "Host" heads
202− unless (isJust host') $ throwIO HostNotIncluded
203− let host = fromJust host'
204− let len = fromMaybe 0 $ do
205− bs <- lookup "Content-Length" heads
206− let str = B.unpack bs
210− let (serverName', _) = B.break (== ':') host
212− { requestMethod = method
213− , httpVersion = httpversion
214− , pathInfo = B.pack rpath
215− , queryString = gets
216− , serverName = serverName'
218− , requestHeaders = heads
220− , requestBody = requestBodyHandle handle len
221− , errorHandler = System.IO.hPutStr System.IO.stderr
222− , remoteHost = B.pack remoteHost'
225−parseFirst :: ByteString
226− -> IO (ByteString, ByteString, ByteString, HttpVersion)
228− let pieces = B.words s
229− (method, query, http') <-
231− [x, y, z] -> return (x, y, z)
232− _ -> throwIO $ BadFirstLine $ B.unpack s
233− let (hfirst, hsecond) = B.splitAt 5 http'
234− unless (hfirst == B.pack "HTTP/") $ throwIO NonHttp
235− let (rpath, qstring) = B.break (== '?') query
236− return (method, rpath, qstring, hsecond)
238−sendResponse :: HttpVersion -> Handle -> Response -> IO ()
239−sendResponse httpversion h res = do
240− B.hPut h $ B.pack "HTTP/"
241− B.hPut h $ httpversion
242− B.hPut h $ B.pack " "
243− B.hPut h $ B.pack $ show $ statusCode $ status res
244− B.hPut h $ B.pack " "
245− B.hPut h $ statusMessage $ status res
246− B.hPut h $ B.pack "\r\n"
247− mapM_ putHeader $ responseHeaders res
248− B.hPut h $ B.pack "\r\n"
249− case responseBody res of
250− ResponseFile fp -> unsafeSendFile h fp
251− ResponseEnumerator (Enumerator enum) -> enum myPut h >> return ()
252− ResponseLBS lbs -> L.hPut h lbs
257− putHeader (x, y) = do
258− B.hPut h $ ciOriginal x
259− B.hPut h $ B.pack ": "
261− B.hPut h $ B.pack "\r\n"
263−parseHeaderNoAttr :: ByteString -> (ByteString, ByteString)
264−parseHeaderNoAttr s =
265− let (k, rest) = B.span (/= ':') s
266− rest' = if not (B.null rest) &&
267− B.head rest == ':' &&
268− not (B.null $ B.tail rest) &&
269− B.head (B.tail rest) == ' '
274−requestBodyHandle :: Handle -> Int -> Source
275−requestBodyHandle h =
278− go i = Just `fmap` B.hGet h (min i defaultChunkSize)
280−requestBodyFunc :: (Int -> IO (Maybe B.ByteString)) -> Int -> Source
281−requestBodyFunc _ 0 = Source $ return Nothing
282−requestBodyFunc h len = Source $ do
285− Nothing -> return Nothing
287− let newLen = len - B.length bs
288− return $ Just (bs, requestBodyFunc h $ max 0 newLen)